From 57debbe0228e3a2d1b824adcea784a2a31353102 Mon Sep 17 00:00:00 2001 From: yanhui <2353496031@qq.com> Date: Thu, 22 Oct 2026 11:40:11 +0800 Subject: [PATCH 1/3] =?UTF-8?q?fix(wle):=20=E9=92=89=E9=92=89=E6=8E=A8?= =?UTF-8?q?=E9=80=81=E6=98=8E=E7=BB=86=E5=8E=BB=E6=8E=89=E5=8F=91=E8=B4=A7?= =?UTF-8?q?=E6=97=B6=E9=97=B4=E5=88=97?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 业务反馈(2026-10-22):推送明细里不需要发货时间。 - buildDetailLine 从「仓|内部单号|快递|异常类型|发货时间|待处理」 改为「仓|内部单号|快递|异常类型|待处理小时数」 - 删除仅此处使用的 shortTime() 及其导出,不留死代码 - 文件头补口径说明,防止以后把 sendTime 加回去 - 单测同步:断言新列序,并显式断言消息里不出现 sendTime(传了也不能渲染) --- .../warehouseLogisticsExceptionNotifyJob.js | 19 +++++++------------ .../warehouseLogisticsExceptionNotify.test.js | 9 ++++++--- 2 files changed, 13 insertions(+), 15 deletions(-) diff --git a/backend/src/jobs/warehouseLogisticsExceptionNotifyJob.js b/backend/src/jobs/warehouseLogisticsExceptionNotifyJob.js index 0663c70..5fac23f 100644 --- a/backend/src/jobs/warehouseLogisticsExceptionNotifyJob.js +++ b/backend/src/jobs/warehouseLogisticsExceptionNotifyJob.js @@ -9,6 +9,8 @@ * ② 不发 .xlsx 附件 —— 群机器人 webhook 发不了文件,业务也确认群里看文本就够。 * 内容 = 汇总(发货/异常/异常率/分类型/分仓)+ 前 N 条明细 + 页面链接。 * N 由页面配置(默认 30,上限 50);要全部明细就点链接去页面导出 Excel。 + * 明细一行 = 仓|内部单号|快递|异常类型|待处理小时数。 + * ⚠️ **不带发货时间**(业务 2026-10-22 要求),别再加 sendTime 回去。 * ③ 消息里**必须带「异常」字样**:钉钉群机器人若用「自定义关键词」安全设置, * 关键词一般是「异常」,缺了会被拒(errcode 310000)。 * ④ 一天只推一次:状态存 system_config,按「推送日(北京时间)」去重, @@ -74,29 +76,23 @@ function cleanCell(value, fallback = '') { return text || fallback; } -/** '2026-10-21 16:00:00' → '10-21 16:00';空值给「—」 */ -function shortTime(value) { - const text = cleanCell(value); - if (!text) return '—'; - const match = text.match(/^(\d{4})-(\d{2})-(\d{2})[ T](\d{2}):(\d{2})/); - if (!match) return text.slice(0, 16); - return `${match[2]}-${match[3]} ${match[4]}:${match[5]}`; -} - function formatHours(value) { const num = Number(value); if (!Number.isFinite(num)) return '—'; return `${Math.round(num * 10) / 10}h`; } -/** 明细一行:仓|内部单号|快递|异常类型|发货时间|待处理 */ +/** + * 明细一行:仓|内部单号|快递|异常类型|待处理小时数 + * ⚠️ 业务明确要求明细**不带发货时间**(20-22 反馈),别再加 sendTime 回去 —— + * 仓库看单子只需知道「哪个单、什么异常、压了多久」,发货时间在页面上看。 + */ function buildDetailLine(row, index) { const parts = [ cleanCell(row?.warehouseName, '未知仓'), cleanCell(row?.oId, '无单号'), cleanCell(row?.carrier, '未知快递'), cleanCell(row?.exceptionLabel, '异常'), - shortTime(row?.sendTime), formatHours(row?.pendingHours), ]; return `${index}. ${parts.join('|')}`; @@ -428,6 +424,5 @@ module.exports = { cleanCell, pushWarehouseLogisticsExceptionNotify, runWarehouseLogisticsExceptionNotifyJob, - shortTime, startWarehouseLogisticsExceptionNotifyScheduler, }; diff --git a/backend/test/warehouseLogisticsExceptionNotify.test.js b/backend/test/warehouseLogisticsExceptionNotify.test.js index 1864c31..2883c17 100644 --- a/backend/test/warehouseLogisticsExceptionNotify.test.js +++ b/backend/test/warehouseLogisticsExceptionNotify.test.js @@ -118,7 +118,7 @@ test('群消息正文:抬头带「异常」关键词、数字与分类型、 assert.equal(built.truncated, false); }); -test('明细行:仓|内部单号|快递|异常类型|发货时间|待处理,字段里换行要压平', () => { +test('明细行:仓|内部单号|快递|异常类型|待处理(不带发货时间),字段里换行要压平', () => { const line = job.buildDetailLine({ warehouseName: '崔桥工厂仓', oId: '9845001', @@ -129,14 +129,17 @@ test('明细行:仓|内部单号|快递|异常类型|发货时间| lastLogisticInfo: '第一行\n第二行', }, 3); - assert.equal(line, '3. 崔桥工厂仓|9845001|京东快递|已揽收未中转|10-21 16:00|17.3h'); + assert.equal(line, '3. 崔桥工厂仓|9845001|京东快递|已揽收未中转|17.3h'); + // 业务要求:明细不带发货时间(哪怕传了 sendTime 也不能出现在消息里) + assert.ok(!line.includes('10-21'), '明细行不该再带发货时间'); + assert.ok(!line.includes('16:00'), '明细行不该再带发货时间'); // 物流描述里的换行不能漏进消息(会把一条明细劈成两行,看着像两条异常) assert.equal(job.cleanCell('甲\n乙\t丙 丁'), '甲 乙 丙 丁'); // 空值兜底:不能让消息里出现「undefined|null」 assert.equal( job.buildDetailLine({}, 1), - '1. 未知仓|无单号|未知快递|异常|—|—' + '1. 未知仓|无单号|未知快递|异常|—' ); }); -- 2.47.2 From b223db7961231fd4b12f4e8501274280f3753d5a Mon Sep 17 00:00:00 2001 From: yanhui <2353496031@qq.com> Date: Thu, 22 Oct 2026 11:46:06 +0800 Subject: [PATCH 2/3] =?UTF-8?q?fix(wle):=20=E6=8E=A8=E9=80=81=E6=98=8E?= =?UTF-8?q?=E7=BB=86=E7=AC=AC=E4=BA=8C=E5=88=97=E6=94=B9=E4=B8=BA=E5=BF=AB?= =?UTF-8?q?=E9=80=92=E5=8D=95=E5=8F=B7?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 业务二次要求(2026-10-22):明细里显示快递单号,不要内部单号。 - buildDetailLine 第二段从 `oId` 改为 `trackingNo`(上游 `logistic_id`) - 未揽收 / 无物流单的包裹本来就没有快递单号 → 退回 `oId`, 否则这条在群里无法定位(不让它变成「无单号」) - 单测:样例带 trackingNo 并断言内部单号不出现;补「无 trackingNo 时退回 oId」用例 - 文件头与函数注释同步口径 --- .../src/jobs/warehouseLogisticsExceptionNotifyJob.js | 11 +++++++---- .../test/warehouseLogisticsExceptionNotify.test.js | 12 ++++++++++-- 2 files changed, 17 insertions(+), 6 deletions(-) diff --git a/backend/src/jobs/warehouseLogisticsExceptionNotifyJob.js b/backend/src/jobs/warehouseLogisticsExceptionNotifyJob.js index 5fac23f..e4a4014 100644 --- a/backend/src/jobs/warehouseLogisticsExceptionNotifyJob.js +++ b/backend/src/jobs/warehouseLogisticsExceptionNotifyJob.js @@ -9,8 +9,9 @@ * ② 不发 .xlsx 附件 —— 群机器人 webhook 发不了文件,业务也确认群里看文本就够。 * 内容 = 汇总(发货/异常/异常率/分类型/分仓)+ 前 N 条明细 + 页面链接。 * N 由页面配置(默认 30,上限 50);要全部明细就点链接去页面导出 Excel。 - * 明细一行 = 仓|内部单号|快递|异常类型|待处理小时数。 + * 明细一行 = 仓|快递单号|快递|异常类型|待处理小时数。 * ⚠️ **不带发货时间**(业务 2026-10-22 要求),别再加 sendTime 回去。 + * ⚠️ 第二段是**快递单号**不是内部单号(同 10-22 二次要求)。 * ③ 消息里**必须带「异常」字样**:钉钉群机器人若用「自定义关键词」安全设置, * 关键词一般是「异常」,缺了会被拒(errcode 310000)。 * ④ 一天只推一次:状态存 system_config,按「推送日(北京时间)」去重, @@ -83,14 +84,16 @@ function formatHours(value) { } /** - * 明细一行:仓|内部单号|快递|异常类型|待处理小时数 - * ⚠️ 业务明确要求明细**不带发货时间**(20-22 反馈),别再加 sendTime 回去 —— + * 明细一行:仓|快递单号|快递|异常类型|待处理小时数 + * ⚠️ 业务明确要求明细**不带发货时间**(10-22 反馈),别再加 sendTime 回去 —— * 仓库看单子只需知道「哪个单、什么异常、压了多久」,发货时间在页面上看。 + * ⚠️ 第二段用**快递单号**(`trackingNo` = 上游 `logistic_id`),不是内部单号 `oId`(10-22 二次要求)。 + * 「未揽收 / 无物流单」这类包裹本来就没有快递单号 → 退回内部单号,否则群里这条无法定位。 */ function buildDetailLine(row, index) { const parts = [ cleanCell(row?.warehouseName, '未知仓'), - cleanCell(row?.oId, '无单号'), + cleanCell(row?.trackingNo || row?.oId, '无单号'), cleanCell(row?.carrier, '未知快递'), cleanCell(row?.exceptionLabel, '异常'), formatHours(row?.pendingHours), diff --git a/backend/test/warehouseLogisticsExceptionNotify.test.js b/backend/test/warehouseLogisticsExceptionNotify.test.js index 2883c17..ff06e80 100644 --- a/backend/test/warehouseLogisticsExceptionNotify.test.js +++ b/backend/test/warehouseLogisticsExceptionNotify.test.js @@ -118,10 +118,11 @@ test('群消息正文:抬头带「异常」关键词、数字与分类型、 assert.equal(built.truncated, false); }); -test('明细行:仓|内部单号|快递|异常类型|待处理(不带发货时间),字段里换行要压平', () => { +test('明细行:仓|快递单号|快递|异常类型|待处理(不带发货时间),字段里换行要压平', () => { const line = job.buildDetailLine({ warehouseName: '崔桥工厂仓', oId: '9845001', + trackingNo: 'JDVA00123456789', carrier: '京东快递', exceptionLabel: '已揽收未中转', sendTime: '2026-10-21 16:00:00', @@ -129,13 +130,20 @@ test('明细行:仓|内部单号|快递|异常类型|待处理(不 lastLogisticInfo: '第一行\n第二行', }, 3); - assert.equal(line, '3. 崔桥工厂仓|9845001|京东快递|已揽收未中转|17.3h'); + assert.equal(line, '3. 崔桥工厂仓|JDVA00123456789|京东快递|已揽收未中转|17.3h'); + // 业务要求:第二段是快递单号,不是内部单号 + assert.ok(!line.includes('9845001'), '明细行不该再显示内部单号'); // 业务要求:明细不带发货时间(哪怕传了 sendTime 也不能出现在消息里) assert.ok(!line.includes('10-21'), '明细行不该再带发货时间'); assert.ok(!line.includes('16:00'), '明细行不该再带发货时间'); // 物流描述里的换行不能漏进消息(会把一条明细劈成两行,看着像两条异常) assert.equal(job.cleanCell('甲\n乙\t丙 丁'), '甲 乙 丙 丁'); + // 未揽收/无物流单没有快递单号 → 退回内部单号(否则群里这条无法定位) + assert.equal( + job.buildDetailLine({ oId: '9845002', carrier: '中通' }, 1), + '1. 未知仓|9845002|中通|异常|—' + ); // 空值兜底:不能让消息里出现「undefined|null」 assert.equal( job.buildDetailLine({}, 1), -- 2.47.2 From 7939816b64649203c6e219dc36b801e412f48dd1 Mon Sep 17 00:00:00 2001 From: yanhui <2353496031@qq.com> Date: Thu, 22 Oct 2026 11:56:11 +0800 Subject: [PATCH 3/3] =?UTF-8?q?fix(wle):=20=E6=8E=A8=E9=80=81=E6=98=8E?= =?UTF-8?q?=E7=BB=86=E5=86=8D=E5=8E=BB=E6=8E=89=E6=9C=AB=E5=B0=BE=E3=80=8C?= =?UTF-8?q?=E5=BE=85=E5=A4=84=E7=90=86=E5=B0=8F=E6=97=B6=E6=95=B0=E3=80=8D?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 业务三次要求(2026-10-22):明细最后面的超时时间也不要。 - buildDetailLine 现在是 4 段:仓|快递单号|快递|异常类型 - 删除仅此处使用的 formatHours()(不留死代码) - 「无快递单号时退回内部单号」的兜底按业务确认保留 - 文件头/函数注释补第三条口径,单测断言正好 4 段且不含 17.3h 这类时长 --- .../warehouseLogisticsExceptionNotifyJob.js | 18 ++++++------------ .../warehouseLogisticsExceptionNotify.test.js | 11 +++++++---- 2 files changed, 13 insertions(+), 16 deletions(-) diff --git a/backend/src/jobs/warehouseLogisticsExceptionNotifyJob.js b/backend/src/jobs/warehouseLogisticsExceptionNotifyJob.js index e4a4014..07ce039 100644 --- a/backend/src/jobs/warehouseLogisticsExceptionNotifyJob.js +++ b/backend/src/jobs/warehouseLogisticsExceptionNotifyJob.js @@ -9,9 +9,9 @@ * ② 不发 .xlsx 附件 —— 群机器人 webhook 发不了文件,业务也确认群里看文本就够。 * 内容 = 汇总(发货/异常/异常率/分类型/分仓)+ 前 N 条明细 + 页面链接。 * N 由页面配置(默认 30,上限 50);要全部明细就点链接去页面导出 Excel。 - * 明细一行 = 仓|快递单号|快递|异常类型|待处理小时数。 - * ⚠️ **不带发货时间**(业务 2026-10-22 要求),别再加 sendTime 回去。 - * ⚠️ 第二段是**快递单号**不是内部单号(同 10-22 二次要求)。 + * 明细一行 = 仓|快递单号|快递|异常类型(**只有 4 段**)。 + * ⚠️ **不带发货时间**、**不带待处理小时数**(业务 2026-10-22 要求),时间类都到页面看。 + * ⚠️ 第二段是**快递单号**不是内部单号(同 10-22 二次要求;无单号才退回内部单号)。 * ③ 消息里**必须带「异常」字样**:钉钉群机器人若用「自定义关键词」安全设置, * 关键词一般是「异常」,缺了会被拒(errcode 310000)。 * ④ 一天只推一次:状态存 system_config,按「推送日(北京时间)」去重, @@ -77,18 +77,13 @@ function cleanCell(value, fallback = '') { return text || fallback; } -function formatHours(value) { - const num = Number(value); - if (!Number.isFinite(num)) return '—'; - return `${Math.round(num * 10) / 10}h`; -} - /** - * 明细一行:仓|快递单号|快递|异常类型|待处理小时数 + * 明细一行:仓|快递单号|快递|异常类型 * ⚠️ 业务明确要求明细**不带发货时间**(10-22 反馈),别再加 sendTime 回去 —— - * 仓库看单子只需知道「哪个单、什么异常、压了多久」,发货时间在页面上看。 + * 仓库看单子只需知道「哪个单、什么异常」,时间类信息一律到页面上看。 * ⚠️ 第二段用**快递单号**(`trackingNo` = 上游 `logistic_id`),不是内部单号 `oId`(10-22 二次要求)。 * 「未揽收 / 无物流单」这类包裹本来就没有快递单号 → 退回内部单号,否则群里这条无法定位。 + * ⚠️ 末尾的「待处理小时数」(`pendingHours`)也已按 10-22 三次要求去掉,别再加回来。 */ function buildDetailLine(row, index) { const parts = [ @@ -96,7 +91,6 @@ function buildDetailLine(row, index) { cleanCell(row?.trackingNo || row?.oId, '无单号'), cleanCell(row?.carrier, '未知快递'), cleanCell(row?.exceptionLabel, '异常'), - formatHours(row?.pendingHours), ]; return `${index}. ${parts.join('|')}`; } diff --git a/backend/test/warehouseLogisticsExceptionNotify.test.js b/backend/test/warehouseLogisticsExceptionNotify.test.js index ff06e80..50c7995 100644 --- a/backend/test/warehouseLogisticsExceptionNotify.test.js +++ b/backend/test/warehouseLogisticsExceptionNotify.test.js @@ -118,7 +118,7 @@ test('群消息正文:抬头带「异常」关键词、数字与分类型、 assert.equal(built.truncated, false); }); -test('明细行:仓|快递单号|快递|异常类型|待处理(不带发货时间),字段里换行要压平', () => { +test('明细行:仓|快递单号|快递|异常类型(4 段,不带发货时间和待处理时长),字段里换行要压平', () => { const line = job.buildDetailLine({ warehouseName: '崔桥工厂仓', oId: '9845001', @@ -130,24 +130,27 @@ test('明细行:仓|快递单号|快递|异常类型|待处理(不 lastLogisticInfo: '第一行\n第二行', }, 3); - assert.equal(line, '3. 崔桥工厂仓|JDVA00123456789|京东快递|已揽收未中转|17.3h'); + assert.equal(line, '3. 崔桥工厂仓|JDVA00123456789|京东快递|已揽收未中转'); // 业务要求:第二段是快递单号,不是内部单号 assert.ok(!line.includes('9845001'), '明细行不该再显示内部单号'); // 业务要求:明细不带发货时间(哪怕传了 sendTime 也不能出现在消息里) assert.ok(!line.includes('10-21'), '明细行不该再带发货时间'); assert.ok(!line.includes('16:00'), '明细行不该再带发货时间'); + // 业务要求:末尾的待处理小时数也去掉(哪怕传了 pendingHours 也不能出现在消息里) + assert.ok(!line.includes('17.3h'), '明细行不该再带待处理时长'); + assert.equal(line.split('|').length, 4, '明细必须正好 4 段'); // 物流描述里的换行不能漏进消息(会把一条明细劈成两行,看着像两条异常) assert.equal(job.cleanCell('甲\n乙\t丙 丁'), '甲 乙 丙 丁'); // 未揽收/无物流单没有快递单号 → 退回内部单号(否则群里这条无法定位) assert.equal( job.buildDetailLine({ oId: '9845002', carrier: '中通' }, 1), - '1. 未知仓|9845002|中通|异常|—' + '1. 未知仓|9845002|中通|异常' ); // 空值兜底:不能让消息里出现「undefined|null」 assert.equal( job.buildDetailLine({}, 1), - '1. 未知仓|无单号|未知快递|异常|—' + '1. 未知仓|无单号|未知快递|异常' ); }); -- 2.47.2