| 123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778 |
- 'use strict';
- /**
- * 智能问数智能体 · 跨主体对比工具
- * ----------------------------------------------------------------------------
- * cross_entity:湖内全部主体(多账号/多食堂)本期 vs 基期核心指标对比,
- * 供「跨校对比」「哪家餐厅更好」类问题。窗口锚定当前主体的湖内区间,
- * 无本期数据的主体自动滤除并说明。
- */
- const { ymdDash } = require('./lake.js');
- const z2 = (n) => String(n).padStart(2, '0');
- function dayShift(ymd, delta) {
- const d = new Date(Number(ymd.slice(0, 4)), Number(ymd.slice(4, 6)) - 1, Number(ymd.slice(6, 8)));
- d.setDate(d.getDate() + delta);
- return `${d.getFullYear()}${z2(d.getMonth() + 1)}${z2(d.getDate())}`;
- }
- function entitySanitize(args, ctx) {
- const a = args || {};
- const { dateRange } = ctx;
- const dates = ctx.svc.store.datesInLake(ctx.entityKey);
- let from = /^\d{8}$/.test(String(a.from || '')) ? String(a.from) : '';
- let to = /^\d{8}$/.test(String(a.to || '')) ? String(a.to) : '';
- from = from && from >= dateRange.from && from <= dateRange.to ? from : '';
- to = to && to >= dateRange.from && to <= dateRange.to ? to : '';
- if (!from || !to || from > to) {
- if (!dates.length) return { empty: true };
- to = dates[dates.length - 1];
- from = dates[Math.max(0, dates.length - 7)];
- }
- const len = Math.max(1, Math.round(
- (new Date(Number(to.slice(0, 4)), Number(to.slice(4, 6)) - 1, Number(to.slice(6, 8)))
- - new Date(Number(from.slice(0, 4)), Number(from.slice(4, 6)) - 1, Number(from.slice(6, 8)))) / 86400000) + 1);
- const prevTo = dayShift(from, -1);
- const prevFrom = dayShift(prevTo, -(len - 1));
- return { from, to, prevFrom, prevTo, empty: false };
- }
- function windowTotals(svc, key, from, to) {
- const rows = svc.store.queryRows(key, { from, to, scope: 'shop' });
- const s = svc.summarizeRows(rows);
- return { gross: s.grossAmt, orders: s.validOrderCnt, days: s.dayCount, hasData: rows.length > 0 };
- }
- function entityHandler(ctx, args) {
- const { svc } = ctx;
- if (args.empty) return { summary: '当前主体湖内暂无数据,无法跨主体对比。', data: { empty: true } };
- const { from, to, prevFrom, prevTo } = args;
- const entities = svc.store.listEntities();
- const out = [];
- const skipped = [];
- for (const e of entities) {
- const cur = windowTotals(svc, e.key, from, to);
- if (!cur.hasData) { skipped.push(e.name || e.key); continue; }
- const prev = windowTotals(svc, e.key, prevFrom, prevTo);
- out.push({
- key: e.key,
- name: e.name || e.key,
- shopCount: e.shopCount || 0,
- cur,
- prev,
- grossDeltaPct: prev.gross > 0 ? +(((cur.gross - prev.gross) / prev.gross) * 100).toFixed(1) : null,
- });
- }
- out.sort((a, b) => b.cur.gross - a.cur.gross);
- const lines = [`跨主体对比(本期 ${ymdDash(from)} ~ ${ymdDash(to)} vs 上期 ${ymdDash(prevFrom)} ~ ${ymdDash(prevTo)},按本期营业额降序):`];
- for (const e of out) {
- const delta = e.grossDeltaPct == null ? '上期无数据' : `环比 ${e.grossDeltaPct >= 0 ? '+' : ''}${e.grossDeltaPct}%`;
- lines.push(` ${e.name}(${e.shopCount} 店):营业额 ${Math.round(e.cur.gross)} / 有效订单 ${Math.round(e.cur.orders)}(${delta})`);
- }
- if (skipped.length) lines.push(`本期无数据未纳入:${skipped.join('、')}`);
- if (!out.length) lines.push('湖内没有任何主体在本期有数据。');
- return { summary: lines.join('\n'), data: { from, to, prevFrom, prevTo, entities: out, skipped } };
- }
- module.exports = { entitySanitize, entityHandler };
|