gen.js 3.7 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182
  1. #!/usr/bin/env node
  2. // ==============================================================================
  3. // 江中肝纯片 · VOC 验证报告生成器
  4. // 用法:node scripts/tools/gen-voc-validation.js
  5. // 输出:reports/jiangzhong-liver-voc-validation-report.html
  6. // 依赖:
  7. // - data/jiangzhong-liver-voc.json (已有 VOC 基础数据)
  8. // - data/jiangzhong-liver-voc-extended.json (Batch 2 多平台扩采数据,可选)
  9. // - scripts/tools/voc-analyze.js (分析层)
  10. // - scripts/tools/voc-validation-render.js (渲染层,分批次扩展)
  11. // ==============================================================================
  12. const fs = require('fs');
  13. const path = require('path');
  14. const { analyze } = require('./voc-analyze.js');
  15. const { buildHTML, CHAPTERS, CHAPTER_SUBSECTIONS } = require('./voc-validation-render.js');
  16. const INPUT_BASE = path.join('data', 'jiangzhong-liver-voc.json');
  17. const INPUT_EXTENDED = path.join('data', 'jiangzhong-liver-voc-extended.json');
  18. const OUTPUT = path.join('reports', 'jiangzhong-liver-voc-validation-report.html');
  19. function mtimeStr(fp) {
  20. try {
  21. const d = new Date(fs.statSync(fp).mtime);
  22. return d.getFullYear() + '-' + String(d.getMonth() + 1).padStart(2, '0') + '-' + String(d.getDate()).padStart(2, '0');
  23. } catch { return '—'; }
  24. }
  25. // 计算实际 slide 总数(2 + 各章节 1 divider + N 子章)
  26. const actualTotal = 2 + CHAPTERS.map((c) => 1 + (CHAPTER_SUBSECTIONS[c.key] || []).length).reduce((a, b) => a + b, 0);
  27. console.log(`╔═══════════════════════════════════════════════════════════╗`);
  28. console.log(`║ 江中肝纯片 · VOC 验证报告生成(10亿×4渠道×5模块) ║`);
  29. console.log(`╚═══════════════════════════════════════════════════════════╝`);
  30. console.log(` 输入基础: ${INPUT_BASE}`);
  31. console.log(` 输入扩展: ${fs.existsSync(INPUT_EXTENDED) ? INPUT_EXTENDED : '(未生成,可跳过)'}`);
  32. console.log(` 输出: ${OUTPUT}`);
  33. console.log(` 章节: ${CHAPTERS.length} 大维度(方法论/包装/定价/定位话术/场景/竞品/落地)`);
  34. console.log(` slide 总数: ${actualTotal}`);
  35. if (!fs.existsSync(INPUT_BASE)) {
  36. console.error(`\n ❌ 基础 VOC 数据不存在:${INPUT_BASE}`);
  37. process.exit(1);
  38. }
  39. const t0 = Date.now();
  40. const vocBase = JSON.parse(fs.readFileSync(INPUT_BASE, 'utf-8'));
  41. // 合并扩展数据(Batch 2 采集后会生成)
  42. let vocExtended = null;
  43. if (fs.existsSync(INPUT_EXTENDED)) {
  44. try {
  45. vocExtended = JSON.parse(fs.readFileSync(INPUT_EXTENDED, 'utf-8'));
  46. console.log(` ✓ 已合并 VOC 扩展数据`);
  47. } catch (e) {
  48. console.warn(` ⚠ VOC 扩展数据解析失败:${e.message}`);
  49. }
  50. }
  51. const A = analyze(vocBase);
  52. // 附加扩展数据引用,便于 Ch2-7 渲染器使用
  53. A.extended = vocExtended;
  54. const collectedAt = mtimeStr(INPUT_BASE);
  55. const html = buildHTML({ product: '江中肝纯片', A, collectedAt });
  56. fs.mkdirSync(path.dirname(OUTPUT), { recursive: true });
  57. fs.writeFileSync(OUTPUT, html, 'utf-8');
  58. const size = fs.statSync(OUTPUT).size;
  59. const ms = Date.now() - t0;
  60. console.log(`\n ✅ 生成完成`);
  61. console.log(` 路径: ${OUTPUT}`);
  62. console.log(` 大小: ${(size / 1024).toFixed(1)} KB`);
  63. console.log(` 耗时: ${ms} ms`);
  64. console.log(` VOC 基础: ${A.metrics?.notes || '?'} 笔记 / ${A.metrics?.sampleComments || '?'} 评论 / Amazon ${A.amazon?.total || 0} 款`);
  65. if (vocExtended) {
  66. console.log(` VOC 扩展: 多平台数据已合并`);
  67. }
  68. console.log('');