#!/usr/bin/env node /** * 江中猴菇饮 OTC · VOC 种子数据生成器 * * 基于公开渠道观察到的用户讨论模式整理的代表性 VOC 样本库。 * 明确标识 source='pattern-curated',Batch 3+ 渲染时会以"模式样本"标注。 * * 输出:docs/jiangzhong-houguyin/raw/_seed.json + comments-flat.jsonl */ const fs = require('fs'); const path = require('path'); const PATTERNS = require('./houguyin-seed-patterns.js'); const ROOT = path.resolve(__dirname, '..', '..'); const OUT_DIR = path.join(ROOT, 'docs', 'jiangzhong-houguyin', 'raw'); const OUT_PATH = path.join(OUT_DIR, '_seed.json'); const FLAT_PATH = path.join(OUT_DIR, 'comments-flat.jsonl'); if (!fs.existsSync(OUT_DIR)) fs.mkdirSync(OUT_DIR, { recursive: true }); // ================================================================ // 生成器:将 pattern group 展开为完整 VOC 记录 // ================================================================ const IP_POOL = ['北京', '上海', '广东', '浙江', '江苏', '四川', '山东', '河南', '湖北', '湖南', '福建', '辽宁', '陕西', '河北', '安徽', '天津', '重庆']; const NICK_PREFIX = ['养胃派', '胃友', '小胃口', '温胃人', '猴友', '药店回忆', '江湖药师', '办公室牛马', '熬夜选手', '应酬达人', '老胃病', '胃镜新手', '断药挣扎者', '糖友', '送礼党', '职场妈妈', '差旅客', '实习生', '二胎妈妈', '产品经理']; function hash(s) { let h = 0; for (let i = 0; i < s.length; i++) { h = ((h << 5) - h + s.charCodeAt(i)) | 0; } return Math.abs(h); } function expandEntry(entry, group, idx) { const h = hash(entry.c + group.key + idx); const nick = `${NICK_PREFIX[h % NICK_PREFIX.length]}_${String((h >> 4) % 999).padStart(3, '0')}`; const ip = IP_POOL[(h >> 8) % IP_POOL.length]; const likes = group.likesMin + (h % Math.max(1, group.likesMax - group.likesMin)); const platforms = group.platforms || ['xhs']; const platform = platforms[(h >> 12) % platforms.length]; return { id: `seed_${group.key}_${String(idx).padStart(3, '0')}`, platform, product: group.product, keyword: group.keyword || group.product, type: group.type || 'comment', nickname: nick, ip, content: entry.c, likes, rating: group.rating || null, hypothesis: group.hypotheses, tags: entry.tags || [], sentiment: group.sentiment, source: 'pattern-curated', basis: group.basis || '公开讨论模式归纳', }; } function generate() { const items = []; for (const group of PATTERNS) { group.entries.forEach((e, i) => items.push(expandEntry(e, group, i))); } const meta = { collectedAt: new Date().toISOString().slice(0, 10), platforms: {}, products: {}, hypotheses: {}, stage: 'batch-2-seed', sourceNote: '所有条目 source=pattern-curated,基于公开讨论模式整理的代表性样本;Batch 3+ 渲染会明确标注' }; for (const it of items) { meta.platforms[it.platform] = (meta.platforms[it.platform] || 0) + 1; meta.products[it.product] = (meta.products[it.product] || 0) + 1; for (const h of it.hypothesis) meta.hypotheses[h] = (meta.hypotheses[h] || 0) + 1; } return { meta, items }; } function main() { console.log('╔═══════════════════════════════════════════════════════════╗'); console.log('║ 猴菇饮 OTC · VOC 种子数据生成(公开讨论模式归纳) ║'); console.log('╚═══════════════════════════════════════════════════════════╝'); const data = generate(); fs.writeFileSync(OUT_PATH, JSON.stringify(data, null, 2), 'utf8'); fs.writeFileSync(FLAT_PATH, data.items.map((it) => JSON.stringify(it)).join('\n'), 'utf8'); console.log(` ✅ 生成 ${data.items.length} 条 seed VOC`); console.log(` 平台分布:`, data.meta.platforms); console.log(` 假设覆盖:`, data.meta.hypotheses); console.log(` 产品维度: ${Object.keys(data.meta.products).length} 种`); console.log(` 输出: ${path.relative(ROOT, OUT_PATH)}`); } if (require.main === module) main(); module.exports = { generate };