diff --git a/docs/current-state.md b/docs/current-state.md index 513b8d0..517b1d8 100644 --- a/docs/current-state.md +++ b/docs/current-state.md @@ -5,7 +5,7 @@ ## 当前快照 - 日期:2026-07-29 -- 阶段:T-236 已完成顺运宝验证码错误有限重试 +- 阶段:T-237 已规划完整单号 55 秒同步导入,待实现 - Git:当前分支为 `main`;T-001 至 T-004、T-101 至 T-104、T-201 至 T-219 均按文档提交、实现提交的顺序纳入历史 - 生产代码:`android-buyer/` 已接入 Roubao Android 源码 @@ -14,9 +14,10 @@ - 后端:Go 1.23.0 + Gin 1.11.0 + SQLite + Goose 3.26.0;已实现图片/任务业务、 SSR 管理 Web、ADMIN/BUYER 联合认证、设备 readiness、原子 claim、租约状态机、 task-scoped 参考图和 `authctl` -- ERP 货运:Go 后端以受锁内存 ERP 会话直连异步按完整单号或创建日期同步;v12 保存 +- ERP 货运:Go 后端以受锁内存 ERP 会话直连按完整单号或创建日期同步;v12 保存 同步记录、货运头和全部明细,canonical hash 控制 revision,Admin 已有 `/freight`、 - `/freight/import`、`/freight/{id}` 与对应 JSON API。 + `/freight/import`、`/freight/{id}` 与对应 JSON API。T-237 计划将完整单号改为 55 秒 + 同步响应并可靠写终态,日期范围仍后台异步。 - ERP Go 迁移:T-225 已用脱敏 fixture 固定 `internal/platform/shunyunbao` 的 header、 单号/日期查询、分页、详情批量和字段 allowlist,并使货运用例依赖来源中立错误。T-226 已增加受锁保护的 Go 内存 Cookie jar、验证码 ticket、登录和用户校验,以及 ADMIN 的 diff --git a/docs/tasks/T-237.md b/docs/tasks/T-237.md new file mode 100644 index 0000000..18668eb --- /dev/null +++ b/docs/tasks/T-237.md @@ -0,0 +1,66 @@ +--- +id: T-237 +title: 完整单号货运同步导入 +phase: 2 +deps: + - T-236 +status: PLANNED +created: 2026-07-29 +context_ref: cf83fb5 +work_branch: null +write_paths: + - docs/tasks/T-237.md + - docs/api.md + - docs/current-state.md + - backend-api/cmd/api/** + - backend-api/internal/config/** + - backend-api/internal/usecase/freight_* + - backend-api/internal/transport/httpapi/** + - backend-api/internal/transport/webui/** +--- + +## 问题 / 背景 + +Admin 按完整订单号导入 ERP 货运时,当前请求只完成登录预检和同步记录创建,随后以 goroutine +后台查询 ERP。页面立即显示 `RUNNING`,采购人员需要反复刷新,且后台 90 秒 context 超时后又 +使用同一个已取消 context 写失败状态,存在同步记录长期停留在 `RUNNING` 的风险。 + +单号导入只处理一个确定订单,内部系统更需要即时、明确的成功或失败结果。创建日期范围可能 +包含分页和多个详情请求,不适合受相同页面等待时间约束。 + +## 方案 + +1. 仅将 `ORDER_NUMBER` 改为同步执行:认证预检、创建同步记录、查询订单/详情、规范化和事务 + 落库都在同一请求的 55 秒总预算内完成。`CREATED_RANGE` 和“同步至现在”继续异步。 +2. 同步记录仍完整经历 `PENDING -> RUNNING -> SUCCEEDED/FAILED`。成功响应前必须已提交货运头 + 和全部商品明细;Admin 随后跳转 `/freight`,立即从 SQLite 列表看到导入数据。 +3. 55 秒超时、浏览器取消、ERP/协议错误或存储失败均使用独立的短时 cleanup context 收敛为 + `FAILED`,禁止遗留 `RUNNING`。超时使用稳定错误码 `FREIGHT_SYNC_TIMEOUT`。 +4. 同一 API 进程同时只允许一个完整单号同步导入;占用时快速返回稳定 + `FREIGHT_SYNC_BUSY`,不让后续请求排队消耗 55 秒。 +5. 保持 Idempotency-Key:相同键和请求重放返回已有最终结果,不重复查询或写入;不同 payload + 继续冲突。首次成功 API 返回 `201`,成功重放返回 `200`,不再以 `202` 表示后台受理。 +6. HTTP Server `WriteTimeout` 从 30 秒提高到 70 秒,为 55 秒业务预算、TLS 和响应写入留余量; + ERP 单次 HTTP timeout 仍为 30 秒。 + +## 验收要点 + +- [ ] 完整单号 POST 只在货运头和明细事务提交后返回成功,返回时 run 为 `SUCCEEDED`。 +- [ ] Admin 成功后跳转 `/freight`,列表立即显示本次货运单;页面不再要求刷新 `RUNNING`。 +- [ ] 完整单号从请求进入到执行结束最多 55 秒;超时可靠保存 `FAILED/FREIGHT_SYNC_TIMEOUT`。 +- [ ] ERP、协议、存储和请求取消也可靠保存终态,不复用已取消 context 进行失败收尾。 +- [ ] 同进程并发完整单号导入快速返回 `FREIGHT_SYNC_BUSY`;幂等成功重放不重复执行。 +- [ ] 日期范围导入继续返回 `202/PENDING` 并在后台执行。 +- [ ] HTTP `WriteTimeout` 为 70 秒,覆盖同步业务预算。 +- [ ] 标准 Go 测试、race、vet 和三个入口构建通过。 + +## 边界 + +- 不修改 ERP 登录、OCR、验证码重试、字段 allowlist、货运 schema 或采购任务生成规则。 +- 不将创建日期范围导入改为同步,也不在浏览器轮询或引入消息队列。 +- 不在自动测试中访问真实 ERP;超时和并发使用可控 fake 验证。 + +## 执行记录 + +- 2026-07-29:创建任务。确认同步化范围仅为完整单号;55 秒是整个单号请求预算,服务端写 + 超时提高至 70 秒。终态收尾、并发拒绝和幂等重放与同步响应一起实现。