Page:
T-249
Pages
00-ai-start-here
01-vision
02-requirements
03-tech-stack
04-architecture
05-coding-rules
06-tasks
07-user-stories
08-interaction-checklist
Design
Home
Integrations-shunyunbao-contract
PPT
T-001
T-002
T-003
T-004
T-101
T-102
T-103
T-104
T-201
T-202
T-203
T-204
T-205
T-206
T-207
T-208
T-209
T-210
T-211
T-212
T-213
T-214
T-215
T-216
T-217
T-218
T-219
T-220
T-221
T-222
T-223
T-224
T-225
T-226
T-227
T-228
T-229
T-230
T-231
T-232
T-233
T-234
T-235
T-236
T-237
T-238
T-239
T-240
T-241
T-242
T-243
T-244
T-245
T-246
T-247
T-248
T-249
T-250
T-251
T-252
T-253
T-254
T-255
T-256
T-257
T-258
T-259
T-260
T-261
T-262
T-263
T-264
T-265
T-266
T-267
T-268
T-269
T-270
T-271
T-272
T-273
T-274
T-275
T-276
T-277
T-278
T-279
T-280
T-281
Task-Template
Tasks
agent-context
api
clean-state-checklist
current-state
routes
Clone
This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
同步来源:
docs/tasks/T-249.md· commitafc651f75a3a
id: T-249 title: 采购任务安全重试与重复采购防护 phase: 2 deps:
- T-218
- T-223
- T-248 status: DONE created: 2026-07-30 context_ref: 773a3cd work_branch: null write_paths:
- docs/tasks/T-249.md
- docs/current-state.md
- docs/api.md
- backend-api/migrations/**
- backend-api/internal/domain/**
- backend-api/internal/usecase/**
- backend-api/internal/repository/sqlite/**
- backend-api/internal/transport/httpapi/**
- backend-api/internal/transport/webui/**
需求 / 背景
ERP 货运商品当前按 (freight_order_item_id, source_revision) 只允许创建一条采购需求,
同一采购需求也只关联一个采购任务。前一个唯一约束可以防止页面双击、网络重试和多人操作
形成重复采购,必须保留;但采购任务因设备中断、自动化失败或下单前人工取消进入
FAILED/CANCELED 后没有恢复入口,采购人员只能看到历史任务,无法继续采购。
本任务不开放“再次创建相同采购订单”。同一 ERP 商品 revision 继续只有一个业务采购需求
和一个采购任务身份;符合安全条件时,将原任务重新排入 PENDING,由 Roubao 领取后创建
新的 execution attempt。既有候选、证据、人工选择、失败结果和事件全部保留。
修订契约
需求和任务身份
UNIQUE (freight_order_item_id, source_revision)保持不变;重复创建采购需求继续幂等返回 原需求。procurement_requests.purchase_task_id与purchase_task_sources的一对一来源关系保持 不变;安全重试不复制参考图、不创建第二个任务 ID,也不改写 ERP 来源快照。- Admin 文案使用“重新执行采购任务”,不得表示已创建新的拼多多订单。
可重试状态和下单围栏
- 只允许
FAILED或CANCELED任务重新排队;PENDING/CLAIMED/RUNNING/ WAITING_CONFIRMATION/SUCCEEDED一律拒绝。 - 事务内检查该任务不存在任何
order_submissions记录,且历史 execution/outcome 均未 标记order_submitted=true。只要已创建FENCED/RECONCILED/MANUAL_REVIEW提交记录, 或是否下单无法确认,就保持禁止重试。 - 重试成功将任务改为
PENDING、增加version、清除当前 claim 和取消控制字段;任务 标题、SKU、数量、预算、参考图、来源关联和全部历史执行数据保持不变。 - Roubao 再次领取时沿用既有 claim generation 和 execution attempt 递增机制;旧 claim、 authorization、command 和 execution 不能授权新一轮动作。
原因、审计和幂等
- 重试必须提交原因码:
AUTOMATION_FAILED、DEVICE_INTERRUPTED、ADMIN_CANCELED或OTHER。OTHER必须填写说明,说明去除首尾空格后最多 500 个 UTF-8 字节。 - v20 保存不可变重试审计:任务、重试序号、重试前状态、原因、操作人和时间;同时产生
TASK_REQUEUED任务事件。 - Admin API 和 SSR 都要求幂等键。相同键和相同请求返回当前任务;相同键不同请求返回
幂等冲突。并发请求最多一次从终态切换到
PENDING。 - 稳定错误区分状态冲突和下单不确定:前者为
TASK_RETRY_NOT_ALLOWED,后者为TASK_RETRY_ORDER_UNCERTAIN,不得用通用 500 掩盖。
Admin 交互
- 任务详情仅在状态和下单历史表明可重试时显示“重新执行”入口;表单要求选择原因,
OTHER提供说明并在服务端再次校验。 - 有提交围栏或待付款订单时不显示可执行按钮,明确提示必须先核对拼多多订单,禁止重复 下单。
- 重试成功返回同一任务详情并显示“任务已重新排队”;货运详情继续链接同一采购任务。
方案
- v20 增加
task_retries审计表,并重建task_events约束以允许TASK_REQUEUED;Down 在已有重试记录或事件时 fail-closed。 TaskService增加受校验的 retry command;领域层集中维护原因码、状态判断和长度限制。- SQLite immediate 写事务完成幂等读取、安全门禁、审计插入、任务状态更新和事件写入,
并同时检查
task_executions、execution_outcomes与order_submissions。 - Admin JSON API 增加
POST /api/v1/tasks/:id/retry;SSR 增加POST /tasks/:id/retry和任务详情重试表单。 - 任务详情返回是否可重试及阻塞原因;最终安全裁决始终在仓储事务内完成,页面判断只 用于交互提示。
验收要点
- 同一货运商品 revision 仍不能创建第二条采购需求或第二个任务 ID。
FAILED/CANCELED且无下单痕迹的任务可原子恢复为PENDING。- 新一次 Roubao 领取创建递增的 execution attempt,旧证据和结果仍可审计。
- 活跃、成功、已围栏、待人工复核或待付款任务均不能重试。
- 重试原因、操作人、原状态和时间可查询;
OTHER无说明时拒绝。 - API/SSR 幂等、并发和稳定错误码测试通过。
- v20 上下迁移、Go 单测、race、vet 和入口构建通过。
边界
- 本任务不实现拆单、部分数量补采或同一需求多任务;这些场景必须先扩展拼多多订单的 已付款、取消、关闭、退款和实购数量状态,不能仅凭任务失败状态猜测。
- 不删除或覆盖旧 execution、候选、授权、命令、证据、结果、提交围栏和订单编号。
- 不绕过拼多多登录、验证码、风险控制或支付边界。
执行记录
- 2026-07-30:冻结“单一采购需求/任务身份,多次受控 execution”的恢复契约;已产生 下单围栏或下单状态不确定时 fail-closed。
- 2026-07-30:新增 v20
task_retries审计和TASK_REQUEUED事件;Admin API/SSR 支持带幂等键和原因的原任务重新排队,SQLite 事务同时检查 execution outcome 与 order submission 下单信号。 - 2026-07-30:
go test ./...、go test -race ./...、go vet ./...和三个 Go 入口构建通过;真实 Gin/SQLite + Playwright 验证CANCELED -> PENDING、同一任务 ID、重试历史和成功提示,1440px 与 390px 视口均无横向溢出。