'use strict';
/**
* 智能问数 · 深度分析报告(确定性生成,零模型)
* ----------------------------------------------------------------------------
* GET /api/agent/report-deep?entity&from&to → 自包含 HTML:
* KPI 概览 / 逐日序列 / 门店排行 / 环比归因 / 数据体检 verdicts / 统计异常 / 同行对标。
* 全部章节复用问数工具的确定性口径(同源不打折),build_report 工具的 deep 旗指向这里。
*/
const nlQuery = require('../../nl-query.js');
const lake = require('./tools/lake.js');
const cross = require('./tools/cross.js');
const compare = require('./tools/compare.js');
const anomaly = require('./tools/anomaly.js');
const attribution = require('./tools/attribution.js');
const peer = require('./tools/peer.js');
function esc(v) {
return String(v ?? '').replace(/[&<>"']/g, (c) => ({ '&': '&', '<': '<', '>': '>', '"': '"', "'": ''' }[c]));
}
function buildDeepReportHtml({ entity, from, to, svc }) {
const key = svc.store.sanitizeKey(entity);
const meta = svc.store.getEntity(key) || {};
const dateRange = nlQuery.lakeDateRange(key, svc);
const f = from && /^\d{8}$/.test(String(from)) ? String(from) : dateRange.from;
const t = to && /^\d{8}$/.test(String(to)) ? String(to) : dateRange.to;
const ctx = { svc, entityKey: key, meta, shops: meta.shops || [], dateRange };
const safe = (fn) => { try { return fn(); } catch (e) { return { summary: String(e.message || e), data: null }; } };
const quality = safe(() => lake.qualityHandler(ctx, lake.qualitySanitize({ from: f, to: t }, ctx)));
const crossr = safe(() => cross.crossHandler(ctx, cross.crossSanitize({ from: f, to: t }, ctx)));
const cmp = safe(() => compare.compareHandler(ctx, compare.compareSanitize({ from: f, to: t }, ctx)));
const an = safe(() => anomaly.anomalyHandler(ctx, anomaly.anomalySanitize({ metric: 'grossAmt', from: f, to: t }, ctx)));
const attr = safe(() => attribution.attributionHandler(ctx, attribution.attributionSanitize({ from: f, to: t }, ctx)));
const bench = safe(() => peer.benchHandler(ctx, peer.benchSanitize({ field: 'gross_amt', from: f, to: t }, ctx)));
const trend = safe(() => svc.aggregateReport({ entity: key, from: f, to: t, group: 'date' }));
const byShop = safe(() => svc.aggregateReport({ entity: key, from: f, to: t, group: 'shop' }));
const statusColor = { ok: '#16a34a', warn: '#d97706', fail: '#dc2626' };
const verdictRows = (verdicts) => (verdicts || []).map((v) => `
| ${esc(v.status === 'ok' ? '通过' : v.status === 'warn' ? '警告' : '不一致')} |
${esc(v.check)} | ${esc(v.detail || '')} |
`).join('');
const dayRows = ((trend && trend.rows) || []).map((r) => `
| ${esc(String(r.date).replace(/^(\d{4})(\d{2})(\d{2})$/, '$1-$2-$3'))} |
${Math.round(r.grossAmt || 0).toLocaleString('zh-CN')} |
${Math.round(r.validOrderCnt || 0).toLocaleString('zh-CN')} |
${Math.round(r.expUv || 0).toLocaleString('zh-CN')} |
${((r.clkExpRate || 0) * 100).toFixed(2)}% |
`).join('');
const shopRows = ((byShop && byShop.rows) || []).slice(0, 20).map((r, i) => `
| ${i + 1} | ${esc(r.shopName || r.shopId)} |
${Math.round(r.grossAmt || 0).toLocaleString('zh-CN')} |
${Math.round(r.validOrderCnt || 0).toLocaleString('zh-CN')} |
${((r.clkExpRate || 0) * 100).toFixed(2)}% |
`).join('');
const cmpRows = ((cmp.data && cmp.data.deltas) || []).map((d) => `
| ${esc(d.label)} | ${Math.round(d.cur * 100) / 100} | ${Math.round(d.prev * 100) / 100} |
${d.deltaPct == null ? '—' : (d.deltaPct >= 0 ? '+' : '') + (d.deltaPct * 100).toFixed(1) + '%'} |
`).join('');
const attrLines = attr && attr.summary ? attr.summary.split('\n').slice(1).map((l) => `${esc(l)}`).join('') : '归因不可用';
const anomalyLines = an && an.summary ? an.summary.split('\n').slice(1).map((l) => `${esc(l)}`).join('') : '不可用';
const benchLines = bench && bench.summary ? bench.summary.split('\n').slice(1).map((l) => `${esc(l)}`).join('') : '不可用';
const qualityList = (quality.data && quality.data.findings || []).map((fd) => `[${esc(fd.level)}] ${esc(fd.check)}:${esc(fd.detail)}`).join('');
return `
深度分析报告 · ${esc(meta.name || key)} ${f}-${t}
深度分析报告 · ${esc(meta.name || key)}
区间 ${f}-${t}(生成于 ${new Date().toLocaleString('zh-CN')})· 确定性口径生成,数字与问数工具同源
二、核心指标与环比
| 指标 | 本期 | 上期 | 变化 |
${cmpRows || '| 不可用 |
'}
六、逐日明细
| 日期 | 营业额 | 有效订单 | 曝光 | 进店率 |
${dayRows || '| 无数据 |
'}
七、门店排行(Top 20)
| # | 门店 | 营业额 | 有效订单 | 进店率 |
${shopRows || '| 无数据 |
'}
八、校验结论汇总
| 状态 | 校验 | 说明 |
${verdictRows((quality.data && quality.data.verdicts) || [])}
${verdictRows((crossr.data && crossr.data.verdicts) || [])}
${verdictRows((cmp.data && cmp.data.verdicts) || [])}
${verdictRows((an.data && an.data.verdicts) || [])}
`;
}
module.exports = { buildDeepReportHtml };