'use strict'; /** * 智能问数 · 指标层 * ---------------------------------------------------------------------------- * 把「指标 = 维度 × 聚合 × 时间粒度 × 口径」从列注册表升级为一等对象: * · 基础指标:合计 / 加权(比率按曝光或进店人数加权)/ 比值(客单价) * · 派生指标:基于基础指标动态计算(7 日移动平均等),模型引用指标 key, * 不自己组合口径——flash 模型引用比自算的出错率低一个量级。 * 与 REPORT_COLUMNS 的关系:列注册表服务表格语义(label/同义词/类型), * 本注册表服务问数聚合语义(怎么算),两者键保持一致。 */ const BASE = { grossAmt: { name: '营业额', unit: 'money', agg: 'sum', synonyms: ['流水', 'GMV', '销售额'] }, validOrderCnt: { name: '有效订单', unit: 'int', agg: 'sum', synonyms: ['单量', '订单量'] }, incomeAmt: { name: '收入', unit: 'money', agg: 'sum', synonyms: ['到账'] }, expenseAmt: { name: '支出', unit: 'money', agg: 'sum', synonyms: ['费用'] }, expUv: { name: '曝光人数', unit: 'int', agg: 'sum', synonyms: ['曝光'] }, orderUserCnt: { name: '下单人数', unit: 'int', agg: 'sum', synonyms: ['购买人数'] }, actOrdCnt: { name: '活动订单', unit: 'int', agg: 'sum' }, actSubsidyAmt: { name: '活动补贴', unit: 'money', agg: 'sum' }, clkExpRate: { name: '进店转化率', unit: 'pct', agg: 'weighted', weightBy: 'expUv' }, ordClkRate: { name: '下单转化率', unit: 'pct', agg: 'weighted', weightBy: 'orderUserCnt' }, perActualPay: { name: '客单价', unit: 'money', agg: 'ratio', num: 'grossAmt', den: 'validOrderCnt' }, }; /** 派生指标:基于基础指标按窗口动态计算(按天粒度) */ const DERIVED = { gross_mov7: { name: '营业额7日移动平均', unit: 'money', base: 'grossAmt', window: 7 }, orders_mov7: { name: '有效订单7日移动平均', unit: 'int', base: 'validOrderCnt', window: 7 }, uv_mov7: { name: '曝光人数7日移动平均', unit: 'int', base: 'expUv', window: 7 }, }; const ALL = { ...BASE, ...DERIVED }; function get(key) { return ALL[key] || null; } /** 常驻提示词:聚合口径一句话说清(含派生指标) */ function manifestText() { const lines = Object.entries(ALL).map(([key, m]) => { const how = m.agg === 'sum' ? '合计' : m.agg === 'weighted' ? `加权平均(按${(BASE[m.weightBy] || {}).name || m.weightBy}加权)` : m.agg === 'ratio' ? `比值(${(BASE[m.num] || {}).name || m.num} / ${(BASE[m.den] || {}).name || m.den})` : `${m.window} 日移动平均(基于${(BASE[m.base] || {}).name || m.base})`; return `- ${key}|${m.name}|${how}`; }); return lines.join('\n'); } /** 单日聚合:把当天门店行聚成一个数(口径与 summarizeRows 同源——比率加权) */ function dayValue(key, rows) { const m = ALL[key]; if (!m || !rows.length) return null; if (m.agg === 'weighted') { let wv = 0; let w = 0; for (const r of rows) { const weight = Number(r[m.weightBy]) || 0; wv += (Number(r[key]) || 0) * weight; w += weight; } return w > 0 ? +(wv / w).toFixed(4) : null; } if (m.agg === 'ratio') { const num = rows.reduce((s, r) => s + (Number(r[m.num]) || 0), 0); const den = rows.reduce((s, r) => s + (Number(r[m.den]) || 0), 0); return den > 0 ? +(num / den).toFixed(2) : null; } return Math.round(rows.reduce((s, r) => s + (Number(r[key]) || 0), 0) * 100) / 100; } module.exports = { BASE, DERIVED, ALL, get, manifestText, dayValue };