PDD 商品替换(四):续做入口改造(复用既有 AgentRetry,不新增创建路径) #132

Closed
opened 2026-08-28 16:02:09 +08:00 by ila · 6 comments
Owner

本正文为最终版(2026-08-28 重写)。 此前的历史评论仅作决策过程记录;如与本正文冲突,一律以本正文为准。原方案「新增续做采购能力」已作废——经复核,AgentRetry 已存在且完全满足需求,本工单缩减为状态与入口改造。

所属与来源

  • 关联工单:#129 数据模型、#130 Agent 入口、#131 生效与匹配(均为前置)。
  • 来源:用户于 2026-08-28 提出「因为是内部系统,尽量减少耦合、让采购流程简单易用,建议 Admin 和 Agent 都可以让失败的、准备了新 PDD 商品的采购任务继续尝试采购」。
  • 类型:Server + Android Agent / 失败采购任务续做的入口与资格改造。
  • 设计证据:复用 Agent 采购任务详情中既有的重试入口,仅改变其文案与显示条件;不新增按钮与页面,提供标注截图即可。

当前事实(提交 30d8238 复核)

AgentRetry 已经存在(purchase/retry.go:143),其注释明确:

delegates creation and idempotency to BatchRetry so Admin and Agent use exactly the same archive, device and irreversible-boundary checks

具体能力:

  • 设备 token 鉴权,且限制为本设备、近 30 天的任务(retry.go:154-156);
  • 请求只含 taskId 与 requestId,不接受任何业务参数;
  • 内部直接委托 BatchRetry,与 Admin 走完全相同的档案、设备与不可逆边界校验;
  • 失败旧任务保持不可变历史;当前虾皮/PDD 商品、映射、价格护栏与规则在重试时重新解析(retry.go:67-69);
  • 新任务创建后,旧失败任务因不再是最新任务而无法再次成功重试;
  • 详情接口已有 Retryable 字段(purchase/agent_history.go:41、admin_query.go:68)。

因此不新增第二套创建能力与第二套幂等链路。 此前正文中「为设备 token 新开创建路径」的判断有误,据此产生的风险分析一并作废。

目标

  1. 替换与规格匹配完成后,Agent 上的既有重试入口以「继续采购」呈现,一键接着买。
  2. Admin 侧保持现状(BatchRetry),无需改动。
  3. 资格判定一律由服务端下发,UI 不自行组合状态推断。

非目标

  • 不新增续做采购的接口与创建路径;一律复用既有 AgentRetry → BatchRetry。
  • 不修改 BatchRetry 的既有校验、价格护栏、规则解析与不可逆边界。
  • 不在 Agent 端做规格映射选择与规格决策。
  • 不新增 Admin 界面。
  • 不实现支付;不新增下单与地址目标。

实施方案

一、服务端

  1. #131 的分项匹配完成后,重新计算既有 retryable。
  2. Agent 采购任务详情接口补充:replacementMappingStatus、continuePurchaseEligible、continuePurchaseDisabledReason。
  3. replacementMappingStatus 必须读取该任务对应虾皮商品的 replacement_item,不得使用 #129 主表的全局 mapping_status——一个 PDD 商品可被多个虾皮商品共用,各自结果可能不同。
  4. continuePurchaseEligible 的判定为:既有全部 retryEligibility 通过 且 分项状态为 matched。
    • 「任务 failed + 失效错误码 + 存在 matched 替换」只是额外门禁,不得替代既有 retryEligibility 的任何一条。
  5. 点击仍调用既有 POST /purchase-tasks/{taskId}/retry(AgentRetry);Admin 仍调用 BatchRetry。
  6. BatchRetry 的 mappingTargetsValid、最新任务判定、不可逆边界、订单事实、设备在线/空闲/能力、价格与规则校验,继续作为最终事实:即使 replacementMappingStatus 为 matched,若映射后来被人工清空、PDD 重新采集导致候选变化、价格缺失或设备变忙,最终仍由 Retry 校验拒绝。

二、Agent 端

  1. matched 且 continuePurchaseEligible 为真 → 既有重试入口显示为**「继续采购」**。
  2. matching / manual_required → 只显示 #130 定义的一行状态文字,不显示按钮。
  3. 不可用时显示服务端下发的 continuePurchaseDisabledReason,不在客户端拼接原因。
  4. 沿用项目既有的服务端 retryable / reason 模式,UI 不做任何状态组合推断。

安全边界

  • 不新增任何采购任务创建路径;Admin 与 Agent 落到同一 BatchRetry / Create。
  • 设备 token 的能力边界不变(本设备、近 30 天、只传 taskId 与 requestId)。
  • 失败任务保持不可变。
  • 真机验证该入口可能创建正式采购任务,执行前须取得人工授权;永久禁止支付。

验收标准

  • 未新增平行的采购创建实现;Admin 与 Agent 均落到同一 BatchRetry / Create 路径(以调用链检查与测试佐证)。
  • 资格判定读取分项 replacement_item 状态,未使用主表全局状态。
  • matched 且既有 retryEligibility 全通过时显示「继续采购」,点击成功创建新任务。
  • matching / manual_required 时不显示按钮,只显示状态文字。
  • 覆盖以下拒绝场景:matched 但映射已被人工清空、matched 但 PDD 重新采集导致候选变化、matched 但设备忙、已有更新的任务、已越过不可逆边界、价格缺失。
  • 幂等:相同 requestId 重复请求不产生第二条任务;不同 requestId 重复点击受既有「最新任务」规则拦截。
  • 客户端无本地状态推断逻辑;不可用原因来自服务端。
  • Admin 侧行为与本工单实施前完全一致。

验证方式

  • go test ./app/goauto/purchase/... ./app/goauto/access/...
  • cd android && .\gradlew.bat :app:testDebugUnitTest :app:assembleDebug
  • 端到端:构造一条因商品失效或售罄失败的采购任务 → 完成替换与匹配(#130 / #131)→ 分别从 Admin 与 Agent 各续做一次,断言两条路径产出一致。
  • 真机 PKG110 验证入口;执行前取得人工授权,记录设备、Agent 版本、原任务号与新任务号。
  • 真机、多设备与异常路径未覆盖部分如实回写。

依赖、并行与风险

  • 前置依赖 #129、#130、#131;四者构成闭环,需一并端到端验收。
  • 风险:状态显示为 matched 但最终 Retry 被拒,用户会看到「点了没成功」。缓解:服务端返回明确的 continuePurchaseDisabledReason,并在最终拒绝时给出可读原因。
  • 回退:还原本工单提交即可;Admin 的批量重试与既有 AgentRetry 不受影响。

文档影响

  • Wiki Business-Rules-and-Glossary:失败采购任务续做的条件与规格前置要求。
  • Wiki Android-Agent-API-Contract:采购任务详情接口新增的三个字段;明确未新增创建接口。
  • 按 Wiki-first 门禁:先改线上页面并回读 revision,再执行一轮 sync 与一轮 sync --check,把页面与 revision 写回本工单。

状态

待实施(前置 #129、#130、#131)。

> **本正文为最终版(2026-08-28 重写)。** 此前的历史评论仅作决策过程记录;如与本正文冲突,一律以本正文为准。原方案「新增续做采购能力」已作废——经复核,`AgentRetry` 已存在且完全满足需求,本工单缩减为状态与入口改造。 ## 所属与来源 - 关联工单:#129 数据模型、#130 Agent 入口、#131 生效与匹配(均为前置)。 - 来源:用户于 2026-08-28 提出「因为是内部系统,尽量减少耦合、让采购流程简单易用,建议 Admin 和 Agent 都可以让失败的、准备了新 PDD 商品的采购任务继续尝试采购」。 - 类型:Server + Android Agent / 失败采购任务续做的入口与资格改造。 - 设计证据:复用 Agent 采购任务详情中**既有**的重试入口,仅改变其文案与显示条件;不新增按钮与页面,提供标注截图即可。 ## 当前事实(提交 30d8238 复核) **`AgentRetry` 已经存在**(`purchase/retry.go:143`),其注释明确: > delegates creation and idempotency to BatchRetry so Admin and Agent use exactly the same archive, device and irreversible-boundary checks 具体能力: - 设备 token 鉴权,且限制为**本设备、近 30 天**的任务(`retry.go:154-156`); - 请求只含 `taskId` 与 `requestId`,不接受任何业务参数; - 内部直接委托 `BatchRetry`,与 Admin 走完全相同的档案、设备与不可逆边界校验; - 失败旧任务保持不可变历史;当前虾皮/PDD 商品、映射、价格护栏与规则在重试时重新解析(`retry.go:67-69`); - 新任务创建后,旧失败任务因不再是最新任务而无法再次成功重试; - 详情接口已有 `Retryable` 字段(`purchase/agent_history.go:41`、`admin_query.go:68`)。 **因此不新增第二套创建能力与第二套幂等链路。** 此前正文中「为设备 token 新开创建路径」的判断有误,据此产生的风险分析一并作废。 ## 目标 1. 替换与规格匹配完成后,Agent 上的既有重试入口以「继续采购」呈现,一键接着买。 2. Admin 侧保持现状(`BatchRetry`),无需改动。 3. 资格判定一律由服务端下发,UI 不自行组合状态推断。 ## 非目标 - **不新增续做采购的接口与创建路径**;一律复用既有 `AgentRetry` → `BatchRetry`。 - 不修改 `BatchRetry` 的既有校验、价格护栏、规则解析与不可逆边界。 - 不在 Agent 端做规格映射选择与规格决策。 - 不新增 Admin 界面。 - 不实现支付;不新增下单与地址目标。 ## 实施方案 ### 一、服务端 1. #131 的分项匹配完成后,重新计算既有 `retryable`。 2. Agent 采购任务详情接口补充:`replacementMappingStatus`、`continuePurchaseEligible`、`continuePurchaseDisabledReason`。 3. `replacementMappingStatus` 必须读取**该任务对应虾皮商品的 `replacement_item`**,不得使用 #129 主表的全局 `mapping_status`——一个 PDD 商品可被多个虾皮商品共用,各自结果可能不同。 4. `continuePurchaseEligible` 的判定为:既有全部 `retryEligibility` 通过 **且** 分项状态为 `matched`。 - 「任务 failed + 失效错误码 + 存在 matched 替换」只是**额外门禁**,不得替代既有 `retryEligibility` 的任何一条。 5. 点击仍调用既有 `POST /purchase-tasks/{taskId}/retry`(`AgentRetry`);Admin 仍调用 `BatchRetry`。 6. `BatchRetry` 的 `mappingTargetsValid`、最新任务判定、不可逆边界、订单事实、设备在线/空闲/能力、价格与规则校验,**继续作为最终事实**:即使 `replacementMappingStatus` 为 `matched`,若映射后来被人工清空、PDD 重新采集导致候选变化、价格缺失或设备变忙,最终仍由 Retry 校验拒绝。 ### 二、Agent 端 7. `matched` 且 `continuePurchaseEligible` 为真 → 既有重试入口显示为**「继续采购」**。 8. `matching` / `manual_required` → 只显示 #130 定义的一行状态文字,**不显示按钮**。 9. 不可用时显示服务端下发的 `continuePurchaseDisabledReason`,不在客户端拼接原因。 10. 沿用项目既有的服务端 `retryable` / `reason` 模式,UI 不做任何状态组合推断。 ## 安全边界 - 不新增任何采购任务创建路径;Admin 与 Agent 落到同一 `BatchRetry` / `Create`。 - 设备 token 的能力边界不变(本设备、近 30 天、只传 taskId 与 requestId)。 - 失败任务保持不可变。 - 真机验证该入口可能创建**正式采购任务**,执行前须取得人工授权;永久禁止支付。 ## 验收标准 - [ ] 未新增平行的采购创建实现;Admin 与 Agent 均落到同一 `BatchRetry` / `Create` 路径(以调用链检查与测试佐证)。 - [ ] 资格判定读取分项 `replacement_item` 状态,未使用主表全局状态。 - [ ] `matched` 且既有 `retryEligibility` 全通过时显示「继续采购」,点击成功创建新任务。 - [ ] `matching` / `manual_required` 时不显示按钮,只显示状态文字。 - [ ] 覆盖以下拒绝场景:`matched` 但映射已被人工清空、`matched` 但 PDD 重新采集导致候选变化、`matched` 但设备忙、已有更新的任务、已越过不可逆边界、价格缺失。 - [ ] 幂等:相同 `requestId` 重复请求不产生第二条任务;不同 `requestId` 重复点击受既有「最新任务」规则拦截。 - [ ] 客户端无本地状态推断逻辑;不可用原因来自服务端。 - [ ] Admin 侧行为与本工单实施前完全一致。 ## 验证方式 - `go test ./app/goauto/purchase/... ./app/goauto/access/...` - `cd android && .\gradlew.bat :app:testDebugUnitTest :app:assembleDebug` - 端到端:构造一条因商品失效或售罄失败的采购任务 → 完成替换与匹配(#130 / #131)→ 分别从 Admin 与 Agent 各续做一次,断言两条路径产出一致。 - 真机 PKG110 验证入口;执行前取得人工授权,记录设备、Agent 版本、原任务号与新任务号。 - 真机、多设备与异常路径未覆盖部分如实回写。 ## 依赖、并行与风险 - 前置依赖 #129、#130、#131;四者构成闭环,需一并端到端验收。 - 风险:状态显示为 `matched` 但最终 Retry 被拒,用户会看到「点了没成功」。缓解:服务端返回明确的 `continuePurchaseDisabledReason`,并在最终拒绝时给出可读原因。 - 回退:还原本工单提交即可;Admin 的批量重试与既有 `AgentRetry` 不受影响。 ## 文档影响 - Wiki `Business-Rules-and-Glossary`:失败采购任务续做的条件与规格前置要求。 - Wiki `Android-Agent-API-Contract`:采购任务详情接口新增的三个字段;明确未新增创建接口。 - 按 Wiki-first 门禁:先改线上页面并回读 revision,再执行一轮 `sync` 与一轮 `sync --check`,把页面与 revision 写回本工单。 ## 状态 待实施(前置 #129、#130、#131)。
Author
Owner

修订:续做的前置条件改为依据 mapping_status

用户于 2026-08-28 确认整体流程为「替换 → 自动匹配规格 → Agent 刷新看状态 → 继续采购」,AI 匹配按 B 方案处理。本评论据此调整续做的判据与提示。

一、第三条校验细化

正文第 2 项的三条校验中,第三条「该任务原 pdd_product_id 存在一条生效的替换记录」细化为:

  • 存在一条生效的替换记录,且其 mapping_status = matched(#129 新增字段)。

其余两条(任务为 failed、失败码属商品失效类)不变。

二、按状态返回,不在服务端重复判断映射

  • mapping_status = matching → 拒绝并提示 规格匹配中,请稍后重试;
  • mapping_status = manual_required → 拒绝并提示 规格待匹配,请在 Admin 处理;
  • mapping_status = matched → 受理,走既有 BatchRetry 单任务路径。

服务端仍保留 BatchRetry 自身的映射校验作为最终防线(mappingTargetsValid),但 Agent 侧的按钮显隐与预期提示以 mapping_status 为准,避免用户点了才知道不行。

三、按钮显示条件

「继续采购」按钮仅在 mapping_status = matched 时出现;其余状态显示 #130 定义的一行状态文字,不显示按钮。

因此正文第 9 项「客户端按任务状态与是否存在替换记录判断」调整为「按任务状态与 mapping_status 判断」。

四、B 方案带来的边界说明

matched 可能来自两种途径:

  1. AI 匹配置信度 ≥ 阈值,自动确认(开关开启时);
  2. 人工在 Admin 确认映射后置为 matched。

两者对本工单等价,续做逻辑不区分来源。但自动确认的映射必须已完整记录 source / confidence / reason(由 #131 保证),以便日后追溯买错规格的责任链路。

五、验收标准调整

  • mapping_status 为 matching 或 manual_required 时续做被拒绝,提示分别可读且不同。
  • mapping_status = matched 时续做成功,新任务的商品与映射来源与 Admin 重试一致。
  • 「继续采购」按钮仅在 matched 时出现。
  • AI 自动确认与人工确认两条来源,续做行为一致。

正文其余内容(复用 BatchRetry、Agent 只传任务号与 requestId、不在手机端做规格决策、幂等与仅成功一次、设备 token 反向测试、按钮区分约束)保持不变。

## 修订:续做的前置条件改为依据 `mapping_status` 用户于 2026-08-28 确认整体流程为「替换 → 自动匹配规格 → Agent 刷新看状态 → 继续采购」,AI 匹配按 B 方案处理。本评论据此调整续做的判据与提示。 ### 一、第三条校验细化 正文第 2 项的三条校验中,第三条「该任务原 `pdd_product_id` 存在一条生效的替换记录」细化为: - 存在一条生效的替换记录,**且其 `mapping_status = matched`**(#129 新增字段)。 其余两条(任务为 `failed`、失败码属商品失效类)不变。 ### 二、按状态返回,不在服务端重复判断映射 - `mapping_status = matching` → 拒绝并提示 `规格匹配中,请稍后重试`; - `mapping_status = manual_required` → 拒绝并提示 `规格待匹配,请在 Admin 处理`; - `mapping_status = matched` → 受理,走既有 `BatchRetry` 单任务路径。 服务端仍保留 `BatchRetry` 自身的映射校验作为最终防线(`mappingTargetsValid`),但 Agent 侧的按钮显隐与预期提示以 `mapping_status` 为准,避免用户点了才知道不行。 ### 三、按钮显示条件 「继续采购」按钮仅在 `mapping_status = matched` 时出现;其余状态显示 #130 定义的一行状态文字,不显示按钮。 因此正文第 9 项「客户端按任务状态与是否存在替换记录判断」调整为「按任务状态与 `mapping_status` 判断」。 ### 四、B 方案带来的边界说明 `matched` 可能来自两种途径: 1. AI 匹配置信度 ≥ 阈值,自动确认(开关开启时); 2. 人工在 Admin 确认映射后置为 `matched`。 两者对本工单等价,续做逻辑不区分来源。但**自动确认的映射必须已完整记录 source / confidence / reason**(由 #131 保证),以便日后追溯买错规格的责任链路。 ### 五、验收标准调整 - [ ] `mapping_status` 为 `matching` 或 `manual_required` 时续做被拒绝,提示分别可读且不同。 - [ ] `mapping_status = matched` 时续做成功,新任务的商品与映射来源与 Admin 重试一致。 - [ ] 「继续采购」按钮仅在 `matched` 时出现。 - [ ] AI 自动确认与人工确认两条来源,续做行为一致。 正文其余内容(复用 `BatchRetry`、Agent 只传任务号与 requestId、不在手机端做规格决策、幂等与仅成功一次、设备 token 反向测试、按钮区分约束)保持不变。
Author
Owner

Codex 全栈复核:建议缩减为复用现有 AgentRetry 的状态与入口改造

目标合理,但当前代码已有 AgentRetry → BatchRetry:

  • Device Token 只能访问本设备近 30 天任务;
  • 请求只含 taskId/requestId;
  • 失败旧任务保持不可变;
  • 当前虾皮/PDD 商品、映射、价格护栏和规则会在重试时重新解析;
  • 新任务创建后,旧失败任务因不再是最新任务而无法再次成功重试。

因此不建议新增第二套“续做采购”创建能力和第二套幂等链路。建议本工单调整为:

  1. #131 分项匹配完成后,服务端重新计算现有 retryable。
  2. 详情接口补充 replacementMappingStatus、continuePurchaseEligible、continuePurchaseDisabledReason。
  3. matched 且现有 retryEligibility 全部通过时,Agent 将现有“重试采购”入口显示为“继续采购”。
  4. 点击仍调用既有 POST /purchase-tasks/{taskId}/retry,Admin 仍调用 BatchRetry。
  5. matching/manual_required 只显示状态,不显示按钮。
  6. BatchRetry 的 mappingTargetsValid、最新任务、不可逆边界、订单事实、设备在线/空闲/能力、价格和规则校验继续作为最终事实。

必须修正的判据

  • replacement 主表的全局 mapping_status 不能决定某条采购任务是否可继续;必须读取该任务对应 shopee_product 的 replacement item 状态,或由服务端直接返回 eligibility。
  • “任务 failed + 失效错误码 + 存在 matched replacement”只能作为额外门禁,不能替代现有全部 retryEligibility。
  • UI 不应自行组合状态推断是否可创建采购任务;沿用项目已有的服务端 retryable/reason 模式。
  • 若映射后来被人工清空、PDD 重新采集导致候选变化、价格缺失或设备变忙,即使旧状态为 matched,也必须由最终 Retry 校验拒绝。

验收补充

  • 证明没有新增平行的采购创建实现,Admin/Agent 都落到同一 BatchRetry/Create 路径。
  • 覆盖 matched 但映射已失效、matched 但设备忙、已有更新任务、已越过不可逆边界、重复 requestId、不同 requestId 重复点击。
  • 真机验证该入口可能创建新的正式采购任务,仍须在执行前取得人工授权;永久禁止支付。

若 Claude Code 认为必须保留独立 endpoint,请明确说明现有 AgentRetry 不能满足的具体差异,并保证它只是薄包装,所有现有门禁一条不少。

## Codex 全栈复核:建议缩减为复用现有 AgentRetry 的状态与入口改造 目标合理,但当前代码已有 `AgentRetry → BatchRetry`: - Device Token 只能访问本设备近 30 天任务; - 请求只含 taskId/requestId; - 失败旧任务保持不可变; - 当前虾皮/PDD 商品、映射、价格护栏和规则会在重试时重新解析; - 新任务创建后,旧失败任务因不再是最新任务而无法再次成功重试。 因此不建议新增第二套“续做采购”创建能力和第二套幂等链路。建议本工单调整为: 1. #131 分项匹配完成后,服务端重新计算现有 `retryable`。 2. 详情接口补充 `replacementMappingStatus`、`continuePurchaseEligible`、`continuePurchaseDisabledReason`。 3. `matched` 且现有 retryEligibility 全部通过时,Agent 将现有“重试采购”入口显示为“继续采购”。 4. 点击仍调用既有 `POST /purchase-tasks/{taskId}/retry`,Admin 仍调用 BatchRetry。 5. `matching/manual_required` 只显示状态,不显示按钮。 6. BatchRetry 的 `mappingTargetsValid`、最新任务、不可逆边界、订单事实、设备在线/空闲/能力、价格和规则校验继续作为最终事实。 ### 必须修正的判据 - replacement 主表的全局 `mapping_status` 不能决定某条采购任务是否可继续;必须读取该任务对应 `shopee_product` 的 replacement item 状态,或由服务端直接返回 eligibility。 - “任务 failed + 失效错误码 + 存在 matched replacement”只能作为额外门禁,不能替代现有全部 retryEligibility。 - UI 不应自行组合状态推断是否可创建采购任务;沿用项目已有的服务端 `retryable/reason` 模式。 - 若映射后来被人工清空、PDD 重新采集导致候选变化、价格缺失或设备变忙,即使旧状态为 matched,也必须由最终 Retry 校验拒绝。 ### 验收补充 - 证明没有新增平行的采购创建实现,Admin/Agent 都落到同一 BatchRetry/Create 路径。 - 覆盖 matched 但映射已失效、matched 但设备忙、已有更新任务、已越过不可逆边界、重复 requestId、不同 requestId 重复点击。 - 真机验证该入口可能创建新的正式采购任务,仍须在执行前取得人工授权;永久禁止支付。 若 Claude Code 认为必须保留独立 endpoint,请明确说明现有 AgentRetry 不能满足的具体差异,并保证它只是薄包装,所有现有门禁一条不少。
ila changed title from PDD 商品替换(四):失败采购任务在 Admin 与 Agent 均可续做 to PDD 商品替换(四):续做入口改造(复用既有 AgentRetry,不新增创建路径) 2026-08-28 17:02:09 +08:00
Author
Owner

合并轻量原型 v1(与 #130 共用,草稿待确认)

为避免两份设计证据漂移,#132 复用 #130 的同一合并原型:

  • 交互原型:prototypes/130/v1/index.html
  • #132 关键标注:prototypes/130/v1/annotated-continue-purchase.png
  • 版本识别:#130 + #132 combined lightweight prototype v1,2026-08-28
  • 状态:草稿,待用户确认;未提交 Git

#132 落图结论:只有分项 matched 且服务端 continuePurchaseEligible=true 时,既有重试入口显示为“继续采购”;matching/manual_required 无按钮;matched 但设备忙等既有资格失败时只显示服务端原因。未新增按钮、页面或采购任务创建路径,点击语义仍为既有 AgentRetry → BatchRetry。

## 合并轻量原型 v1(与 #130 共用,草稿待确认) 为避免两份设计证据漂移,#132 复用 #130 的同一合并原型: - 交互原型:`prototypes/130/v1/index.html` - #132 关键标注:`prototypes/130/v1/annotated-continue-purchase.png` - 版本识别:#130 + #132 combined lightweight prototype v1,2026-08-28 - 状态:**草稿,待用户确认**;未提交 Git #132 落图结论:只有分项 `matched` 且服务端 `continuePurchaseEligible=true` 时,既有重试入口显示为“继续采购”;`matching/manual_required` 无按钮;`matched` 但设备忙等既有资格失败时只显示服务端原因。未新增按钮、页面或采购任务创建路径,点击语义仍为既有 AgentRetry → BatchRetry。
Author
Owner

原型验收通过

用户于 2026-08-28 明确确认:与 #130 共用的合并轻量原型 v1 审核通过。

  • 确认人:用户
  • 确认时间:2026-08-28(Asia/Shanghai)
  • #132 覆盖范围:matched 且服务端资格通过时,将既有重试入口显示为“继续采购”;matching/manual_required 不显示按钮;服务端拒绝时显示下发原因
  • 设计证据:prototypes/130/v1/index.html、prototypes/130/v1/annotated-continue-purchase.png
  • v1 入口 SHA-256:1413CB75799B5D16FA756E9BD8687D98DB4A105E794DCDB4E53A00C4E5E22825

该版本不再原位覆盖。#132 仍以后置依赖 #129、#131、#130 完成为实施前提;真机点击“继续采购”可能创建正式采购任务,执行该项真机验收前仍需单独取得授权,永久禁止支付。

## 原型验收通过 用户于 2026-08-28 明确确认:与 #130 共用的合并轻量原型 v1 审核通过。 - 确认人:用户 - 确认时间:2026-08-28(Asia/Shanghai) - #132 覆盖范围:matched 且服务端资格通过时,将既有重试入口显示为“继续采购”;matching/manual_required 不显示按钮;服务端拒绝时显示下发原因 - 设计证据:`prototypes/130/v1/index.html`、`prototypes/130/v1/annotated-continue-purchase.png` - v1 入口 SHA-256:`1413CB75799B5D16FA756E9BD8687D98DB4A105E794DCDB4E53A00C4E5E22825` 该版本不再原位覆盖。#132 仍以后置依赖 #129、#131、#130 完成为实施前提;真机点击“继续采购”可能创建正式采购任务,执行该项真机验收前仍需单独取得授权,永久禁止支付。
Author
Owner

实施完成,提交待验收

已按最终正文实施并推送:

  • commit:0f522d5 feat(#132): continue purchase after replacement
  • 分支:main
  • 前置提交:#129 696e0bc、#131 92385c4、#130 33d4bb1、#134 4862d48

实现结论

  • 未新增采购创建接口或平行创建实现;点击“继续采购”仍调用既有 POST /api/agent/v1/purchase-tasks/{taskId}/retry,调用链保持 AgentRetry → BatchRetry → Create。
  • Agent 采购详情增加 continuePurchaseEligible、continuePurchaseDisabledReason;replacementMappingStatus 继续来自该任务对应虾皮商品的 replacement item,不读主表总体状态。
  • 只有 item 为 matched 才计算继续资格。资格复用既有任务终态、最新任务、不可逆边界、当前档案、PDD 候选、价格、设备在线/空闲与能力门禁。
  • 详情资格校验不调用 AI,且必须已有持久化确认映射;映射被清空或候选变化时不会用新推导结果“补过”门禁。
  • AgentRetry 在委托 BatchRetry 前重新校验 item 和继续资格,避免详情读取后状态漂移;BatchRetry/Create 仍执行最终校验。
  • Android 只按服务端 continuePurchaseEligible 显示既有入口并改文案为“继续采购”;matching/manual_required 无按钮,matched 但不可用显示服务端原因。
  • Admin BatchRetry 代码与行为未修改;无新增页面、轮询、订单动作或支付能力。
  • 已确认原型 prototypes/130/v1 未修改、未纳入提交。

自动化验证

  • go test ./app/goauto/purchase ./app/goauto/access:通过
  • 新增覆盖:
    • matched replacement 时详情可继续,AgentRetry 创建的新任务使用当前替代 PDD 档案;
    • matched 但持久化映射被清空时详情与点击均拒绝,且 AI 调用次数为 0;
    • 既有测试继续覆盖设备忙、更新任务、不可逆边界、幂等和完整 BatchRetry 门禁。
  • .\scripts\verify.ps1 -Component all:通过
    • server 全量测试/构建通过
    • web lint 0 errors(既有 30 warnings),生产构建通过
    • Android debug/release 单测、debug APK 构建通过
  • python dev_scripts/harness.py check --strict:通过
  • python dev_scripts/harness.py sync + sync --check:通过

长期文档

  • Business-Rules-and-Glossary revision:6e33660c976c4412c3c456d04d5b4784a3043732
  • Android-Agent-API-Contract revision:81d3fec214250f9b5be4b049e65ca6a1144e52ad
  • 在线回读与本地镜像一致性检查均通过。

尚未验证 / 安全边界

  • 当前 adb devices -l 没有连接设备,APK 未安装,PKG110 入口显示未做真机检查。
  • 未执行“继续采购”真机点击;该动作可能创建正式采购任务,按工单要求仍需单独人工授权。
  • 未创建真实采购任务、订单,未执行支付。
  • 工单保持待验收。
## 实施完成,提交待验收 已按最终正文实施并推送: - commit:`0f522d5 feat(#132): continue purchase after replacement` - 分支:`main` - 前置提交:#129 `696e0bc`、#131 `92385c4`、#130 `33d4bb1`、#134 `4862d48` ### 实现结论 - 未新增采购创建接口或平行创建实现;点击“继续采购”仍调用既有 `POST /api/agent/v1/purchase-tasks/{taskId}/retry`,调用链保持 `AgentRetry → BatchRetry → Create`。 - Agent 采购详情增加 `continuePurchaseEligible`、`continuePurchaseDisabledReason`;`replacementMappingStatus` 继续来自该任务对应虾皮商品的 replacement item,不读主表总体状态。 - 只有 item 为 `matched` 才计算继续资格。资格复用既有任务终态、最新任务、不可逆边界、当前档案、PDD 候选、价格、设备在线/空闲与能力门禁。 - 详情资格校验不调用 AI,且必须已有持久化确认映射;映射被清空或候选变化时不会用新推导结果“补过”门禁。 - AgentRetry 在委托 BatchRetry 前重新校验 item 和继续资格,避免详情读取后状态漂移;BatchRetry/Create 仍执行最终校验。 - Android 只按服务端 `continuePurchaseEligible` 显示既有入口并改文案为“继续采购”;matching/manual_required 无按钮,matched 但不可用显示服务端原因。 - Admin BatchRetry 代码与行为未修改;无新增页面、轮询、订单动作或支付能力。 - 已确认原型 `prototypes/130/v1` 未修改、未纳入提交。 ### 自动化验证 - `go test ./app/goauto/purchase ./app/goauto/access`:通过 - 新增覆盖: - matched replacement 时详情可继续,AgentRetry 创建的新任务使用当前替代 PDD 档案; - matched 但持久化映射被清空时详情与点击均拒绝,且 AI 调用次数为 0; - 既有测试继续覆盖设备忙、更新任务、不可逆边界、幂等和完整 BatchRetry 门禁。 - `.\scripts\verify.ps1 -Component all`:通过 - server 全量测试/构建通过 - web lint 0 errors(既有 30 warnings),生产构建通过 - Android debug/release 单测、debug APK 构建通过 - `python dev_scripts/harness.py check --strict`:通过 - `python dev_scripts/harness.py sync` + `sync --check`:通过 ### 长期文档 - Business-Rules-and-Glossary revision:`6e33660c976c4412c3c456d04d5b4784a3043732` - Android-Agent-API-Contract revision:`81d3fec214250f9b5be4b049e65ca6a1144e52ad` - 在线回读与本地镜像一致性检查均通过。 ### 尚未验证 / 安全边界 - 当前 `adb devices -l` 没有连接设备,APK 未安装,PKG110 入口显示未做真机检查。 - 未执行“继续采购”真机点击;该动作可能创建正式采购任务,按工单要求仍需单独人工授权。 - 未创建真实采购任务、订单,未执行支付。 - 工单保持待验收。
Author
Owner

用户于 2026-08-29 明确确认本工单通过验收。验收结论已记录,现关闭工单。没有新的长期事实变化,本次不重复同步 Wiki。

用户于 2026-08-29 明确确认本工单通过验收。验收结论已记录,现关闭工单。没有新的长期事实变化,本次不重复同步 Wiki。
ila closed this issue 2026-08-29 20:43:44 +08:00
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#132