| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163 |
- #!/usr/bin/env node
- const fs = require('fs');
- const path = require('path');
- const ROOT = path.resolve(__dirname, '..');
- const DEFAULT_INPUT = path.join(ROOT, 'docs', 'ai-optimization-proof-gap-software-table.md');
- const DEFAULT_OUTPUT = path.join(ROOT, 'outputs', 'tihao-proof-gap-software-form-latest');
- const SOFTWARE_HEADER = [
- 'brief编号',
- '策略',
- '排名',
- '平台',
- '博主名称',
- '综合分',
- 'brief匹配分',
- '参考风格分',
- '主页证据分',
- '视觉质感分',
- '调性一致分',
- '证据加分',
- '证据风险扣分',
- '推荐理由',
- '风险提示',
- '主页链接',
- '人工复核标签'
- ];
- function main() {
- const args = parseArgs(process.argv.slice(2));
- const input = path.resolve(args.input || DEFAULT_INPUT);
- const outputDir = path.resolve(args.output || DEFAULT_OUTPUT);
- const result = buildProofGapSoftwareForm({ input, outputDir });
- fs.mkdirSync(outputDir, { recursive: true });
- fs.writeFileSync(result.files.csv, withBom(renderCsv([SOFTWARE_HEADER, ...result.rows])), 'utf8');
- fs.writeFileSync(result.files.markdown, withBom(renderMarkdown(result)), 'utf8');
- fs.writeFileSync(result.files.summary, JSON.stringify(result.summary, null, 2), 'utf8');
- console.log(JSON.stringify({
- outputDir,
- csv: result.files.csv,
- markdown: result.files.markdown,
- summary: result.files.summary,
- rowCount: result.summary.rowCount,
- duplicateIdCount: result.summary.duplicateIdCount,
- headerMatches: result.summary.headerMatches
- }, null, 2));
- if (args.strict && !result.summary.passed) process.exitCode = 1;
- }
- function buildProofGapSoftwareForm({ input, outputDir }) {
- const text = fs.readFileSync(input, 'utf8').replace(/^\uFEFF/, '');
- const rows = text.split(/\r?\n/).map(splitMarkdownRow).filter(cells => cells.length);
- const header = rows.find(cells => cells[0] === 'brief编号') || [];
- const dataRows = rows.filter(cells => /^GAP-\d{3}$/.test(cells[0] || ''));
- const ids = dataRows.map(cells => cells[0]);
- const duplicateIds = ids.filter((id, index) => ids.indexOf(id) !== index);
- const headerMatches = JSON.stringify(header) === JSON.stringify(SOFTWARE_HEADER);
- const expectedTitles = [
- '真实历史 Brief 数据集',
- '真实视频资源表',
- '真实视频证据 A/B',
- '真实 live/provider 长跑证明',
- '商务复核和客户效果'
- ];
- const actualTitles = dataRows.map(cells => cells[4]);
- const missingTitles = expectedTitles.filter(title => !actualTitles.includes(title));
- const rowWidthOk = dataRows.every(cells => cells.length === SOFTWARE_HEADER.length);
- const summary = {
- generatedAt: new Date().toISOString(),
- input: path.relative(ROOT, input).replace(/\\/g, '/'),
- outputDir: path.relative(ROOT, outputDir).replace(/\\/g, '/'),
- headerMatches,
- rowWidthOk,
- rowCount: dataRows.length,
- duplicateIdCount: duplicateIds.length,
- duplicateIds: Array.from(new Set(duplicateIds)),
- expectedTitles,
- actualTitles,
- missingTitles,
- passed: headerMatches && rowWidthOk && dataRows.length === 5 && duplicateIds.length === 0 && missingTitles.length === 0,
- guardrails: [
- '本表单用于安排补证任务,不证明客户效果完成。',
- '不能把 sample、smoke、模板、provider fallback 或接口 200 当成业务证明。',
- '表单不得包含 sessionToken、Authorization、模型 token 或 npm token。'
- ]
- };
- return {
- rows: dataRows,
- summary,
- files: {
- csv: path.join(outputDir, 'tihao-proof-gap-software-form.csv'),
- markdown: path.join(outputDir, 'tihao-proof-gap-software-form.md'),
- summary: path.join(outputDir, 'tihao-proof-gap-software-form-summary.json')
- }
- };
- }
- function renderMarkdown(result) {
- const lines = [
- '# 最新提号补证表单(软件端格式)',
- '',
- `- 生成时间:${result.summary.generatedAt}`,
- `- 行数:${result.summary.rowCount}`,
- `- 表头匹配:${result.summary.headerMatches ? '是' : '否'}`,
- `- 是否通过:${result.summary.passed ? '是' : '否'}`,
- '',
- '| ' + SOFTWARE_HEADER.join(' | ') + ' |',
- '| ' + SOFTWARE_HEADER.map(() => '---').join(' | ') + ' |',
- ...result.rows.map(row => '| ' + row.map(escapeMarkdownCell).join(' | ') + ' |'),
- '',
- '## 边界',
- '',
- ...result.summary.guardrails.map(item => `- ${item}`)
- ];
- return lines.join('\n');
- }
- function renderCsv(rows) {
- return rows.map(row => row.map(csvCell).join(',')).join('\n');
- }
- function splitMarkdownRow(line) {
- if (!line || !line.trim().startsWith('|')) return [];
- return line.trim().replace(/^\|/, '').replace(/\|$/, '').split('|').map(cell => cell.trim());
- }
- function parseArgs(argv) {
- const args = {};
- for (let index = 0; index < argv.length; index += 1) {
- const raw = argv[index];
- if (!raw.startsWith('--')) continue;
- const key = raw.slice(2).replace(/-([a-z])/g, (_, char) => char.toUpperCase());
- const next = argv[index + 1];
- if (!next || next.startsWith('--')) args[key] = true;
- else {
- args[key] = next;
- index += 1;
- }
- }
- return args;
- }
- function csvCell(value) {
- const text = String(value ?? '');
- return /[",\r\n]/.test(text) ? `"${text.replace(/"/g, '""')}"` : text;
- }
- function escapeMarkdownCell(value) {
- return String(value ?? '').replace(/\|/g, '/').replace(/\r?\n/g, ' ');
- }
- function withBom(text) {
- return `\uFEFF${text}`;
- }
- if (require.main === module) main();
- module.exports = {
- SOFTWARE_HEADER,
- buildProofGapSoftwareForm
- };
|