fix: MKH-BOM 新增日销订单表兜底,修复上游快照漏算导致「库存 0 却不报缺货」 #18

Merged
yanhui merged 1 commits from codex/mkh-sale-fallback-0919 into main 2026-09-19 14:20:53 +08:00
Collaborator

目的与范围

  • 解决的问题:MKH-BOM 补货预警的日销盲区。上游 rpt.pm_stockMonitor.fifteenAvgSale 会漏算
    (快照 09-18 下,窗口内有真实发货的 178 个 MKH 成品编码里 24 个被记成 0,其中 16 个订单全部来自
    样品专用店铺属合理口径,剩 8 个是真实店铺);此时若待发货订单也为 0,判定会落到「无销量」分支、
    hasWarning=false,商品彻底隐身,库存为 0 也不报缺货。
    业务实例:MKHDNZ0409260601548 近 15 天实发 2 件(09-09 / 09-19)、库存 0,页面无任何提示。

  • 修改的模块/文件

    文件 改动
    backend/src/services/mkhBomService.js 新增日销订单表兜底(文件头 ⑦-a);loadPendingOrderIndex 重构为 loadOrderIndex,一条查询同时取「待发货」与「近 15 天已发货」;PENDING_ORDER_TABLES 更名 ORDER_TABLES
    backend/test/mkhBomService.test.js 新增兜底回归测试;订单索引用例补「已发货跨表累加」「样品/测试店排除」断言
    frontend/src/pages/MkhBomData/index.jsx 日销来自兜底时加虚线下划 + 悬停说明
    frontend/src/pages/MkhBomData/style.css 新增 .mkh-sale-fallback 样式
  • 是否使用 AI 及负责开发者:使用 AI 辅助开发,负责开发者 yanhui。

同步与兼容性

  • 起始基线 SHA:d650fd0(PR #17 合并后的 main)
  • 最近同步的 main SHA:d650fd0
  • 待审核任务分支 SHA:3e64702
  • 并行 PR、共享接口、迁移/配置影响:无并行 PR;无数据库迁移、无表结构变更、无配置变更。
    /api/mkh-bom/list 与导出接口只新增响应字段(dailySaleSource / snapshotDailySale /
    recentShippedQty),不删不改既有字段;ORDER_TABLES 是模块内私有导出(原 PENDING_ORDER_TABLES 更名,
    两张表的口径未变),无外部调用方。
  • 冲突及处理理由:无冲突 —— 分支直接从 d650fd0 切出。
  • 如何确认保留了其他开发者的已有行为:git diff --stat 仅涉及上表 4 个文件;
    AGENTS.md / 仓库提交与合并规范.md / 服务器部署规范.md 与 main 零差异(防回退检查通过)。

验证

  • 实际执行的命令及结果
    • cd backend && node scripts/check-syntax.js → 151 文件通过

    • cd backend && node --test test/*.test.js → 338 项 337 通过

    • cd backend && node --test test/mkhBomService.test.js → 44/44 通过

    • cd frontend && node node_modules/eslint/bin/eslint.js src/pages/MkhBomData/index.jsx → 0 问题

    • cd frontend && node node_modules/vite/bin/vite.js build → 构建成功

    • 真实数据端到端(本地接口 /api/mkh-bom/list,全量 4,223 个成品):

      指标 修复前 修复后
      日销 > 0 的商品 177 198
      有效预警(缺货 + 预警) 18 28
      兜底生效(source=orders) — 21

      · 21 个等级变化全部是「无销量 → 缺货 / 正常 / 已排产」,没有任何既有预警被降级
      · 其中 10 个新缺货均为「库存 0 + 近 15 天真实发货 9~27 件」的真盲区
      · MKHDNZ0409260601548:日销 0 → 0.1333(source=orders),不再隐身
      · 样品店排除按整行生效:实测某编码窗口内共 5 件(含样品店 1 件)→ 索引记为 4 件

  • 未运行或失败的检查及原因:resilientSecretStore 权限断言(期望 0o600、Windows 实际 0o666)
    为平台固有失败,与本次改动无关;未在生产环境验证(部署需另行授权)。
  • 人工验证路径(如适用):页面 /planning/operation/mkh-bom-data → 搜索 MKHDNZ0409260601548
    → 日销列显示 0.13 且带虚线下划(悬停说明该值来自订单实算、非上游快照),
    预警由「无销量」变为「已排产」(该成品当前有未完成加工单,按既有 ⑧ 口径不预警)。

风险与回退

  • 已知风险
    • 兜底只作用于「快照日销 = 0」的商品,快照 > 0 的商品行为完全不变,不覆盖上游数字。
    • 订单源不可用时 shippedBySku 为空 → 兜底自动失效,退化成旧口径,不会误报。
    • 兜底值来自订单表实算,与上游快照存在口径差异,前端已加来源提示避免业务误判为数据错误。
    • 若某商品同时有「快照日销 0 + 近 15 天发货 + 待发货订单」,会从「订单驱动」切到「日销驱动」
      分支(已有真实需求历史时用「结余库存 ÷ 日销」衡量更准),已加单测锁定该优先级。
  • 回退方式和数据兼容性:本提交可整体 revert;无表结构变更、无数据迁移,回退不涉及数据恢复。
## 目的与范围 - **解决的问题**:MKH-BOM 补货预警的**日销盲区**。上游 `rpt.pm_stockMonitor.fifteenAvgSale` 会漏算 (快照 09-18 下,窗口内有真实发货的 178 个 MKH 成品编码里 **24 个被记成 0**,其中 16 个订单全部来自 样品专用店铺属合理口径,剩 8 个是真实店铺);此时若待发货订单也为 0,判定会落到「无销量」分支、 `hasWarning=false`,**商品彻底隐身,库存为 0 也不报缺货**。 业务实例:`MKHDNZ0409260601548` 近 15 天实发 2 件(09-09 / 09-19)、库存 0,页面无任何提示。 - **修改的模块/文件** | 文件 | 改动 | |---|---| | `backend/src/services/mkhBomService.js` | 新增日销订单表兜底(文件头 ⑦-a);`loadPendingOrderIndex` 重构为 `loadOrderIndex`,一条查询同时取「待发货」与「近 15 天已发货」;`PENDING_ORDER_TABLES` 更名 `ORDER_TABLES` | | `backend/test/mkhBomService.test.js` | 新增兜底回归测试;订单索引用例补「已发货跨表累加」「样品/测试店排除」断言 | | `frontend/src/pages/MkhBomData/index.jsx` | 日销来自兜底时加虚线下划 + 悬停说明 | | `frontend/src/pages/MkhBomData/style.css` | 新增 `.mkh-sale-fallback` 样式 | - **是否使用 AI 及负责开发者**:使用 AI 辅助开发,负责开发者 **yanhui**。 ## 同步与兼容性 - 起始基线 SHA:`d650fd0`(PR #17 合并后的 main) - 最近同步的 main SHA:`d650fd0` - 待审核任务分支 SHA:`3e64702` - **并行 PR、共享接口、迁移/配置影响**:无并行 PR;**无数据库迁移、无表结构变更、无配置变更**。 `/api/mkh-bom/list` 与导出接口只**新增**响应字段(`dailySaleSource` / `snapshotDailySale` / `recentShippedQty`),不删不改既有字段;`ORDER_TABLES` 是模块内私有导出(原 `PENDING_ORDER_TABLES` 更名, 两张表的口径未变),无外部调用方。 - **冲突及处理理由**:无冲突 —— 分支直接从 `d650fd0` 切出。 - **如何确认保留了其他开发者的已有行为**:`git diff --stat` 仅涉及上表 4 个文件; `AGENTS.md` / `仓库提交与合并规范.md` / `服务器部署规范.md` 与 main **零差异**(防回退检查通过)。 ## 验证 - **实际执行的命令及结果** - `cd backend && node scripts/check-syntax.js` → **151 文件通过** - `cd backend && node --test test/*.test.js` → **338 项 337 通过** - `cd backend && node --test test/mkhBomService.test.js` → **44/44 通过** - `cd frontend && node node_modules/eslint/bin/eslint.js src/pages/MkhBomData/index.jsx` → **0 问题** - `cd frontend && node node_modules/vite/bin/vite.js build` → **构建成功** - **真实数据端到端**(本地接口 `/api/mkh-bom/list`,全量 4,223 个成品): | 指标 | 修复前 | 修复后 | |---|---|---| | 日销 > 0 的商品 | 177 | **198** | | 有效预警(缺货 + 预警) | 18 | **28** | | 兜底生效(source=orders) | — | 21 | · 21 个等级变化**全部是「无销量 → 缺货 / 正常 / 已排产」**,**没有任何既有预警被降级** · 其中 **10 个新缺货**均为「库存 0 + 近 15 天真实发货 9~27 件」的真盲区 · `MKHDNZ0409260601548`:日销 `0 → 0.1333`(source=orders),不再隐身 · 样品店排除按**整行**生效:实测某编码窗口内共 5 件(含样品店 1 件)→ 索引记为 4 件 - **未运行或失败的检查及原因**:`resilientSecretStore` 权限断言(期望 `0o600`、Windows 实际 `0o666`) 为**平台固有失败**,与本次改动无关;未在生产环境验证(部署需另行授权)。 - **人工验证路径(如适用)**:页面 `/planning/operation/mkh-bom-data` → 搜索 `MKHDNZ0409260601548` → 日销列显示 `0.13` 且带虚线下划(悬停说明该值来自订单实算、非上游快照), 预警由「无销量」变为「已排产」(该成品当前有未完成加工单,按既有 ⑧ 口径不预警)。 ## 风险与回退 - **已知风险** - 兜底**只作用于「快照日销 = 0」的商品**,快照 > 0 的商品行为完全不变,不覆盖上游数字。 - 订单源不可用时 `shippedBySku` 为空 → 兜底自动失效,**退化成旧口径,不会误报**。 - 兜底值来自订单表实算,与上游快照存在口径差异,前端已加来源提示避免业务误判为数据错误。 - 若某商品同时有「快照日销 0 + 近 15 天发货 + 待发货订单」,会从「订单驱动」切到「日销驱动」 分支(已有真实需求历史时用「结余库存 ÷ 日销」衡量更准),已加单测锁定该优先级。 - **回退方式和数据兼容性**:本提交可**整体 revert**;无表结构变更、无数据迁移,回退不涉及数据恢复。
yanhui added 1 commit 2026-09-19 13:25:11 +08:00
背景
- 业务反馈 MKHDNZ0409260601548 近 15 天实发 2 件、库存 0,页面既不报缺货也不进任何预警。
- 根因是双重盲区:
  ① 上游 rpt.pm_stockMonitor.fifteenAvgSale 漏算 —— 快照 09-18 下,窗口内有真实发货的
     178 个 MKH 成品编码里有 24 个被记成 0;其中 16 个订单全部来自样品专用店铺(上游同样
     不计样品单,属合理口径),剩 8 个是真实店铺(天猫/京东/拼多多)。该列是**保留两位
     小数的日均**(最小非零 0.07 = 1 件 ÷ 15 天),无法用四舍五入解释。
  ② 快照日销 = 0 且待发货订单 = 0 时,判定直接落到「无销量」分支、hasWarning=false,
     彻底排除在预警之外 —— 库存为 0 也救不回来。

改动
- 日销兜底(服务文件头新增 ⑦-a):**快照日销 ≤ 0 且近 15 天有真实发货**时,日销改用
  订单表实算值 = 已发货件数 ÷ 15;快照 > 0 一律以上游为准,不覆盖,避免两套口径打架。
  · 排除样品/测试专用店铺(shop_name 含「样品」「测试」),与上游口径对齐
  · 窗口 = 含今天在内的 15 个自然日,与分母同口径
  · 结果透出 dailySaleSource('snapshot'|'orders') / snapshotDailySale / recentShippedQty
- 取数:loadPendingOrderIndex 重构为 loadOrderIndex,一条查询同时覆盖「待发货
  (send_date IS NULL)」与「近 15 天已发货(send_date 在窗口内)」。实测合并后总耗时
  约 1.25s ≈ 单查待发货,而拆成两条独立查询要在现有开销之上再加一次全表扫描
  (首轮冷执行计划下 qmcust 单表可劣化到 20s+)。
- PENDING_ORDER_TABLES 更名 ORDER_TABLES:两张表的口径没变,只是不再仅服务待发货。
- 前端:日销来自兜底时加虚线下划 + 悬停说明,业务拿该数字与源系统 fifteenAvgSale
  对照时不会被误判为数据错误。

验证
- 语法检查:151 文件通过。
- 后端测试:338 项 337 通过。唯一失败为 resilientSecretStore 的权限断言(期望 0o600、
  Windows 实际 0o666),平台固有,与本改动无关。
- 新增回归测试:兜底生效 / 快照非零不覆盖 / 无发货不兜底 / 与订单驱动的优先级 /
  订单源降级时退化成旧口径;订单索引测试补充「已发货同编码跨表累加」与「样品、测试店排除」断言。
- 真实数据端到端(本地接口 /api/mkh-bom/list,4,223 个成品):
  · MKHDNZ0409260601548:日销 0 → 0.1333(source=orders),不再隐身
  · 日销 > 0 的商品 177 → 198;有效预警(缺货 + 预警)18 → 28
  · 21 个等级变化**全部是「无销量 → 缺货/正常/已排产」**,没有任何既有预警被降级
  · 其中 10 个新缺货均为「库存 0 + 近 15 天真实发货 9~27 件」的真盲区
  · 样品店排除按整行生效:实测某编码窗口内 5 件(含样品店 1 件)→ 索引记为 4 件
- 前端 eslint 通过、vite build 通过。

风险与回退
- 只影响「快照日销 = 0」的商品,快照 > 0 的商品行为完全不变。
- 订单源不可用时兜底自动失效,退化成旧口径(不会误报)。
- 回退:本提交可整体 revert,无表结构变更、无数据迁移。
yanhui merged commit 4946018251 into main 2026-09-19 14:20:53 +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#18
No description provided.