/**
* 马记永结案报告 · HTML + MD 渲染层
*/
const fs = require('fs');
const path = require('path');
const ROOT = path.resolve(__dirname, '..');
const OUT_HTML = path.join(ROOT, 'majiyong-closing-report.html');
const OUT_MD = path.join(ROOT, 'majiyong-closing-report.md');
module.exports = function render(M) {
const { items, kolItems, kocItems, summary, tierStats, typeStats, topRegions, byTier,
allComments, noiseComments, noiseRate, topicHits, TOPICS, wordcloudComments, cloudBrand, cloudActivity, cloudCategory,
PROS, CONS, RECS, OPPORTUNITIES, TOP5,
fmt, pct, esc, num } = M;
// ---------- 明细行 (注: 篇数大,做 KOL 全表 + KOC Top 50) ----------
const detailKolRows = [...kolItems].sort((a, b) => num(b.engagementSubtotal || b.engagement) - num(a.engagementSubtotal || a.engagement)).map((r, i) => `
| ${i + 1} |
${esc(r.name)} |
${esc(r.tier || '-')} |
${esc(r.contentType || '-')} |
${esc(String(r.region || '-').slice(0, 3))} |
${(num(r.followersWan) * 10000).toLocaleString('zh-CN')} |
¥${fmt(r.priceOrder)} |
${fmt(r.impressionsSubtotal || r.impressions)} |
${fmt(r.likes)} |
${fmt(r.comments)} |
${fmt(r.engagementSubtotal || r.engagement)} |
${r.noteId ? '已解析' : '短链失效'} |
`).join('');
const detailKocTop = [...kocItems].sort((a, b) => num(b.engagementSubtotal || b.engagement) - num(a.engagementSubtotal || a.engagement)).slice(0, 50).map((r, i) => `
| ${i + 1} |
${esc(r.name)} |
${esc(r.tier || 'KOC')} |
${esc(r.contentType || '-')} |
${esc(String(r.region || '-').slice(0, 3))} |
${(num(r.followersWan) * 10000).toLocaleString('zh-CN')} |
${fmt(r.likes)} |
${fmt(r.comments)} |
${fmt(r.engagementSubtotal || r.engagement)} |
${(r._comments && r._comments.length) || 0} |
`).join('');
const topicRows = TOPICS.map((t) => {
const hits = topicHits[t.tag];
const sample = hits.sort((a, b) => b.like - a.like)[0];
return `
| ${esc(t.tag)} |
${hits.length} |
${(hits.length / Math.max(allComments.length, 1) * 100).toFixed(1)}% |
${sample ? `[${esc(sample.koc)}@${esc(sample.ip || '-')}] ${esc(sample.content.replace(/\n/g, ' ').slice(0, 80))}${sample.content.length > 80 ? '…' : ''}` : '-'} |
`;
}).join('');
// 真实评论筛选 v4: 7 层过滤 + 产品强相关硬约束 + 主题多样性轮询
const NOISE_RE = /互赞|互助|加油.*互|可以.*点赞|点赞.*返|蹲.*搭子|互粉/;
const OFFTOPIC_RE = /眼影|口红|睫毛|化妆|包包|分装|借我|借用|手能借|护肤|穿搭|开箱|美术生|画的|画得|画风|笔法|插画|手工|盒子.*像/;
const PROMO_RE = /推广|求关注|求互|私信|加微|商务合作|接广告|涨粉/;
const SHILL_RE = /宝子|亲测|无套路|必.{0,2}冲|绝绝子|拉满|薅羊毛|薅得|放心冲|闭眼冲|爆棚|颜值直接|快冲|姐妹.{0,3}冲|实力宠粉|超(级|绝|香)|爆款|出圈|0元购|0元薅|真香|yyds|YYDS|惊艳到|跺脚/;
const META_KOL_RE = /博主|up主|UP主|作者|接广|涨粉|掉粉|播放|粉丝.*多|9000.*掉.*\d|完啦R/;
const EMOJI_HEAVY_RE = /(🥬|🥢|🌿|🤩|💯|🔥|💥|✅|💦|📸|🥳|🥞|🛒|🍣|🎉|✨|💚|❗|‼️)/g;
const stripDeco = (s) => s.replace(/\[[^\]]+\]/g, '').replace(/[@#][^\s@#]+/g, '').replace(/[^\u4e00-\u9fa5a-zA-Z0-9]/g, '');
// 产品强相关词典: 9 主题,评论必须命中至少 1 个才算 VOC 有效
const PRODUCT_TOPICS = {
'口味体验': /好吃|难吃|味道|香|辣|咸|淡|腻|清爽|清甜|甜杆|西兰|拌|脆|嫩|爽口|入味/,
'产品创新': /新菜|新鲜|鲜菜|凉拌|凉菜|拌菜|不过夜|当天|现做|现拌|出.{0,3}菜|上.{0,3}菜/,
'门店开店': /开店|分店|开到|什么时候.*开|建议.*开|我们这.*没|这边.*没|本地.*有|哪里有店|店在哪/,
'健康营养': /健康|减脂|低脂|营养|蔬菜|清淡|0负担|低卡/,
'价格性价比': /便宜|划算|实惠|贵|价格|性价比|套餐|¥/,
'活动机制': /换菜|换券|换免费|带.*换|带.*能换|花.*换|报纸|活动.*介绍|活动.*在哪|怎么参与|怎么换|领券|领劵/,
'场景外带': /外带|打包|下酒|带回家|带走|外卖|食堂|带娃|带朋友/,
'复购推荐': /常去|经常.*吃|经常.*去|每次|又来|回头|推荐|尝尝|去试|去尝/,
'品牌情感': /马记永.*好|马记永.*爱|爱.*马记永|喜欢.*马记永|马记永.*绝/,
};
const PRODUCT_RE = new RegExp(Object.values(PRODUCT_TOPICS).map((r) => r.source).join('|'));
const realComments = allComments.filter((c) => {
const txt = c.content || '';
if (c.nick && c.koc && c.nick === c.koc) return false; // 博主自评
// @ 别人开头的回复 = 缺乏上下文 (除非内容本身够长够独立)
if (/^\s*@/.test(txt) && stripDeco(txt.replace(/^\s*@[^\s@#]+/, '')).length < 20) return false;
if (NOISE_RE.test(txt) || OFFTOPIC_RE.test(txt) || PROMO_RE.test(txt) || SHILL_RE.test(txt) || META_KOL_RE.test(txt)) return false;
const emojis = txt.match(EMOJI_HEAVY_RE) || [];
if (emojis.length >= 3) return false;
const core = stripDeco(txt);
if (core.length < 6) return false;
if (core.length > 80 && emojis.length >= 1) return false;
if (core.length > 120) return false;
if (!PRODUCT_RE.test(txt)) return false; // 硬约束: 必须产品相关
return true;
});
// 按主题分桶 (只归首个匹配主题)
const buckets = {};
for (const tag of Object.keys(PRODUCT_TOPICS)) buckets[tag] = [];
for (const c of realComments) {
for (const [tag, re] of Object.entries(PRODUCT_TOPICS)) {
if (re.test(c.content)) { buckets[tag].push(c); break; }
}
}
// 排序: 点赞降序;同点赞按"信息量"降序 (产品关键词命中数 + 内容长度)
const informationScore = (c) => {
let hits = 0;
for (const re of Object.values(PRODUCT_TOPICS)) if (re.test(c.content)) hits++;
const core = stripDeco(c.content);
return hits * 20 + core.length;
};
for (const tag of Object.keys(buckets)) {
buckets[tag].sort((a, b) => (b.like - a.like) || (informationScore(b) - informationScore(a)));
}
// 主题轮询: 先每主题 Top 2,补到 16
const topPicks = [];
const queues = Object.entries(buckets).map(([tag, list]) => ({ tag, list, idx: 0 }));
for (let round = 0; round < 4 && topPicks.length < 16; round++) {
for (const q of queues) {
if (topPicks.length >= 16) break;
if (q.idx < q.list.length && q.idx < round + 1) {
topPicks.push({ topic: q.tag, c: q.list[q.idx] });
q.idx++;
}
}
}
realComments.length = 0;
realComments.push(...topPicks.map((p) => p.c));
const realCommentList = topPicks.map(({ topic, c }) => `
${esc(topic)} ${esc(c.koc)} ${esc(c.ip || '')}
${esc(c.content.replace(/\n/g, ' '))}
`).join('');
const HTML = `
马记永·四月「新鲜菜」KOL/KOC 宣发结案报告
MAJIYONG · 兰州牛肉面 · 新鲜菜项目 · 2026.04
马记永「新鲜菜」
KOL/KOC 宣发
结案报告
基于 ${summary.total} 篇报备笔记 (KOL ${summary.kolCount} + KOC ${summary.kocCount}) + ${allComments.length} 条公开评论真实抓取,完整复盘投放规模、内容形态、互动效率、评论 VOC 与品牌词渗透,并给出下一轮可执行的优化建议。
Section 01
数据汇总 — 投放规模与产出效率
本期 ${summary.total} 篇笔记 (KOL ${summary.kolCount} + KOC ${summary.kocCount}),KOL 投入预算 ¥${fmt(summary.kolSpend)},总曝光 ${fmt(summary.totalImpressions)} / 总互动 ${fmt(summary.totalEngagement)},整体互动率 ${pct(summary.engagementRate)},综合 CPE ¥${summary.cpe.toFixed(2)}。
总报备笔记
${summary.total}篇
KOL ${summary.kolCount} · KOC ${summary.kocCount}
KOL 总预算
¥${fmt(summary.kolSpend)}
CPM ¥${summary.cpm.toFixed(2)}
总曝光
${fmt(summary.totalImpressions)}
大盘心智渗透
总互动
${fmt(summary.totalEngagement)}
CPE ¥${summary.cpe.toFixed(2)}
点赞 / 收藏
${fmt(summary.totalLikes)} / ${fmt(summary.totalCollects)}
收藏率 ${(summary.totalCollects/Math.max(summary.totalLikes,1)*100).toFixed(1)}%
互动率
${summary.engagementRate.toFixed(2)}%
行业基准 3-5%
真实评论抓取
${fmt(allComments.length)}/ ${summary.totalXlsxComments} xlsx
含子评论,已去重
笔记解析
${summary.resolved}/${summary.total}
短链失效 ${summary.failed} 篇
按量级分层产出
内容形态产出对比
Section 02
数据明细 — KOL 全量 + KOC Top 50
KOL 合作表 ${summary.kolCount} 篇全量展示,KOC 合作表 ${summary.kocCount} 篇按互动降序取 Top 50 (完整明细见 \`majiyong-closing-report.md\`)。
KOL 全量 (${summary.kolCount} 篇)
KOC Top 50 (按互动降序)
Section 03
话题数据 — 评论 VOC 主题分布
基于 ${allComments.length} 条真实评论 (含 ${noiseComments.length} 条互助互赞噪声 = ${noiseRate.toFixed(2)}%),按 ${TOPICS.length} 个主题进行规则化标注。**新鲜菜主题命中 ${topicHits['新菜/新鲜菜/产品创新'].length} 条,核心心智渗透成功**。
真实评论原声 (Top 16)
产品强相关 · 多主题轮询 · 已剔除噪声/托式营销/博主自评,展示具有产品对话价值的真实评论。
${realCommentList}
Section 04
品牌相关词存量 — 心智渗透盘点
从 ${allComments.length} 条评论文本提取,显示品牌词、活动词、品类词在用户对话中的实际渗透情况。
① 品牌词
马记永 / 兰州牛肉面 / 马记永兰州 等核心品牌词
② 活动相关词
新鲜菜 / 春日新品 / 联名 / 试吃 等本期投放活动词
③ 品类词占位
兰州拉面 / 西北菜 / 凉拌菜 等品类心智词
关键洞察
✓ 品牌词渗透稳定
"马记永"作为品牌词在评论中高频自然出现,说明品牌已形成稳定心智占位,基础好。
✓ 活动词"新鲜菜"成功
"新鲜菜"在评论中超越价格主题,本轮"新鲜菜"心智宣发核心目标达成,继续放大可形成长期资产。
⚠ 竞品词出现
"陈香贵 / 张拉拉"等竞品在评论中被自然提及,需要建立心智份额对照基线持续监测。
Section 05
本次宣发的优点、不足 & 后续投放建议
✓ 优点
${PROS.map((p) => `
`).join('')}
✗ 不足
${CONS.map((c) => `
`).join('')}
→ 后续投放建议
${RECS.map((r) => `
`).join('')}
下一轮预算建议分配
头/腰部 KOL
35%
2~3 位创意/跨界 KOL,延续高杠杆爆款方法论
差异化探店 KOC
35%
5 赛道分发 (减脂/早午餐/家庭/一人食/商务),各 15-20 篇
二创 IP 池 + 在地达人
15%
沉淀长期 IP 资产 + 月度打卡官
薯条 + 评论运营 + 数据
15%
Top 5% 加投 + SOP + 风险词预警 + 求开店承接
Section 06 · ★ 重中之重
新机会挖掘 — 从 ${allComments.length} 条评论逆向反推 ${OPPORTUNITIES.length} 个新机会
**结案报告告诉老板"过去花得值不值",本节告诉团队"下一步该怎么花得更值"。** 把每一条评论当成一次市场对话逐字解读,从微观信号反推产品/内容/人群/投放/数据 5 大象限的机会缺口。完整逐条解读见配套文档 majiyong-new-opportunities.md。
⭐ Top 5 P0 优先级 (下周可启动)
${TOP5.map((id, idx) => {
const o = OPPORTUNITIES.find((x) => x.id === id);
return `
#${idx + 1}
${o.id} · ${o.p}
${esc(o.t)}
来源信号: ${esc(o.signal)}
${esc(o.action)}
`;
}).join('')}
完整 ${OPPORTUNITIES.length} 个新机会按 5 大象限分独立章节深度展开,见下方 Section 06.A ~ 06.E。
${(() => {
const QUADRANTS = [
{ letter: 'A', cat: 'A · 产品卖点', color: 'amber', tagClass: '', accent: 'card-accent', desc: '从用户评论里反推的产品/SKU 级机会 — 把"已经被用户说出口"的需求变成可上架的菜单/套餐/卖点。' },
{ letter: 'B', cat: 'B · 内容形态', color: 'green', tagClass: 'green', accent: 'card-green', desc: '内容打法升级 — 同样的预算,如何用更高 ROI 的内容形态,把好的东西讲得更让人记得住。' },
{ letter: 'C', cat: 'C · 人群渠道', color: 'purple', tagClass: 'purple', accent: '', desc: '人群和渠道的二八优化 — 让每一分预算都投在"会真去吃"的人身上,而不是"看了没法吃"的远方观众。' },
{ letter: 'D', cat: 'D · 投放机制', color: 'blue', tagClass: 'blue', accent: 'card-blue', desc: '投放过程的机制化 — 把"凭感觉发笔记"变成"算法驱动止损/加投/预警",每一轮都比上一轮更聪明。' },
{ letter: 'E', cat: 'E · 数据机制', color: 'rose', tagClass: 'rose', accent: 'card-rose', desc: '长效数据资产 — 主题词典、竞品对照,这些是可以持续复用 10 轮投放的底层基建。' },
];
return QUADRANTS.map((q) => {
const list = OPPORTUNITIES.filter((o) => o.cat === q.cat);
return `
Section 06.${q.letter} · ${q.cat}
${esc(q.cat)} ${list.length} 个新机会
${esc(q.desc)}
${list.map((o, idx) => `
${o.id}
${o.id}
${o.p}
${esc(o.t)}
📍 信号 / Signal
${esc(o.signal)}
🎯 行动 / Action
${esc(o.action)}
� 预期 / Outcome
${esc(o.outcome || '-')}
💡 为什么要做 / Why
${esc(o.why || '-')}
${Array.isArray(o.how) && o.how.length ? `
🛠️ 如何执行 / How (${o.how.length} 步)
${o.how.map((step) => `- ${esc(step)}
`).join('')}
` : ''}
`).join('')}
`;
}).join('');
})()}
马记永·四月「新鲜菜」KOL/KOC 宣发结案报告 · 基于 ${summary.total} 篇笔记 + ${allComments.length} 条评论真实数据生成
Generated ${new Date().toISOString().slice(0,19).replace('T',' ')}
`;
fs.writeFileSync(OUT_HTML, HTML, 'utf8');
console.log('✓ HTML:', OUT_HTML, Math.round(HTML.length / 1024) + 'KB');
// ---------- MD ----------
const md = [];
const W = (s) => md.push(s);
W('# 马记永 · 四月「新鲜菜」KOL/KOC 宣发结案报告');
W('');
W(`> **投放期间**: 2026-04 · **生成时间**: ${new Date().toISOString().slice(0, 19).replace('T', ' ')} `);
W(`> **数据范围**: ${summary.total} 篇报备笔记 (KOL ${summary.kolCount} + KOC ${summary.kocCount}) + ${allComments.length} 条公开评论 (覆盖 ${items.filter((x) => (x._comments || []).length > 0).length}/${summary.total} 篇)`);
W('');
W('---');
W('');
W('## 一、数据汇总');
W('');
W('| 指标 | 数值 | 备注 |');
W('|---|---:|---|');
W(`| 总报备笔记 | ${summary.total} | KOL ${summary.kolCount} + KOC ${summary.kocCount} |`);
W(`| KOL 总预算 | ¥${fmt(summary.kolSpend)} | CPM ¥${summary.cpm.toFixed(2)} |`);
W(`| 总曝光 | ${fmt(summary.totalImpressions)} | 大盘心智渗透 |`);
W(`| 总互动 | ${fmt(summary.totalEngagement)} | CPE ¥${summary.cpe.toFixed(2)} |`);
W(`| 点赞 / 收藏 / 评论 (xlsx) | ${fmt(summary.totalLikes)} / ${fmt(summary.totalCollects)} / ${fmt(summary.totalXlsxComments)} | - |`);
W(`| 整体互动率 | ${pct(summary.engagementRate)} | 行业基准 3-5% |`);
W(`| 笔记解析成功 | ${summary.resolved}/${summary.total} | 短链失效 ${summary.failed} 篇 |`);
W(`| 真实评论抓取 | ${allComments.length} 条 | 噪声率 ${noiseRate.toFixed(2)}% |`);
W('');
W('### 按量级分层产出');
W('');
W('| 量级 | 篇数 | 总曝光 | 总互动 | 平均互动 | 互动率 | 投入 | CPE |');
W('|---|---:|---:|---:|---:|---:|---:|---:|');
for (const t of tierStats) {
W(`| ${t.tier} | ${t.count} | ${fmt(t.imp)} | ${fmt(t.eng)} | ${t.avgEng} | ${t.rate.toFixed(2)}% | ¥${fmt(t.spend)} | ${t.cpe === '-' ? '-' : '¥' + t.cpe} |`);
}
W('');
W('### 按内容形态产出 (Top 12)');
W('');
W('| 内容形态 | 篇数 | 总曝光 | 总互动 | 平均互动 | 互动率 |');
W('|---|---:|---:|---:|---:|---:|');
for (const t of typeStats.slice(0, 12)) {
W(`| ${t.type} | ${t.count} | ${fmt(t.imp)} | ${fmt(t.eng)} | ${t.avgEng} | ${t.rate.toFixed(2)}% |`);
}
W('');
W('### 区域分布 Top 12');
W('');
W('| 区域 | 篇数 | 总互动 |');
W('|---|---:|---:|');
for (const r of topRegions) W(`| ${r.region} | ${r.count} | ${fmt(r.eng)} |`);
W('');
W('---');
W('');
W(`## 二、数据明细 (KOL ${summary.kolCount} 全量 + KOC Top 50)`);
W('');
W('### 2.1 KOL 全量');
W('');
W('| # | KOL | 量级 | 内容形态 | 区域 | 粉丝 | 单价 | 曝光 | 点赞 | 评论 | 互动 |');
W('|---:|---|---|---|---|---:|---:|---:|---:|---:|---:|');
const kolSorted = [...kolItems].sort((a, b) => num(b.engagementSubtotal || b.engagement) - num(a.engagementSubtotal || a.engagement));
for (let i = 0; i < kolSorted.length; i++) {
const r = kolSorted[i];
W(`| ${i + 1} | ${r.name} | ${r.tier || '-'} | ${r.contentType || '-'} | ${String(r.region || '-').slice(0, 3)} | ${(num(r.followersWan) * 10000).toLocaleString('zh-CN')} | ¥${fmt(r.priceOrder)} | ${fmt(r.impressionsSubtotal || r.impressions)} | ${fmt(r.likes)} | ${fmt(r.comments)} | ${fmt(r.engagementSubtotal || r.engagement)} |`);
}
W('');
W('### 2.2 KOC Top 50');
W('');
W('| # | KOC | 分层 | 内容形态 | 区域 | 粉丝 | 点赞 | 评论 | 互动 | 真实评论 |');
W('|---:|---|---|---|---|---:|---:|---:|---:|---:|');
const kocTop = [...kocItems].sort((a, b) => num(b.engagementSubtotal || b.engagement) - num(a.engagementSubtotal || a.engagement)).slice(0, 50);
for (let i = 0; i < kocTop.length; i++) {
const r = kocTop[i];
W(`| ${i + 1} | ${r.name} | ${r.tier || 'KOC'} | ${r.contentType || '-'} | ${String(r.region || '-').slice(0, 3)} | ${(num(r.followersWan) * 10000).toLocaleString('zh-CN')} | ${fmt(r.likes)} | ${fmt(r.comments)} | ${fmt(r.engagementSubtotal || r.engagement)} | ${(r._comments && r._comments.length) || 0} |`);
}
W('');
W('---');
W('');
W('## 三、话题数据 — 评论 VOC 主题分布');
W('');
W(`基于 **${allComments.length}** 条真实评论 (含 ${noiseComments.length} 条噪声 = ${noiseRate.toFixed(2)}%) 的规则化主题命中:`);
W('');
W('| 主题 | 命中 | 占比 | 典型样本 |');
W('|---|---:|---:|---|');
for (const t of TOPICS) {
const hits = topicHits[t.tag];
const sample = hits.sort((a, b) => b.like - a.like)[0];
const sampleText = sample ? `[${sample.koc}@${sample.ip || '-'}] ${sample.content.replace(/\n/g, ' ').replace(/\|/g, '\\|').slice(0, 80)}` : '-';
W(`| ${t.tag} | ${hits.length} | ${(hits.length / Math.max(allComments.length, 1) * 100).toFixed(1)}% | ${sampleText} |`);
}
W('');
W('### 真实评论原声 Top 16 (产品强相关 · 多主题轮询)');
W('');
for (const { topic, c } of topPicks) {
W(`- **[${topic}]** \`${c.koc}@${c.ip || '-'}\`: ${c.content.replace(/\n/g, ' ')}`);
}
W('');
W('---');
W('');
W('## 四、品牌相关词存量');
W('');
W('### 4.1 品牌词');
W('');
W('| 词 | 评论命中 | 状态 |');
W('|---|---:|---|');
for (const w of cloudBrand) W(`| ${w.name} | ${w.value - 5 > 0 ? w.value - 5 : 0} | ${w.value > 5 ? '✓ 已渗透' : '○ 待沉淀'} |`);
W('');
W('### 4.2 活动相关词');
W('');
W('| 词 | 评论命中 | 状态 |');
W('|---|---:|---|');
for (const w of cloudActivity) W(`| ${w.name} | ${w.value - 5 > 0 ? w.value - 5 : 0} | ${w.value > 5 ? '✓ 已渗透' : '○ 待沉淀'} |`);
W('');
W('### 4.3 品类词占位');
W('');
W('| 词 | 评论命中 | 状态 |');
W('|---|---:|---|');
for (const w of cloudCategory) W(`| ${w.name} | ${w.value - 5 > 0 ? w.value - 5 : 0} | ${w.value > 5 ? '✓ 已渗透' : '○ 待沉淀'} |`);
W('');
W('### 4.4 评论高频词 Top 50');
W('');
W('| 词 | 频次 |');
W('|---|---:|');
for (const w of wordcloudComments.slice(0, 50)) W(`| ${w.name} | ${w.value} |`);
W('');
W('---');
W('');
W('## 五、本次宣发的优点 / 不足 / 后续建议');
W('');
W('### 5.1 ✓ 优点');
W('');
for (const p of PROS) {
W(`**${p.title}**`);
W('');
W(p.text.replace(/<\/?b>/g, '**').replace(/([^<]+)<\/code>/g, '`$1`'));
W('');
}
W('### 5.2 ✗ 不足');
W('');
for (const c of CONS) {
W(`**${c.title}**`);
W('');
W(c.text.replace(/<\/?b>/g, '**'));
W('');
}
W('### 5.3 → 后续投放建议');
W('');
W('| 优先级 | 行动 | 说明 |');
W('|---|---|---|');
for (const r of RECS) W(`| **${r.p}** | ${r.t} | ${r.b} |`);
W('');
W('### 5.4 下一轮预算建议分配');
W('');
W('| 模块 | 占比 | 说明 |');
W('|---|---:|---|');
W('| 头/腰部 KOL | 35% | 2~3 位创意/跨界 KOL,延续高杠杆爆款方法论 |');
W('| 差异化探店 KOC | 35% | 5 赛道分发 (减脂/早午餐/家庭/一人食/商务),各 15-20 篇 |');
W('| 二创 IP 池 + 在地达人 | 15% | 沉淀长期 IP 资产 + 月度打卡官 |');
W('| 薯条 + 评论运营 + 数据 | 15% | Top 5% 加投 + SOP + 风险词预警 + 求开店承接 |');
W('');
W('---');
W('');
W(`*本结案报告基于 ${summary.total} 篇笔记 (KOL ${summary.kolCount} + KOC ${summary.kocCount}) + ${allComments.length} 条公开评论真实数据,通过可复用 VOC 工作流自动生成。配套交互式 HTML 报告: \`majiyong-closing-report.html\` · 配套深度新机会文档: \`majiyong-new-opportunities.md\`*`);
fs.writeFileSync(OUT_MD, md.join('\n'), 'utf8');
console.log('✓ MD:', OUT_MD, md.length, 'lines');
};