feat(wst): 数据源停更熔断——订单表停更时醒目标注,别把「没同步」当「没发货」 #92

Merged
yanhui merged 1 commits from yanhui-wst-stale-1029 into main 2026-10-29 09:13:26 +08:00
Collaborator

业务问题

10-28 18:00 轮页面核验出 546 单超时未发,业务核对后反馈「基本都发了,怎么会核验出未发」。

根因:不是判定写错,是订单表停更

jst_Orders 当时从 14:40 就停止同步了(停了 4 小时)。停更之后发出的单,send_date 一直是空,于是全被判成「未发货」:

来源表 超时单数 该表同步状态(18:35 实测)
jst_Orders 540 最后更新 14:40(已停 4 小时)
jst_qmcust_orderlist 6 17:30 正常

两个订单表各自独立同步,同一天完全可能一个正常一个停更。原来只拿候选行的 MAX(updated_at) 判快照滞后,会被「碰巧新」的行掩盖 —— 必须直接问源表。

改动

后端 warehouseShipTimelinessService.js

  • 新增 loadSourceFreshness():分别直接查 jst_Orders.MAX(updateTime) 与 jst_qmcust_orderlist.MAX(local_updated)(限轮次窗口起 30 天,避免全表扫描),两源并行
    • 阈值 CANGKU_WST_SOURCE_STALE_MINUTES,默认 60 分钟
    • 查询失败或取不到更新时间 → 不判停更(宁可漏报,不谎报为停更)
  • 明细行新增 src / srcLabel / sourceStale;summary 新增 sourceStaleUnshipped
  • freshness.stale 改为「候选行滞后 OR 任一源停更」;hint 说清停更源、停更时刻、以及各源各贡献多少单(不能只写「这张表」)
  • 响应新增 dataSources 数组;导出数据一并带上

前端 WarehouseShipTimeliness/index.jsx

  • 红条 Alert:「N 单所在订单表已停止同步,这些单很可能已经发货」(error 级),描述里逐源列出「最后同步 14:40:05(已 3.9 小时)」
  • 超时统计卡下方小字:「其中 N 单数据未同步」
  • 明细「内部单号」旁「未同步」红标签 + tooltip,一眼看出哪几行不可信
  • 导出新增「数据源 / 数据源是否停更」两列(是(该单可能已发))

验证

  • 后端 npm run check:494/496,2 个失败是既有环境问题(负缓存 TTL 计时、Windows 文件权限 0o600),改动前同样失败
  • warehouseShipTimeliness.test.js 23/23 通过(含新增「停更表超时单必须打标并计入 sourceStaleUnshipped」用例)
  • 前端 eslint 干净
  • 本地真渲染(Playwright + 手签 token,localhost:5173):
    • 真数据:13:00 前 notStarted 正常展示「—」,无 pageerror
    • mock 停更数据:红条 / 逐源行 / 统计小字 / 3 行「未同步」标签全部渲染,无 pageerror
  • 真实接口 10-28 13:00 轮:dataSources 正确识别 jst_Orders 停更(滞后 96 分钟)而 qmcust 正常;sourceStaleUnshipped=1146 与明细逐行打标同源同口径

风险与回退

  • 只新增字段与展示,不改动分轮、起算点、候选池口径 —— 超时单量本身不变
  • 低峰期(凌晨)可能出现「1 小时无新单」被判停更,但页面只在 13:00 / 18:00 核验时刻展示超时数据,这两个时点 1 小时不同步即属异常
  • 回退:revert 本提交即可,无数据迁移、无新表、无新依赖

关联

  • 上游 ETL 停更问题已第三次推 IT 处理(本 PR 只做「停更时别骗人」,不解决同步本身)
  • 承接 PR #91(起算点 = max(审核, modified, created)),两者叠加后:起算点准 + 数据源停更会被标出来
## 业务问题 10-28 18:00 轮页面核验出 **546 单超时未发**,业务核对后反馈「基本都发了,怎么会核验出未发」。 ## 根因:不是判定写错,是订单表停更 `jst_Orders` 当时**从 14:40 就停止同步了**(停了 4 小时)。停更之后发出的单,`send_date` 一直是空,于是全被判成「未发货」: | 来源表 | 超时单数 | 该表同步状态(18:35 实测) | |---|---|---| | `jst_Orders` | 540 | 最后更新 **14:40**(已停 4 小时) | | `jst_qmcust_orderlist` | 6 | 17:30 正常 | 两个订单表**各自独立同步**,同一天完全可能一个正常一个停更。原来只拿候选行的 `MAX(updated_at)` 判快照滞后,会被「碰巧新」的行掩盖 —— 必须直接问源表。 ## 改动 **后端 `warehouseShipTimelinessService.js`** - 新增 `loadSourceFreshness()`:分别直接查 `jst_Orders.MAX(updateTime)` 与 `jst_qmcust_orderlist.MAX(local_updated)`(限轮次窗口起 30 天,避免全表扫描),两源并行 - 阈值 `CANGKU_WST_SOURCE_STALE_MINUTES`,默认 60 分钟 - 查询失败或取不到更新时间 → **不判停更**(宁可漏报,不谎报为停更) - 明细行新增 `src` / `srcLabel` / `sourceStale`;`summary` 新增 `sourceStaleUnshipped` - `freshness.stale` 改为「候选行滞后 **OR** 任一源停更」;`hint` 说清停更源、停更时刻、以及各源各贡献多少单(不能只写「这张表」) - 响应新增 `dataSources` 数组;导出数据一并带上 **前端 `WarehouseShipTimeliness/index.jsx`** - 红条 Alert:「N 单所在订单表已停止同步,这些单很可能已经发货」(error 级),描述里逐源列出「最后同步 14:40:05(已 3.9 小时)」 - 超时统计卡下方小字:「其中 N 单数据未同步」 - 明细「内部单号」旁「未同步」红标签 + tooltip,一眼看出哪几行不可信 - 导出新增「数据源 / 数据源是否停更」两列(`是(该单可能已发)`) ## 验证 - 后端 `npm run check`:**494/496**,2 个失败是既有环境问题(负缓存 TTL 计时、Windows 文件权限 0o600),改动前同样失败 - `warehouseShipTimeliness.test.js` **23/23** 通过(含新增「停更表超时单必须打标并计入 `sourceStaleUnshipped`」用例) - 前端 eslint 干净 - 本地真渲染(Playwright + 手签 token,localhost:5173): - 真数据:13:00 前 `notStarted` 正常展示「—」,无 pageerror - mock 停更数据:红条 / 逐源行 / 统计小字 / 3 行「未同步」标签全部渲染,无 pageerror - 真实接口 10-28 13:00 轮:`dataSources` 正确识别 `jst_Orders` 停更(滞后 96 分钟)而 qmcust 正常;`sourceStaleUnshipped=1146` 与明细逐行打标同源同口径 ## 风险与回退 - 只**新增字段与展示**,不改动分轮、起算点、候选池口径 —— 超时单量本身不变 - 低峰期(凌晨)可能出现「1 小时无新单」被判停更,但页面只在 13:00 / 18:00 核验时刻展示超时数据,这两个时点 1 小时不同步即属异常 - 回退:revert 本提交即可,无数据迁移、无新表、无新依赖 ## 关联 - 上游 ETL 停更问题已第三次推 IT 处理(本 PR 只做「停更时别骗人」,不解决同步本身) - 承接 PR #91(起算点 = max(审核, modified, created)),两者叠加后:起算点准 + 数据源停更会被标出来
yanhui added 1 commit 2026-10-29 08:26:47 +08:00
## 业务问题

10-28 18:00 轮页面核验出 546 单超时未发,业务核对「基本都发了,怎么会核验出未发」。

## 根因(不是判定写错,是数据源停更)

`jst_Orders` 当时**从 14:40 就停止同步了**(停了 4 小时),停更之后发出的单
`send_date` 一直是空,因此全被判成「未发货」:

| 来源表 | 超时单数 | 该表状态 |
|---|---|---|
| `jst_Orders` | 540 | 最后更新 14:40(停 4 小时) |
| `jst_qmcust_orderlist` | 6 | 17:30 正常 |

两个订单表**各自独立同步**,同一天完全可能一个正常一个停更。原来只拿候选行的
`MAX(updated_at)` 判快照滞后,会被「碰巧新」的行掩盖 —— 必须直接问源表。

## 改动

后端 `warehouseShipTimelinessService.js`:

- 新增 `loadSourceFreshness()`:分别直接查 `jst_Orders.MAX(updateTime)` 与
  `jst_qmcust_orderlist.MAX(local_updated)`(限轮次窗口起 30 天,避免全表扫描),
  两源并行;按 `CANGKU_WST_SOURCE_STALE_MINUTES`(默认 60 分钟)判停更
  - 查询失败或取不到更新时间 → **不判停更**(宁可漏报,不谎报为停更)
- 明细行新增 `src` / `srcLabel` / `sourceStale`;`summary` 新增 `sourceStaleUnshipped`
- `freshness.stale` 改为「候选行滞后 **OR** 任一源停更」;`hint` 说清停更源、
  停更时刻、以及各源各贡献多少单(不能只写「这张表」)
- 导出数据带 `dataSources`,Excel 脱离页面也有上下文
- 响应新增 `dataSources` 数组(每源 label / lastUpdate / lagMinutes / stale)

前端 `WarehouseShipTimeliness/index.jsx`:

- 红条 Alert:「N 单所在订单表已停止同步,这些单很可能已经发货」(error 级),
  描述里逐源列出「最后同步 14:40:05(已 3.9 小时)」
- 超时统计卡下方小字:「其中 N 单数据未同步」(悬停有说明)
- 明细「内部单号」旁「未同步」红标签 + tooltip,一眼看出哪几行不可信
- 导出新增「数据源 / 数据源是否停更」两列(`是(该单可能已发)`)

## 验证

- 后端 `npm run check`:**494/496**,2 个失败是既有环境问题(负缓存 TTL 计时、
  Windows 文件权限 0o600),改动前同样失败
- `warehouseShipTimeliness.test.js` **23/23** 通过(含新增「停更表超时单必须打标
  并计入 sourceStaleUnshipped」用例)
- 本地真渲染(Playwright + 手签 token,localhost:5173):
  - 真数据:13:00 前 `notStarted` 正常展示「—」,无 pageerror
  - mock 停更数据:红条 / 逐源行 / 统计小字 / 3 行「未同步」标签全部渲染,无 pageerror
- 真实接口 10-28 13:00 轮:`dataSources` 正确识别 `jst_Orders` 停更(滞后 96 分钟)
  而 qmcust 正常;`sourceStaleUnshipped=1146` 与明细逐行打标同源同口径

## 风险与回退

- 只新增字段与展示,**不改动分轮、起算点、候选池口径** —— 超时单量本身不变
- 阈值可用 `CANGKU_WST_SOURCE_STALE_MINUTES` 调;不配默认 60 分钟
- 低峰期(凌晨)可能出现「1 小时无新单」被判停更,但页面只在 13:00 / 18:00
  核验时刻展示超时数据,这两个时点 1 小时不同步即属异常
- 回退:revert 本提交即可,无数据迁移、无新表、无新依赖
yanhui merged commit c0e48688c1 into main 2026-10-29 09:13:26 +08:00
Sign in to join this conversation.
No Reviewers
No Label
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: lunanting/cangku#92
No description provided.