| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596 |
- #!/usr/bin/env node
- const fs = require('fs');
- const os = require('os');
- const path = require('path');
- const { buildMetrics, readCsv } = require('./review-metrics');
- const { buildCustomerEffectSummary } = require('./customer-effect-audit');
- function main() {
- const dir = fs.mkdtempSync(path.join(os.tmpdir(), 'tihao-customer-effect-'));
- const reviewCsv = path.join(dir, 'reviewed.csv');
- fs.writeFileSync(reviewCsv, withBom([
- 'brief编号,策略,排名,平台,博主名称,综合分,brief匹配分,参考风格分,主页证据分,视觉质感分,调性一致分,证据加分,证据风险扣分,推荐理由,风险提示,主页链接,人工复核标签,客户选择,归因类型,本轮人工补号量',
- 'brief-a,baseline-live,1,小红书,基础召回A,80,80,40,60,70,70,0,0,基础召回,需复核,http://example.com/a1,商务复核,客户选中,,3',
- 'brief-a,baseline-live,2,小红书,基础召回B,79,79,40,60,70,70,0,0,基础召回,调性偏泛,http://example.com/a2,跑偏,,召回关键词错,3',
- 'brief-a,baseline-live,3,小红书,基础召回C,78,78,40,60,70,70,0,0,基础召回,需复核,http://example.com/a3,商务复核,客户选中,,3',
- 'brief-a,reference-account,4,小红书,参考召回A,90,90,88,85,85,88,2,0,参考相似,需复核,http://example.com/a4,可直接发客户,客户选中,,3',
- 'brief-a,reference-account,5,小红书,参考召回B,89,89,87,85,85,88,2,0,参考相似,需复核,http://example.com/a5,商务复核,客户选中,,3',
- 'brief-a,homepage-evidence,6,小红书,主页证据A,88,88,82,90,85,88,2,0,主页匹配,需复核,http://example.com/a6,商务复核,客户选中,,3',
- 'brief-a,homepage-evidence,7,小红书,主页证据B,87,87,82,89,85,88,2,0,主页匹配,需复核,http://example.com/a7,商务复核,客户选中,,3',
- 'brief-a,video-enhanced,8,小红书,视频证据A,86,86,84,88,85,88,2,0,视频证据匹配,需复核,http://example.com/a8,商务复核,客户选中,,3',
- 'brief-a,video-enhanced,9,小红书,视频证据B,85,85,84,88,85,88,2,0,视频证据匹配,需复核,http://example.com/a9,商务复核,,,3',
- 'brief-a,result-first,10,小红书,结果优先A,84,84,80,86,84,86,2,0,综合匹配,需复核,http://example.com/a10,商务复核,客户选中,,3'
- ].join('\n')), 'utf8');
- const rows = readCsv(reviewCsv);
- const reviewMetrics = buildMetrics({ inputPath: reviewCsv, rows });
- const passingHistory = {
- acceptance: { readyForCustomerEffectProof: true },
- items: [
- { id: 'brief-a', manualSupplementBaseline: 6 }
- ]
- };
- const passSummary = buildCustomerEffectSummary({
- reviewPath: reviewCsv,
- reviewRows: rows,
- reviewMetrics,
- history: passingHistory,
- historyAuditPath: path.join(dir, 'historical-dataset-audit.json')
- });
- assert(passSummary.customerSelectedRate === 0.8, 'customer selected rate should be 80%');
- assert(passSummary.referenceCustomerSelectedRate === 0.8333, 'reference selected rate should be 83%');
- assert(passSummary.manualSupplementReductionRate === 0.5, 'manual supplement reduction should be 50%');
- assert(passSummary.acceptance.customerSelectedRate30Pass, '30% selected-rate gate should pass');
- assert(passSummary.acceptance.customerSelectedRate50Pass, '50% selected-rate gate should pass');
- assert(passSummary.acceptance.referenceCustomerSelectedRate40Pass, 'reference selected-rate gate should pass');
- assert(passSummary.acceptance.manualSupplementReduction50Pass, 'manual supplement reduction gate should pass');
- assert(passSummary.acceptance.overallPass, 'complete customer effect proof should pass');
- const missingCurrentCsv = path.join(dir, 'missing-current.csv');
- fs.writeFileSync(missingCurrentCsv, withBom([
- 'brief编号,策略,排名,平台,博主名称,综合分,brief匹配分,参考风格分,主页证据分,视觉质感分,调性一致分,证据加分,证据风险扣分,推荐理由,风险提示,主页链接,人工复核标签,客户选择,归因类型',
- 'brief-b,reference-account,1,小红书,参考召回B,90,90,88,85,85,88,2,0,参考相似,需复核,http://example.com/b1,可直接发客户,客户选中,'
- ].join('\n')), 'utf8');
- const missingCurrentRows = readCsv(missingCurrentCsv);
- const missingCurrentSummary = buildCustomerEffectSummary({
- reviewPath: missingCurrentCsv,
- reviewRows: missingCurrentRows,
- reviewMetrics: buildMetrics({ inputPath: missingCurrentCsv, rows: missingCurrentRows }),
- history: passingHistory,
- historyAuditPath: path.join(dir, 'historical-dataset-audit.json')
- });
- assert(!missingCurrentSummary.acceptance.currentManualSupplementAvailable, 'missing current manual supplement count should be pending');
- assert(!missingCurrentSummary.acceptance.overallPass, 'missing current manual supplement count should block overall pass');
- const missingBaselineSummary = buildCustomerEffectSummary({
- reviewPath: reviewCsv,
- reviewRows: rows,
- reviewMetrics,
- history: {
- acceptance: { readyForCustomerEffectProof: false },
- items: [{ id: 'brief-a', manualSupplementBaseline: null }]
- },
- historyAuditPath: path.join(dir, 'bad-history.json')
- });
- assert(!missingBaselineSummary.acceptance.manualSupplementBaselineAvailable, 'missing baseline should be detected');
- assert(!missingBaselineSummary.acceptance.historyReadyForCustomerEffectProof, 'bad history audit should block customer proof');
- assert(!missingBaselineSummary.acceptance.overallPass, 'missing baseline should block overall pass');
- console.log(JSON.stringify({
- ok: true,
- output: dir,
- customerSelectedRate: passSummary.customerSelectedRate,
- referenceCustomerSelectedRate: passSummary.referenceCustomerSelectedRate,
- manualSupplementReductionRate: passSummary.manualSupplementReductionRate
- }, null, 2));
- }
- function withBom(text) {
- return `\uFEFF${text}`;
- }
- function assert(condition, message) {
- if (!condition) throw new Error(message);
- }
- if (require.main === module) main();
|