소스 검색

feat: account column in report tables and CSV

语义层新增锁定列 account(账号名),置于首位;queryReport/aggregateReport
按主体名动态注入(旧数据无需重同步),明细/按门店/按日期三个视图与 CSV
导出全部携带,多账号数据合并时可区分来源。
KenZ1117 2 주 전
부모
커밋
777ae5eb7f
2개의 변경된 파일과 13개의 추가작업 그리고 5개의 파일을 삭제
  1. 8 1
      napos-service.js
  2. 5 4
      public/reports.js

+ 8 - 1
napos-service.js

@@ -26,6 +26,9 @@ const DEFAULT_CDP_PORT = Number(process.env.NAPOS_CDP_PORT || 9669);
 // synonyms / description 供「智能问数」的 LLM 做实体对齐与口径解释
 
 const REPORT_COLUMNS = [
+  { key: 'account', label: '账号名', type: 'text', locked: true, aggregable: false,
+    synonyms: ['账号', '账号名', '账户', '主体'],
+    description: '数据所属的商家账号(主体)名称;多账号数据合并查看或导出时用于区分来源' },
   { key: 'date', label: '日期', type: 'date', locked: true, aggregable: false,
     synonyms: ['日期', '天', '哪天', '日期时间'],
     description: '数据日期,格式 YYYYMMDD;当天数据未结算,有效区间一般为 T-1 及更早' },
@@ -415,6 +418,8 @@ function queryReport({ entity, from, to, shopId, shops, scope, q } = {}) {
     const needle = String(q).toLowerCase();
     rows = rows.filter((r) => String(r.shopName || '').toLowerCase().includes(needle));
   }
+  const account = meta ? meta.name : key;
+  rows = rows.map((r) => ({ ...r, account }));
   return {
     entity: meta ? { key: meta.key, name: meta.name } : { key, name: key },
     from: from || null, to: to || null,
@@ -427,6 +432,7 @@ function queryReport({ entity, from, to, shopId, shops, scope, q } = {}) {
 /** BI 聚合视图:group = 'shop'(按门店)| 'date'(按日期) */
 function aggregateReport({ entity, from, to, group = 'shop', shops } = {}) {
   const key = store.sanitizeKey(entity);
+  const meta = store.getEntity(key);
   let rows = store.queryRows(key, { from, to, scope: 'shop' });
   if (Array.isArray(shops) && shops.length) {
     const wanted = new Set(shops.map(String));
@@ -448,7 +454,8 @@ function aggregateReport({ entity, from, to, group = 'shop', shops } = {}) {
     out.push({ ...base, ...s });
   }
   out.sort((a, b) => (group === 'date' ? (a.date < b.date ? -1 : 1) : (b.grossAmt || 0) - (a.grossAmt || 0)));
-  return { columns: REPORT_COLUMNS, rows: out, summary: summarizeRows(rows) };
+  const account = meta ? meta.name : key;
+  return { columns: REPORT_COLUMNS, rows: out.map((r) => ({ ...r, account })), summary: summarizeRows(rows) };
 }
 
 // ---------- CSV 导出 ----------

+ 5 - 4
public/reports.js

@@ -276,14 +276,15 @@
   // ---------- 报表表格(数据采集页) ----------
 
   function metricColumns() {
-    return state.columns.filter((c) => c.key !== 'date' && c.key !== 'shopName' && state.visible.has(c.key));
+    return state.columns.filter((c) => c.key !== 'date' && c.key !== 'shopName' && c.key !== 'account' && state.visible.has(c.key));
   }
 
   function tableColumns() {
     const metricCols = metricColumns();
-    if (state.mode === 'shop') return [{ key: 'shopName', label: '门店名称', type: 'text' }, ...metricCols];
-    if (state.mode === 'date') return [{ key: 'date', label: '日期', type: 'date' }, ...metricCols];
-    return [{ key: 'date', label: '日期', type: 'date' }, { key: 'shopName', label: '门店名称', type: 'text' }, ...metricCols];
+    const accountCol = { key: 'account', label: '账号名', type: 'text' };
+    if (state.mode === 'shop') return [accountCol, { key: 'shopName', label: '门店名称', type: 'text' }, ...metricCols];
+    if (state.mode === 'date') return [accountCol, { key: 'date', label: '日期', type: 'date' }, ...metricCols];
+    return [accountCol, { key: 'date', label: '日期', type: 'date' }, { key: 'shopName', label: '门店名称', type: 'text' }, ...metricCols];
   }
 
   function sortedRows() {