db-export.js 2.3 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869
  1. #!/usr/bin/env node
  2. 'use strict';
  3. // 导出店铺运营数据为 JSON(用于 SQLite ↔ PostgreSQL 搬迁)
  4. // 用法:
  5. // node scripts/db-export.js # 导出到 ops-export.json(不含原件)
  6. // node scripts/db-export.js out.json --with-files # 连原件一起导出(base64)
  7. //
  8. // 说明:不带 --with-files 时只导出元数据与数据行;导入端要求每个上传都有原件,
  9. // 因此完整搬迁务必加 --with-files。
  10. const fs = require('fs');
  11. const path = require('path');
  12. const { createContext } = require('../server/context.js');
  13. const { createOpsStore } = require('../ops-store.js');
  14. (async () => {
  15. const args = process.argv.slice(2);
  16. const withFiles = args.includes('--with-files');
  17. const outPath = path.resolve(args.find((a) => !a.startsWith('--')) || 'ops-export.json');
  18. const ctx = createContext();
  19. const store = createOpsStore(ctx.config);
  20. const health = await store.health();
  21. if (!health.ok) {
  22. console.error(`读取失败:${health.reason || 'storage-unreachable'} ${health.detail || ''}`);
  23. process.exit(1);
  24. }
  25. const uploads = await store.listUploads();
  26. if (withFiles) {
  27. for (const upload of uploads) {
  28. const record = await store.getContent(upload.id);
  29. upload.contentBase64 = record ? record.content.toString('base64') : '';
  30. }
  31. }
  32. const rows = await store.queryRows({});
  33. const platformRows = await store.queryPlatformRows({});
  34. const summary = await store.summary();
  35. const payload = {
  36. version: 1,
  37. exportedAt: new Date().toISOString(),
  38. driver: store.kind,
  39. withFiles,
  40. uploads,
  41. shops: summary.shops,
  42. platformRows,
  43. rows: rows.map((r) => ({
  44. date: r.date,
  45. shopKey: r.shopKey,
  46. uploadId: r.uploadId,
  47. shopId: r.shopId,
  48. shopName: r.shopName,
  49. school: r.school,
  50. status: r.status,
  51. order_cnt: r.order_cnt,
  52. revenue: r.revenue,
  53. exposure: r.exposure,
  54. click_rate: r.click_rate,
  55. raw: r.raw,
  56. quality: r.quality,
  57. })),
  58. };
  59. fs.writeFileSync(outPath, JSON.stringify(payload, null, 2));
  60. console.log(`已导出 ${uploads.length} 次上传、${rows.length} 行人工数据、${platformRows.length} 行平台明细 → ${outPath}${withFiles ? '(含原件)' : '(不含原件)'}`);
  61. await store.close();
  62. })();