yanhui 555ac6b116 fix(wle): 0 单不再冒充「全部正常」——窗口空时查数据源是否停更
11-02 09:00 那批推到钉钉群的是「已发货 0 单 / 0 个包裹,异常 0 个包裹(0%)」,
业务问「怎么可能 0 单」。查库确认:物流镜像表 jst_order_express_details
最后灌库停在 2026-11-01 07:55,11-01 当天 12 个发货仓一条发货记录都没有
(订单表 jst_Orders 同日有 4601 单发货,说明货是真发了,只是物流没进库)。

根因不是口径,是「0 单没有告警」这个设计缺陷:freshness 只用本批窗口内行的
query_time 算,窗口一行都没有时 snapshotAt 为空串 → stale=false、hint='',
于是「上游没同步」和「当天真没发货」在页面和钉钉里长得完全一样,还写「全部正常」。

改动
- service:窗口 0 行时补查一次表级 MAX(query_time)(全表 13.5s,只在空窗口付这笔钱,
  随主结果进 5 分钟缓存),新增 freshness.emptyWindow / sourceLastUpdate /
  sourceAgeMinutes / sourceStale,并按「真停更 / 表新但当天无记录 / 取不到时间」
  三种情况给不同 hint。
- 钉钉正文:0 条时不再无脑写「本批无转运异常,全部正常」;真停更写
  「⚠️ 物流数据未同步:镜像表最后更新 …(已 N 小时未同步)… 0 单不代表当天没发货」,
  表新但当天无记录则说明「库里 0 条发货记录(数据源正常),请到聚水潭核对」。
- 前端:emptyWindow 走红条(原来只判 coversDeadline/stale,空窗口不告警),
  发货卡片副标改为「数据未同步,见上方红条」。
- 前端顺带修:钉钉消息链接带 ?date=YYYY-MM-DD,但页面从不读这个参数,
  点进去永远显示「今天这批」;现按 URL 初始化核验日。

验证
- npm --prefix backend run check:505 tests / 503 pass;新增 5 个用例
  (停更 hint、表新但 0 条、有行时不付全表 MAX、推送文案两态)全过。
  既有失败 2 个与本任务无关:cloudDatabaseConfigCache 负缓存 TTL、
  resilientSecretStore 的 Windows 0600 权限断言。
- 生产库实跑(本地 4002,直连 JSReportReplica):
  date=2026-11-02 → emptyWindow=true、sourceLastUpdate=2026-11-01 07:55:04、
  sourceAgeMinutes≈1590、hint 正确;date=2026-11-01 → 4390 单 / 9 异常、
  emptyWindow=false,原有「未覆盖核验时刻」提示不变。
- 钉钉 dryRun 正文实测:前 3 行即带「⚠️ 物流数据未同步 … 0 单不代表当天没发货」。
- Playwright 真渲染(5173):
  ?date=2026-11-02 → 红条 + 「数据未同步,见上方红条」,0 pageerror;
  ?date=2026-11-01 → 4,390 单 / 9 异常正常出表,且 URL 参数已生效。
- 前端 eslint 干净,vite build 通过。

风险与回退
- 只增字段与提示,不改任何统计口径;packageCount/shippedOrders 计算逻辑未动。
- 空窗口多一条全表 MAX 查询(13.5s),已用 5 分钟缓存兜住,且只在异常路径触发。
- 回退:单文件 revert 本提交即可,无数据结构变更。
2026-11-02 10:31:11 +08:00
..