db-export.js 2.2 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667
  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 summary = await store.summary();
  34. const payload = {
  35. version: 1,
  36. exportedAt: new Date().toISOString(),
  37. driver: store.kind,
  38. withFiles,
  39. uploads,
  40. shops: summary.shops,
  41. rows: rows.map((r) => ({
  42. date: r.date,
  43. shopKey: r.shopKey,
  44. uploadId: r.uploadId,
  45. shopId: r.shopId,
  46. shopName: r.shopName,
  47. school: r.school,
  48. status: r.status,
  49. order_cnt: r.order_cnt,
  50. revenue: r.revenue,
  51. exposure: r.exposure,
  52. click_rate: r.click_rate,
  53. raw: r.raw,
  54. quality: r.quality,
  55. })),
  56. };
  57. fs.writeFileSync(outPath, JSON.stringify(payload, null, 2));
  58. console.log(`已导出 ${uploads.length} 次上传、${rows.length} 行 → ${outPath}${withFiles ? '(含原件)' : '(不含原件)'}`);
  59. await store.close();
  60. })();