voc-validation-gen.js 4.0 KB

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