function asArray(value) { return Array.isArray(value) ? value : []; } function hasSourceUrl(item) { return Boolean(item && (item.sourceUrl || item.webUrl || item.url)); } function hasImage(item) { return Boolean( item && (item.localCoverPath || item.coverUrl || (Array.isArray(item.imageUrls) && item.imageUrls.length)) ); } function hasText(item) { return Boolean( item && String(item.contentExcerpt || item.rawExcerpt || item.content || item.desc || item.title || '').trim() ); } function assessEvidenceQuality({ items, comments, itemLabel = '样本', collectionMode = 'sample' }) { const rows = asArray(items); const commentRows = asArray(comments); const sourceUrlCount = rows.filter(hasSourceUrl).length; const imageCount = rows.filter(hasImage).length; const textCount = rows.filter(hasText).length; const highLikeCommentCount = commentRows.filter(comment => Number(comment.likeCount || 0) > 0).length; let score = 0; if (rows.length >= 8) score += 2; else if (rows.length >= 3) score += 1; if (commentRows.length >= 50) score += 2; else if (commentRows.length >= 10) score += 1; if (sourceUrlCount >= Math.min(rows.length, 3)) score += 1; if (imageCount >= Math.min(rows.length, 3)) score += 1; if (highLikeCommentCount >= 3) score += 1; if (textCount >= Math.min(rows.length, 3)) score += 1; const grade = score >= 7 ? 'A' : score >= 4 ? 'B' : 'C'; const canMakeBusinessDecision = grade === 'A' || (grade === 'B' && collectionMode === 'live'); const recommendation = canMakeBusinessDecision ? '可以作为第一轮经营判断依据,但仍建议结合门店/销售/私域反馈校准。' : '只适合作为初步机会假设,不建议直接当最终经营结论。'; return { grade, itemLabel, itemCount: rows.length, commentCount: commentRows.length, sourceUrlCount, imageCount, textCount, highLikeCommentCount, canMakeBusinessDecision, recommendation }; } function buildEvidenceQualitySection(quality) { return [ '## 证据质量门槛', '', `- 本轮证据质量:${quality.grade}`, `- ${quality.itemLabel}数量:${quality.itemCount}`, `- 评论数量:${quality.commentCount}`, `- 含原文/原视频链接:${quality.sourceUrlCount}`, `- 含图片/封面:${quality.imageCount}`, `- 含文本摘录:${quality.textCount}`, `- 含点赞评论:${quality.highLikeCommentCount}`, `- 是否适合直接做经营判断:${quality.canMakeBusinessDecision ? '适合第一轮判断' : '仅适合初步假设'}`, `- 使用建议:${quality.recommendation}` ]; } function buildNoSampleFallbackMessage({ platformLabel, profile, examples = [] }) { const keywords = asArray(profile && profile.keywords).slice(0, 5).join('、') || '当前关键词'; const exampleLines = examples.length ? examples.map(item => `- ${item}`) : [ '- 换成更具体的品类词、场景词或城市词', '- 从产品词扩展到用户真实说法', '- 先用 sample 模式演示完整流程,但明确标注非真实采集' ]; return [ `本次 live 模式没有采到可用于分析的${platformLabel}样本。`, '', '这不代表行业没有机会,更可能是关键词过窄、平台内容表达不一样,或本轮上游没有返回可用内容。', '', '建议按这三步继续:', `1. 先保留当前方向:${keywords}`, '2. 自动换一组更贴近用户说法的关键词再试一次', '3. 如果仍然没有样本,降级到相关大词做第一轮机会假设,并明确标注“非真实采集/待验证”', '', '可尝试的关键词方向:', ...exampleLines ].join('\n'); } module.exports = { assessEvidenceQuality, buildEvidenceQualitySection, buildNoSampleFallbackMessage };