fix: 仓储发货时效按北京时间取「今天」(生产容器 UTC 导致 00:00~08:00 轮次错位) #70

Merged
yanhui merged 1 commits from codex/ship-timeliness-tz-1021 into main 2026-10-21 08:19:16 +08:00
Collaborator

目的与范围

  • 解决的问题:PR #69 上线后(2026-10-21 08:04 生产实测)发现轮次窗口整体错位 ——
    生产容器 /etc/timezone = Etc/UTC,而 MSSQL GETDATE() 返回北京时间,
    mssql 驱动 useUTC:false 把 DB 值构造成「墙钟当 UTC」的 Date ⇒ 比真实 new Date() 快 8 小时。
    后果:北京时间每天 00:00~08:00,服务端取到的「今天」是昨天,
    13:00 轮把整个历史未发池算进来(实测 overdueUnshipped=3895,而本地东八区同一轮只有 6 单)。
  • 修改的模块/文件:
    • backend/src/services/warehouseShipTimelinessRegistry.js:新增 beijingNow() /
      BEIJING_OFFSET_MINUTES,resolveActiveRound / describeRounds 的默认 now 改为北京时间。
    • backend/src/services/warehouseShipTimelinessService.js:getShipTimeliness 改用
      registry.beijingNow();文件头补时区说明。
    • backend/test/warehouseShipTimeliness.test.js:新增 2 个时区用例。
  • 是否使用 AI 及负责开发者:AI 辅助实现,负责开发者 yanhui。

同步与兼容性

  • 起始基线 SHA:4b02ad212d27c2158ecfcb23aac7b2fa9ad3e06d(origin/main,含 PR #69)
  • 最近同步的 main SHA:4b02ad212d27c2158ecfcb23aac7b2fa9ad3e06d
  • 待审核任务分支 SHA:f42f461430214b2faa09fb3600cd29c55f9b5c4d
  • 并行 PR、共享接口、迁移/配置影响:无并行 PR。不改容器 TZ、不改
    /opt/cangku/shared/backend.env
    —— 该 env 里没有 TZ,改了会波及全部既有定时任务
    (dailyRefresh / dailyInventorySnapshot / alibaba1688ImageSearchExpiryJob 等都各自显式写了
    Asia/Shanghai)。本次只让这一个模块自己按北京时间取值。
  • 冲突及处理理由(无冲突也应说明):分支基于最新 origin/main 之上,远端未再前进;
    grep -rn "^<<<<<<< \|^>>>>>>> \|^=======$" 扫描 backend/src 无冲突标记。
  • 如何确认保留了其他开发者的已有行为:改动仅落在发货时效模块 3 个文件(+72 / -4),
    未触碰规范文档与其它模块;本机已是东八区,beijingNow() 平移量为 0,本地行为完全不变。

验证

  • 实际执行的命令及结果:
    • node --test backend/test/warehouseShipTimeliness.test.js → 19 tests / 19 pass / 0 fail
      (新增:跨日 00:00~08:00 不取成前一天;北京时间 07:48 判 notStarted、13:30 判 NOON 轮且
      deadline=13:00 / windowEnd=07:00)
    • 部署后将在生产容器内直调服务层复算 NOON 轮,与修复前的 3895 单对照(见部署记录)
  • 未运行或失败的检查及原因:未跑全量 npm run check(后端全量测试连外部库、耗时数十分钟);
    已针对本次改动文件跑单文件测试覆盖。
  • 人工验证路径:规划 → 运营 → 仓储中心 24 小时发货时效;重点看顶部「今天」的日期与两轮窗口。

风险与回退

  • 已知风险:beijingNow() 是「平移 Date」而非真正的时区库,只保证本地 getter 读出北京时间;
    模块内后续若新增时间计算,必须继续走这个入口,别再裸用 new Date()(已在文件头写明)。
  • 回退方式和数据兼容性:纯计算逻辑、无写库、无迁移;回退直接 revert 合并提交即可,
    任务分支 codex/ship-timeliness-tz-1021 保留不删。
## 目的与范围 - 解决的问题:PR #69 上线后(2026-10-21 08:04 生产实测)发现**轮次窗口整体错位** —— 生产容器 `/etc/timezone = Etc/UTC`,而 MSSQL `GETDATE()` 返回**北京时间**, mssql 驱动 `useUTC:false` 把 DB 值构造成「墙钟当 UTC」的 Date ⇒ 比真实 `new Date()` 快 8 小时。 后果:**北京时间每天 00:00~08:00,服务端取到的「今天」是昨天**, 13:00 轮把整个历史未发池算进来(实测 `overdueUnshipped=3895`,而本地东八区同一轮只有 6 单)。 - 修改的模块/文件: - `backend/src/services/warehouseShipTimelinessRegistry.js`:新增 `beijingNow()` / `BEIJING_OFFSET_MINUTES`,`resolveActiveRound` / `describeRounds` 的默认 now 改为北京时间。 - `backend/src/services/warehouseShipTimelinessService.js`:`getShipTimeliness` 改用 `registry.beijingNow()`;文件头补时区说明。 - `backend/test/warehouseShipTimeliness.test.js`:新增 2 个时区用例。 - 是否使用 AI 及负责开发者:AI 辅助实现,负责开发者 yanhui。 ## 同步与兼容性 - 起始基线 SHA:`4b02ad212d27c2158ecfcb23aac7b2fa9ad3e06d`(origin/main,含 PR #69) - 最近同步的 main SHA:`4b02ad212d27c2158ecfcb23aac7b2fa9ad3e06d` - 待审核任务分支 SHA:`f42f461430214b2faa09fb3600cd29c55f9b5c4d` - 并行 PR、共享接口、迁移/配置影响:无并行 PR。**不改容器 TZ、不改 `/opt/cangku/shared/backend.env`** —— 该 env 里没有 TZ,改了会波及全部既有定时任务 (`dailyRefresh` / `dailyInventorySnapshot` / `alibaba1688ImageSearchExpiryJob` 等都各自显式写了 `Asia/Shanghai`)。本次只让这一个模块自己按北京时间取值。 - 冲突及处理理由(无冲突也应说明):分支基于最新 `origin/main` 之上,远端未再前进; `grep -rn "^<<<<<<< \|^>>>>>>> \|^=======$"` 扫描 `backend/src` 无冲突标记。 - 如何确认保留了其他开发者的已有行为:改动仅落在发货时效模块 3 个文件(+72 / -4), 未触碰规范文档与其它模块;本机已是东八区,`beijingNow()` 平移量为 0,**本地行为完全不变**。 ## 验证 - 实际执行的命令及结果: - `node --test backend/test/warehouseShipTimeliness.test.js` → **19 tests / 19 pass / 0 fail** (新增:跨日 00:00~08:00 不取成前一天;北京时间 07:48 判 `notStarted`、13:30 判 NOON 轮且 `deadline=13:00` / `windowEnd=07:00`) - 部署后将在生产容器内直调服务层复算 NOON 轮,与修复前的 3895 单对照(见部署记录) - 未运行或失败的检查及原因:未跑全量 `npm run check`(后端全量测试连外部库、耗时数十分钟); 已针对本次改动文件跑单文件测试覆盖。 - 人工验证路径:规划 → 运营 → 仓储中心 24 小时发货时效;重点看顶部「今天」的日期与两轮窗口。 ## 风险与回退 - 已知风险:`beijingNow()` 是「平移 Date」而非真正的时区库,只保证本地 getter 读出北京时间; 模块内后续若新增时间计算,必须继续走这个入口,别再裸用 `new Date()`(已在文件头写明)。 - 回退方式和数据兼容性:纯计算逻辑、无写库、无迁移;回退直接 revert 合并提交即可, 任务分支 `codex/ship-timeliness-tz-1021` 保留不删。
yanhui added 1 commit 2026-10-21 08:18:40 +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。
Author
Collaborator

PR #70 合并授权记录(规范第 6 节 · 路径二)

  • 授权人:yanhui(仓库所有者,同时是本次提交的开发者)
  • 授权时间:2026-10-21 08:15 (GMT+8)
  • 授权原话:

    走下一轮部署
    (上下文:AI 汇报「生产容器 UTC 导致北京时间 00:00~08:00 轮次错位,要不要我现在改 + 走下一轮 PR 部署」,
    用户回复「走下一轮部署」→ 明确包含本次修复的合并与部署授权)

  • 授权范围:仅本次 PR #70(codex/ship-timeliness-tz-1021 → main,3 个文件,+72 / -4,
    仅发货时效模块的时区取值),以及该合并提交的生产部署;不含其他 PR、不含强推 main。
  • 授权时最终提交 SHA:f42f461430214b2faa09fb3600cd29c55f9b5c4d
  • 基线 main SHA:4b02ad212d27c2158ecfcb23aac7b2fa9ad3e06d
  • 说明:规范要求「至少一名非提交者审核」。本 PR 为 AI 辅助、开发者即 yanhui 本人,
    无第三方审核者可用;仓库所有者给出明确合并+部署指令,按规范 6.1 以所有者书面授权代替独立审核。
    AI 未自审自合,授权记录已同步写入 PR 评论。
  • 合并前已完成的检查:
    • node --test backend/test/warehouseShipTimeliness.test.js → 19 / 19 pass
    • grep -rn "^<<<<<<< \|^>>>>>>> \|^=======$" 扫描 backend/src → 无冲突标记
    • PR mergeable = true、head_sha == 本地 HEAD、base_sha == 当前 origin/main
    • 未改动规范文档、未改动容器 TZ / 共享 env(避免波及既有定时任务)
# PR #70 合并授权记录(规范第 6 节 · 路径二) - **授权人**:yanhui(仓库所有者,同时是本次提交的开发者) - **授权时间**:2026-10-21 08:15 (GMT+8) - **授权原话**: > 走下一轮部署 (上下文:AI 汇报「生产容器 UTC 导致北京时间 00:00~08:00 轮次错位,要不要我现在改 + 走下一轮 PR 部署」, 用户回复「走下一轮部署」→ 明确包含本次修复的合并与部署授权) - **授权范围**:仅本次 PR #70(`codex/ship-timeliness-tz-1021` → `main`,3 个文件,+72 / -4, 仅发货时效模块的时区取值),以及该合并提交的生产部署;不含其他 PR、不含强推 main。 - **授权时最终提交 SHA**:`f42f461430214b2faa09fb3600cd29c55f9b5c4d` - **基线 main SHA**:`4b02ad212d27c2158ecfcb23aac7b2fa9ad3e06d` - **说明**:规范要求「至少一名非提交者审核」。本 PR 为 AI 辅助、开发者即 yanhui 本人, 无第三方审核者可用;仓库所有者给出明确合并+部署指令,按规范 6.1 以所有者书面授权代替独立审核。 AI 未自审自合,授权记录已同步写入 PR 评论。 - **合并前已完成的检查**: - `node --test backend/test/warehouseShipTimeliness.test.js` → 19 / 19 pass - `grep -rn "^<<<<<<< \|^>>>>>>> \|^=======$"` 扫描 `backend/src` → 无冲突标记 - PR `mergeable = true`、`head_sha` == 本地 HEAD、`base_sha` == 当前 `origin/main` - 未改动规范文档、未改动容器 TZ / 共享 env(避免波及既有定时任务)
yanhui merged commit 42f60eb0ce into main 2026-10-21 08:19:16 +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#70
No description provided.