forked from lunanting/cangku
业务原话(2026-10-27):「发货时效也需要钉钉通知:13 点核验 7 点前的超时单, 核验完成后通知;18:00 核验的订单,核验完成后也通知。」 ## 做了什么 - 新增 `warehouseShipTimelinessNotifySettings`:配置与状态存取,两个轮次各一个推送时刻 (NOON 默认 13:05 / AFTERNOON 默认 18:05),加签密钥走 resilientSecretStore。 - 新增 `warehouseShipTimelinessNotifyJob`:正文组装 + 每分钟 tick + 启动后 30 分钟补推窗口。 - 路由:`GET/PUT /api/warehouse-ship-timeliness/notify/settings`、 `GET .../notify/state`、`POST .../notify/run`(dryRun / probe / force)。 - `index.js` 注册 `startShipTimelinessNotifyScheduler()`。 - 前端:页面右上「钉钉通知」→ 弹窗(开关、两个时刻、webhook、加签密钥、明细条数、 @所有人、页面链接、受控预览块、上次推送)。顺带把 held 明细弹窗的 `destroyOnClose` 改成 `destroyOnHidden`(antd 5.25 已弃用)。 ## 口径要点 - **一天两推,各绑一个轮次**:13:00 轮核验「今天 07:00 前审核」、18:00 轮核验 「截止 16:00 前审核」(都含历史未发)。两轮互不影响,两个时刻配成同一个 HH:mm 时两轮都推。 - **默认核验点 + 5 分钟**,不是整点:状态镜像 `query_time` 要略晚于核验时刻, 整点推容易 `coversDeadline=false`(数据没覆盖核验点、超时数偏大)。 这种时候消息里会带一行提示,仍照发(业务要的是「核验完就通知」)。 - **去重键 `<北京日期>|<轮次键>`**,一天两轮各自独立;推送成功才写状态,失败不写。 - **0 单超时也推**(群里看到「本轮 0 单」= 任务没挂)。 - **明细一行 = 仓|内部单号|线上单号|审核 时间|已等 X 小时**; 历史单的审核时间带 MM-DD —— 13:00 轮含历史未发单,只写「审核 16:13」会被读成今天。 - 明细按仓聚集 + 每仓保底 1 条,钉钉 text 超长时从末尾砍。 ## 验证 - `backend/test/warehouseShipTimelinessNotify.test.js` 新增 17 条,全过。 - 后端 `check-syntax` 通过;`node --test test/*.test.js` 491 条 / 489 过 / 2 失败, 失败两条是既有环境问题(`cloudDatabaseConfigCache` 负缓存用例依赖数据库不可达、 `resilientSecretStore` 的 0600 权限断言在 Windows 恒为 0o666),与本改动无关。 - 前端 `eslint src/pages/WarehouseShipTimeliness src/api/warehouseShipTimeliness.js` 0 错; `vite build` 通过(含 `verify-router-lazy`)。 - 本地真渲染:页面右上出现「钉钉通知」,弹窗完整渲染、点「预览正文」出正文块,无 pageerror (验收时把 `/notify/**` 全部 mock —— 本地系统库与生产是同一个,避免写坏生产配置)。 - 本地实数据预览(13:00 轮,冷查 46s):超时 15 单 / 4 个仓 / 不计考核 672 / 剔除 1; 同一时刻请求 18:00 轮返回 `before_deadline`(未到 18:00 不推半成品)。 ## 风险与回退 - 默认关闭(`enabled=false`)且未配 webhook 时一切接口只返回 `not_configured`,不会发消息。 - 想彻底停掉:`DISABLE_WST_NOTIFY=true` 或把推送开关关掉;两者都不影响页面本身。 - 回退:删掉 `startShipTimelinessNotifyScheduler()` 一行即可停调度,新增文件无副作用。
49 lines
2.1 KiB
JavaScript
49 lines
2.1 KiB
JavaScript
import request from './request';
|
||
|
||
// 发货时效要扫两张订单表 + 出库表(rpt 下全是视图),冷启动实测 40~50 秒,
|
||
// 超时必须放大到 3 分钟,否则前端会先断开、后端还在跑。
|
||
const TIMELINESS_TIMEOUT_MS = 3 * 60 * 1000;
|
||
|
||
// 仓储中心 24 小时发货时效:每个主仓「超时未发」数量 + 超时订单明细
|
||
// params.round 'NOON' | 'AFTERNOON'(不传按当前时刻自动判定)
|
||
// params.limit 明细条数上限
|
||
export const getWarehouseShipTimeliness = (params = {}) => request.get(
|
||
'/api/warehouse-ship-timeliness/list',
|
||
{
|
||
params,
|
||
timeout: TIMELINESS_TIMEOUT_MS,
|
||
headers: { 'Cache-Control': 'no-cache' },
|
||
}
|
||
);
|
||
|
||
// 仅取今日两轮核验的描述(不打订单表,秒回),用于顶部提示
|
||
export const getWarehouseShipTimelinessRounds = () => request.get(
|
||
'/api/warehouse-ship-timeliness/rounds',
|
||
{ timeout: 30000 }
|
||
);
|
||
|
||
// ── 两轮核验完成后推文本到钉钉群(2026-10-27 新增)────────────────────────
|
||
// 通道 = 群自定义机器人 webhook(不发 .xlsx,只发 text;加签密钥可选)
|
||
// 读配置:加签密钥只回掩码(webhookSecretConfigured + webhookSecretPreview)
|
||
export const getShipTimelinessNotifySettings = () => request.get(
|
||
'/api/warehouse-ship-timeliness/notify/settings',
|
||
{ timeout: 30000, headers: { 'Cache-Control': 'no-cache' } }
|
||
);
|
||
|
||
// 保存配置:加签密钥留空 = 沿用已存的,不必每次重填
|
||
export const saveShipTimelinessNotifySettings = (payload = {}) => request.put(
|
||
'/api/warehouse-ship-timeliness/notify/settings',
|
||
payload,
|
||
{ timeout: 30000 }
|
||
);
|
||
|
||
// 立即推送 / 预览正文 / 测试通道
|
||
// dryRun=true 只组装消息不发送(返回正文全文)
|
||
// probe=true 只发一句通道测试,不查业务数据
|
||
// 冷启动要全量扫订单状态视图(生产实测数十秒到几分钟),超时给足
|
||
export const runShipTimelinessNotify = (payload = {}) => request.post(
|
||
'/api/warehouse-ship-timeliness/notify/run',
|
||
payload,
|
||
{ timeout: 10 * 60 * 1000 }
|
||
);
|