|
|
4e4f8890c1
|
fix(wst): 发货时效剔除「已作废」单 —— 问题单 / 发货前售后 / 售后已作废三条兜底
业务反馈 3 单(so_id 3655421011854634 / 3658479011191285 / 110215007857459363)
在聚水潭后台都已作废,却仍出现在「仓储中心 24 小时发货时效」的超时明细里。
根因:
- 页面只认状态表 jst_order_express_details,这三单 order_status_name 仍是「发货中」
+ send_time 为空;售后状态是「暂无售后」—— 上游没把作废同步过来;
- 现有的作废兜底只认订单表 status ∈ Merged/Split/Cancelled 以及 is_merge/is_split,
覆盖不到 status=Question(聚水潭后台显示「已作废/问题单」)和 labels 含「发货前售后」。
改动:
- registry 新增 VOID_QUESTION_STATUS='Question'、VOID_ORDER_LABEL_KEYWORDS=['发货前售后']、
AFTERSALE_VOID_STATUS='已作废';
- 回订单表补事实时增投一个 `CASE WHEN labels LIKE N'%发货前售后%' THEN 1 ELSE 0 END`
布尔列(不传 labels 原文,避免宽列回表;实测冷查 42.6s,与改前同量级);
- 状态表主查询增投一列 aftersale_status_name;
- 主循环在「发货中」分支之后判这三种作废证据,命中则并入 voidCount、不进考核。
⚠️ Question 不能直接塞进 VOID_ORDER_STATUS:那个数组在状态分类之前判定,
订单表 Question 的单有一大批在状态表是「异常」,会被从「异常待处理」清单里一起吞掉
(实测 heldCount 472 → 10,少掉 462 单)。故单独放在 SHIPPING_STATUS 之后判。
验证:
- 单测 19/19 通过(新增 38/39/40 三条作废形态用例,锁定 heldCount 不被影响);
- 真实库 13:00 轮:超时 3 单 → 1 单(9823980 / 9880810 已剔除),
heldCount 保持 472,voidCount 4442 → 4444(恰好 +2);
- 剩余 9889575 未剔除:订单表 Delivering、无出库单、无任何作废标记(见 PR 正文说明)。
风险与回退:
- 仅收紧「发货中」分支的作废判定,不回退任何既有判据;
- 若业务认为 Question 单仍需考核,改 VOID_QUESTION_STATUS 一处即可恢复。
|
2026-10-22 17:54:34 +08:00 |
|
|
|
f42f461430
|
fix: 仓储发货时效按北京时间取「今天」(生产容器是 UTC,每天 00:00~08:00 轮次错位)
问题(2026-10-21 生产实测,PR #69 上线后发现):
- 生产容器 `/etc/timezone = Etc/UTC`、`date` = UTC;而 MSSQL `GETDATE()` 返回北京时间,
mssql 驱动 `useUTC:false` 把 DB 值构造成「墙钟当 UTC」的 Date ⇒ 比真实 now 快 8 小时。
- 于是**北京时间每天 00:00~08:00,服务端 `new Date()` 取到的「今天」是昨天**:
round.date / deadline / windowEnd 整体错位。
实测 08:04 跑 13:00 轮得 `overdueUnshipped=3895`(历史未发池被整段纳入),
而本地(东八区)同一轮只有 6 单。
修法(只动这一个模块,不动容器时区):
- registry 新增 `beijingNow()`:把真实时刻平移成「本地 getter 读出来正好是北京时间」的 Date,
本机已是东八区时平移量为 0,行为不变。
- `resolveActiveRound` / `describeRounds` 的 now 默认值改为 `beijingNow()`;
service 的 `getShipTimeliness` 同样改用 `registry.beijingNow()`。
- ⚠️ 没改容器 TZ:`/opt/cangku/shared/backend.env` 里没有 TZ,改了会波及全部既有定时任务
(dailyRefresh / dailyInventorySnapshot 等都各自显式写了 Asia/Shanghai)。
测试:
- 新增 2 个用例(跨日 00:00~08:00 不取成前一天;默认按北京时间判定 13:00 前 notStarted)。
- `node --test backend/test/warehouseShipTimeliness.test.js` → 19 / 19 pass。
|
2026-10-21 08:17:52 +08:00 |
|
|
|
530d1ad27a
|
feat: 仓储中心发货时效独立页面 + 口径定版(审核时间分轮/快递剔除/停更告警)
一、前端:从 MkhBomData 侧栏抽成独立页面
- 新增 frontend/src/pages/WarehouseShipTimeliness/{index.jsx,style.css},
路由 /planning/operation/warehouse-ship-timeliness,侧栏导航与 BasicLayout 各加 1 行入口。
- 删除 frontend/src/pages/MkhBomData/WarehouseShipTimelinessSidebar.jsx,
并把 MkhBomData/index.jsx、style.css 里相关代码与样式一并摘除(-131 / -277 行)。
二、业务口径(2026-10-20 业务多次纠偏后的定版)
- 轮次归属改用「审核时间」= jst_order_express_details.last_oa_confirm_time
(聚水潭订单列表「审核时间」列,最后一次审核,重审会刷新):
13:00 轮 = 今天 07:00 前审核 + 历史未发;18:00 轮 = 截止 16:00 前审核 + 历史未发。
不再按「创建时间」切自然日窗口,也不再有「满 24 小时」门槛。
- 仓级例外(仅 18:00 轮):山东青岗集仓 / 山东品牌仓 15:00 前审核、河南洛宁仓 13:00 前审核,
核验时刻仍为 18:00(registry 新增 windowEndHourByRound)。
- 明细去掉「创建时间」,改显示「发货时间」(send_time);未发但有运单号的标「已打单 · 未揽收」。
三、已发判定与不考核快递
- 已发 = 状态表 send_time 非空 OR 订单表 status='Sent'(对齐聚水潭销售订单口径)。
不再用运单号反推(已发货与未揽收单都会出现运单号,判不出来)。
- 自提 / 中铁 / 壹米滴答 三家快递不做未发考核,剔除量计入 summary.carrierSkipped。
四、数据源停更必须让用户看见
- freshness 由写死 stale:false 改为实算:快照距今 >90 分钟 → 黄告警;
快照早于本轮核验时刻 → 红告警「数据未覆盖核验时刻,超时单量偏大」。
五、测试
- backend/test/warehouseShipTimeliness.test.js 扩到 17 用例(仓级例外、重审取 last_oa、
快递剔除、Sent 判已发、coversDeadline 告警)。
|
2026-10-21 07:52:02 +08:00 |
|
|
|
893f4d6aca
|
feat: 新增「仓储中心24小时发货时效」侧栏(11 个主仓 · 12:00/16:00 双轮核验)
需求(yanhui 2026-10-20):在 MKH-BOM 列表左侧加一栏「仓储中心24小时发货时效」,
下列 11 个主仓(横林二仓、横林三仓、横林二仓品牌仓、崔桥工厂仓、崔桥工厂品牌仓、
崔桥工厂半成品仓、新苏北仓、睢宁仓、山东青岗集仓、山东品牌仓、河南洛宁仓),
从系统库存抓取已发货/未发货订单,按订单创建时间判定发货是否及时:
· 每天 12:00 核验「当天创建时间 7:00 之前」的订单是否已完成发货
· 每天 16:00 核验「当天创建时间 16:00 之前」的订单是否已完成发货
已确认的三条设计决策:
1. 子仓归属 → 连子仓一起算(京东云仓/自营仓/运营C专用仓/唯品仓等并入主仓)
2. 崔桥工厂半成品仓 → 系统无同名仓,先留空占位(不映射到配件仓 15718196)
3. 展示结构 → 只显示超时未发
实现要点
- 仓库归属用「关键词最长匹配」(不写死 wms_co_id),新增子仓只要名字含主仓关键词
就自动归入,缓存 1h。实测 28 个子仓全部正确归入 11 个主仓,132 个代发仓零误并。
- 12:00 之前**不做核验**:resolveActiveRound 返回 null,服务层直接返回 notStarted,
既不查订单表(接口 165ms)也不回填昨天的数据。前端显示「未到核验时间」+
「12:00 开始首轮核验(核验当日 07:00 前创建的订单)」,11 个仓结构保留、数字显示「—」。
- 订单取数:两张表店铺互斥(jst_Orders = 京东/拼多多/抖音/得物/唯品会,
jst_qmcust_orderlist = 天猫/淘宝/1688),并行分开查 + Node 侧按 o_id 去重合并,
用 Promise.allSettled 保证单表失败时另一表数据仍可用(detailDegraded 标记)。
- 三态判定 pending / shippedLate / onTime:send_date 与出库表 io_date 取先到的发货痕迹,
避免上游 send_date 回写滞后导致「已发的被判成没发」。
- 状态过滤:排除 Cancelled / Merged / **Split**(拆单父单永不发货,不排除会捞出一年前
1.3 万条历史单),Question 仍计入待发。
- freshness 字段如实报告出库表最新 io_date,前端「发货数据可能滞后」告警,
不拿一份可能滞后的数据去考核仓库。
排除的两次错误优化(均在代码注释里留了实测数据与复测脚本路径)
1. 用 created 替代 order_date 做窗口过滤(快 10 倍)—— 已证伪:2581 行里完全相等的只有 4 行,
差 >1h 的 412 行,198 行跨日;典型形态 order_date=10-18 23:58 而 created=10-19 04:00:08
(上游凌晨 4 点批量补录,created 是灌库时间不是下单时间)。业务口径是「订单创建时间」,
只有 order_date 正确。
2. 「UNION ALL 一条 SQL 慢 5~8 倍」—— 被后续实测推翻(46.3s vs 并行 48.2s,基本持平)。
验证
- 新增 backend/test/warehouseShipTimeliness.test.js,10 条用例全绿,覆盖:
最长匹配不误并、子仓命名、山东/河南高频前缀不误并、占位仓、12:00 前返回 null、
12:00 轮窗口 00:00~07:00、16:00 轮窗口 00:00~16:00、状态排除表、三态分桶互斥、
跨表重复 o_id 去重、round.windowEnd 必须带出(否则前端渲染成「核验 前」)。
- check-syntax 185 文件通过;全量 node --test 415 项 / 413 过 / 2 fail,
两条失败均为已知环境因素(负缓存带 TTL 因 .env 有 CLOUD_DB_* 短路;
resilientSecretStore 的 Windows 权限断言,基线恒失败)。
- 真渲染验收(组件级沙盒 + 真实接口数据):两态均 0 控制台错误;
已核验态 358 单超时未发、8 个仓有数字、点仓切换选中/再点取消均正常;
多分辨率验证 1920/1600 侧栏 208px、1440/1366 收窄 176px、列表始终竖向。
|
2026-10-20 11:23:08 +08:00 |
|