Wiki 在线更新、回读并同步检查通过:Business Rules b78825c5e140a3e24948107628d6ced0db6f6a37;Architecture 5ccc58883979c6593f9d16d7865d5b44716cb374;Agent API Contract c7dd62e1d67e725a83bb343d1a25f18a49b68c8e。
Blocking a user prevents them from interacting with repositories, such as opening or commenting on pull requests or issues. Learn more about blocking a user.
所属与来源
AGENTS.md「Gitea 交互与工单最小读取」记录回退原因。当前事实(提交
30d8238复核)采购任务的需求来源是虾皮订单行:目标颜色/尺码、数量、订单号均来自 SYB 导入的数据(
purchase/batch.go:300-333)。硬校验挡住备货:
models/purchase.go的syncPurchaseGuardSlots中唯一约束天然兼容备货:同一函数中守卫列的赋值为
SYBProductID为空时ActiveSlot为 NULL,ux_purchase_task_active_syb不生效,多条备货任务可并存。SYBProductID与ShopeeProductID本就是可空字段(models/purchase.go:71-74)。设备与账号并发守卫(
DeviceRunSlot/AccountRunSlot)与 SYB 无关,对备货任务继续有效。BatchRetry按SYBProductID重新解析商品、映射与价格(purchase/retry.go),备货任务无法使用现有重试路径。规格映射的 AI/人工匹配是为「虾皮规格 → PDD 规格」而设;备货直接在 PDD 商品已采集的规格中选择,无需匹配。
目标
非目标(明确不做,后期可独立追加)
关键设计决定
一、复用
purchase_task,不新建表新建独立表看似不动既有模型更安全,实则要复制整套任务生命周期、租约、认领、设备互斥、Agent 执行链路、结果回传与历史查询,等于把采购做两遍,后续每处改动都要改两边。
复用同一张表 + 一个类型字段才是真正的简单,且 Agent 收到的 payload 结构完全不变,Android 侧零改动。
二、
task_type必须现在就加若用「
SYBProductID为空」隐式表示备货,该约定会渗入所有查询与分支判断;日后要改为显式类型需回填历史数据并修改全部隐式判断处。现在加一个枚举列成本接近于零。这是本工单唯一坚持「现在就要有」的结构性改动。
三、不预留字段,但保证信息不丢失
将来做库存时需要哪些字段现在无法准确预判,提前加列是负担。真正要保证的是可回溯性——买了哪个 PDD 商品、什么规格、几件、实际多少钱、订单号,这些既有快照字段(
PDDGoodsIDSnapshot、Mapped*、Quantity、ActualUnitPriceCent、订单结果字段)本就齐全,无需额外工作。四、执行模式沿用,不裁剪
不为备货单独限制只能
live或只能rehearsal:沿用现有executionMode无需新增分支,裁剪反而要写新逻辑。实施方案
purchase_task新增task_type:syb_order/stock,check 约束;迁移中把存量行全部置为syb_order。syb_order:SYBProductID必填(行为与现在完全一致);stock:SYBProductID与ShopeeProductID必须为空,避免出现半绑定状态。SpecSource新增direct_select,表示规格由人工从该 PDD 商品已采集的规格中直接选定,不经 exact/AI/人工映射链路。active,所选颜色/尺码确实存在于其已采集规格中;Mapped*直接取所选规格值,SpecSource = direct_select;ShopeeItemIDSnapshot/ShopeeOrderNoSnapshot/ShopeeTitleSnapshot/ShopeeShopNameSnapshot)留空;BatchRetry/AgentRetry、顺云宝回填、退货与对账、按 SYB 行的批量预检,均需按task_type过滤,并在被误调用时返回可读错误,不得静默处理。task_type,供列表与详情区分展示。安全边界
验收标准
task_type迁移完成,存量行全部为syb_order,既有采购流程行为无任何变化。stock类型任务可在SYBProductID为空的前提下创建成功,且ActiveSlot为 NULL、多条可并存。stock类型带上SYBProductID或ShopeeProductID时被拒绝。syb_order类型缺少SYBProductID时仍被拒绝(既有校验未被削弱)。active时被拒绝。SpecSource为direct_select,未触发任何 AI 或人工映射流程。BatchRetry/AgentRetry、顺云宝回填、退货对账、批量预检对stock任务返回可读错误,不静默处理。验证方式
go test ./app/goauto/purchase/... ./app/goauto/models/... ./app/goauto/...依赖、并行与风险
purchase_task模型会导致真机问题难以归因。task_type列(不删列)即可;存量数据不受影响。文档影响
Business-Rules-and-Glossary:备货采购的定义、与订单采购的差异、明确不参与库存与对账。Architecture-and-Code-Map:task_type模型扩展与备货创建路径。Android-Agent-API-Contract:说明 payload 结构不变、Agent 无需区分任务类型。sync与一轮sync --check,把页面与 revision 写回本工单。状态
待实施(数据库迁移需实施前再次人工确认)。
排除清单补充:备货任务不参与商品替换流程
用户于 2026-08-28 询问「PDD 商品模块创建的采购,商品失效后能否也走 Agent 采集替代商品再采购」。经分析确认:备货任务不走替换流程,失效后直接重新创建一条备货采购。
本补充并入本工单「实施方案」第 5 项的排除清单。之所以由本工单承担而非回改 #130 / #131:
task_type由本工单引入,在本工单落地前系统中不存在备货任务,因此 #130 / #131 不加排除逻辑不会产生缺陷——谁引入新类型,谁负责补齐相关流程的排除。一、为什么备货任务不适合走替换流程
逐环节核对 #129~#132 对备货任务的适用性:
purchase_task,失败后会出现在 Agent 采购记录中,按现有条件入口是开的direct_select、不经映射链路,该逻辑对其完全空转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的任务未被改写:外键与全部快照逐字段比对无变化。实施完成,待验收
Gitea MCP 在当前会话没有提供可调用接口,因此按仓库规则回退到 Gitea API;凭据只从本机忽略文件读取,未写入代码、日志、工单或 Wiki。
实现
purchase_task.task_type = syb_order / stock、数据库 CHECK/索引和追加迁移1787885300000_stock_purchase.go;旧记录显式回填syb_order,MySQL 既有spec_sourceCHECK 幂等扩展direct_select。POST /api/admin/v1/purchase-tasks/stock。服务端固定使用内置采购规则,只接受activePDD 商品;颜色/尺码必须逐字命中当前可选规格,参考价从所选颜色归档派生,相同requestId幂等返回原任务。direct_select、不占用 SYB 活动槽;同一 PDD 商品可存在多个活动备货任务,执行期仍复用设备、账号、租约和价格保护边界。taskType;采购员获得新接口的既有采购权限。stock显式拒绝并返回可读原因。Agent 详情沿用 #130 字段隐藏替换入口并提示“备货采购商品失效后,请重新创建备货采购”。验证
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:通过。b78825c5e140a3e24948107628d6ced0db6f6a37;Architecture5ccc58883979c6593f9d16d7865d5b44716cb374;Agent API Contractc7dd62e1d67e725a83bb343d1a25f18a49b68c8e。提交:
679f469(已推送origin/main)。未执行/验收边界
本机迁移状态更新
在 #140/#141 获授权的 Admin 重启中,#135 迁移版本
1787885300000已确认记录,purchase_task.task_type与包含direct_select的规格来源约束已在本机 MySQL 8.4 生效。该记录仅更新本机部署事实,#135 仍等待用户业务验收。用户于 2026-08-29 明确确认本工单通过验收。验收结论已记录,现关闭工单。没有新的长期事实变化,本次不重复同步 Wiki。