|
|
f6b755aac4
|
feat: 新增每日进销存(大货库存监控)
|
2026-09-23 17:37:41 +08:00 |
|
|
|
700d7ae96d
|
feat: 新增每日进销存(大货库存监控)
|
2026-09-23 17:37:40 +08:00 |
|
|
|
37c1adf4f2
|
feat: 新增每日进销存(大货库存监控)
|
2026-09-23 17:37:39 +08:00 |
|
|
|
d52151d7dc
|
Merge pull request 'refactor: 移除大货库存监控功能(26 仓)' (#29) from codex/remove-large-stock-monitor-0923 into main
|
2026-09-23 16:12:50 +08:00 |
|
|
|
592c52f891
|
refactor: 移除大货库存监控功能(26 仓)
应用户要求下线该功能组,侧边栏不再显示「大货库存监控」及 26 个仓库二级菜单。
移除内容:
- 前端页面 LargeStockMonitor、18 列定义、API 封装与样式
- 路由 /planning/large-stock-monitor/:warehouseSlug 与权限点 large_stock_monitor
- 侧边栏菜单组 stock-monitor-large 与 SIDEBAR_ROUTE_MAP 26 条映射
- 后端 /api/large-stock-monitor 路由挂载、largeStockMonitor.js、
largeStockMonitorConfig.js、largeStockMonitorFlow.js 及端到端校验脚本
验证:
- 全仓无 largeStockMonitor 残留引用;后端 346 用例 345 通过
(1 项失败为 resilientSecretStore.test.js,Windows 权限语义基线既有失败)
- 前端 lint + 测试 + 构建通过,产物中 LargeStockMonitor chunk 已消失
- 运行时验证:/api/large-stock-monitor/* 返回 404,页面与登录不受影响
|
2026-09-23 16:07:04 +08:00 |
|
|
|
d4e6c6e4a2
|
Merge pull request 'fix: 配件库存用最新箱仓数据补齐' (#28) from codex/component-pack-stock-fallback-0923 into main
|
2026-09-23 15:43:53 +08:00 |
|
134
|
66e0ddf709
|
fix: 用箱仓库存补齐滞后的配件库存
|
2026-09-23 15:42:46 +08:00 |
|
|
|
2e52e4a393
|
Merge pull request 'feat: 新增大货库存监控(26 仓)' (#27) from codex/large-stock-monitor-0923 into main
|
2026-09-23 14:01:44 +08:00 |
|
|
|
dcf3375dbe
|
Merge branch 'main' into codex/cangku-returns-monitor-001 (sync #25 #26)
|
2026-09-23 13:50:26 +08:00 |
|
|
|
2bc949099b
|
feat: 新增大货库存监控(26 仓)
按「库存监控」表格在侧边栏增加一级/二级菜单与共用页面
(路由 /planning/large-stock-monitor/:warehouseSlug)。
后端:
- 新增 largeStockMonitorConfig.js:26 仓配置,含 wms_co_id / 快照仓名 / routeKey / slug 映射
- 新增 largeStockMonitor.js:config / overview / sku-list 三个接口
- 新增 largeStockMonitorFlow.js:采购进仓、采购退货、销售出仓、销售退货四类单据流水取数
- 新增 verify-large-stock-monitor.js:26 仓端到端校验脚本
- index.js:挂载 /api/large-stock-monitor 路由
口径说明:
- 库存周转天数 = 月度发货总件数 / 月度日均库存件数 × 100%,下沉到 SQL CASE WHEN 计算
- 四级分级(滞销预警/慢流/呆滞/僵尸)按库龄 + 周转率双维度判定
- 期末数量取聚水潭实际库存 jst_inventoryInfo(按 wms_co_id 单仓),
不用 pm_stockMonitor.jsStock —— 后者 warehouse 是「归属仓」口径,
会把已调拨到外仓的货仍计在本仓名下(崔桥工厂仓因此虚高约 2.2 万)
- 截止时刻统一为快照日次日 01:00(跑批时点),库龄/分级均以此为基准,不再用 GETDATE()
前端:
- 新增 LargeStockMonitor 页面、18 列定义、API 封装与样式
- 路由与侧边栏:26 条二级菜单 + 权限点
冲突处理:
- 主仓库已下线 /api/dashboard(backend/src/api/dashboard.js 被移除),
相应删除 dashboardRoutes 的 require 与挂载,避免后端启动时 require 失败崩溃
- 保留主仓库新增的「模块化库存监控」菜单组及其子项
|
2026-09-23 13:42:58 +08:00 |
|
|
|
254d1dd9f9
|
Merge pull request 'docs: 明确前端冒烟检查的预期鉴权响应' (#26) from codex/deploy-doc-frontend-smoke-0923 into main
|
2026-09-23 11:54:13 +08:00 |
|
134
|
181ea0237d
|
docs: 修正文档排版
|
2026-09-23 11:52:32 +08:00 |
|
134
|
783bb5182b
|
docs: 明确登录页预期鉴权响应
|
2026-09-23 11:52:07 +08:00 |
|
|
|
ac44a00a49
|
Merge pull request 'docs: 补充前端骨架屏部署验收规范' (#25) from codex/deploy-doc-frontend-smoke-0923 into main
|
2026-09-23 11:45:04 +08:00 |
|
134
|
b3b38d4aa2
|
docs: 增加前端部署运行时验收规则
|
2026-09-23 11:43:29 +08:00 |
|
|
|
acc088dd1a
|
Merge branch 'main' into codex/cangku-returns-monitor-001
|
2026-09-23 11:39:39 +08:00 |
|
|
|
5034cc5782
|
Merge pull request 'fix: 根治前端循环分块导致的永久骨架屏' (#24) from codex/web-bootstrap-fix-0923 into main
|
2026-09-23 11:36:49 +08:00 |
|
134
|
d32494ca12
|
fix: 消除前端循环分块导致的骨架屏
|
2026-09-23 11:35:47 +08:00 |
|
|
|
b3f4d1af64
|
feat: 客退数据监控五仓看板与本地运行稳定性加固
功能:
- 退货库存预警:横林二仓/苏北/睢宁/山东青岗集/河南洛宁 五张看板,
按各仓客退上架仓对应的自定义库存位取数(各仓位不同:横林/苏北/睢宁 c1,山东 c2)
- 支持导入聚水潭「箱及仓位库存」导出文件覆盖创建时间、件数、供应商
(副本只有仓库级数量,没有箱级维度,真实上架时间只能靠导入)
- 退货入库核销与每日库存监控:状态落本地系统库,含定时任务与差异清单
- 服务端导出(xlsx/csv),不受列表分页与展示上限约束
稳定性:
- 前端幂等请求在后端重启窗口内自动退避重连,不再直接抛 500
- 后端补齐统一 JSON 错误兜底与 /api JSON 404,异常不再返回 HTML
- 隧道 ssh 改为绝对路径解析并隐藏窗口,避免非交互式启动 ENOENT 与黑窗弹窗
- 业务库 DATETIME 按北京时间解析(useUTC: false),修复时间显示整体 +8 小时
合并:与上游「隧道断线自愈 + 配置负缓存」为同一问题的两份实现,
本分支采用上游方案(带 TTL 的负缓存 + 统一失效函数 + 单测)。
|
2026-09-23 11:32:16 +08:00 |
|
|
|
6b0bf59e74
|
Merge pull request 'fix: MKH 刷新实时读取订单库存并阻止旧请求覆盖' (#23) from codex/mkh-sync-freshness-0923 into main
|
2026-09-23 11:18:13 +08:00 |
|
134
|
b8e60a19d2
|
fix: MKH刷新改用实时订单库存口径
|
2026-09-23 11:12:08 +08:00 |
|
134
|
fa91b9242c
|
fix: 根治MKH同步后旧请求覆盖最新数据
|
2026-09-23 11:05:28 +08:00 |
|
|
|
97a93766e8
|
Merge pull request 'fix: MKH-BOM 配件仓位仓名改用 wms_co_id 关联,修正误显示「常州简尚家居有限公司」' (#22) from codex/mkh-pack-warehouse-join-0919 into main
|
2026-09-22 23:58:40 +08:00 |
|
|
|
f4303c0aed
|
Merge pull request 'fix: 隧道断线自愈 + 修复配置负缓存永久固化(根治「隧道一断即整栈不可用 / 必须重启后端」)' (#21) from codex/tunnel-selfheal-config-cache-0922 into main
|
2026-09-22 23:58:24 +08:00 |
|
|
|
a58fc7d67b
|
Merge pull request 'fix: MKH-BOM 修复在途旧查询回写缓存,导致「同步最新数据」点完仍是旧数据' (#20) from codex/mkh-cache-stale-write-fix-0922 into main
|
2026-09-22 23:58:04 +08:00 |
|
|
|
bbabda5016
|
Merge pull request 'fix: MKH-BOM 预警与库存口径修复:日销以出库为准 + 库存增加箱表兜底' (#19)
|
2026-09-22 23:14:37 +08:00 |
|
|
|
38fa3f2127
|
fix: 隧道断线自愈 + 修复配置负缓存永久固化
远端 RST 会把隧道反复踢掉(09-20 实测当天断 5 次,每次存活 15~25 分钟)。原先的
实现把「一次网络抖动」放大成「整个开发栈不可用」,根因有两处,本次一并修掉。
1) scripts/start-remote.mjs —— 隧道断了却拉着应用陪葬
隧道进程 exit 时直接 stopChildren(),把后端和前端一起杀掉,于是每一次 RST 都
升级成一次「网站打不开」,还得靠外部守护清残留、整树重启才能恢复。
改为「退避重连隧道本身,后端/前端进程不受影响」——这正是 autossh 的语义
(autossh 只负责重启 ssh,从不牵连被它服务的应用)。建立失败走 1s→30s 指数
退避,建立成功即重置退避。
顺带补齐保活参数:TCPKeepAlive=yes、ConnectTimeout=10,探活间隔 30s → 15s,
以便更快发现已被对端 RST 的死连接。
2) backend/src/config/cloudDatabase.js —— 配置负缓存被永久固化
系统库读取失败时把「空候选」写进缓存,而入口判断是
`if (cachedCloudConfigCandidates)` —— [] 在 JS 里是 truthy,于是这次失败被
永久固化:之后永不重试,直接抛「业务数据库尚未配置」500,隧道恢复了也没用,
必须重启后端。
改为「带 TTL 的负缓存」(默认 30s,可用 CLOUD_DB_CONFIG_NEGATIVE_TTL_MS 调整):
入口用 `!== null` 判断,失败态到期自动重新解析,使隧道恢复后无需人工干预。
缓存清理收敛到 invalidateCloudConfigCache() 单点,避免日后只清数组、忘了清
TTL 时间戳而退化回「永不过期」。同时让「测试连接」入口显式失效,保证用户改完
配置点测试能立即看到真实结果。
顺带把请求超时默认值 600000ms → 60000ms:10 分钟 × maxAttempts=2 最坏挂
20 分钟,用户体感就是「卡死」;冷查实测 16~42s,60s 仍有充足余量。
验证:
- 新增 backend/test/cloudDatabaseConfigCache.test.js,锁定负缓存三段语义
(TTL 内不重复解析 / 到期必须重试 / 上游恢复后自动复原且成功态不被 TTL 淘汰);
test/remote-access.test.mjs 补隧道保活参数、退避曲线,以及「自愈循环不得调用
stopChildren」的防腐化断言。
- 两处均做对照组实验:去掉保护后测试如实报错(负缓存 → 报 TTL 到期未重试;
隧道 → 把 stopChildren 加回去即被用例抓住),证明用例真能拦住回归。
- 后端全量 340 项 / 339 通过,唯一失败为 resilientSecretStore 的 Windows 权限
断言(环境固有,与本次无关);check-syntax 通过。
- 本地实测:kill 掉隧道进程后 1 秒内自动重建(PID 36268 → 15816),后端进程
PID 未变(29580)、/health 仍返回 200。
|
2026-09-22 19:32:40 +08:00 |
|
|
|
add148192d
|
fix: MKH-BOM 修复在途旧查询回写缓存,导致「同步最新数据」点完仍是旧数据
问题:点「同步最新数据」后,页面在 2~10 分钟内仍显示旧数据,用户表现为「像在用缓存」。
根因:本页 10 个进程内缓存都是「先挂 pending → await 结果 → 无条件写回」的写法。
若某个查询 in-flight 期间用户点了同步(clearMkhBomCache 清空全部缓存),那个**发起于
清缓存之前**的查询读到的仍是旧快照,返回时会无条件把旧值写回缓存,覆盖掉清缓存后新
请求填入的新值 —— 谁后完成谁生效。并发越高(多人同时使用、或先点刷新再点同步)越容
易撞上,且清缓存反而把旧数据「钉」在缓存里 2~10 分钟(各缓存 TTL),表现为按钮失灵。
修法:引入缓存代际 cacheEpoch。
- clearMkhBomCache() 先自增代际,再清各缓存;
- 每个查询发起时记下当时的代际,写回缓存前比对:代际对不上说明中途被清过缓存,
本次结果只返回给发起它的那个请求,不写缓存。
覆盖 7 个变量型缓存(officialBomIndex / businessBom / productBom / mkhProduct /
workOrderIndex / orderIndex / replenishIndex)与 3 个按 key 的 Map 缓存
(officialBomDetail / componentLocation / componentStock)。
POST /api/mkh-bom/sync 响应新增 cacheEpoch 字段,便于线上核对清缓存是否生效。
验证:
- 新增 2 个用例:① 复现竞态——对照组(去掉写回保护)报 expected 1 / actual 9,正是
「点同步后拿到旧值」;加保护后通过。② 同代际内缓存照常命中的回归保护。
- 后端全量 node --test test/*.test.js:340 用例 / 339 通过,唯一失败为环境固有的
resilientSecretStore Windows 权限断言(与基线一致)。
- 本地端到端:预热 24.3s → 缓存 58ms;sync 返回 cacheEpoch=1;冷查 8.5s 确实重查;
再查 37ms 缓存重新生效;数据指纹前后一致。
|
2026-09-22 18:39:09 +08:00 |
|
|
|
5e029f831f
|
fix: MKH-BOM 库存增加箱表兜底,修复「当日入库当天显示库存为 0」
现象:MKHDNZ0409260700491 当天已入库、实物有货,但页面上「现有库存」一直是 0;
点「同步最新数据」也不变。
根因:页面唯一的库存源 rpt.jst_inventoryInfo 是「有变化才写 updateTime」的同步表,
品牌仓尤其稀疏。该 SKU 在 inventoryInfo 里只有主仓 14138566 的一行 qty=0,
品牌仓连行都没有;而小时级的箱表 rpt.jst_packInventoryInfo 里,
品牌仓 bin='P-1' / qty=4 / createTime 当天 15:19 —— 当日入库的货只有箱表看得见。
页面取不到实时值 → 回退 09-18 监控快照 jsStock=0 → 显示 0。
「同步最新数据」按钮只清服务端缓存,不触发上游同步,所以点多少次都是 0。
改法(只补 0,不覆盖有效值):
① inventoryInfo 品牌仓 qty > 0 → 用它(stockSource='realtime',旧口径不变)
② 否则箱表品牌仓 bin 非空行合计 > 0 → 用它(stockSource='pack')
③ 否则 inventoryInfo 有品牌仓行(qty≤0) → 用 0
④ 否则回退监控快照 jsStock → stockSource='monitor'
为什么不拿箱表当主源:两表都 > 0 且不等的 28 个成品里 25 个是 inventoryInfo 更大
(箱表只记已装箱的货,散货不计),拿它当主源会系统性低估库存、误报缺货。
另外必须过滤 bin 为空的行——那是装箱记录不是库存(品牌仓合计 21 件)。
影响面(实测):品牌仓 4,408 个成品里只有 3 个走箱表兜底
(MKHDNZ0409260601548=5 / MKHDNZ0409260700059=4 / MKHDNZ0409260700491=4,
同一箱 115870495000001201、货位 P-1、均为当天新建行),
预警列表 27 → 24 条(缺货 21 → 18),少的正是这 3 条「入库当天被误判缺货」。
前端:「现有库存」列 tooltip 增加 pack 来源说明(当天入库、实时库存表尚未同步)。
验证:check-syntax 151 文件通过;node --test 339/340(唯一失败是既有的
resilientSecretStore Windows 权限断言);mkhBomService.test.js 47/47;
HTTP 实测该 SKU stock=4 / stockSource=pack,点「同步最新数据」后仍正确。
遗留(需业务/上游跟进):inventoryInfo 当天 06:41 后就没再更新过
(09-17 / 09-18 都是全天逐小时更新),疑似上游同步任务异常;
箱表兜底只解决「当日入库看不见」,存量库存仍依赖该表恢复。
|
2026-09-19 18:11:38 +08:00 |
|
|
|
0e0fcbb378
|
fix: MKH-BOM 日销兜底增加出库校验,标了「已发货」但无实际出库的单不计销量
业务口径:标了「已发货」但没有实际出库的 SKU,不需要出现缺货预警。
即在 ⑦-a 的金额/标签判据之上,再以出库表为准判定「有没有真出货」。
已发货分支计入销量需同时满足(待发货分支不变):
1. 非样品/测试店铺(⑦-a)
2. 非异常单:pay_amount <= 0.01 或 labels 含「特殊单」(⑦-a)
3. 出库表能查到该 o_id:rpt.jst_salesOutboundInfo / rpt.jst_qmcust_salesoutbound
两个必须豁免的情形(否则会大面积误杀真实单):
· 拆分单(status='Split'):出库记录挂在子单上,父单必然查不到。
实测窗口内 40 张 Split 单 100% 无出库记录;不豁免会误杀
「京东-HX旗舰店104 09-19 实付 1194.14 元」这类真实单
· 今天发货的单:当天出库尚未同步完(09-19 全天出库表仅 1 行,09-18 有 14,690 行)。
豁免只到「昨天」为止——09-18 的 18 张无出库 Sent 单 100% 是特殊单,
且出库表 09-18 数据完整,豁免 09-18 会放过刷单
豁免只免「出库校验」,金额/标签判据照样生效。
真实数据验收(近 15 天窗口,1,086 张已发货 MKH 单):
· 出库表命中 889 张 → 197 张无出库 = 40 张拆分单 + 157 张 Sent
· 157 张里 131 张是 ≤0.01 元或「特殊单」(即 ⑦-a 已排除的刷单),
唯一 1 张正常金额的是 09-05 的 388 元单(标签「发货后取消/发货后售后/风控订单」)
· 兜底生效 SKU = 12 个(与只按金额/标签判据一致),兜底日销最大值 = 0.2
· 10 个异常单 SKU 在预警列表中零出现;总预警 27 条(缺货 21 + 仅预警 6)
性能:1086 个订单号分 2 块并行 UNION 查询,冷缓存约 0.4~1.4s;
整窗口拉全量 DISTINCT o_id 实测要 8s,故不采用。
降级策略:出库数据取不到时整体跳过出库校验(退化成只按金额/标签判据)并标 degraded。
漏杀只是多报一条预警,错杀会让真实缺货彻底隐身。
注意:jst_salesOutboundInfo.qty 全表 1,119,606 行恒为 0,只能判「有没有出库行」,
不能用 SUM(qty)(永远得 0)。
验证:check-syntax 151 文件通过;node --test 338/339(唯一失败是既有的
resilientSecretStore Windows 权限断言,与本改动无关);mkhBomService.test.js 45/45。
|
2026-09-19 17:53:16 +08:00 |
|
|
|
da08b03dcb
|
fix: MKH-BOM 配件仓位仓名改用 wms_co_id 关联,修正误显示「常州简尚家居有限公司」
根因
- rpt.jst_packInventoryInfo.wh_id **不是** rpt.jst_warehouseInfo.Id:
全表只有 6 个取值(1/2/3/4/6/7),90% 的行都是 wh_id=1,
按 `w.Id = p.wh_id` 关联后,727 个配件的仓位仓名被统一显示成「常州简尚家居有限公司」。
- 该表的仓 id 列是自带的 `wms_co_id`:
09-19 实测 30,236 行 **0 NULL**、14 个仓取值,按 `w.wms_co_id = p.wms_co_id`
关联即得到正确仓名「简尚-崔桥工厂半成品包装项目仓」。
- 旧结论「该表 wms_co_id 全表 NULL,必须用 wh_id」只对 09-17 的表状态成立
(当时 85,152 行、只有 3 个箱、bin 全空、与 MKH 配件零交集);
该表由上游**小时级增量刷新**(业务确认 09-19),09-18 重建后 wms_co_id 已全部有值。
改动
- backend/src/services/mkhBomService.js
· loadComponentLocations() 的 JOIN 与 GROUP BY:p.wh_id → p.wms_co_id
· 文件头 ⑤ 与 formatPackLocation() 注释更新为正确关联列,并记录该表的小时级
增量刷新特性(行级 updateTime 只反映「有变化的行」,不能当表的更新频率;
行数/箱数/命中数会随上游变化,不要写死)。
- backend/src/services/modularStockMonitorService.js
· 补注:该页只取 bin、不 join 仓名,不受此关联列问题影响;命中数会随上游变化。
- backend/test/mkhBomService.test.js
· 仓位格式化用例的 fixture 改回真实仓名,避免继续固化错误仓名。
影响与验证
- 仅影响 MKH-BOM 页「配件仓位」列与导出配件明细的仓名文本;
A4 打印预览本就会去掉仓名前缀(stripWarehousePrefix),不受影响。
- 配件监控页不 join 仓名,无影响。
- 实测:修正后 15 个真实配件编码输出「简尚-崔桥工厂半成品包装项目仓 · BCP01-01-1」。
- 后端:check-syntax 151 文件通过;node --test 337/338 通过
(唯一失败为既有环境问题 resilientSecretStore 权限断言,与本改动无关)。
|
2026-09-19 17:08:00 +08:00 |
|
|
|
dff2422b34
|
fix: MKH-BOM 日销兜底排除 0.01 元特殊单,修复「没发过货却有日销」
背景
- 业务反馈 MKHDNZ0410260700005 从没发过货,页面却显示日销 0.6 并报缺货。
- 根因是上一版日销兜底(⑦-a)只看 send_date,把「0.01 元特殊单」当成了真实销售:
· 拼多多-亿家达书房家具官方旗舰店在 2026-09-18 10:35–10:36 **一分钟内**批量生成
18 张 MKH 订单,实付合计仅 0.18 元、免额合计 16,344 元(单张:实付 0.01 元、
商品额 908.01、免额 908 全额免),备注统一为 SSDD,标签含「特殊单」「转x00005」;
同批还有 18 张商品码为占位码 1.X00005 的 0.01 元订单。
· 这批单 status=Sent、有物流单号,但 **rpt.jst_salesOutboundInfo 里查不到对应出库记录**
(同店同时段其他订单有 6,467 行出库)—— 即系统标了「已发货」,货并没有真出。
· 后果:把 10 个 SKU 的日销从 0 抬到 0.6~1.8(共 162 件),凭空产出缺货预警。
改动
- loadOrderIndex 的**已发货分支**增加异常单过滤:命中任一即不计入日销
· pay_amount <= 0.01(0.01 元全免单)
· labels 含「特殊单」
- 查询补充取出 o.pay_amount / o.labels(两张订单表均有这两列,已确认)。
- 金额取不到(null / 空串 / 非数字)时**不排除** —— 宁可少排也别误杀,取不到就退化成旧口径。
- ⚠️ **不使用 free_amount > 0 作为判据**:正常订单普遍带折扣免额(窗口内实测 569/1086 行
free_amount > 0),按它筛会把实付 356 / 458 / 1338 元的正常单一并误杀(第一版即踩此坑)。
- 本次只作用于「已发货」分支,待发货(⑩)口径未改动。
验证
- 语法检查:151 文件通过。
- 后端测试:338 项 337 通过。唯一失败为 resilientSecretStore 权限断言(期望 0o600、
Windows 实际 0o666),平台固有,与本改动无关。mkhBomService 单测 44/44。
- 新增回归断言:实付 0.01 / 0 元及字符串金额判为异常单;标签含「特殊单」判为异常单;
**有免额的正常单不得被排除**;金额缺省/为 null/非数字时不排除。
- 订单索引用例补充:0.01 元单与「特殊单」标签行不进入 shippedBySku,而带折扣免额的
正常单照常计入(shippedBySku.size 由 1 调整为 2)。
- 真实数据端到端(业务库实时,窗口 15 天):
· 应排除的 10 个异常单 SKU(MKHDNZ0410260700001/0002/0004/0005/0007/0008/0009/
0010/0011/0012)**全部已排除**;
· 应保留的 12 个真实单 SKU **全部保留**(含本问题之外的 MKHDNZ0409260601548 等);
· 兜底生效 SKU 由 **22 → 12**;兜底日销最大值由 **1.8000 → 0.2000**
(0.2 来自 MKHDNZ0409260700483 的 3 件真实发货,属正常)。
风险与回退
- 只影响「快照日销 ≤ 0 且近 15 天有发货」的商品;快照 > 0 的商品行为完全不变。
- 实付 ≤ 0.01 元的订单在任何业务口径下都不构成正常销售,排除不会误伤真实需求。
- 回退:本提交可整体 revert,无表结构变更、无数据迁移。
- 遗留(未在本次改动内):待发货(⑩)分支同样可能被 0.01 元单驱动,
是否一并排除需业务确认;「售后 / 补发」类订单是否计入亦有待定夺。
|
2026-09-19 14:49:39 +08:00 |
|
|
|
4946018251
|
Merge pull request 'fix: MKH-BOM 新增日销订单表兜底,修复上游快照漏算导致「库存 0 却不报缺货」' (#18)
|
2026-09-19 14:20:52 +08:00 |
|
|
|
3e64702f12
|
fix: MKH-BOM 新增日销订单表兜底,修复上游快照漏算导致「库存 0 却不报缺货」
背景
- 业务反馈 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,无表结构变更、无数据迁移。
|
2026-09-19 13:24:15 +08:00 |
|
|
|
d650fd02f2
|
Merge pull request 'fix: MKH-BOM 待发货订单合并两张渠道表,修复天猫/淘宝订单漏报' (#17)
|
2026-09-19 13:06:11 +08:00 |
|
|
|
f156b90dbb
|
fix: MKH-BOM 待发货订单合并两张渠道表,修复天猫/淘宝订单漏报
问题
MKHDNZ0409260700059、MKHDNZ0409260601228 等成品明明有待发货订单,
补货预警却显示 0 单、判为「不预警」,业务反馈到系统。
根因
业务库的订单按渠道拆成**互不重叠的两张表**,而 loadPendingOrderIndex()
只读了 rpt.jst_Orders(京东/京喜/拼多多/抖音/得物/唯品会),
天猫 / 淘宝 / 1688 / 阿里巴巴渠道的待发货需求**全部被漏掉**。
实测(2026-09-19,近 30 天窗口)
- 两表店铺名交集 0(jst_Orders 398 店 vs qmcust 78 店),so_id 交集 5 条,
o_id 交集 **0 条** —— 合并无重复计件风险;
- jst_Orders 里 `shop_name LIKE '%天猫%'` = **0 行**
(不是命名差异:同一品牌在不同平台是不同店铺名,按品牌名反查会误判);
- MKH 成品待发货:仅 jst_Orders = 36 编码 / 50 件 →
合并后 = **40 编码 / 56 件**,补回 4 个编码:
MKHDNZ0409260700059 / MKHDNZ0409260601228 /
MKHDNZ0409260601530 / MKHDNZ0409260700498。
修复
- PENDING_ORDER_TABLE → PENDING_ORDER_TABLES(两张表,新增 qmcust 源);
- Promise.allSettled 并行读取,按 o_id 去重后合并(去重为防御性逻辑);
- 部分源失败时**保留成功源的数据**并透出 degraded=true,用短 TTL 尽快重试,
不再像原先那样单源失败就整表降级为空索引(那会静默漏报全部需求);
- 口径范围:**渠道全并**(2026-09-19 业务确认),含 1688 / 阿里巴巴 /
分销导单专用店铺。
测试
- 新增 2 个回归测试:两张表都读到 + 跨表同 o_id 只计一次 + 组合装不计入;
单源失败时 degraded=true 且成功源数据保留。
- 后端 337 项:336 通过,1 项失败为 Windows 平台固有差异
(resilientSecretStore 断言 0o600,Windows 实际 0o666),与本改动无关。
- 语法检查 151 文件通过。
|
2026-09-19 12:25:43 +08:00 |
|
|
|
cc66e5100f
|
Merge pull request 'feat: MKH-BOM 补货预警(订单驱动 + 配件不足)与日销口径改近 15 天' (#16)
|
2026-09-18 17:56:13 +08:00 |
|
|
|
b7aca2343b
|
feat(配件库存监控/MKH-BOM): 新增「采购在途」数量列与在途明细弹窗,补货预警改按结余库存判定
配件库存监控:
- 列表「实时库存」后新增「采购在途数量」列,口径 SUM(MAX(0, jst_purchaseItem.qty - inQty))
- 顶部「实时库存合计」后新增可点击「采购在途」卡片,弹窗展示在途明细
(商品编码/商品名称/采购数量/已入库/在途数量/计划到货日期/采购单号),支持关键词、排序、分页
- 新增后端接口 GET /api/modular-stock-monitor/purchase-in-transit
- 筛选器品类改直查 jst_sku 主表,避免新增 OUTER APPLY 拖慢筛选
- 采购在途列仅取数展示,不参与 ORDER BY / 聚合(规避 MSSQL 对 OUTER APPLY 列的灾难性执行计划)
MKH-BOM 补货预警:
- 日销驱动与已排产分支改用「结余库存 = 现有库存 - 待发货件数」判定可用天数与预警等级
- 建议补货量按 ceil(3 天日销 + 待发货件数 - 现有库存) 计算
- 新增 scope=work-order 范围筛选;列表新增「结余库存」列
- 打印模板剥离公司名前缀,打印字号调大
测试:mkhBomService 单测新增结余库存缓冲/紧张用例与回归用例
|
2026-09-18 17:44:01 +08:00 |
|
|
|
be52bdd0b5
|
feat(配件库存监控/MKH-BOM): 两页面新增「同步最新数据」按钮
需求:配件库存监控、MKH-BOM 资料两个页面各加一个同步按钮,任何时候点击都能
立即拉取最新数据,不必等凌晨 00:00 的定时刷新。
后端:
- mkhBomService 新增 clearMkhBomCache():清空本页全部服务端缓存
(businessBom / officialBomIndex / officialBomDetail / productBom /
mkhProduct / replenishIndex / workOrderIndex / pendingOrderIndex /
componentStock / componentLocation),返回 syncedAt + 各缓存清除条目数。
- modularStockMonitorService 新增 syncModularStock():本页无进程内缓存
(每次请求直连副本库),同步动作 = 重新解析并返回最新监控日,前端据此重拉列表。
- 新增接口 POST /api/mkh-bom/sync、POST /api/modular-stock-monitor/sync。
前端:
- api/mkhBom.js 新增 syncMkhBom;api/modularStockMonitor.js 新增 syncModularStock。
- 两个页面工具栏各加「同步最新数据」主按钮(SyncOutlined,带 loading 态),
点击 → 调后端 sync → 成功后重新拉列表(MKH-BOM 页同时清空展开明细缓存
detailCache,避免看到旧的配件明细)→ 提示「已同步最新数据」;
失败时展示后端返回的错误信息。
口径说明(重要):
- 两页数据来自副本库 JSReportReplica,由上游聚水潭同步;应用层无法触发上游拉取。
本按钮的作用是「清服务端缓存 + 直连副本库重取最新」,保证看到的不是
2~15 分钟前的缓存结果,而不是重新跑一遍上游同步。
- 后端已内置的 dailyRefresh(cron 0 0 * * *,Asia/Shanghai)刷的是
summary_daily_* / summary_monthly_* 汇总表,不覆盖这两页数据源,
与本按钮既不重复也不冲突,可并存。
验证:
- 后端 4 个改动文件 node --check 全部通过。
- 前端 4 个改动文件(含 2 个 jsx)用 @babel/parser 解析全部通过。
- 实测:syncModularStock 返回 monitorDate=2026-09-16;
clearMkhBomCache 返回 syncedAt 与各缓存清除条目数。
- 单测 mkhBomService.test.js 40/40 通过,无回归。
- 前端 lint/build 未运行:frontend/node_modules 仍是之前被中断 npm ci 破坏的
坏目录(frontend/node_modules_broken_0917/,未纳入提交)。
合并前需在本机 npm ci 恢复后,打开两个页面确认按钮渲染与点击效果。
- 后端改动需重启服务后才生效。
|
2026-09-17 18:14:57 +08:00 |
|
|
|
bc8c46d5be
|
配件库存监控:未配置MKH 不进任何缺货/预警
业务确认(2026-09-17):未配置MKH 的配件不属于模块化体系,预警列一律显示「正常」,
既不报缺货(实时库存<=0)也不报预警(可用天数<=7)。
实现:
- 新增 buildWarningOrderSql() 统一表达式(0缺货/1预警/2正常),未配置MKH(cfg.configured=0) 直接判 2。
- 列表映射 mapRow 改用 SQL 算好的 warningOrder 推导 warningLevel(删除重复 normalizeWarningLevel,
避免前后端各算一份导致排序与显示不一致)。
- 汇总计数 buildSummary.outOfStockCount / lowStockDaysCount 改用统一表达式,不再计入未配置MKH。
- 筛选 buildFilters:按预警等级筛选、onlyWarning=1(仅看缺货/预警)均排除未配置MKH。
实测(接口直跑,目标仓 794 SKU):
- 未配置MKH 95 个全部判「正常」(其中 55 个库存=0、58 个可用天数<=7,原会预警)。
- summary.outOfStockCount=13 / lowStockDaysCount=3,与已配置MKH 页面分布一致。
- onlyWarning=1 返回 16 条,0 条未配置MKH。
|
2026-09-17 16:41:25 +08:00 |
|
|
|
0bb7c5905d
|
fix(模块化库存监控): 已配置MKH 改从聚水潭 SKU 级 BOM 维护(jst_skuBomItem)判定
业务反馈上一版「已配置MKH」数量不对(仅 29 个),原因是用了
jst_comboProductItem 组合装引用口径,漏掉了真正以 MKH 成品为父项的 BOM 配件。
修正:
- 数据源由 jst_comboProductItem 改为 rpt.jst_skuBomItem(聚水潭 SKU 级 BOM 明细,
含配件商品编码 map_outer_sku_id)。
- 判定口径:目标仓商品编码出现在 jst_skuBomItem.map_outer_sku_id,且父项
sku_id 以 'MKH' 开头(即被某 MKH 模块化成品 BOM 引用为配件) → 已配置MKH。
- 实测目标仓 794 个 SKU 中 699 个命中(map_outer_sku_id 匹配、父项 MKH 过滤),
95 个未配置,合计 794,与接口返回 summary 一致。
- 同步前端状态列 tooltip 与页脚口径说明。
- 移除对「产品资料系统数据库商品档案」「jst_comboProductItem 组合装」两套旧口径的依赖。
注:pm_bomBaseInfo(聚水潭 BOM 维护) 的配件行无 SKU 编码列(仅中文品名/位号),
其组件 SKU 编码实际落在 jst_skuBomItem.map_outer_sku_id,故采用后者。
|
2026-09-17 16:20:17 +08:00 |
|
|
|
e6ea1246f9
|
配件库存监控:已配置MKH改为"被MKH模块化成品引用"判定
- backend/src/services/modularStockMonitorService.js
- 「已配置MKH」判定口径切回 jst_comboProductItem 组合装引用:
该 SKU 出现在 jst_comboProductItem.src_sku_id 且 combine_sku_id 以 'MKH' 开头
即视为被 MKH 模块化成品(组合装/成品档案)引用为配件。
- 移除产品资料系统数据库覆盖清单逻辑(resolveProductCoverage / productCoveredCsv),
产品资料库仅保留作图片来源。
- 实测目标仓 794 SKU 中 29 个命中(原产品资料库口径 661 已废弃)。
- frontend/src/pages/ModularStockMonitor/index.jsx
- 状态列 tooltip 文案同步为「是否被 MKH 模块化成品作为配件引用」。
注:本改动会令已配置MKH数量从 661 降至 29(数据使然,非 bug)。
|
2026-09-17 16:01:20 +08:00 |
|
|
|
75a8795723
|
配件库存监控:表头"SKU编码"改为"商品编码"
- frontend/src/pages/ModularStockMonitor/index.jsx
- 表格列头 "SKU编码" → "商品编码"
- MKH 配置状态 tooltip 文案同步调整
- 搜索框 placeholder "SKU 编码 / 名称" → "商品编码 / 名称"
- backend/src/services/modularStockMonitorService.js
- 文件头注释同步更新(仅文案,无逻辑改动)
关于"从 BOM 维护匹配已配置 MKH 成品编码":
已探测 rpt.pm_bomBaseInfo 与目标仓 SKU 的编码体系完全脱钩
(sku_id/i_id/name 均 0 命中),若直接切换数据源会导致已配置数
从 661 归零。经用户确认,本次保留现有产品资料库判定口径,
仅做表头文案变更。
|
2026-09-17 15:44:30 +08:00 |
|
|
|
8ba052649d
|
feat: 补货预警-日销为0有代发订单时按库存÷代发件数算可用天数预警
- ⑩订单驱动分支改为:可用天数 = 现有库存 ÷ 代发(待发货)订单件数
· 可用天数 = 0(无库存)→ 缺货
· 0 < 可用天数 < 3 天 → 预警(建议补到 3 天缓冲:3×代发件数−现有库存)
· 可用天数 ≥ 3 天 → 库存充足,不预警(阈值语义与日销分支一致)
- buildReplenishMetrics 订单驱动分支不再把 stockDays 记 null,统一如实记录
- 同步前端「可用天数」列/导出/数据口径说明文案(不再显示「按订单」)
- 更新 mkhBomService.test.js 对应用例,单测 40/40 通过
|
2026-09-17 15:04:50 +08:00 |
|
|
|
3e02c3e393
|
feat: 日销口径由近 7 天改为近 15 天(MKH 补货预警 + 模块化库存监控)
业务要求「近7日日销」改「近30天日销」。实测业务库与产品资料库均无 30 天日销,
已实现的最长周期是 pm_stockMonitor.fifteenAvgSale(15 天),经与业务确认改用 15 天。
为什么没有 30 天(2026-09-17 实测,避免重复排查):
- rpt.pm_stockMonitor 只有 threeAvgSale(3天)/sevenAvgSale(7天)/fifteenAvgSale(15天),
且整表只保留 1 天快照(2026-09-16),攒不出 30 天历史
- rpt.dws_sn_sales_full_info_di(stat_date+skuid+goods_count,新鲜到 09-15)
里 MKH 商品 0 个;目标仓 794 个 SKU 命中也是 0
- rpt.dws_order_sales_cost_detail_di 停在 2025-04(历史归档)
- rpt.jst_salesOutboundInfo 只有出库单头,无 SKU 明细;业务库无出库明细表
- rpt.op_BarcodeSalePrice30dDaily 名字带 30d,但只有 salePrice 没有销量
改动:
- backend/src/services/mkhBomService.js
· SALE_WINDOW_DAYS 7 → 15,新增 DAILY_SALE_COLUMN='fifteenAvgSale',
监控快照 SQL 按该常量插值取数(以后改口径只改常量)
· buildReplenishMetrics 入参 sevenAvgSale → avgSale(语义与周期解耦)
· 文件头 ⑦ 补「为什么是 15 天」的完整排查结论;⑦ / ⑩ / ⑪ 实测数字按新口径更新
- backend/src/services/modularStockMonitorService.js
· 取数列与 API 字段名 sevenAvgSale/avgShip7d → fifteenAvgSale/avgShip15d
(字段名同步改,避免名不副实)
- frontend/src/pages/MkhBomData/index.jsx
· 列头与口径说明「近7日」→「近15天」;动态文案兜底 7 → 15
- frontend/src/pages/ModularStockMonitor/index.jsx
· 列头「近7日日均发货量」→「近15天日均发货量」,dataIndex 同步改
· 口径说明补充:可用天数仍沿用 jsUseDays(源系统口径),与日销并非同一时间窗
影响(同一份 09-16 快照,7 天 vs 15 天):
有销商品 114 → 170 周期拉长,更多商品进得了「有销」集合
补货预警 25 → 19 反而变少:15 天日销普遍低于 7 天(近期销量走低),
可用天数 = 库存 ÷ 日销 随之变大,跌破 3 天的变少
缺货 9 → 10
已排产 11 → 13
订单驱动 14 → 8 更多商品自己有日销,不再需要靠订单兜底
配件不足 10 → 12
「预警变少」是口径变化的正常结果,不是 bug。
验证:
- 后端语法检查 151 个文件通过
- mkhBomService + mkhReplenishAlertJob 单测 52/52 通过
- 后端全量 334 tests / 333 pass(唯一失败是 resilientSecretStore 的
Windows 文件权限断言,与本改动无关的环境固有问题)
- 接口实测:total 4223 / warningCount 19 / outOfStockCount 10 /
saleWindowDays 15 / componentShortageCount 12
|
2026-09-17 14:06:50 +08:00 |
|
|
|
06952d8a04
|
Merge remote-tracking branch 'origin/main' into codex/mkh-alert-export-0917
origin/main 已前进 11 个提交(PR #11~#15),其中 #11「配件仓位只取箱及仓位库存表」
与 #12「从模块化成品列表排除配件档案」改动了与本分支相同的三个文件。
冲突处理(3 个文件,逐处保留双方意图,未使用 ours/theirs 批量取舍):
- backend/src/services/mkhBomService.js(4 处):取本分支侧。本分支的 ⑤~⑪ 注释与
loadComponentLocations 是 origin/main 那段的超集,且双方都已删除
buildInventoryWarehouseMap 回退链,语义一致;同时并入 origin/main 收紧后的
MKH_WHERE(必须是 jst_skuBomItem 正式父项)与文件头 ① 的新口径说明。
- backend/test/mkhBomService.test.js(1 处):hunk 边界冲突,双方各缺一个 `});`。
origin/main 新增的「无箱记录不回退仓库」测试按语义插在仓位测试之后,
再接本分支新增的测试,两处闭合各自补回。
- frontend/src/pages/MkhBomData/index.jsx(2 处):合并双方文案。数据口径采用
origin/main 的表述(正式 BOM 父项、仓位只取 rpt.jst_packInventoryInfo),
并保留本分支的补货预警 / 待发货订单 / 在途加工单 / 配件不足四段说明。
顺带校正 mkhBomService.js 注释中因成品口径收紧而过期的数字
(成品总数 4408 → 见 ①,预警数 26 → 25),仅改注释,不动逻辑。
合并后重新验证:后端语法检查 151 个文件通过;mkhBomService + mkhReplenishAlertJob
单测 52/52 通过;接口实测 total 4223 / warningCount 25 / componentShortageCount 10。
|
2026-09-17 13:52:40 +08:00 |
|
|
|
f69d26796e
|
feat: MKH-BOM 补货预警支持待发订单驱动与配件不足标记
补货预警在原有「销量 + 库存」口径之外增加两条判定,均不改变已上线行为:
1. 待发订单驱动
- MKH 成品条码历史无销售量、但存在未发货订单时,同样进入补货预警。
- 建议补货量按「未发货件数 − 现有库存」计算,无订单时不参与该口径。
- 列表新增待发货订单列,展示件数、单数与最早下单时间。
2. 配件不足
- 对判定为缺货或预警的商品,按 BOM 计算可组装数:
可组装数 = 各配件 ⌊(现有库存 − 未完成加工单占用) ÷ 单台用量⌋ 取最小值。
- 可组装数为 0 时标记「配件不足,无法补货」并保留在清单中,
建议补货量显示「—」,同时给出短板配件及其库存。
- 配件库存只认「简尚-崔桥工厂半成品包装项目仓」的现有库存,
按业务确认不计采购在途。
- 配件库存查询失败时降级为空索引并标记 componentStockDegraded,
不写入缓存,避免把瞬时故障固化为错误数据。
导出与通知文案同步:导出新增「配件不足」列、补货量按同一口径输出;
缺货通知在配件不足时插入短板配件提示并把建议补货量显示为「—」。
验证:backend 相关单测全部通过(新增 9 例);语法检查通过;
前端 eslint 零错误且 vite build 通过;接口实测与页面渲染结果见 PR。
|
2026-09-17 12:18:32 +08:00 |
|
|
|
a928135a63
|
Merge pull request 'fix: 兼容生产服务器旧版 Git' (#15)
Avoid unsupported git -C and branch --show-current commands in the production build script.
|
2026-09-17 11:56:10 +08:00 |
|
lunanting
|
5302b22fc1
|
fix: 兼容生产服务器旧版Git
|
2026-09-17 11:55:37 +08:00 |
|
|
|
fd89eb64d2
|
Merge pull request 'build: 使用兼容生产环境的前端构建流程' (#14)
Use standard Vite and an isolated Node.js 22 production build script verified against the deployed SHA.
|
2026-09-17 11:54:50 +08:00 |
|