import type { NormalizedCandidate } from './recommendation.service.ts'; function escapeHtml(value: string): string { return value .replaceAll('&', '&') .replaceAll('<', '<') .replaceAll('>', '>') .replaceAll('"', '"'); } /** * 生成媒体资源推荐表 Excel (HTML table format compatible with MS Excel) * 按模板生成:账号名称、ID、账号类型、地区、链接、粉丝数、赞藏数、 * 图文/视频报价、推荐等级、推荐理由等字段 */ export function generateExcelBuffer(candidates: NormalizedCandidate[], taskFileName?: string): Buffer { const title = taskFileName ? taskFileName.replace(/\.[^.]+$/, '') : '达人推荐表'; const now = new Date().toLocaleDateString('zh-CN'); const headerRow = ` 序号 平台 账号名称 账号ID 地区 主页链接 粉丝数 图文报价 视频报价 最低报价 内容标签 综合评分 风格匹配 推荐等级 推荐理由 风险提示 数据来源 `; const rows = candidates.map((c, index) => ` ${index + 1} ${escapeHtml(mapPlatformDisplay(c.platform))} ${escapeHtml(c.displayName)} ${escapeHtml(c.platformUserId)} ${escapeHtml(c.location || '-')} ${escapeHtml(c.profileUrl)} ${formatFans(c.fansCount)} ${c.imagePrice > 0 ? `¥${c.imagePrice}` : '-'} ${c.videoPrice > 0 ? `¥${c.videoPrice}` : '-'} ${c.minPrice > 0 ? `¥${c.minPrice}` : '-'} ${escapeHtml(c.contentTags.join('、') || '-')} ${c.score} ${c.styleMatch}% ${escapeHtml(c.recommendStatus)} ${escapeHtml(c.recommendReason || '-')} ${escapeHtml(c.riskNote || '-')} ${escapeHtml(c.sourceProvider)} ` ).join(''); const html = ` ${headerRow} ${rows}
${escapeHtml(title)} - 媒体资源推荐表
生成时间:${now} | 候选总数:${candidates.length} | 强推荐:${candidates.filter(c => c.recommendStatus === '强推荐').length} | 备选:${candidates.filter(c => c.recommendStatus === '备选').length}
`; return Buffer.from(html, 'utf-8'); } function mapPlatformDisplay(platform: string): string { const map: Record = { xiaohongshu: '小红书', douyin: '抖音', bilibili: 'B站', weibo: '微博', weixin: '微信/公众号', }; return map[platform] || platform; } function formatFans(count: number): string { if (count === 0) return '-'; if (count >= 10000) return `${(count / 10000).toFixed(1)}万`; return String(count); }