优化:放开 SYB 商品页创建采购的低风险前置限制(A 类) #190

Open
opened 2026-09-01 16:49:49 +08:00 by ila · 2 comments
Owner

原始需求摘要

用户 2026-09-01 提出:「admin后台syb商品页,创建采购时太多限制了,因为是内部使用系统,需要放开限制」。经确认拆为 A/B 两单,本单为 A 类(低风险摩擦项)。B 类见配套工单(删除 #188 enforcePersistedMatch)。

目标

消除 SYB 商品列表页三类「被拦住但无法就地解决」的阻塞,改为人工就地确认后可继续创建采购。

非目标

  • 不改动已支付订单、未结束采购任务、重采购授权、价格上限四项门禁
  • 不涉及 enforcePersistedMatch(属配套工单 B)

当前事实(代码)

  • server/app/goauto/purchase/batch.go:390-397 — sybSpecsTrusted 为假时返回 SYB_PARSE_FAILED,NextAction=reparse
  • server/app/goauto/purchase/ai_match_eligibility.go — hasSelectableOtherDimension 命中即 disabled("PDD 商品包含颜色、尺码之外的可选规格,请人工处理")
  • 同文件 — len(dataset.skuCombinationsByPDD[pdd.ID]) == 0 时 disabled("缺少当前 PDD 商品的完整可售 SKU 组合,请先重新采集")
  • models.SYBProduct.ManuallyConfirmed 已存在(提交 4a1b4af),A1 可直接复用,无需迁移

方案

  1. A1 解析失败/存疑:SYB 列表行内编辑 TargetColor/TargetSize,保存时置 ManuallyConfirmed=true,行状态即时变为可采购。复用现有字段,无数据库迁移。
  2. A2 第三维度:hasSelectableOtherDimension 不再直接 disabled,改为返回该维度可选值,人工在弹层中选定后随规格映射一并保存。
  3. A3 缺少可售 SKU 组合:增加「确认可售并继续」人工旁路,记录操作人与时间。

三个写入动作统一走现有 ManualRequest 幂等 RequestID 模式(对齐 manual.go:13 AuthorizeRePurchase),落库记录操作人、时间、原值,保证可追溯。

前置依赖与并行

  • 前置依赖:无
  • 可并行:与工单 B 可并行,但两者都改 batch.go 同一区域,建议本单先合入

子项目影响

  • server/:app/goauto/purchase 包
  • web/:SYB 商品列表页

设计证据

  • A2/A3 涉及新弹层与新按钮,属新交互,需先出原型并经用户确认后才能编写生产代码
  • A1 为现有列表的行内编辑,提供标注截图即可

风险

  • A3 允许人工声明「可售」。若声明错误,表现为采购任务在 Agent 端失败,不会误买其他规格(规格值仍由人工指定),且有操作记录可追溯。风险可接受。
  • A2 第三维度选值若与实际 SKU 不匹配,同样表现为任务失败而非错买。

验收

  • 三类原因在 SYB 列表页均可就地处理,处理后行状态即时变为可创建采购
  • 每次人工确认在库中留下操作人 + 时间记录
  • 已支付订单、未结束任务、重采购授权、价格上限四项门禁行为无变化(回归测试覆盖)

验证

  • .\scripts\verify.ps1 -Component all
  • 补充 A1/A2/A3 各自的单元测试

文档影响

  • docs/03-business-rules-and-glossary.md:采购前置条件规则变化
  • 若接口字段变化,同步 docs/08-agent-api-contract.md
  • 需走一轮 Wiki 更新 + 在线回读 + sync + sync --check
## 原始需求摘要 用户 2026-09-01 提出:「admin后台syb商品页,创建采购时太多限制了,因为是内部使用系统,需要放开限制」。经确认拆为 A/B 两单,本单为 A 类(低风险摩擦项)。B 类见配套工单(删除 #188 `enforcePersistedMatch`)。 ## 目标 消除 SYB 商品列表页三类「被拦住但无法就地解决」的阻塞,改为人工就地确认后可继续创建采购。 ## 非目标 - 不改动已支付订单、未结束采购任务、重采购授权、价格上限四项门禁 - 不涉及 `enforcePersistedMatch`(属配套工单 B) ## 当前事实(代码) - `server/app/goauto/purchase/batch.go:390-397` — `sybSpecsTrusted` 为假时返回 `SYB_PARSE_FAILED`,`NextAction=reparse` - `server/app/goauto/purchase/ai_match_eligibility.go` — `hasSelectableOtherDimension` 命中即 `disabled("PDD 商品包含颜色、尺码之外的可选规格,请人工处理")` - 同文件 — `len(dataset.skuCombinationsByPDD[pdd.ID]) == 0` 时 `disabled("缺少当前 PDD 商品的完整可售 SKU 组合,请先重新采集")` - `models.SYBProduct.ManuallyConfirmed` 已存在(提交 4a1b4af),A1 可直接复用,无需迁移 ## 方案 1. **A1 解析失败/存疑**:SYB 列表行内编辑 `TargetColor`/`TargetSize`,保存时置 `ManuallyConfirmed=true`,行状态即时变为可采购。复用现有字段,无数据库迁移。 2. **A2 第三维度**:`hasSelectableOtherDimension` 不再直接 `disabled`,改为返回该维度可选值,人工在弹层中选定后随规格映射一并保存。 3. **A3 缺少可售 SKU 组合**:增加「确认可售并继续」人工旁路,记录操作人与时间。 三个写入动作统一走现有 `ManualRequest` 幂等 `RequestID` 模式(对齐 `manual.go:13 AuthorizeRePurchase`),落库记录操作人、时间、原值,保证可追溯。 ## 前置依赖与并行 - 前置依赖:无 - 可并行:与工单 B 可并行,但两者都改 `batch.go` 同一区域,建议本单先合入 ## 子项目影响 - `server/`:`app/goauto/purchase` 包 - `web/`:SYB 商品列表页 ## 设计证据 - A2/A3 涉及新弹层与新按钮,属新交互,**需先出原型并经用户确认后才能编写生产代码** - A1 为现有列表的行内编辑,提供标注截图即可 ## 风险 - A3 允许人工声明「可售」。若声明错误,表现为采购任务在 Agent 端失败,**不会误买其他规格**(规格值仍由人工指定),且有操作记录可追溯。风险可接受。 - A2 第三维度选值若与实际 SKU 不匹配,同样表现为任务失败而非错买。 ## 验收 - [ ] 三类原因在 SYB 列表页均可就地处理,处理后行状态即时变为可创建采购 - [ ] 每次人工确认在库中留下操作人 + 时间记录 - [ ] 已支付订单、未结束任务、重采购授权、价格上限四项门禁行为无变化(回归测试覆盖) ## 验证 - `.\scripts\verify.ps1 -Component all` - 补充 A1/A2/A3 各自的单元测试 ## 文档影响 - `docs/03-business-rules-and-glossary.md`:采购前置条件规则变化 - 若接口字段变化,同步 `docs/08-agent-api-contract.md` - 需走一轮 Wiki 更新 + 在线回读 + `sync` + `sync --check`
Author
Owner

实施进展

A1 已完成(未提交)

后端与修正弹层本已存在(PATCH /:productId/correction → sybimport.ManualCorrect,且 sybSpecsTrusted 自 4a1b4af 起已信任 ManuallyConfirmed),缺口只在入口:列表页 nextAction=reparse 原先跳转打开详情抽屉,用户还需在抽屉内再找「人工修正」。

改动仅限 web/src/views/goauto/syb-products/index.vue:

  • runPurchaseNextAction 中 reparse 分支改为直接 openCorrect(row),不再绕详情抽屉
  • openCorrect(row = null) 支持列表行与抽屉两个来源,新增 fromList 标记决定保存后刷新列表还是刷新抽屉
  • 动作按钮文案 查看并处理 → 人工修正
  • 顺带修正抽屉内 @click="openCorrect" 会把 MouseEvent 当作 row 传入的缺陷,改为 @click="openCorrect()"

验证:npx eslint 通过;grep 确认 tests/e2e/ 无用例依赖被改动的文案或行为。

A2 / A3 待设计确认

按本工单「设计证据」要求,A2/A3 为新交互,原型确认后才编写生产代码。

原型链接:https://claude.ai/code/artifact/89f715d6-cb26-4749-8a62-024e9ea3bdec
版本:v1 草稿
创建日期:2026-09-01
状态:待用户确认
覆盖范围:A2 第三维度规格选择弹层、A3 确认规格可售弹层;两者各覆盖正常、空、加载中、失败、禁用、权限边界六类状态;含三处人工确认的落库对照表。
版本识别方式:页面顶部 masthead 标注「工单 #190 / 版本 v1 草稿 / 2026-09-01」。

原型中提出三个待拍板问题,答复前不进入编码:

  1. A3 的「我已人工核对」勾选框是否保留(多一次点击换一条明确的人工核对记录)
  2. A2 选定的第三维度值存商品级(蝦皮商品规格映射,可复用)还是订单级(本条 SYB 明细,更准)
  3. A3 的确认是永久有效,还是商品重新采集后自动失效需重新确认

问题 2 的答复直接决定是否需要数据库迁移,属高风险项,需单独确认。

## 实施进展 ### A1 已完成(未提交) 后端与修正弹层本已存在(`PATCH /:productId/correction` → `sybimport.ManualCorrect`,且 `sybSpecsTrusted` 自 4a1b4af 起已信任 `ManuallyConfirmed`),缺口只在**入口**:列表页 `nextAction=reparse` 原先跳转打开详情抽屉,用户还需在抽屉内再找「人工修正」。 改动仅限 `web/src/views/goauto/syb-products/index.vue`: - `runPurchaseNextAction` 中 `reparse` 分支改为直接 `openCorrect(row)`,不再绕详情抽屉 - `openCorrect(row = null)` 支持列表行与抽屉两个来源,新增 `fromList` 标记决定保存后刷新列表还是刷新抽屉 - 动作按钮文案 `查看并处理` → `人工修正` - 顺带修正抽屉内 `@click="openCorrect"` 会把 MouseEvent 当作 row 传入的缺陷,改为 `@click="openCorrect()"` 验证:`npx eslint` 通过;`grep` 确认 `tests/e2e/` 无用例依赖被改动的文案或行为。 ### A2 / A3 待设计确认 按本工单「设计证据」要求,A2/A3 为新交互,原型确认后才编写生产代码。 **原型链接**:https://claude.ai/code/artifact/89f715d6-cb26-4749-8a62-024e9ea3bdec **版本**:v1 草稿 **创建日期**:2026-09-01 **状态**:待用户确认 **覆盖范围**:A2 第三维度规格选择弹层、A3 确认规格可售弹层;两者各覆盖正常、空、加载中、失败、禁用、权限边界六类状态;含三处人工确认的落库对照表。 **版本识别方式**:页面顶部 masthead 标注「工单 #190 / 版本 v1 草稿 / 2026-09-01」。 原型中提出三个待拍板问题,答复前不进入编码: 1. A3 的「我已人工核对」勾选框是否保留(多一次点击换一条明确的人工核对记录) 2. A2 选定的第三维度值存商品级(蝦皮商品规格映射,可复用)还是订单级(本条 SYB 明细,更准) 3. A3 的确认是永久有效,还是商品重新采集后自动失效需重新确认 问题 2 的答复直接决定是否需要数据库迁移,属高风险项,需单独确认。
Author
Owner

待验收

范围变更说明

本工单最初的 A1/A2/A3 方案(为每个限制配人工确认弹层)经用户明确否决:需求是删除限制,不是新增人工确认交互。因此 A1 已撤销,A2/A3 原方案作废,改为按用户逐条勾选的清单直接删除拦截条件。原型链接(v1 草稿)已不再作为实施依据。

已完成

提交

  • 55c2b6b Revert A1(就地人工修正入口)
  • 31c32d6 删除 AI 匹配与采购创建的规格前置限制
  • 4eac729 同步业务规则镜像

删除的拦截(用户逐条确认)

AI 匹配 ai_match_eligibility.go(净删 59 行):

原拦截 状态
采购规格解析存疑 / 解析失败 已删除
PDD 商品包含颜色、尺码之外的可选规格 已删除
蝦皮商品中未找到目标颜色 / 尺码 已删除
缺少当前 PDD 商品的完整可售 SKU 组合 已删除

随之失效的 shopeeHasTarget、hasSelectableOtherDimension 及 encoding/json、productspec 导入一并删除。

规格映射 batch.go 预检 + service.go 创建:

  • 「规格匹配已失效」不再拒绝,降级为 spec_source=unresolved
  • 「规格映射不完整 / 确定性匹配失败」不再拒绝,同样降级为 unresolved
  • 两条路径行为对齐。此前预检与创建各有一套判断,只改预检会出现「预检通过、创建报错」
  • 降级而非单纯删除的原因:purchasePriceRange 在 mappedColor 为空时取全部颜色价格区间,保留失效映射反而会卡在 PDD_PRICE_MISSING

保留的门禁及原因

service.go 中 spec_source=unresolved 且规则不含 purchase.spec-probe.v1 时仍拒绝创建。删除它会产生 Agent 无法执行的任务。

未执行的条目

用户原始清单中的 4 条未执行,原因如下:

编号 内容 未执行原因
4 已有待执行 / 执行中的采集任务 撞 AGENTS.md 永久规则「同一 PDD 商品不能同时存在多个 pending 或 running 采集任务」,且并发属高风险项。删除需同步修改 AGENTS.md 并单独确认
5 没有可用采集规则 撞「规则创建即生效;删除后不能创建新任务」,且任务缺少 ruleSnapshot 时 Agent 无执行依据
17 PDD 价格缺失 非纯删除。MinUnitPriceCent/MaxUnitPriceCent 有 max >= min 数据库约束,Agent priceGuard 依赖该区间,删除需先定兜底策略。且 #16 完成后其触发面已收窄至「商品完全无任何颜色价格」
19 enforcePersistedMatch 属工单 #191 范围,避免两单提交纠缠

用户已确认接受上述 4 条不做。

已知后果(用户已知悉并接受)

删除解析状态门禁后,批量 AI 匹配会以 parse_status=uncertain 或 failed 的目标规格进行匹配,AI 会把可能错误的目标映射到 PDD 规格并保存映射结果。#191 删除 enforcePersistedMatch 后,该链路将不再有服务端复核点。本项目为内部系统,该风险由人工承担。

注:enforcePersistedMatch 本工单未改动,仍在 BatchPreview 中生效,因此 SYB 列表批量创建路径的「已保存确认映射 + 命中可售组合」要求当前仍然成立,直至 #191 实施。

验证

  • go build ./... 通过
  • go test ./app/goauto/... 全部通过
  • 两个断言旧行为的用例按新行为改写:
    • TestBatchPreviewRejectsUncertainParseAndMissingSKUCombination → TestBatchPreviewAllowsUncertainParseAndMissingSKUCombination
    • TestCreateLiveRejectsExpiredConfirmedMapping → TestCreateLiveDowngradesExpiredConfirmedMappingToSpecProbe
  • 未执行真机验证,Agent 侧规格探测路径的实际行为未在本工单覆盖

文档影响

有长期文档影响,已完成闭环:

  • 线上 Wiki 页 Business-Rules-and-Glossary 已更新并在线回读
  • revision:a6f63cc745cf0e0df7ca94521a70fb6af3adea26
  • 已执行一轮 harness.py sync 与一轮 sync --check,检查通过
  • 镜像 docs/03-business-rules-and-glossary.md 已提交(4eac729)

附带发现(不在本工单范围)

docs/12-syb-erp-interface.md 每次 sync 都会产生重复的 gitea-wiki-mirror 头部块。根因是线上 Wiki 页 SYB-ERP-Interface-Contract 的正文中嵌入了一份镜像头,即镜像文件曾被反向粘贴回 Wiki(AGENTS.md 明确禁止)。sync --check 仍报一致,不影响本工单闭环。建议另建工单清理该 Wiki 页正文。

分支

feat/190-syb-inline-correction(分支名沿用自已撤销的 A1,未改名以保持已推送引用有效)

## 待验收 ### 范围变更说明 本工单最初的 A1/A2/A3 方案(为每个限制配人工确认弹层)经用户明确否决:需求是**删除限制**,不是新增人工确认交互。因此 A1 已撤销,A2/A3 原方案作废,改为按用户逐条勾选的清单直接删除拦截条件。原型链接(v1 草稿)已不再作为实施依据。 ### 已完成 **提交** - `55c2b6b` Revert A1(就地人工修正入口) - `31c32d6` 删除 AI 匹配与采购创建的规格前置限制 - `4eac729` 同步业务规则镜像 **删除的拦截(用户逐条确认)** AI 匹配 `ai_match_eligibility.go`(净删 59 行): | 原拦截 | 状态 | |---|---| | 采购规格解析存疑 / 解析失败 | 已删除 | | PDD 商品包含颜色、尺码之外的可选规格 | 已删除 | | 蝦皮商品中未找到目标颜色 / 尺码 | 已删除 | | 缺少当前 PDD 商品的完整可售 SKU 组合 | 已删除 | 随之失效的 `shopeeHasTarget`、`hasSelectableOtherDimension` 及 `encoding/json`、`productspec` 导入一并删除。 规格映射 `batch.go` 预检 + `service.go` 创建: - 「规格匹配已失效」不再拒绝,降级为 `spec_source=unresolved` - 「规格映射不完整 / 确定性匹配失败」不再拒绝,同样降级为 `unresolved` - 两条路径行为对齐。此前预检与创建各有一套判断,只改预检会出现「预检通过、创建报错」 - 降级而非单纯删除的原因:`purchasePriceRange` 在 `mappedColor` 为空时取全部颜色价格区间,保留失效映射反而会卡在 `PDD_PRICE_MISSING` **保留的门禁及原因** `service.go` 中 `spec_source=unresolved` 且规则不含 `purchase.spec-probe.v1` 时仍拒绝创建。删除它会产生 Agent 无法执行的任务。 ### 未执行的条目 用户原始清单中的 4 条未执行,原因如下: | 编号 | 内容 | 未执行原因 | |---|---|---| | 4 | 已有待执行 / 执行中的采集任务 | 撞 `AGENTS.md` 永久规则「同一 PDD 商品不能同时存在多个 `pending` 或 `running` 采集任务」,且并发属高风险项。删除需同步修改 `AGENTS.md` 并单独确认 | | 5 | 没有可用采集规则 | 撞「规则创建即生效;删除后不能创建新任务」,且任务缺少 `ruleSnapshot` 时 Agent 无执行依据 | | 17 | PDD 价格缺失 | 非纯删除。`MinUnitPriceCent`/`MaxUnitPriceCent` 有 `max >= min` 数据库约束,Agent `priceGuard` 依赖该区间,删除需先定兜底策略。且 #16 完成后其触发面已收窄至「商品完全无任何颜色价格」 | | 19 | `enforcePersistedMatch` | 属工单 #191 范围,避免两单提交纠缠 | 用户已确认接受上述 4 条不做。 ### 已知后果(用户已知悉并接受) 删除解析状态门禁后,批量 AI 匹配会以 `parse_status=uncertain` 或 `failed` 的目标规格进行匹配,AI 会把可能错误的目标映射到 PDD 规格并保存映射结果。#191 删除 `enforcePersistedMatch` 后,该链路将不再有服务端复核点。本项目为内部系统,该风险由人工承担。 注:`enforcePersistedMatch` 本工单未改动,仍在 `BatchPreview` 中生效,因此 SYB 列表批量创建路径的「已保存确认映射 + 命中可售组合」要求当前仍然成立,直至 #191 实施。 ### 验证 - `go build ./...` 通过 - `go test ./app/goauto/...` 全部通过 - 两个断言旧行为的用例按新行为改写: - `TestBatchPreviewRejectsUncertainParseAndMissingSKUCombination` → `TestBatchPreviewAllowsUncertainParseAndMissingSKUCombination` - `TestCreateLiveRejectsExpiredConfirmedMapping` → `TestCreateLiveDowngradesExpiredConfirmedMappingToSpecProbe` - 未执行真机验证,Agent 侧规格探测路径的实际行为未在本工单覆盖 ### 文档影响 有长期文档影响,已完成闭环: - 线上 Wiki 页 `Business-Rules-and-Glossary` 已更新并在线回读 - revision:`a6f63cc745cf0e0df7ca94521a70fb6af3adea26` - 已执行一轮 `harness.py sync` 与一轮 `sync --check`,检查通过 - 镜像 `docs/03-business-rules-and-glossary.md` 已提交(`4eac729`) ### 附带发现(不在本工单范围) `docs/12-syb-erp-interface.md` 每次 `sync` 都会产生重复的 `gitea-wiki-mirror` 头部块。根因是线上 Wiki 页 `SYB-ERP-Interface-Contract` 的正文中嵌入了一份镜像头,即镜像文件曾被反向粘贴回 Wiki(`AGENTS.md` 明确禁止)。`sync --check` 仍报一致,不影响本工单闭环。建议另建工单清理该 Wiki 页正文。 ### 分支 `feat/190-syb-inline-correction`(分支名沿用自已撤销的 A1,未改名以保持已推送引用有效)
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#190