diff --git a/docs/00-ai-start-here.md b/docs/00-ai-start-here.md index a9266a8..bd9902f 100644 --- a/docs/00-ai-start-here.md +++ b/docs/00-ai-start-here.md @@ -58,7 +58,7 @@ T-205 原子领取/租约状态机、T-206 Android 登录/有限离线、T-207 证据回传、T-211 参考图召回和 SKU 硬匹配、T-212 候选身份映射,以及 T-213 受控 规格组合/价格核验均已完成。T-208 的原始候选观测、模型评估、确定性推荐、逐候选 结构化人工理由和修订历史也已完成。T-214 商品持久身份和重新定位指纹也已完成; -下一步实现 T-215 Admin 下单授权,不得直接把候选链接或列表 ordinal 当成授权。 +当前正在实现 T-215 Admin 下单授权,不得直接把候选链接或列表 ordinal 当成授权。 手机从管理后端领取任务并回传结果,VLM、拼多多自动化和人工确认在 App 本地完成。 T-206 增加有限离线执行;T-207 已复用 Roubao 端上 OpenAI 兼容适配器并加密本地 Key。 管理后端不保存/代理 VLM,后台任务不能覆盖手机 provider 配置。 diff --git a/docs/02-requirements.md b/docs/02-requirements.md index bb51869..f0e3b74 100644 --- a/docs/02-requirements.md +++ b/docs/02-requirements.md @@ -30,9 +30,10 @@ | F-003 | 手动领取任务 | 空闲且就绪的 App 点击后只领取一条待处理任务。 | P0 | US-003 | | F-004 | 解析采购需求 | 系统从图片和文字提取搜索词、属性及约束,并保留原始输入。 | P0 | US-004 | | F-005 | 参考图搜索与规格候选判断 | App 用参考图在拼多多召回商品,以 SKU 颜色/尺码硬约束检查最多 5 个结果,回传匹配候选或明确无匹配。 | P0 | US-004 | -| F-006 | 人工确认停止点 | 自动化停在候选/订单确认位置,采购员确认结果或拒绝候选。 | P0 | US-005 | +| F-006 | Admin 人工选品 | 候选回传后自动化停止,Admin 查看证据并明确选择或拒绝候选。 | P0 | US-005、US-010 | | F-007 | 结果与异常回传 | 管理员和采购员看到成功、失败、取消及可恢复建议。 | P0 | US-002、US-006 | | F-009 | 手机独立执行与 VLM | App 本地完成模型判断和自动化,后端只控制任务并接收结果。 | P0 | US-009 | +| F-010 | 授权创建待付款订单 | Admin 选品后签发一次性授权,Roubao 选择已确认 SKU/数量并创建待付款订单;付款始终人工完成。 | P0 | US-010 | ### F-009 手机独立执行与 VLM @@ -100,8 +101,8 @@ T-206/T-207 已跑通领取、执行、候选/证据和最小结果回传;T-20 活跃任务都必须说明原因。 7. 遇到验证码、登录失效、风控提示、页面未知、预算不满足或模型低置信度时停止, 不猜测点击。 -8. MVP 的“成功”表示完成验证闭环并得到人工确认的候选结果, - `order_submitted` 必须为 `false`。 +8. 候选采集阶段 `order_submitted=false`。只有 Admin 对当前 candidate key 签发的 + 一次性授权可进入订单创建;创建后必须停在待付款状态,禁止自动支付。 9. 取消和失败不得自动转成新任务;是否重试由人员显式决定。 10. 管理页面和管理 API 只允许有效 ADMIN 会话;App 执行接口只允许有效 BUYER 与 预授权设备联合身份,两种凭证不能互换。 @@ -127,6 +128,10 @@ T-206/T-207 已跑通领取、执行、候选/证据和最小结果回传;T-20 20. 拼多多候选召回必须使用当前任务参考图;标题和 SKU 不能替代图片作为召回输入。 SKU 中可唯一识别的颜色、尺码是候选硬约束:任一不匹配或无法确认时不得进入自动 Top 5。回传可以少于 5 个,禁止用弱匹配凑数。 +21. Admin 下单授权必须绑定 task、execution、task content hash、candidate key、 + review version、原始 SKU 和数量;URL、列表 ordinal 或模型推荐均不能单独授权。 +22. 一个授权最多创建一个拼多多订单。网络超时或页面不确定时必须先对账,不能重按 + 提交;系统不保存支付凭证,不点击付款。 ## 六、第一层本地样本约定 @@ -179,15 +184,16 @@ T-004 已固定首版规则:推荐私有目录为被 Git 忽略的 `private-fi 搜索输入,进入结果并检查最多 5 个候选;候选只有在原始 SKU 的颜色、尺码硬约束 均确认匹配时才能进入回传列表。匹配项按评估结果排序且最多 5 个,不足时不得凑数; 无合理候选时明确结束而不是随意选择。 -- F-006/US-005/IX-007:流程到达人工确认点后停止;MVP 任意路径都不能触发最终 - 提交订单或支付。T-104 的本地确认只允许标记建议候选可用或拒绝本次候选, - `order_submitted` 固定为 `false`。 +- F-006/US-005/US-010/IX-011:候选回传后进入 `WAITING_CONFIRMATION`;Admin 必须 + 按 candidate key 查看证据并为接受项及所有拒绝项选择结构化理由。 - F-007/US-006/IX-008:失败包含稳定错误码、失败步骤、可读说明和必要截图;重新 打开任务后证据仍可查看。 - US-007/IX-001/IX-004:管理员可登录和退出,采购员可用预授权设备取得 1 小时 Bearer token;匿名、角色错误、禁用、过期、篡改和跨端凭证替代均被拒绝。 - F-009/US-009/IX-010:后台任务不能覆盖手机 VLM 配置;断开管理后端超过 90 秒后, App 仍可在服务端下发的有限授权内执行,授权到期安全停止且任务不被重复分配。 +- F-010/US-010/IX-011:只有当前 Admin 一次性授权可驱动 Roubao 创建一笔待付款 + 订单;SKU、数量和价格重新核验,订单号/下单时间回传后提醒人员去拼多多付款。 ### 试验验收 @@ -195,7 +201,7 @@ T-004 已固定首版规则:推荐私有目录为被 Git 忽略的 `private-fi - 至少 80% 能自动到达合理候选商品页或给出正确的“无匹配/需人工”结论。 - 至少 70% 的首选候选被采购人员判定可接受。 -- 不发生重复领取、重复执行导致的不可逆操作或订单提交。 +- 不发生重复领取、未授权订单、重复订单或自动付款。 - 每条失败任务都能定位到具体步骤和错误类别。 - 记录每条任务的耗时、人工介入点和候选接受结果,用于决定是否进入 V2。 - 同时记录实际曝光候选、模型预测、系统推荐和人工结构化理由;准确率以人工结论为 @@ -211,7 +217,7 @@ T-004 已固定首版规则:推荐私有目录为被 Git 忽略的 `private-fi | 任务到达 | MVP 由 App 手动领取;后续通知只作唤醒/提示。 | | 搜索方式 | 正式候选召回使用当前任务参考图进入拼多多原生以图搜图;关键词只保留为开发诊断能力,不作为该业务链路的召回输入。 | | 搜索结果 | 最多检查图片搜索返回的前 5 个可见候选,以 SKU 颜色/尺码硬约束过滤后回传 `0..5` 个,避免无界遍历和凑数。 | -| 下单边界 | MVP 停在候选或订单确认页,不提交订单、不支付。 | +| 下单边界 | Admin 明确选品后只允许创建一笔待付款订单;订单号回传后由人员在拼多多确认并付款,系统不自动支付。 | | 账号边界 | 验证版为 ADMIN 会话 + BUYER/预授权设备联合身份;完整人员 RBAC 后置。 | | 第一层任务输入 | 从本机私有蝦皮订单文件生成测试任务,不先建设 Go-Gin。 | | 执行边界 | 手机本地完成 VLM、拼多多自动化和人工确认;后端负责身份、任务控制和结果审计。 | @@ -231,6 +237,6 @@ T-004 已固定首版规则:推荐私有目录为被 Git 忽略的 `private-fi 模型、测试凭证、成本上限、数据留存地区和图片隐私规则仍待确认。候选评估契约和 本机 mock 集成已实现,但尚无真实模型质量证据。 - 拼多多平台条款、自动化允许范围和账号风控需要业务方确认;项目不实现绕过措施。 -- 后续若允许提交订单,必须先明确 SKU、收货地址、运费、优惠、发票、金额审批、 - 幂等和人工确认规则,并单独更新需求。 -- “最终产品是否自动支付”没有定案,当前明确排除。 +- 创建待付款订单前必须在 T-215 至 T-218 固定 SKU、数量、价格、地址可见性、运费/ + 优惠不确定性、一次性提交和订单列表对账规则;不满足时安全停止。 +- 自动支付明确排除;人员必须在拼多多订单列表复核后付款。 diff --git a/docs/04-architecture.md b/docs/04-architecture.md index 0f8877c..8ec1452 100644 --- a/docs/04-architecture.md +++ b/docs/04-architecture.md @@ -568,6 +568,27 @@ evidence asset ID;后端逐一核对其实际 SHA-256 后才接受候选。Adm key、四个指纹和受控截图;后续选品授权必须引用 key 并绑定任务内容 hash 和 review 版本,不能只信 URL 或列表 ordinal。 +### Admin 下单授权(T-215) + +非空候选保存后,任务从 `RUNNING` 进入 `WAITING_CONFIRMATION`。Admin 选择不会调用 +Android 或拼多多,而是在后端追加一版 human review 和一条 +`order_authorizations`: + +```text +Admin task detail + -> candidate key + 全候选结构化理由 + -> 校验 task/execution/content hash/version + -> candidate key 映射原 observation + -> 追加 admin human review + -> 生成 PENDING_DELIVERY authorization 快照 + -> T-216 device command pull +``` + +授权快照固定原始任务 SKU/数量、候选规格/价格、review 版本和 T-214 四个身份指纹; +请求不能覆盖这些字段。一次 execution 最多一条 active 授权;设备领取前改选只能 +追加新版本并把旧记录标记为 `SUPERSEDED`,设备领取后必须走显式停止/撤销协议。 +浏览器不持有设备 claim token,授权表也不保存拼多多登录、支付或收货凭证。 + 人工理由使用版本化 allowlist。接受或拒绝至少有一个理由且指定主要理由; `OTHER` 才要求 4-200 字备注。拒绝推荐后改选必须同时产生一条原推荐项负标签和一条 替代项正标签;全部无匹配时每个曝光候选都有负标签。人工修正追加新 review 并引用 diff --git a/docs/06-tasks.md b/docs/06-tasks.md index baab2f6..3620e47 100644 --- a/docs/06-tasks.md +++ b/docs/06-tasks.md @@ -47,6 +47,7 @@ | T-212 | 修复 Top 5 ordinal 与证据映射 | T-211 | 重排、推荐、证据和人工接受引用同一原始候选 | | T-213 | 拼多多候选 SKU 组合与价格核验 | T-212 | 受控选择唯一颜色/尺码并读取组合价;不确认购买,不进入订单或支付 | | T-214 | 建立候选商品持久身份与重新定位指纹 | T-208、T-213 | candidate key 绑定原 observation、卡片/详情/双证据指纹;不伪造平台链接 | +| T-215 | Admin 候选确认与一次性下单授权 | T-214 | 非空候选进入等待确认;Admin 按 candidate key 提交逐项理由并生成不可变待投递授权 | ## Phase 3:端到端验证 diff --git a/docs/07-user-stories.md b/docs/07-user-stories.md index 52e2147..8d470a8 100644 --- a/docs/07-user-stories.md +++ b/docs/07-user-stories.md @@ -16,6 +16,7 @@ | US-007 | 建立受控会话和设备身份 | P0 | 采购管理员、采购执行员 | 未授权人员和设备不能接触任务 | F-001、F-003 | IX-001、IX-004 | 已定 | | US-008 | 积累可信的候选决策样本 | P1 | 采购执行员、优化人员 | 用真实曝光和人工理由评估并改进模型 | F-008 | IX-009 | 已定,T-208 后置 | | US-009 | 手机独立完成采购任务 | P0 | 采购执行员 | 后端短时不可用时仍在有限授权内安全执行 | F-009 | IX-010 | 已定,T-206/T-207 | +| US-010 | 审批并创建待付款订单 | P0 | 采购管理员、采购执行员 | Admin 选定候选后只创建一笔可核对的待付款订单 | F-006、F-010 | IX-011 | 已定,T-215 至 T-219 | ## US-001 创建清晰的采购任务 @@ -208,6 +209,30 @@ 两种模式都停在人工确认且 `order_submitted=false`。 5. 离线完成的结果恢复联网后幂等补报;离线期间的取消在恢复后的首个同步点处理。 +## US-010 审批并创建待付款订单 + +- 关联页面:管理 Web“任务详情/候选确认”、Android“等待授权/订单执行”。 +- 前置条件:当前 execution 已回传带持久 candidate key 的候选和双证据。 + +作为采购管理员,我希望查看候选证据、给选择和拒绝填写理由并签发一次性授权,从而 +让 Roubao 只按我确认的商品、SKU 和数量创建一笔待付款订单;作为采购执行员,我希望 +订单号和下单时间自动回传,随后由人员在拼多多复核并付款。 + +**范围** + +- 包含:Admin 选品、逐候选理由、不可变授权、设备命令、重新定位、SKU/数量复核、 + 单次提交、订单列表对账和待付款提醒。 +- 不包含:自动支付、验证码/风控绕过、自动修改地址、无界重试或仅凭 URL 下单。 + +**验收场景** + +1. 非空候选回传后任务进入等待 Admin 确认,手机不会自行选定或提交。 +2. Admin 只能按当前 candidate key 授权,数量来自原任务;改选保留旧 review 和授权。 +3. Roubao 领取授权后重新用参考图定位候选,并复核详情/规格指纹、SKU、数量和价格。 +4. 每个授权最多执行一次提交;响应不确定时先去订单列表对账,不能再次点击。 +5. 找到新订单后回传拼多多订单号和下单时间,Admin 显示“待人工确认付款”。 +6. 任意流程都不点击付款,不保存支付密码或验证码。 + ## 待确认 - 密码重置流程后置;T-204 使用本地 `authctl` 显式创建种子 ADMIN/BUYER 和预授权 diff --git a/docs/08-interaction-checklist.md b/docs/08-interaction-checklist.md index a184a11..f1da538 100644 --- a/docs/08-interaction-checklist.md +++ b/docs/08-interaction-checklist.md @@ -17,6 +17,7 @@ | IX-008 | US-006 | App/管理端错误状态 | 自动失败、取消、重试上传 | 显示结构化原因和恢复动作 | P0 | 已定 | | IX-009 | US-008 | App 候选理由/管理端决策详情 | 接受、拒绝、改选或修正 | 保存逐候选结构化人工标签 | P1 | T-208 已实现 | | IX-010 | US-009 | App 独立执行设置/同步状态 | 配置模式、离线执行或补报 | 授权内独立执行并可审计同步 | P0 | T-206 离线控制已实现,结果补报待 T-207 | +| IX-011 | US-010 | Admin 候选授权/App 订单执行 | 选择候选并授权、设备领取执行 | 创建一笔可对账的待付款订单并提醒人工付款 | P0 | T-215 至 T-219 | ## IX-001 管理 Web 登录 @@ -307,6 +308,40 @@ - 结果补报失败保留加密本地记录和原幂等 key;不能恢复终态后的自动化。 - App 可以显示 provider 名称和模型,但不得回显 Key、Authorization 或完整敏感 URL。 +## IX-011 Admin 候选授权与待付款订单 + +- 页面:管理 Web“任务详情/候选确认”、Android“等待授权/订单执行”。 +- 角色:采购管理员、采购执行员。 +- 服务依赖:T-215 order authorization、T-216 device command、T-217/T-218 + 订单自动化与对账。 + +**正常路径** + +1. 任务进入 `WAITING_CONFIRMATION` 后,Admin 查看原始要求、候选标题/SKU/组合价格、 + 模型理由、candidate key 和详情/规格截图。 +2. Admin 单选一个候选,为接受项和其余拒绝项选择结构化理由,并确认“只创建待付款 + 订单,不授权付款”。 +3. 后端生成不可变 `PENDING_DELIVERY` 授权;设备领取后页面显示投递/执行进度。 +4. Roubao 重新定位商品,复核 SKU/数量/价格后只提交一次,再从订单列表读取订单号 + 和下单时间。 +5. Admin 显示“待人工确认付款”,提供订单号、时间和审计证据;人员切换拼多多付款。 + +**状态与异常** + +- 候选证据不完整、任务版本变化、取消已请求或授权过期时禁用授权。 +- 授权尚未投递时改选会创建修订版本;旧授权显示 `SUPERSEDED`,不能覆盖。 +- 投递后不能静默改选;必须先走设备停止/撤销协议。 +- 商品无法重新定位、SKU/数量/价格不一致、地址/运费/优惠导致金额不确定时停止, + 不提交订单。 +- 提交响应超时或页面未知时显示“正在对账”,禁止再次提交;没有唯一新订单时转人工。 +- 页面和 App 都不能提供自动付款按钮。 + +**可访问性** + +- 候选使用带完整 label 的 radio;理由用 select/radio,不依赖颜色或图片顺序。 +- 授权按钮文案包含动作结果,确认对话框默认焦点在取消。 +- 状态更新不改变候选卡和按钮尺寸,错误与待付款提醒可被读屏读取。 + ## 通用交互约束 - Web 和 App 的所有提交都防重复,网络超时后以服务端状态为准。 diff --git a/docs/api.md b/docs/api.md index 43233ed..aa4281f 100644 --- a/docs/api.md +++ b/docs/api.md @@ -259,6 +259,26 @@ T-204 已实现的 `TASK_CREATED`、`TASK_CANCELED` 事件包含可空 `cancel_requested=true`,只有 App 在安全检查点调用 `cancel-ack` 后才进入 `CANCELED` 并结束 execution。 +### `POST /api/v1/tasks/{task_id}/order-authorizations` + +T-215 起由有效 ADMIN 会话对当前 `WAITING_CONFIRMATION` execution 创建一次性待投递 +授权。Cookie API 请求必须带 `X-CSRF-Token` 和 `Idempotency-Key`;请求体见 +[`T-215`](tasks/T-215.md)。关键规则: + +- 顶层和每个 item 只引用 v7 `candidate_key`,不得用 ordinal 或 URL 选品。 +- `items` 恰好覆盖当前 execution 全部 observation,只有顶层 key 对应项为 + `ACCEPT`;所有项必须包含 T-208 schema v1 合法理由。 +- `execution_id`、task content hash 和 expected task version 必须同时匹配;服务端 + 从任务/observation 读取 SKU、数量、候选规格、价格和身份指纹写入授权快照。 +- 首次成功返回 `201` 和 `PENDING_DELIVERY` 授权;同 key 同 body 重放返回相同 + 授权并带 `replayed=true`,同 key 不同 body 返回 `409`。 +- 未投递前改选必须引用当前 `supersedes_authorization_id`;旧授权变为 + `SUPERSEDED`。已投递或不是最新授权时返回 `409`。 + +Admin `GET /api/v1/tasks/{task_id}` 同时返回当前及历史 +`order_authorizations`。授权仅允许 T-216 设备命令领取,不代表浏览器可以直接操作 +手机,也不授权付款。 + ## 设备与领取 ### `POST /api/v1/devices/heartbeat` diff --git a/docs/current-state.md b/docs/current-state.md index 80f1d12..21b3d4f 100644 --- a/docs/current-state.md +++ b/docs/current-state.md @@ -5,7 +5,7 @@ ## 当前快照 - 日期:2026-07-28 -- 阶段:T-214 已完成;下一步 T-215 Admin 候选确认与下单授权 +- 阶段:T-215 Admin 候选确认与一次性下单授权进行中 - Git:当前分支为 `main`;T-001 至 T-004、T-101 至 T-104、T-201 至 T-207、T-209、 T-208、T-210、T-211、T-212、T-213、T-214 均已纳入 Git 历史 - 生产代码:`android-buyer/` 已接入 Roubao Android 源码 @@ -123,6 +123,7 @@ | `docs/tasks/T-213.md` | DONE | 真机选择目标 SKU、读取组合价并安全返回 | | `docs/tasks/T-208.md` | DONE | 归一化候选决策数据并增加结构化人工 review | | `docs/tasks/T-214.md` | DONE | 建立 execution-scoped candidate key 与设备采集指纹 | +| `docs/tasks/T-215.md` | DOING | Admin 按 candidate key 选择并创建不可变待投递授权 | | `docs/design/` | 已确认 | T-202 原型索引、4 个管理页和 7 个 Android 页面 | | `deepseek总结.txt` | 已有 | 历史讨论摘要,不是正式需求权威 | | `android-buyer/` | 已有 | Roubao `main` 固定 commit 的 Android 基线 | @@ -134,8 +135,8 @@ ## 任务摘要 - 已完成:T-001 至 T-004、T-101 至 T-104、T-201 至 T-214。 -- 正在进行:无。 -- 下一步:依次实现 T-215 Admin 下单授权、设备命令、订单 dry-run、单次提交对账和 +- 正在进行:T-215 Admin 候选确认与一次性下单授权。 +- 下一步:依次实现设备命令、订单 dry-run、单次提交对账和 付款提醒。 ## 当前可运行内容 diff --git a/docs/routes.md b/docs/routes.md index d3dec2b..32483f2 100644 --- a/docs/routes.md +++ b/docs/routes.md @@ -10,7 +10,7 @@ T-202 的离线 P0 页面入口见[原型索引](design/index.html)。原型仅 | `/login` | 管理登录 | 建立服务端管理会话 | US-007 | IX-001 | | `/tasks` | 任务列表 | 查看状态、筛选并进入详情 | US-002 | IX-003 | | `/tasks/new` | 新建任务 | 提交图片和采购约束 | US-001 | IX-002 | -| `/tasks/{id}` | 任务详情 | 查看原始输入、时间线、候选和证据 | US-002、US-006 | IX-003、IX-008 | +| `/tasks/{id}` | 任务详情 | 查看输入/候选/证据,并按 candidate key 创建待投递下单授权 | US-002、US-006、US-010 | IX-003、IX-008、IX-011 | MVP 登录后默认进入 `/tasks`。未登录访问受保护页面时跳转 `/login` 并携带安全的 站内返回路径;只接受 `/tasks` 及其本站子路径,拒绝绝对 URL、`//` 和反斜杠。 @@ -45,6 +45,7 @@ Android 导航名称是逻辑目的地,具体 Compose/Fragment 形式待接入 | `POST /api/v1/tasks/{id}/cancel-ack` | 安全停止后确认 `CANCELED` 并结束 execution | | `POST /api/v1/tasks/{id}/events` | 幂等补报本地执行事件 | | `POST /api/v1/tasks/{id}/candidates` | 保存最多 5 个候选、模型判断和本地推荐 | +| `POST /api/v1/tasks/{id}/commands/next` | T-216 起领取 Admin 已创建的设备命令 | | `POST /api/v1/tasks/{id}/complete` | 提交人工结果,强制 `order_submitted=false` | | `POST /api/v1/tasks/{id}/fail` | 提交结构化失败和受控证据 | diff --git a/docs/tasks/T-215.md b/docs/tasks/T-215.md new file mode 100644 index 0000000..a69d4f4 --- /dev/null +++ b/docs/tasks/T-215.md @@ -0,0 +1,146 @@ +--- +id: T-215 +title: Admin 候选确认与一次性下单授权 +phase: 2 +deps: + - T-214 +status: DOING +created: 2026-07-28 +context_ref: 56e7a2b +work_branch: null +write_paths: + - docs/tasks/T-215.md + - docs/00-ai-start-here.md + - docs/02-requirements.md + - docs/04-architecture.md + - docs/06-tasks.md + - docs/07-user-stories.md + - docs/08-interaction-checklist.md + - docs/api.md + - docs/current-state.md + - docs/routes.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/** +--- + +## 问题 / 背景 + +T-214 已给每个候选建立 execution-scoped `candidate_key`,但任务仍由手机上的采购员 +提交 human review 并结束。新需求要求 Admin 采购员查看 Top 5 后选择商品,再把明确 +授权同步给同一台 Roubao 设备;候选列表或 URL 本身不能成为下单许可。 + +本任务实现后台选择、逐候选理由和不可变的一次性授权。它不实现设备拉取或拼多多 +点击;T-216 才把待投递授权转换为设备命令。 + +## 冻结合约 + +### 等待确认 + +1. 当前 execution 首次成功保存非空候选批次后,任务在同一事务从 `RUNNING` 进入 + `WAITING_CONFIRMATION`,增加 task version,并追加 `CANDIDATES_READY` 事件。 +2. 设备 heartbeat 在该状态继续有效,但不能凭候选回传重新开始页面动作。空候选 + 仍允许现有 `NO_MATCH/MANUAL_REQUIRED` 终态,不创建授权。 +3. Admin 只能为当前活跃 execution、当前 task content hash 和 v7 identity 完整的 + observation 授权;取消已请求、授权到期、终态或旧 execution 一律拒绝。 + +### Admin 决策与授权 + +新增管理会话鉴权、CSRF/幂等保护的 +`POST /api/v1/tasks/{task_id}/order-authorizations`: + +```json +{ + "execution_id": "uuid", + "task_content_sha256": "64-char-lowercase-hex", + "expected_task_version": 4, + "candidate_key": "64-char-lowercase-hex", + "reason_schema_version": 1, + "primary_reason_code": "SELECTED_BEST_MATCH", + "note": "", + "supersedes_authorization_id": null, + "items": [ + { + "candidate_key": "selected-key", + "label": "ACCEPT", + "primary_reason_code": "SKU_MATCH", + "reason_codes": ["SKU_MATCH", "IMAGE_MATCH"], + "note": "" + }, + { + "candidate_key": "rejected-key", + "label": "REJECT", + "primary_reason_code": "NOT_BEST_MATCH", + "reason_codes": ["NOT_BEST_MATCH"], + "note": "" + } + ] +} +``` + +1. `items` 必须按 candidate key 恰好覆盖当前 execution 的全部 observation,且只有 + 顶层 `candidate_key` 对应项为 `ACCEPT`。理由沿用 T-208 schema v1 allowlist 和 + `OTHER` 备注规则;无预算任务仍禁止价格类理由。 +2. 仓储在一个 immediate transaction 内把 candidate key 映射回原 ordinal,追加一版 + `candidate_human_reviews`(`actor_device_id=NULL`),再创建一条不可变 + `order_authorizations`。不得信任请求传入的 ordinal、SKU、数量、价格或 URL。 +3. 授权快照由服务端读取:task/execution/content hash、candidate key、review ID/ + version、原始 SKU、任务数量、候选规格摘要、组合价格和四个身份指纹。数量只能来自 + 原始任务,Admin 不能在选品表单中改写。 +4. 授权初始状态为 `PENDING_DELIVERY`,版本从 1 递增;同一 execution 同时最多一条 + active 授权。相同幂等键和请求返回原结果,不新增 review、授权或事件。 +5. 设备领取前允许 Admin 改选,但请求必须引用当前 active + `supersedes_authorization_id`;旧授权原子变为 `SUPERSEDED`,review/授权历史均不 + 覆盖。非当前引用、已投递或已执行授权拒绝修改。 +6. 每次创建或改选追加 `ORDER_AUTHORIZATION_CREATED` 任务事件,事件只记录授权 ID、 + 版本和 candidate key 的短前缀,不记录第三方 URL 或页面全文。 + +### Admin API/Web + +1. Admin 任务详情返回结构化 observations、当前/历史 review 和 order authorization, + 每项展示标题、SKU 摘要、组合价格、受控详情/规格截图、candidate key 和模型理由。 +2. `WAITING_CONFIRMATION` 且存在候选时显示单选候选、选择理由、对其余候选批量拒绝 + 理由、可选备注和“确认商品并授权下单”按钮。表单服务端展开为逐候选 items。 +3. 页面必须明确“仅授权创建待付款订单,不授权付款”;没有可授权候选、数据版本冲突 + 或已有不可修改授权时禁用提交并给出可操作状态。 +4. Admin API/Web 不请求第三方商品/图片 URL,也不提供由浏览器直接触发设备动作的 + 通道。 + +## 数据模型 + +v8 新增: + +- `order_authorizations`:授权 ID、task/execution、版本、candidate key、内容 hash、 + review、原始 SKU/数量、候选 SKU/价格、四个指纹、状态、创建人、时间和可空 + supersedes ID。 +- `admin_order_authorization_requests`:管理员、幂等键、请求 hash、task 和授权引用。 + +状态预留 `PENDING_DELIVERY/DELIVERED/ACKNOWLEDGED/EXECUTING/CONSUMED/FAILED/ +REVOKED/SUPERSEDED`;T-215 只产生 `PENDING_DELIVERY` 和 `SUPERSEDED`。存在授权数据 +时 v8 destructive down 必须失败。 + +## 验收要点 + +- [ ] 非空候选批次原子进入 `WAITING_CONFIRMATION`,重复回传不重复增加事件/version。 +- [ ] Admin 请求只用 candidate key,完整覆盖候选且每项接受/拒绝都有合法理由。 +- [ ] review、授权、旧授权 supersede、任务事件和幂等记录原子提交。 +- [ ] 跨任务/execution、旧 hash/version、伪造 key、缺 identity 和并发双授权被拒绝。 +- [ ] 授权数量/SKU/价格/指纹均由服务端快照,Admin 不能篡改。 +- [ ] Admin API/Web 可选候选、查看证据和理由,明确只创建待付款订单且不授权付款。 +- [ ] v8 migration、Go test/race/vet、根验证和管理页面桌面/移动视口通过。 + +## 边界 + +- 不让设备拉取或确认授权;属于 T-216。 +- 不重新打开拼多多、选择 SKU/数量或进入订单确认页;属于 T-217。 +- 不提交订单、读取订单号或付款;属于 T-218/T-219。 +- 不使用 URL、ordinal、模型推荐或旧手机 review 直接代替 Admin 明确授权。 + +## 执行记录 + +- 2026-07-28:T-214 实现提交 `56e7a2b` 后领取。决定复用 + `WAITING_CONFIRMATION`,并把 Admin review 与一次性授权放在同一事务;设备投递和 + 页面动作继续保持为后续独立权限层。