|
|
e9923d7383
|
Merge pull request 'fix(wle): 钉钉推送明细精简为「仓|快递单号|快递|异常类型」' (#76) from codex/yanhui-wle-detail-no-time-20261022 into main
|
2026-10-22 13:10:31 +08:00 |
|
|
|
9991b92325
|
Merge pull request 'fix(router): 补回 BinReplenishMonitor 的 lazy 声明,修复生产全站白屏' (#77) from codex/hotfix-bin-replenish-lazy-20261022 into main
|
2026-10-22 12:08:20 +08:00 |
|
|
|
9b1ec36c70
|
fix(router): 补回 BinReplenishMonitor 的 lazy 声明,修复全站白屏
现象:生产 https://ck.yydd.ai:8443 打开任意页面都是白屏,连登录页都进不去。
控制台报 `BinReplenishMonitor is not defined`,一个 /api 请求都没发出去。
根因:PR #74 新增「缺货补货监控」时,在 router/index.jsx 的路由表里写了
`element: renderLazyComponent(BinReplenishMonitor)`,但没有像其它 22 个页面那样
在文件顶部补 `const BinReplenishMonitor = lazy(() => import('../pages/BinReplenishMonitor'))`。
该行在模块顶层求值 → 加载即抛 ReferenceError → 整个 React 应用挂掉。
`vite build` 不会报错(未声明标识符在 JS 里是合法语法),所以一路构建通过、部署后白屏。
PR #75 只补了后端的 binReplenishRoutes 引入,前端的这处漏声明一直没人发现。
修复:
1. router/index.jsx 补上 lazy 声明,并留下注释说明「漏一个就是全站白屏」。
2. 新增 frontend/scripts/verify-router-lazy.mjs,静态扫描 router/index.jsx:
凡是 renderLazyComponent(X) 用到的 X,必须在同文件有 `const X = lazy(` 声明,
否则构建直接失败并指出缺少的组件名与行号。已挂到 `npm run build` 前置。
验证:
- 该检查对修复前的 origin/main 版本报错并准确指出
`BinReplenishMonitor (router/index.jsx:143)`;对修复后版本通过(23 used / 23 declared)。
- eslint 通过;vite build 成功(4270 modules / 34s)。
- playwright 真渲染:修复前 /login 白屏 + pageerror;修复后 /login 完整渲染登录表单
(123 字、3 个输入框),两个业务路由正常回到登录引导,无致命错误。
|
2026-10-22 12:06:48 +08:00 |
|
|
|
7939816b64
|
fix(wle): 推送明细再去掉末尾「待处理小时数」
业务三次要求(2026-10-22):明细最后面的超时时间也不要。
- buildDetailLine 现在是 4 段:仓|快递单号|快递|异常类型
- 删除仅此处使用的 formatHours()(不留死代码)
- 「无快递单号时退回内部单号」的兜底按业务确认保留
- 文件头/函数注释补第三条口径,单测断言正好 4 段且不含 17.3h 这类时长
|
2026-10-22 11:56:11 +08:00 |
|
|
|
b223db7961
|
fix(wle): 推送明细第二列改为快递单号
业务二次要求(2026-10-22):明细里显示快递单号,不要内部单号。
- buildDetailLine 第二段从 `oId` 改为 `trackingNo`(上游 `logistic_id`)
- 未揽收 / 无物流单的包裹本来就没有快递单号 → 退回 `oId`,
否则这条在群里无法定位(不让它变成「无单号」)
- 单测:样例带 trackingNo 并断言内部单号不出现;补「无 trackingNo 时退回 oId」用例
- 文件头与函数注释同步口径
|
2026-10-22 11:46:06 +08:00 |
|
|
|
57debbe022
|
fix(wle): 钉钉推送明细去掉发货时间列
业务反馈(2026-10-22):推送明细里不需要发货时间。
- buildDetailLine 从「仓|内部单号|快递|异常类型|发货时间|待处理」
改为「仓|内部单号|快递|异常类型|待处理小时数」
- 删除仅此处使用的 shortTime() 及其导出,不留死代码
- 文件头补口径说明,防止以后把 sendTime 加回去
- 单测同步:断言新列序,并显式断言消息里不出现 sendTime(传了也不能渲染)
|
2026-10-22 11:40:11 +08:00 |
|
|
|
03fd3f97f3
|
Merge pull request 'fix: 补回 binReplenishRoutes 引入,修复后端启动失败' (#75) from codex/lunanting-bin-replenish-001 into main
|
2026-10-22 11:14:30 +08:00 |
|
|
|
a15f289ec6
|
fix: 补回 binReplenishRoutes 引入,修复后端启动失败
|
2026-10-22 11:14:00 +08:00 |
|
|
|
f786a92387
|
Merge pull request 'feat: 新增「缺货补货监控」看板(拣货位缺货 → 从存货位补货)' (#74) from codex/lunanting-bin-replenish-001 into main
Merge pull request #74 from codex/lunanting-bin-replenish-001
|
2026-10-22 11:10:31 +08:00 |
|
|
|
55b755c1d9
|
feat: 新增「缺货补货监控」看板(拣货位缺货 → 从存货位补货)
数据源:聚水潭「箱及仓位库存」rpt.jst_packInventoryInfo(副本里是视图,
按 SKU+货位聚合单次 35~45s,因此服务端缓存 10 分钟 + 启动预热)。
口径:
- 拣货位:按业务导入的货位清单判定(本地表 bin_replenish_pick_bin,按仓库隔离);
副本没有货位类型主数据,未导入时回退为 pack_type='Bin' 的记录,页面明确标注。
- 拣货位库存 / 存货位库存 / 现有库存合计;
- 目标库存 = max(10, 近15天日销 × 目标天数(默认7));
- 建议补货量 = min(存货位库存, 目标 − 拣货位库存);
- 状态:拣货位缺货 / 无货可补 / 拣货位偏低 / 未设拣货位 / 正常。
「未设拣货位」单列,避免把整箱直发商品误报成缺货。
后端:binReplenishService + binReplenishSchema + /api/bin-replenish
(list / export / pick-bins GET-POST-DELETE / ping),建表与预热挂在 localSchema。
前端:/planning/operation/bin-replenish-monitor 页面、侧栏入口、页面权限注册,
含 KPI 卡、状态分段筛选、关键字搜索、目标天数、货位清单导入/清除、服务端导出。
|
2026-10-22 11:08:20 +08:00 |
|
|
|
ac61802b07
|
Merge pull request 'feat(wle): 仓储转运异常每天定点推文本到钉钉群' (#73) from codex/yanhui-wle-notify-text-20261022 into main
|
2026-10-22 11:01:32 +08:00 |
|
|
|
bdc2cf9fd6
|
feat(wle): 仓储转运异常每天定点推文本到钉钉群
在「仓储中心物流转运异常」页新增「异常通知」:每天 09:10(可改)把当天核验批次
的异常情况以**文本消息**推到钉钉群,0 条也推(用来确认任务没挂)。
为什么是 text 而不是 Excel 附件:钉钉群自定义机器人 webhook 只支持
text/markdown/link/actionCard,发文件必须走企业内部应用(AppKey/AppSecret/
robotCode/openConversationId 四项凭据 + 应用权限 + 机器人进群)。业务确认
「群里看文本就够,要明细点链接去页面导出」,于是走最轻的 webhook。
后端
- 新增 services/dingtalkRobotWebhookSender.js:POST /robot/send + msgtype=text
· 加签 urlEncode(base64(HmacSHA256(secret, "timestamp\nsecret"))),timestamp 毫秒
· text.content 按 UTF-8 **字节**截断到 20000(钉钉硬上限),不切半个汉字
· webhook 严格匹配 ^/robot/send$ 且必须有 access_token;errcode/errmsg 原样抛
- 新增 services/warehouseLogisticsExceptionNotifySettings.js:
· webhookUrl 存在 system_config(完整回显,业务要核对);加签密钥走
resilientSecretStore,接口只回 configured + 掩码,留空=沿用已存
· sendTime 非法退回 09:10;maxDetailRows 0~50 默认 30
· 状态 lastSentDate(按推送日去重)等存 system_config
- 新增 jobs/warehouseLogisticsExceptionNotifyJob.js:
· buildNotifyText:抬头【仓储物流转运异常】→ 发货/包裹/异常/异常率 → 分类型
→ 剔除数 → 按仓(只列有异常的仓)→ 明细前 N 条 → 页面链接(带 date)
· 明细行「仓|内部单号|快递|异常类型|发货时间|待处理」,字段内换行压平
· 超长从明细末尾逐条砍并标记 truncated(truncated 只表示被字节上限砍过,
不表示「按配置只列前 N 条」)
· 每分钟 tick 比对北京时间 HH:mm == 配置 sendTime,改时间不用重启
- api:GET/PUT /notify/settings、GET /notify/state、POST /notify/run
(dryRun 预览不校验凭据;probe 只发一句测试文本、不查业务数据)
前端
- 「导出」右侧新增「异常通知」(铃铛)弹窗:开关、推送时间、Webhook、加签密钥、
明细条数、@所有人、页面链接、上次推送结果、三段帮助说明
- 「测试通道」「预览正文」「保存」「立即推送到群」
- 预览改成弹窗内受控区块:本项目跑 React 19 + antd v5,Modal.info 静态方法
拿不到 context 会**静默不渲染**(只留一行 warning,看着像后端没返回)
同步与兼容
- 新配置首次读取为空 → ready=false,页面提示「Webhook 地址还没配」,不影响页面其它功能
- 定时任务只在 enabled=true 时生效;本地 ENABLE_BACKGROUND_JOBS=false 不跑,用页面按钮验
验证
- backend/test/warehouseLogisticsExceptionNotify.test.js 15/15:正文结构与数字、
0 条平安消息、条数上限、字节精简、Webhook 校验、加签 crypto 手算对齐、errcode 透传
- 后端全量 465 项 / 463 过(2 项既有环境失败:Windows 权限位、负缓存需隧道故障态)
- 前端 eslint 0、build 成功
- 真数据干跑:144 条异常 / 列 30 条 / 3768 字节 / 20s,按仓包裹数合计 144 对得上
- playwright 真渲染:弹窗字段 Webhook/加签密钥/明细条数/@所有人/页面链接,
旧字段 AppKey/AppSecret/robotCode/群会话 ID 零残留;预览内联渲染出正文
风险与回退
- 钉钉侧失败只会记 lastState.lastError 并在页面展示,不影响页面取数
- 回退:删除 index.js 里的 startWarehouseLogisticsExceptionNotifyScheduler 注册即可停推送
|
2026-10-22 11:00:10 +08:00 |
|
|
|
b30c4e949c
|
Merge pull request 'feat: 仓储物流转运异常页新增「导出异常明细」(Excel / CSV)' (#72)
|
2026-10-22 10:00:22 +08:00 |
|
|
|
85e2db9f92
|
feat: 仓储中心物流转运异常页新增「导出异常明细」(Excel / CSV)
- 后端新增 GET /api/warehouse-logistics-exception/export:复用列表同一套口径
(getLogisticsExceptions),只把明细上限从列表 2000 放宽到 5000,不分页,
判定/剔除逻辑一行未改,避免「导出与页面两套口径」。
- 结果缓存由单槽改为按 `核验日|明细上限` 分键的 Map(上限 8 条):
页面按默认条数取、导出按 5000 取,单槽会互相踢掉 —— 导出一次,页面下次
自动刷新就得重查库(20~40s)。分键后两者互不影响。
- 前端把明细过滤抽成 filterDetailOrders,表格与导出共用同一个函数,
保证「导出的就是页面看到的」。
- 导出列单独定义 EXPORT_COLUMNS(13 列纯文本标题):页面列用了 Tooltip,
直接复用列定义会把表头导出成 [object Object];运单号/物流状态/最后轨迹
为空时导出为「无运单号 / 无状态 / 无轨迹」,Excel 里不留空格。
- 导出跟随当前核验日 + 选中仓 + 选中异常类型;文件名带核验日与筛选范围
(仓储物流转运异常_<核验日>_<仓>_<类型>);0 行时提示且不生成空文件;
超过 5000 行时如实提示「已截断」。导出按钮做 loading,防重复点。
- 测试:新增 2 个用例 —— ①导出行数等于异常包裹数、字段齐全、带剔除口径;
②缓存按 limit 分键(页面 1000 条那份不被导出 5000 条顶掉)。
warehouseLogisticsException.test.js 26/26 通过。
口径未变:承运商剔除(顺丰系全剔、京喜京东、睢宁中通按仓、武进安能/中铁/
壹米滴答/自提/线下/商家配送)、订单终止与刷单信誉单剔除、异常 5 类判定,
全部沿用 #71 已合并版本。
|
2026-10-22 09:58:22 +08:00 |
|
|
|
9cba2f8140
|
Merge pull request 'feat: 新增「仓储中心物流转运异常」页面(12 发货仓 · 每日 09:00 核验前一日已发货物流)' (#71)
feat: 新增「仓储中心物流转运异常」页面(12 发货仓 · 每日 09:00 核验前一日已发货物流)
|
2026-10-22 09:08:58 +08:00 |
|
|
|
db1ba79416
|
feat: 新增「仓储中心物流转运异常」页面(12 发货仓 · 每日 09:00 核验前一日已发货物流)
- 后端新增 warehouse-logistics-exception 模块(Registry / Service / Api),
挂载 /api/warehouse-logistics-exception;前端新增独立页面并接入左侧栏
「仓储中心物流转运异常」(排在「仓储中心 24 小时发货时效」下面)。
- 核验口径:核验日 09:00 看**前一日已发货**订单的物流记录;12 个发货仓按仓库名
最长匹配归口(子仓并入主仓,「京仓-横林二仓京东云仓」排除)。
只统计揽收前的卡点:无物流单 / 未揽收 / 已揽收未中转 / 快递报异常 / 退回;
运输中、派送中、已签收一律正常(干线与中转轨迹本就不回传,不再按 24h 判停滞)。
明细按包裹、汇总按内部单号去重;结果缓存 5 分钟,force=1 可绕过。
- 剔除口径(业务 2026-10-21 / 10-22 定版,剔掉的既不算异常也不算发货分母):
· 承运商:顺丰系全部(唯品顺丰 / 顺丰干配 / 顺丰大件各账号)、武进安能、中铁、
壹米滴答、京喜京东(快递·快运)、自提、线下、商家配送;
· 有条件剔除:睢宁中通只在睢宁仓考核,其它仓异地打单的剔出;
· 订单已终止:状态含取消/作废,或标签含「发货后取消」「作废」「拦截」;
· 刷单 / 信誉单:订单标签含「刷单」「转空包」。
「武进安能」「京喜京东」均按精确粒度匹配,不泛化成「安能」「京东」,避免误剔
常州安能新、京东自营快递亿家达等正常账号。
- 时区:模块内统一用 Asia/Shanghai 取「今天」,避免生产容器 Etc/UTC 导致核验批次错位。
- 页面:分仓统计卡 + 异常类型筛选 + 明细(承运商 / 运单号 / 物流状态 / 异常标签 /
最后轨迹 / 发货时间 / 待处理小时),页脚展示剔除口径与本批剔除量;
物流快照停更按黄/红两级告警,并标注「历史批次按当前物流快照重算」。
验证(本地隔离环境,非生产库):
- node --test backend/test/warehouseLogisticsException.test.js → 24/24 通过
- npm --prefix frontend run check → 通过
- 本地真实数据接口实测:顺丰系、睢宁中通(异地仓)明细 0 条残留
|
2026-10-22 09:07:49 +08:00 |
|
|
|
42f60eb0ce
|
Merge pull request 'fix: 仓储发货时效按北京时间取「今天」(生产容器 UTC 导致 00:00~08:00 轮次错位)' (#70)
|
2026-10-21 08:19:15 +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 |
|
|
|
4b02ad212d
|
Merge pull request 'feat: 仓储中心发货时效独立页面 + 口径定版(审核时间分轮/快递剔除/停更告警)' (#69)
|
2026-10-21 07:55:56 +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 |
|
|
|
6833b7bc79
|
Merge pull request 'feat: 新增「仓储中心24小时发货时效」侧栏(11 个主仓 · 12:00/16:00 双轮核验)' (#68) from codex/warehouse-ship-timeliness-1020 into main
|
2026-10-20 11:28:11 +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 |
|
|
|
3d9b12eace
|
Merge pull request 'fix: MKH-BOM 成品库存按仓白名单多仓求和(并入横林二仓品牌仓)' (#67) from codex/mkh-brand-warehouse-multi-1017 into main
|
2026-10-17 09:39:34 +08:00 |
|
|
|
c9715c0e5b
|
fix: MKH-BOM 成品库存按仓白名单多仓求和(并入横林二仓品牌仓)
## 问题
业务反馈「MKHDNZ0409260600788 库存在横林二仓品牌仓,重新加了一个品牌仓库存,
需要同步这个仓库库存」。
根因:商品档案 `jst_sku.other_3` 记录的是**归属仓**,不是**实物所在仓**。
该批 MKH 成品的归属仓仍是「简尚-崔桥工厂品牌仓」(15870495),但实物已被整批搬到
2026-10-13 新建的「简尚-横林二仓品牌仓」(16132681)。归属仓库存为 0、新仓有货,
而 ⑦-d 的库存取数只查 `wms_co_id = 15870495` 单仓 → 这批成品库存全被算成 0。
实测影响(2026-10-17):
- 新仓 58 个 SKU **全是 MKH 成品**(`other_3` 全为崔桥品牌仓),合计 **525 件**
- 页面 49 个 SKU 库存值错误,其中 **5 个误报「缺货」、2 个误报「预警」**
## 改动
`backend/src/services/mkhBomService.js`
- 新增 `BRAND_WMS_CO_IDS = [15870495, 16132681]` 成品库存仓白名单(`BRAND_WMS_CO_ID`
保留给仓库名文案与监控快照过滤)
- `jst_inventoryInfo` / `jst_packInventoryInfo` 两条取数改为
`wms_co_id IN (白名单)` + `SUM(qty)` + `GROUP BY sku_id`,**按 SKU 多仓求和**
- ⚠️ 原 `ORDER BY updateTime DESC`(单仓去重)在多仓下只会留一个仓,
必须换成 SUM + GROUP BY —— 本次同时修掉这个隐患
- 配件库存**不动**:新仓一个配件都没有,横林三仓/沭阳仓虽有 MKH 配件但归属别的项目,
并入会让「配件不足」漏判
`backend/test/mkhBomService.test.js`
- 新增 ⑦-e 多仓求和测试(锁 SQL 白名单 + SUM/GROUP BY,并断言求和结果)
- 新增白名单常量测试(防止后续误删横林二仓 ID)
## 验证
- `check-syntax.js`:182 文件通过
- `node --test`:**405 tests / 404 pass / 1 fail**
(唯一失败为 `resilientSecretStore` Windows 权限断言,环境固有,与本改动无关)
- **对照组实验**:stash 回修复前源码跑新测试 → 2 条**全部失败**,
报错精确指向旧 SQL(`wms_co_id = @brandWmsCoId ... ORDER BY updateTime DESC`),证明测试非空过
- **同一数据快照双口径 diff**(生产库实测):参与比对 385 个 SKU,
差异 58 个,**全部是新值 > 旧值,反向 0 个、来源标记变化 0 个**,
合计补回 525 件(= 新仓总量,完全吻合)
- **新代码在生产容器实跑**:`total 4347`(不变)/ **`warning 30 → 23`** /
**`outOfStock 21 → 16`**,与逐 SKU 分析结论一致(消除 5 个误报缺货 + 2 个误报预警)
- **性能**:`STATISTICS IO` 两边均为 `jst_inventoryInfo scan 1 / logical reads 4683`(不增加 IO);
实测 355ms vs 旧 356~914ms,**无退化**
## 风险与回退
- 风险:白名单常量是硬编码,后续上游再新增同类「成品发货仓-真实库存」仓时需追加 ID。
已在代码注释与文件头 ⑦-e 写明。
- 回退:单文件改动,revert 本提交即恢复单仓口径,无数据迁移、无状态残留。
|
2026-10-17 09:34:55 +08:00 |
|
|
|
c80d28c33f
|
Merge pull request 'fix: 动销分析多来源销量先按 仓库+商品 汇总再 join' (#66) from codex/picking-bin-ship-agg-1014 into main
|
2026-10-14 13:57:59 +08:00 |
|
|
|
62cac712db
|
fix: 动销分析多来源销量先按 仓库+商品 汇总再 join
4 个销量来源 union all 后各分支各自 GROUP BY,同一个 仓库+商品 命中多个
分支时外层层 LEFT JOIN 没有二次聚合,一个仓位行被拆成多行、每行只带某一
个分支的销量(实测 SJ038411 同时出现 sales=0 与 sales=44 两行)。
新增 shipAgg CTE:按 仓库+商品 对各来源求和成一行,再与仓位行 join。
SalesQty/分摊销量/日销/等级随之统一。
|
2026-10-14 13:57:14 +08:00 |
|
|
|
e5030d4864
|
Merge pull request 'fix: 云库新增慢查询入口 querySlow(池级超时放宽)' (#65) from codex/cloud-db-slow-query-1014 into main
|
2026-10-14 13:12:40 +08:00 |
|
|
|
0a2fe1c3a3
|
fix: 云库新增慢查询入口 querySlow(池级超时放宽)
mssql 12.2.0 驱动 request.timeout(请求级)实测不生效,超过池默认
60s 的查询必被掐断。动销分析销量 union 4 来源后全仓窗口需 ~2 分钟,
触发 Timeout: Request failed to complete in 60000ms。
- queryWithCandidateConnection 支持 options.connectRequestTimeoutMs
覆盖候选连接的池级 requestTimeout(池级实测生效)
- 新增 querySlow(sqlText, params, options):独立临时连接 + 池级超时
默认 300s(env CLOUD_DB_SLOW_QUERY_TIMEOUT_MS 可调)
- 动销分析取数优先走 querySlow(mock 环境无该方法时回退 query)
|
2026-10-14 13:12:05 +08:00 |
|
|
|
1d4eecb03c
|
Merge pull request 'fix: 动销分析销量补齐组合装子件与全民客户(B2B)渠道' (#64) from codex/picking-bin-combo-qmcust-1014 into main
|
2026-10-14 11:05:54 +08:00 |
|
|
|
2bdc4b5713
|
fix: 动销分析销量补齐组合装子件与全民客户(B2B)渠道
线上订单里组合装以 ZH-子件A-N-子件B-M 拼接编码存在,按 sku_id 精确
匹配永远查不到子件销量;全民客户(jst_qmcust_orderlist)渠道此前完全
未纳入。两处缺失导致大量 SKU(尤其组合装子件)窗口销量被算成 0。
销量 ship CTE 拆为 4 个 UNION ALL 分支:
1) jst_Orders 普通行(sku_type<>combine)
2) jst_Orders 组合装行 JOIN jst_comboProductItem 展开为子件x数量
3) jst_qmcust_orderlist 普通行(排除拆单父单 Split/Question)
4) qmcust 组合装行同样展开
均按 send_date 窗口,仓库过滤不变。等级/日销/分摊销量随数据源自动重算。
|
2026-10-14 11:05:13 +08:00 |
|
|
|
c6c7feaeea
|
Merge pull request 'fix: 排除拣选位报表中的箱记录' (#63) from codex/picking-bin-pack-filter-1012 into main
|
2026-10-12 14:39:06 +08:00 |
|
134
|
fa0e604bf8
|
fix: 排除拣选位报表中的箱记录
|
2026-10-12 14:31:45 +08:00 |
|
|
|
cf8d5f68ae
|
Merge pull request 'fix: 动销分析报表恢复「类型」列(恒为拣选),存货位仍不纳入' (#62) from codex/picking-bin-restore-type-col-1008 into main
|
2026-10-08 16:32:09 +08:00 |
|
|
|
e15898693f
|
fix: 动销分析报表恢复「类型」列(恒为拣选),存货位仍不纳入
上一版为去掉冗余把「类型」整列删了,用户要求的报表列结构中该列仍在,
现恢复:导出与列表均保留「类型」列,值恒为「拣选」(存货位不参与统计,报表中不会出现)
验证(基准日 2026-10-08、30 天):
- 导出表头为 仓库 / 仓位 / 类型 / 商品编码 / 库存数量 / 仓位SKU数 / 商品销量 /
分摊销量 / 商品日销 / 动销等级 / 统计天数 / 统计基准日
- 11897 行,类型列取值集合仅 ['拣选'],空值 0 行;分摊销量合计 98087 = 去重商品销量合计 98087
- 列表接口 binTypes=['拣选'],明细类型集合仅 ['拣选']
- 单测 9/9 通过;语法检查 182 文件通过;eslint 无报错、vite build 成功
|
2026-10-08 16:31:20 +08:00 |
|
|
|
214ede07a5
|
Merge pull request 'feat: 拣选库位动销分析固定只统计拣选位,移除仓位类型切换' (#61) from codex/picking-bin-picking-only-1008 into main
|
2026-10-08 13:10:48 +08:00 |
|
|
|
99fa402092
|
feat: 拣选库位动销分析固定只统计拣选位,移除仓位类型切换
- 取数固定只取拣选位:bin CTE 直接过滤 bin_group 为空(更早过滤,存货位不再进入后续 CTE)
移除可变的 binType IN 过滤与 resolveBinTypes,binType 字段恒为拣选
- 接口 payload 的 binTypes / binTypeOptions 固定为 ['拣选'];缓存键不再含类型
- 列表与导出移除「类型」列(全为拣选,冗余);页面移除类型多选器、底部文案改为「统计范围:仅拣选位」
- 口径说明补充统计范围;Tooltip 同步
验证(基准日 2026-10-08、30 天):
- 列表 11871 行 / 6821 个去重商品 / 库存 499141 / 去重商品销量 96847,明细类型集合仅为 ['拣选']
- 导出 11871 行,表头无「类型」列;分摊销量合计 96847 = 去重商品销量合计 96847(差异 -0.0017 浮点舍入)
- 旧参数 binType=存货 被忽略(仍返回拣选且命中同一缓存);7 / 60 天窗口与单仓过滤均正常
- 单测 9/9 通过;后端语法检查 182 文件通过;前端 eslint 无报错、vite build 成功
|
2026-10-08 13:09:49 +08:00 |
|
|
|
8f12b889c0
|
Merge pull request 'feat: 动销等级改为按商品日销统计,列表与导出统一到仓位×商品粒度' (#60) from codex/picking-bin-grade-by-sku-1008 into main
|
2026-10-08 10:59:14 +08:00 |
|
|
|
88a39975d3
|
feat: 动销等级改为按商品日销统计,列表与导出统一到仓位×商品粒度
- 取数统一为 fetchPickingBinSkuRows:一行 = 一个「仓位 × 商品编码」,列表与导出共用
商品日销 = 商品窗口整档销量 ÷ 天数(不做仓位分摊);同一商品占几个仓位都是同一等级
另输出分摊销量 = 商品销量 ÷ 占用同类型仓位数,供按行求和
- 列表页同步拆到商品粒度:新增商品编码列,去掉 SKU数列,搜索支持商品编码
- 汇总卡片主数值改为去重商品数,另显示明细行数;销量按去重商品口径统计
- 导出新增「分摊销量」「商品日销」列,表头口径标注更明确
- 性能:仓位类型选满时不拼 IN 过滤(避免参数嗅探走坏计划),全类型查询由超时降到约 30 秒
验证(基准日 2026-10-08、30 天、拣选位):
- 明细 11871 行 / 去重商品 6821 个 / 库存合计 499141(与旧口径一致)
- 跨多仓位的 2028 个商品,销量与等级不一致 0 个
- 导出行求和:分摊销量合计 96847 = 去重商品销量合计 96847(差异 -0.0017,浮点舍入)
- 单测 9/9 通过;语法检查 182 文件通过;前端 eslint 无报错、vite build 成功
|
2026-10-08 10:58:17 +08:00 |
|
|
|
195351deae
|
Merge pull request 'feat: 拣选库位动销分析导出增加商品编码,按仓位×商品编码拆行' (#59) from codex/picking-bin-export-sku-1008 into main
|
2026-10-08 09:36:58 +08:00 |
|
|
|
31f184edd0
|
feat: 拣选库位动销分析导出增加商品编码,按仓位×商品编码拆行
- 新增 fetchPickingBinSkuRows:仓位 × 商品编码 粒度明细
库存数量 = 该商品在此仓位的实际库存;销量 = 商品销量 ÷ 其占用同类仓位数(贡献)
- 导出列:仓库 / 仓位 / 类型 / 商品编码 / 库存数量 / SKU数 / 销量 / 日销 / 动销等级 / 统计天数 / 统计基准日
- SKU数、动销等级仍为仓位粒度(同一仓位多行重复),列表页口径不变(仍用 bin 级聚合)
- 同一次取数比对:7055 个仓位,列表与导出库存/销量 0 处差异,库存合计一致 497721
- 单测 11 条通过
|
2026-10-08 09:35:26 +08:00 |
|
|
|
ea2942f118
|
Merge pull request 'feat: 拣选库位动销分析默认按近 30 天销量划分等级' (#58) from codex/picking-bin-window-30d-1007 into main
|
2026-10-07 17:42:06 +08:00 |
|
|
|
817cc74a40
|
feat: 拣选库位动销分析默认按近 30 天销量划分等级
- 默认统计窗口 15 天 -> 30 天(后端 PICKING_BIN_DEFAULT_DAYS 与前端常量同步)
- 可切换窗口补充近 60 天(7 / 15 / 30 / 60)
- 等级阈值不变:爆款≥20 / 热卖 10-20 / 平销 3-10 / 滞销-1 1-3 / 滞销-2 (0,1) / 销售=0
- 单测断言同步更新;实测 30 天窗口:7091 个拣选库位,日销=销量÷30
|
2026-10-07 17:41:17 +08:00 |
|
|
|
e71607386d
|
Merge pull request 'feat: 新增每日进销存业务库校验脚本' (#57) from codex/validate-daily-inventory-biz-1007 into main
|
2026-10-07 17:04:36 +08:00 |
|
|
|
7505a04e37
|
Merge pull request 'feat: 新增二级页面「拣选库位动销分析」(大货库存监控)' (#56) from codex/picking-bin-sales-analysis-1007 into main
|
2026-10-07 17:04:10 +08:00 |
|
|
|
0afcb26119
|
feat: 新增每日进销存业务库校验脚本
- backend/scripts/validateDailyInventoryFromBiz.js <业务日>...
- 直连业务库(凭据经隧道从系统库 database_configs 现取,不落盘):
反算 4 个可还原的流水列(销售出仓/销售退货/采购进仓/采购退货),
与 daily_inventory_official 逐仓比对;期末库存与 jst_inventoryInfo 全仓比对
- 约定:校验类需求默认走业务库,不启浏览器取聚水潭
|
2026-10-07 16:59:56 +08:00 |
|
|
|
70550b5499
|
feat: 新增二级页面「拣选库位动销分析」(大货库存监控)
- 后端:pickingBinSalesAnalysis 配置/服务/路由,仓位库存取 rpt.jst_packInventoryInfo
(bin_group 空值=拣选位、存货=存货位),销量按「SKU 占用同类仓位数平均分摊」,
恒等式「全部库位销量之和 = 有库位 SKU 销量之和」差异为 0
- 动销等级(近 N 天日销,默认 15):爆款>=20、热卖款 10-20、平销款 3-10、
滞销款-1 1-3、滞销款-2 (0,1)、销售=0
- 前端:大货库存监控下新增二级菜单 + 页面(等级卡片、仓库/类型/天数/仓位筛选、
分页表格、xlsx 导出),路由/菜单/页面权限四处注册
- 测试:后端 8 项单测通过;前端 lint/test/build 通过
|
2026-10-07 16:59:39 +08:00 |
|
|
|
ada8d09c27
|
Merge pull request 'fix: MKH-BOM 配件库存增加箱表兜底,修正「配件有货却判配件不足」' (#55) from codex/mkh-component-stock-pack-fallback-0930 into main
|
2026-09-30 19:32:34 +08:00 |
|
|
|
a93691f5d8
|
fix: MKH-BOM 配件库存增加箱表兜底,修正「配件有货却判配件不足」
根因
- 配件监控页取实时库存是三级兜底(inventoryInfo → 同仓箱表 packInventoryInfo
→ 监控快照 jsStock),而 MKH-BOM 的 loadComponentStock **只查 jst_inventoryInfo**,
一级来源到底就结束。
- 上游 jst_inventoryInfo 存在滞后:2026-09-30 该行停在 09-28 14:51 的值 0,
而 jst_packInventoryInfo 当日 10:30 已记 bin=ZT-30 qty=200。
- 结果:同一个仓(15718196)、同一个配件,配件监控页显示 200(判「正常」),
MKH-BOM 判「配件不足」、可组装数 0。
案例 MKHDNZ0409260600849 的配件 DNZ040947N1440V01(瓶颈唯一)。
改动
- backend/src/services/mkhBomService.js
· loadComponentStock():一条语句同时取两张表(UNION ALL + src 标记),
库存表有正库存以它为准;为 0 / 缺行时用同仓 jst_packInventoryInfo 的
bin 非空行合计兜底。**只补 0、不覆盖正库存**,与配件监控页
buildRealtimeStockSql 同口径;查询失败仍整体 degraded(不做判定)。
· 文件头 ⑪:修正「不能用 jst_packInventoryInfo 当配件库存 —— 与 MKH 配件
零交集」这条结论 —— 它是 09-17 的表状态(当时该表 bin 全空),
09-18 上游整表重建后已失效。
· 文件头 ⑤:同理修正该表的关联列与数据现状结论。PR #22 的提交信息声称
已更新 ⑤,但实际 diff 未改,此处补齐(该表 wms_co_id 关联、目标仓
727/794 命中、配件仓 743 个 SKU 有箱记录)。
- backend/test/mkhBomService.test.js
· 新增 3 个用例:箱表兜底生效且不覆盖正库存 / SQL 形状防腐化(两张表 +
bin 非空 + 限定仓)/ 查询失败仍降级不做判定。
对照组已验证:在修复前的实现上前两个用例失败(expected 200、actual 0)。
影响与验证
- 仅影响 MKH-BOM 的「配件不足」标记与可组装数;不改预警级别、排序与预警总数。
- 同一数据快照下的新旧口径对照(监控日 2026-09-29):
配件不足 26 → 24(2 行翻盘)、预警总数 29、缺货 23 两边一致,
0 行反向变化、0 处其他字段变化、0 处级别变化。
- 真库实测:DNZ040947N1440V01 0 → 200、DNZ040959N1440V01 0 → 200;
库存表本就有正数的对照配件(60 / 87)未被箱表覆盖。
- 负载(SET STATISTICS IO,300 编码一个分片):jst_inventoryInfo 4543 逻辑读 +
jst_packInventoryInfo 947 逻辑读,物理读 0、Worktable 0;实测中位耗时
215ms → 391ms(+176ms/分片,配件全集 784 个约 3 个分片,占冷查 61s 的 1% 以内,
且结果按编码缓存 10 分钟)。
说明:SHOWPLAN 权限被拒(账号无该权限),改用 STATISTICS IO + 实测耗时 + 数据量
作为执行计划等价证据。
- 数据量边界:配件仓 jst_inventoryInfo 837 行、jst_packInventoryInfo 1163 行
(bin 非空 1024 行)。
- 后端:check-syntax 179 文件通过;node --test 393 项 392 通过
(唯一失败为既有环境问题 resilientSecretStore 权限断言,与本改动无关)。
|
2026-09-30 17:43:30 +08:00 |
|
|
|
b0b618e7df
|
Merge pull request 'feat: returns board - add latest return-deadline column (workbench source, pending access)' (#54) from codex/lunanting-exit-date-001 into main
|
2026-09-28 16:07:13 +08:00 |
|
|
|
30554720b2
|
feat: 客退看板新增「最晚退厂时间」列(工作平台数据,先占位待接入)
- 新增 supplierExitDateProvider.js:按供应商名匹配「最晚退厂时间」,
数据源为工作平台库 WorkbenchDataShared(同实例,另有 WorkbenchIdentity /
DataPlatformControl)。当前副本账号 cangku 无该库访问权限、系统设置里没有它的
连接配置、.env 的 JIAN_SUN_DB_* 与 WORK_ORDER_* 也为空,因此先返回不可用。
接入方式:IT 开通后配 SUPPLIER_EXIT_DATE_ENABLED=true + SUPPLIER_EXIT_DATE_SQL
(查询需返回 supplierName / exitDate 两列),或直接写死 CODE_* 常量,无需改其他文件。
- returnStockWarningService.js:composeRows 增加 latestExitDate /
latestExitDateLabel / latestExitDateAvailable;列表响应新增 exitDateMeta;
导出列在「供应商是否合作」后新增「最晚退厂时间」(未接入时导出显示「待接入」);
口径说明补一条(含完整取证:工作平台库不可达、副本无任何退厂字段、
fys_supplier.NoCoopTime 是不可用的停更快照)。
- StockWarning.jsx:表格新增「最晚退厂时间」列,有值显示日期、未接入显示「待接入」
(Tooltip 说明数据来源与开通方式);monitor.css 补占位符样式。
|
2026-09-28 16:06:40 +08:00 |
|