备货采购(一):purchase_task 类型扩展与脱离 SYB 的创建路径 #135

Closed
opened 2026-08-28 17:32:46 +08:00 by ila · 4 comments
Owner

所属与来源

  • 关联工单:#116 门禁与校验分级、#127 采购规则落库、#129~#134 PDD 商品替换与界面状态(均建议先于本工单完成)。
  • 来源:用户于 2026-08-28 提出「现在只能在 SYB 商品里创建采购,希望在 PDD 商品模块也能创建采购」,并明确是脱离 SYB 商品的备货采购;随后确认「流程和实现尽量简单些,这样后期也可以更新」。
  • 类型:Server / 采购任务模型扩展与备货采购创建路径。
  • 设计证据:本工单不涉及界面,无需原型。Admin 表单由后续工单实施。
  • 工具回退说明:本工单通过 Gitea API 创建;当前会话未提供 Gitea MCP 工具,按 AGENTS.md「Gitea 交互与工单最小读取」记录回退原因。

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

  1. 采购任务的需求来源是虾皮订单行:目标颜色/尺码、数量、订单号均来自 SYB 导入的数据(purchase/batch.go:300-333)。

  2. 硬校验挡住备货:models/purchase.go 的 syncPurchaseGuardSlots 中

    if task.ExecutionMode == PurchaseExecutionModeLive && task.SYBProductID == nil {
        return fmt.Errorf("live purchase task requires syb_product_id")
    }
    
  3. 唯一约束天然兼容备货:同一函数中守卫列的赋值为

    if active && task.SYBProductID != nil { task.ActiveSlot = &one } else { task.ActiveSlot = nil }
    

    SYBProductID 为空时 ActiveSlot 为 NULL,ux_purchase_task_active_syb 不生效,多条备货任务可并存。SYBProductID 与 ShopeeProductID 本就是可空字段(models/purchase.go:71-74)。

  4. 设备与账号并发守卫(DeviceRunSlot / AccountRunSlot)与 SYB 无关,对备货任务继续有效。

  5. BatchRetry 按 SYBProductID 重新解析商品、映射与价格(purchase/retry.go),备货任务无法使用现有重试路径。

  6. 规格映射的 AI/人工匹配是为「虾皮规格 → PDD 规格」而设;备货直接在 PDD 商品已采集的规格中选择,无需匹配。

目标

  1. 支持创建脱离 SYB 订单行的备货采购任务,从 PDD 商品与其已采集规格直接发起。
  2. 以最小模型改动实现,Agent 端零改动。
  3. 价格护栏、规则快照冻结、设备与账号并发约束、不可逆边界等既有检查全部复用,不另写一套。

非目标(明确不做,后期可独立追加)

  • 不做库存:入库、扣减、批次、与后续订单的关联全部不做。备货任务采购完即结束,只留记录,后续人工处理。
  • 不做备货任务的重试:失败后重新创建一条即可。
  • 不做批量创建:只支持单条创建。
  • 不新建独立的备货任务表:见「关键设计决定」。
  • 不预留任何将来可能用到的字段:需要时再加列。
  • 不做备货专属统计与报表。
  • 不修改 Agent 代码与执行链路。
  • 不修改既有 SYB 订单采购的任何行为。
  • 不实现支付;不放宽 #116 的门禁。

关键设计决定

一、复用 purchase_task,不新建表

新建独立表看似不动既有模型更安全,实则要复制整套任务生命周期、租约、认领、设备互斥、Agent 执行链路、结果回传与历史查询,等于把采购做两遍,后续每处改动都要改两边。

复用同一张表 + 一个类型字段才是真正的简单,且 Agent 收到的 payload 结构完全不变,Android 侧零改动。

二、task_type 必须现在就加

若用「SYBProductID 为空」隐式表示备货,该约定会渗入所有查询与分支判断;日后要改为显式类型需回填历史数据并修改全部隐式判断处。现在加一个枚举列成本接近于零。

这是本工单唯一坚持「现在就要有」的结构性改动。

三、不预留字段,但保证信息不丢失

将来做库存时需要哪些字段现在无法准确预判,提前加列是负担。真正要保证的是可回溯性——买了哪个 PDD 商品、什么规格、几件、实际多少钱、订单号,这些既有快照字段(PDDGoodsIDSnapshot、Mapped*、Quantity、ActualUnitPriceCent、订单结果字段)本就齐全,无需额外工作。

四、执行模式沿用,不裁剪

不为备货单独限制只能 live 或只能 rehearsal:沿用现有 executionMode 无需新增分支,裁剪反而要写新逻辑。

实施方案

  1. purchase_task 新增 task_type:syb_order / stock,check 约束;迁移中把存量行全部置为 syb_order。
  2. 放开第 2 条硬校验,改为按类型判定:
    • syb_order:SYBProductID 必填(行为与现在完全一致);
    • stock:SYBProductID 与 ShopeeProductID 必须为空,避免出现半绑定状态。
  3. SpecSource 新增 direct_select,表示规格由人工从该 PDD 商品已采集的规格中直接选定,不经 exact/AI/人工映射链路。
  4. 新增备货采购的创建服务:
    • 入参:PDD 商品、颜色值、尺码值、数量、价格上下限、执行模式、幂等 requestId;
    • 校验 PDD 商品为 active,所选颜色/尺码确实存在于其已采集规格中;
    • Mapped* 直接取所选规格值,SpecSource = direct_select;
    • 虾皮相关快照字段(ShopeeItemIDSnapshot / ShopeeOrderNoSnapshot / ShopeeTitleSnapshot / ShopeeShopNameSnapshot)留空;
    • 必须复用既有的价格护栏校验、采购规则快照冻结(#127 落地后从数据库读取)、设备与账号并发约束、不可逆边界检查,不得另写一套;
    • 幂等:相同 requestId 返回既有任务。
  5. 明确排除备货任务进入不适用的流程:BatchRetry / AgentRetry、顺云宝回填、退货与对账、按 SYB 行的批量预检,均需按 task_type 过滤,并在被误调用时返回可读错误,不得静默处理。
  6. 采购任务查询接口返回 task_type,供列表与详情区分展示。

安全边界

  • 备货任务同样禁止支付,同样受 #116 门禁与采购规则的动作白名单约束。
  • 创建订单属不可逆操作,既有的高风险门禁与人工确认要求对备货任务同等适用。
  • 不改变既有 SYB 订单采购的任何校验强度。
  • 数据库迁移属高风险改动,实施前需再次人工确认。

验收标准

  • task_type 迁移完成,存量行全部为 syb_order,既有采购流程行为无任何变化。
  • stock 类型任务可在 SYBProductID 为空的前提下创建成功,且 ActiveSlot 为 NULL、多条可并存。
  • stock 类型带上 SYBProductID 或 ShopeeProductID 时被拒绝。
  • syb_order 类型缺少 SYBProductID 时仍被拒绝(既有校验未被削弱)。
  • 所选颜色/尺码不在该 PDD 商品已采集规格中时被拒绝;商品非 active 时被拒绝。
  • 备货任务的 SpecSource 为 direct_select,未触发任何 AI 或人工映射流程。
  • 价格护栏、规则快照冻结、设备与账号并发约束对备货任务同样生效(以复用同一批检查为准,不得存在第二份实现)。
  • 幂等:相同 requestId 不产生第二条任务。
  • BatchRetry / AgentRetry、顺云宝回填、退货对账、批量预检对 stock 任务返回可读错误,不静默处理。
  • Agent 端未做任何改动,且能正常认领并执行备货任务(payload 结构不变)。

验证方式

  • go test ./app/goauto/purchase/... ./app/goauto/models/... ./app/goauto/...
  • 迁移在空库与既有库两种前提下各执行一次。
  • 覆盖用例:备货任务并发创建多条、备货任务被误调用重试/回填、既有 SYB 采购流程回归。
  • 真机 PKG110:创建一条备货采购任务并执行至下单前的安全边界;真实下单前须取得人工授权;记录设备、Agent 版本、任务号与最终状态。
  • 真机、多设备与异常路径未覆盖部分如实回写。

依赖、并行与风险

  • 建议排在 #129~#134 之后:那批正在改采购任务的商品关联,同时改 purchase_task 模型会导致真机问题难以归因。
  • 若 #127(采购规则落库)已完成,规则快照从数据库读取;未完成则沿用现有来源,实施时确认。
  • 风险:备货任务买回的货与后续订单无关联,属已知且已确认的边界。缓解:在文档与界面文案中明确「备货采购不参与库存扣减与订单关联」,避免误解为系统会自动处理。
  • 风险:漏过滤某条按 SYB 假设的既有流程。缓解:第 5 项要求逐条过滤并返回可读错误,验收含针对性用例。
  • 回退:还原提交并保留 task_type 列(不删列)即可;存量数据不受影响。

文档影响

  • Wiki Business-Rules-and-Glossary:备货采购的定义、与订单采购的差异、明确不参与库存与对账。
  • Wiki Architecture-and-Code-Map:task_type 模型扩展与备货创建路径。
  • Wiki Android-Agent-API-Contract:说明 payload 结构不变、Agent 无需区分任务类型。
  • 按 Wiki-first 门禁:先改线上页面并回读 revision,再执行一轮 sync 与一轮 sync --check,把页面与 revision 写回本工单。

状态

待实施(数据库迁移需实施前再次人工确认)。

## 所属与来源 - 关联工单:#116 门禁与校验分级、#127 采购规则落库、#129~#134 PDD 商品替换与界面状态(均建议先于本工单完成)。 - 来源:用户于 2026-08-28 提出「现在只能在 SYB 商品里创建采购,希望在 PDD 商品模块也能创建采购」,并明确是**脱离 SYB 商品的备货采购**;随后确认「流程和实现尽量简单些,这样后期也可以更新」。 - 类型:Server / 采购任务模型扩展与备货采购创建路径。 - 设计证据:本工单不涉及界面,无需原型。Admin 表单由后续工单实施。 - 工具回退说明:本工单通过 Gitea API 创建;当前会话未提供 Gitea MCP 工具,按 `AGENTS.md`「Gitea 交互与工单最小读取」记录回退原因。 ## 当前事实(提交 30d8238 复核) 1. 采购任务的需求来源是虾皮订单行:目标颜色/尺码、数量、订单号均来自 SYB 导入的数据(`purchase/batch.go:300-333`)。 2. **硬校验挡住备货**:`models/purchase.go` 的 `syncPurchaseGuardSlots` 中 ```go if task.ExecutionMode == PurchaseExecutionModeLive && task.SYBProductID == nil { return fmt.Errorf("live purchase task requires syb_product_id") } ``` 3. **唯一约束天然兼容备货**:同一函数中守卫列的赋值为 ```go if active && task.SYBProductID != nil { task.ActiveSlot = &one } else { task.ActiveSlot = nil } ``` `SYBProductID` 为空时 `ActiveSlot` 为 NULL,`ux_purchase_task_active_syb` 不生效,多条备货任务可并存。`SYBProductID` 与 `ShopeeProductID` 本就是可空字段(`models/purchase.go:71-74`)。 4. 设备与账号并发守卫(`DeviceRunSlot` / `AccountRunSlot`)与 SYB 无关,对备货任务继续有效。 5. `BatchRetry` 按 `SYBProductID` 重新解析商品、映射与价格(`purchase/retry.go`),**备货任务无法使用现有重试路径**。 6. 规格映射的 AI/人工匹配是为「虾皮规格 → PDD 规格」而设;备货直接在 PDD 商品已采集的规格中选择,无需匹配。 ## 目标 1. 支持创建脱离 SYB 订单行的备货采购任务,从 PDD 商品与其已采集规格直接发起。 2. 以**最小模型改动**实现,Agent 端零改动。 3. 价格护栏、规则快照冻结、设备与账号并发约束、不可逆边界等既有检查全部复用,不另写一套。 ## 非目标(明确不做,后期可独立追加) - **不做库存**:入库、扣减、批次、与后续订单的关联全部不做。备货任务采购完即结束,只留记录,后续人工处理。 - **不做备货任务的重试**:失败后重新创建一条即可。 - **不做批量创建**:只支持单条创建。 - **不新建独立的备货任务表**:见「关键设计决定」。 - **不预留任何将来可能用到的字段**:需要时再加列。 - 不做备货专属统计与报表。 - 不修改 Agent 代码与执行链路。 - 不修改既有 SYB 订单采购的任何行为。 - 不实现支付;不放宽 #116 的门禁。 ## 关键设计决定 ### 一、复用 `purchase_task`,不新建表 新建独立表看似不动既有模型更安全,实则要复制整套任务生命周期、租约、认领、设备互斥、Agent 执行链路、结果回传与历史查询,等于把采购做两遍,后续每处改动都要改两边。 **复用同一张表 + 一个类型字段才是真正的简单**,且 Agent 收到的 payload 结构完全不变,Android 侧零改动。 ### 二、`task_type` 必须现在就加 若用「`SYBProductID` 为空」隐式表示备货,该约定会渗入所有查询与分支判断;日后要改为显式类型需回填历史数据并修改全部隐式判断处。现在加一个枚举列成本接近于零。 **这是本工单唯一坚持「现在就要有」的结构性改动。** ### 三、不预留字段,但保证信息不丢失 将来做库存时需要哪些字段现在无法准确预判,提前加列是负担。真正要保证的是可回溯性——买了哪个 PDD 商品、什么规格、几件、实际多少钱、订单号,这些既有快照字段(`PDDGoodsIDSnapshot`、`Mapped*`、`Quantity`、`ActualUnitPriceCent`、订单结果字段)本就齐全,无需额外工作。 ### 四、执行模式沿用,不裁剪 不为备货单独限制只能 `live` 或只能 `rehearsal`:沿用现有 `executionMode` 无需新增分支,裁剪反而要写新逻辑。 ## 实施方案 1. `purchase_task` 新增 `task_type`:`syb_order` / `stock`,check 约束;迁移中把存量行全部置为 `syb_order`。 2. 放开第 2 条硬校验,改为按类型判定: - `syb_order`:`SYBProductID` 必填(行为与现在完全一致); - `stock`:`SYBProductID` 与 `ShopeeProductID` **必须为空**,避免出现半绑定状态。 3. `SpecSource` 新增 `direct_select`,表示规格由人工从该 PDD 商品已采集的规格中直接选定,不经 exact/AI/人工映射链路。 4. 新增备货采购的创建服务: - 入参:PDD 商品、颜色值、尺码值、数量、价格上下限、执行模式、幂等 requestId; - 校验 PDD 商品为 `active`,所选颜色/尺码确实存在于其已采集规格中; - `Mapped*` 直接取所选规格值,`SpecSource = direct_select`; - 虾皮相关快照字段(`ShopeeItemIDSnapshot` / `ShopeeOrderNoSnapshot` / `ShopeeTitleSnapshot` / `ShopeeShopNameSnapshot`)留空; - **必须复用既有的价格护栏校验、采购规则快照冻结(#127 落地后从数据库读取)、设备与账号并发约束、不可逆边界检查**,不得另写一套; - 幂等:相同 requestId 返回既有任务。 5. **明确排除备货任务进入不适用的流程**:`BatchRetry` / `AgentRetry`、顺云宝回填、退货与对账、按 SYB 行的批量预检,均需按 `task_type` 过滤,并在被误调用时返回可读错误,不得静默处理。 6. 采购任务查询接口返回 `task_type`,供列表与详情区分展示。 ## 安全边界 - 备货任务同样禁止支付,同样受 #116 门禁与采购规则的动作白名单约束。 - 创建订单属不可逆操作,既有的高风险门禁与人工确认要求对备货任务同等适用。 - 不改变既有 SYB 订单采购的任何校验强度。 - 数据库迁移属高风险改动,实施前需再次人工确认。 ## 验收标准 - [ ] `task_type` 迁移完成,存量行全部为 `syb_order`,既有采购流程行为无任何变化。 - [ ] `stock` 类型任务可在 `SYBProductID` 为空的前提下创建成功,且 `ActiveSlot` 为 NULL、多条可并存。 - [ ] `stock` 类型带上 `SYBProductID` 或 `ShopeeProductID` 时被拒绝。 - [ ] `syb_order` 类型缺少 `SYBProductID` 时仍被拒绝(既有校验未被削弱)。 - [ ] 所选颜色/尺码不在该 PDD 商品已采集规格中时被拒绝;商品非 `active` 时被拒绝。 - [ ] 备货任务的 `SpecSource` 为 `direct_select`,未触发任何 AI 或人工映射流程。 - [ ] 价格护栏、规则快照冻结、设备与账号并发约束对备货任务同样生效(以复用同一批检查为准,不得存在第二份实现)。 - [ ] 幂等:相同 requestId 不产生第二条任务。 - [ ] `BatchRetry` / `AgentRetry`、顺云宝回填、退货对账、批量预检对 `stock` 任务返回可读错误,不静默处理。 - [ ] Agent 端未做任何改动,且能正常认领并执行备货任务(payload 结构不变)。 ## 验证方式 - `go test ./app/goauto/purchase/... ./app/goauto/models/... ./app/goauto/...` - 迁移在空库与既有库两种前提下各执行一次。 - 覆盖用例:备货任务并发创建多条、备货任务被误调用重试/回填、既有 SYB 采购流程回归。 - 真机 PKG110:创建一条备货采购任务并执行至下单前的安全边界;**真实下单前须取得人工授权**;记录设备、Agent 版本、任务号与最终状态。 - 真机、多设备与异常路径未覆盖部分如实回写。 ## 依赖、并行与风险 - 建议排在 #129~#134 之后:那批正在改采购任务的商品关联,同时改 `purchase_task` 模型会导致真机问题难以归因。 - 若 #127(采购规则落库)已完成,规则快照从数据库读取;未完成则沿用现有来源,实施时确认。 - 风险:备货任务买回的货与后续订单无关联,属已知且已确认的边界。缓解:在文档与界面文案中明确「备货采购不参与库存扣减与订单关联」,避免误解为系统会自动处理。 - 风险:漏过滤某条按 SYB 假设的既有流程。缓解:第 5 项要求逐条过滤并返回可读错误,验收含针对性用例。 - 回退:还原提交并保留 `task_type` 列(不删列)即可;存量数据不受影响。 ## 文档影响 - Wiki `Business-Rules-and-Glossary`:备货采购的定义、与订单采购的差异、明确不参与库存与对账。 - Wiki `Architecture-and-Code-Map`:`task_type` 模型扩展与备货创建路径。 - Wiki `Android-Agent-API-Contract`:说明 payload 结构不变、Agent 无需区分任务类型。 - 按 Wiki-first 门禁:先改线上页面并回读 revision,再执行一轮 `sync` 与一轮 `sync --check`,把页面与 revision 写回本工单。 ## 状态 待实施(数据库迁移需实施前再次人工确认)。
Author
Owner

排除清单补充:备货任务不参与商品替换流程

用户于 2026-08-28 询问「PDD 商品模块创建的采购,商品失效后能否也走 Agent 采集替代商品再采购」。经分析确认:备货任务不走替换流程,失效后直接重新创建一条备货采购。

本补充并入本工单「实施方案」第 5 项的排除清单。之所以由本工单承担而非回改 #130 / #131:task_type 由本工单引入,在本工单落地前系统中不存在备货任务,因此 #130 / #131 不加排除逻辑不会产生缺陷——谁引入新类型,谁负责补齐相关流程的排除。

一、为什么备货任务不适合走替换流程

逐环节核对 #129~#132 对备货任务的适用性:

环节 对备货任务的情况
#130 入口 备货任务同为 purchase_task,失败后会出现在 Agent 采购记录中,按现有条件入口是开的
#131 生效 核心动作是「虾皮商品改指新 PDD 商品 + 清其规格映射」,而备货任务没有虾皮商品,规格为 direct_select、不经映射链路,该逻辑对其完全空转
#132 续做 走 AgentRetry → BatchRetry,按 SYBProductID 重新解析;本工单已明确备货任务不支持重试,按钮点不了

结果是用户点了「采集替代商品」、商品也采回来了,但任务动不了,停在半成品状态。

替换流程的价值在于批量迁移——一个 PDD 商品下挂着大量订单任务,人工逐条改不现实。而备货任务是单条、无订单绑定、无规格映射需要重建,重新创建(选商品 → 选规格 → 填数量)比走替换流程(采集 → 生效 → 重新匹配 → 续做)更简单。用复杂机制解决简单问题不划算。

二、实施方案第 5 项的排除清单增加两条

原清单为:BatchRetry / AgentRetry、顺云宝回填、退货与对账、按 SYB 行的批量预检。补充:

5.1 替换生效逻辑(#131)必须跳过 task_type = stock 的任务:不改其 pdd_product_id、不动其任何快照。

否则当同一个 PDD 商品既有订单任务又有备货任务时,替换会一并改写备货任务的外键,而其 Mapped* 规格是按旧商品选定的、新商品未必存在同名规格,将产生「外键指向新商品、规格属于旧商品」的不一致——正是 #131 花力气消除的那类问题。

5.2 替换入口(#130)对 task_type = stock 的失败任务不显示「采集替代商品」,改为显示一行提示,引导重新创建备货采购。文案保持一句话,不追加说明。

三、后续可选(不在本工单范围)

若备货使用频繁、每次重建嫌繁琐,可在 Admin 增加「换商品重建」快捷入口:把原任务的规格、数量、价格带入表单,选新商品后提交。属纯前端表单,不需要动替换机制。现在不做,待实际使用一段时间后再评估。

四、验收标准补充

  • 替换生效时 task_type = stock 的任务未被改写:外键与全部快照逐字段比对无变化。
  • 同一 PDD 商品下同时存在订单任务与备货任务时,替换只影响订单任务。
  • 备货失败任务在 Agent 采购记录详情中不显示「采集替代商品」入口,而显示引导重新创建的一行提示。
  • 备货任务被误传入替换登记接口时返回可读错误,不静默处理。
## 排除清单补充:备货任务不参与商品替换流程 用户于 2026-08-28 询问「PDD 商品模块创建的采购,商品失效后能否也走 Agent 采集替代商品再采购」。经分析确认:**备货任务不走替换流程,失效后直接重新创建一条备货采购**。 本补充并入本工单「实施方案」第 5 项的排除清单。之所以由本工单承担而非回改 #130 / #131:`task_type` 由本工单引入,在本工单落地前系统中不存在备货任务,因此 #130 / #131 不加排除逻辑不会产生缺陷——**谁引入新类型,谁负责补齐相关流程的排除**。 ### 一、为什么备货任务不适合走替换流程 逐环节核对 #129~#132 对备货任务的适用性: | 环节 | 对备货任务的情况 | |---|---| | #130 入口 | 备货任务同为 `purchase_task`,失败后会出现在 Agent 采购记录中,按现有条件**入口是开的** | | #131 生效 | 核心动作是「虾皮商品改指新 PDD 商品 + 清其规格映射」,而**备货任务没有虾皮商品**,规格为 `direct_select`、不经映射链路,该逻辑对其完全空转 | | #132 续做 | 走 `AgentRetry` → `BatchRetry`,按 `SYBProductID` 重新解析;本工单已明确备货任务不支持重试,**按钮点不了** | 结果是用户点了「采集替代商品」、商品也采回来了,但任务动不了,停在半成品状态。 替换流程的价值在于**批量迁移**——一个 PDD 商品下挂着大量订单任务,人工逐条改不现实。而备货任务是单条、无订单绑定、无规格映射需要重建,**重新创建(选商品 → 选规格 → 填数量)比走替换流程(采集 → 生效 → 重新匹配 → 续做)更简单**。用复杂机制解决简单问题不划算。 ### 二、实施方案第 5 项的排除清单增加两条 原清单为:`BatchRetry` / `AgentRetry`、顺云宝回填、退货与对账、按 SYB 行的批量预检。补充: 5.1 **替换生效逻辑(#131)必须跳过 `task_type = stock` 的任务**:不改其 `pdd_product_id`、不动其任何快照。 否则当同一个 PDD 商品既有订单任务又有备货任务时,替换会一并改写备货任务的外键,而其 `Mapped*` 规格是按旧商品选定的、新商品未必存在同名规格,将产生「外键指向新商品、规格属于旧商品」的不一致——正是 #131 花力气消除的那类问题。 5.2 **替换入口(#130)对 `task_type = stock` 的失败任务不显示「采集替代商品」**,改为显示一行提示,引导重新创建备货采购。文案保持一句话,不追加说明。 ### 三、后续可选(不在本工单范围) 若备货使用频繁、每次重建嫌繁琐,可在 Admin 增加「换商品重建」快捷入口:把原任务的规格、数量、价格带入表单,选新商品后提交。属纯前端表单,不需要动替换机制。**现在不做**,待实际使用一段时间后再评估。 ### 四、验收标准补充 - [ ] 替换生效时 `task_type = stock` 的任务未被改写:外键与全部快照逐字段比对无变化。 - [ ] 同一 PDD 商品下同时存在订单任务与备货任务时,替换只影响订单任务。 - [ ] 备货失败任务在 Agent 采购记录详情中不显示「采集替代商品」入口,而显示引导重新创建的一行提示。 - [ ] 备货任务被误传入替换登记接口时返回可读错误,不静默处理。
Author
Owner

实施完成,待验收

Gitea MCP 在当前会话没有提供可调用接口,因此按仓库规则回退到 Gitea API;凭据只从本机忽略文件读取,未写入代码、日志、工单或 Wiki。

实现

  • 新增 purchase_task.task_type = syb_order / stock、数据库 CHECK/索引和追加迁移 1787885300000_stock_purchase.go;旧记录显式回填 syb_order,MySQL 既有 spec_source CHECK 幂等扩展 direct_select。
  • 新增 POST /api/admin/v1/purchase-tasks/stock。服务端固定使用内置采购规则,只接受 active PDD 商品;颜色/尺码必须逐字命中当前可选规格,参考价从所选颜色归档派生,相同 requestId 幂等返回原任务。
  • 备货任务的 SYB/蝦皮关联为空、规格来源为 direct_select、不占用 SYB 活动槽;同一 PDD 商品可存在多个活动备货任务,执行期仍复用设备、账号、租约和价格保护边界。
  • Admin 列表/详情返回并可筛选 taskType;采购员获得新接口的既有采购权限。
  • BatchRetry、AgentRetry、重新采购授权、商品替换和 SYB 回填对 stock 显式拒绝并返回可读原因。Agent 详情沿用 #130 字段隐藏替换入口并提示“备货采购商品失效后,请重新创建备货采购”。
  • 未修改 Android 执行协议、未增加下单或支付能力。

验证

  • go test ./app/goauto/... ./cmd/migrate/migration/version-local:通过。
  • ./scripts/verify.ps1 -Component all:通过;Server 全量测试/构建、Web lint/生产构建、Android 单测/debug APK 均成功。Web lint 仅有既有 30 条 warning,无 error。
  • python dev_scripts/harness.py check --strict:通过。
  • Wiki 在线更新、回读并同步检查通过:Business Rules b78825c5e140a3e24948107628d6ced0db6f6a37;Architecture 5ccc58883979c6593f9d16d7865d5b44716cb374;Agent API Contract c7dd62e1d67e725a83bb343d1a25f18a49b68c8e。

提交:679f469(已推送 origin/main)。

未执行/验收边界

  • 未对正式数据库执行迁移;本次授权仅用于实现和自动化验证迁移代码。
  • 未安装新 Android 包(本工单无 Android 代码变化),未做真机下单,也未执行任何支付动作。
  • 工单保持打开,等待人工验收。
## 实施完成,待验收 Gitea MCP 在当前会话没有提供可调用接口,因此按仓库规则回退到 Gitea API;凭据只从本机忽略文件读取,未写入代码、日志、工单或 Wiki。 ### 实现 - 新增 `purchase_task.task_type = syb_order / stock`、数据库 CHECK/索引和追加迁移 `1787885300000_stock_purchase.go`;旧记录显式回填 `syb_order`,MySQL 既有 `spec_source` CHECK 幂等扩展 `direct_select`。 - 新增 `POST /api/admin/v1/purchase-tasks/stock`。服务端固定使用内置采购规则,只接受 `active` PDD 商品;颜色/尺码必须逐字命中当前可选规格,参考价从所选颜色归档派生,相同 `requestId` 幂等返回原任务。 - 备货任务的 SYB/蝦皮关联为空、规格来源为 `direct_select`、不占用 SYB 活动槽;同一 PDD 商品可存在多个活动备货任务,执行期仍复用设备、账号、租约和价格保护边界。 - Admin 列表/详情返回并可筛选 `taskType`;采购员获得新接口的既有采购权限。 - BatchRetry、AgentRetry、重新采购授权、商品替换和 SYB 回填对 `stock` 显式拒绝并返回可读原因。Agent 详情沿用 #130 字段隐藏替换入口并提示“备货采购商品失效后,请重新创建备货采购”。 - 未修改 Android 执行协议、未增加下单或支付能力。 ### 验证 - `go test ./app/goauto/... ./cmd/migrate/migration/version-local`:通过。 - `./scripts/verify.ps1 -Component all`:通过;Server 全量测试/构建、Web lint/生产构建、Android 单测/debug APK 均成功。Web lint 仅有既有 30 条 warning,无 error。 - `python dev_scripts/harness.py check --strict`:通过。 - Wiki 在线更新、回读并同步检查通过:Business Rules `b78825c5e140a3e24948107628d6ced0db6f6a37`;Architecture `5ccc58883979c6593f9d16d7865d5b44716cb374`;Agent API Contract `c7dd62e1d67e725a83bb343d1a25f18a49b68c8e`。 提交:`679f469`(已推送 `origin/main`)。 ### 未执行/验收边界 - 未对正式数据库执行迁移;本次授权仅用于实现和自动化验证迁移代码。 - 未安装新 Android 包(本工单无 Android 代码变化),未做真机下单,也未执行任何支付动作。 - 工单保持打开,等待人工验收。
Author
Owner

本机迁移状态更新

在 #140/#141 获授权的 Admin 重启中,#135 迁移版本 1787885300000 已确认记录,purchase_task.task_type 与包含 direct_select 的规格来源约束已在本机 MySQL 8.4 生效。该记录仅更新本机部署事实,#135 仍等待用户业务验收。

## 本机迁移状态更新 在 #140/#141 获授权的 Admin 重启中,#135 迁移版本 `1787885300000` 已确认记录,`purchase_task.task_type` 与包含 `direct_select` 的规格来源约束已在本机 MySQL 8.4 生效。该记录仅更新本机部署事实,#135 仍等待用户业务验收。
Author
Owner

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

用户于 2026-08-29 明确确认本工单通过验收。验收结论已记录,现关闭工单。没有新的长期事实变化,本次不重复同步 Wiki。
ila closed this issue 2026-08-29 20:43:51 +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#135