PDD 商品详情增加关联虾皮/SYB 商品与继续采购入口 #161

Open
opened 2026-08-31 08:58:54 +08:00 by ila · 4 comments
Owner

所属与来源

  • 关联工单:#135 / #136 备货采购(同在 PDD 商品详情的另一类入口,需与本工单区分)、#127 采购规则落库。
  • 来源:用户于 2026-08-31 反馈:「PDD 商品页面无法查看到选定的 PDD 商品和虾皮商品或 SYB 商品的关联关系,采集后不知道是哪个 SYB 商品继续采购。」
  • 类型:Server + Admin / PDD 商品的反向关联查询与继续采购入口。
  • 设计证据:在 PDD 商品详情新增一个「关联商品」区块与订单行选择操作,属现有页面的小范围扩展,可复用现有列表与批量创建的交互规范,提供标注截图即可,不需要完整原型。设计证据经用户确认后方可编写生产代码。 需覆盖的状态:
    • 「关联订单继续采购」与既有「创建备货采购」的位置、文案与视觉层级;
    • 无关联商品、加载中、加载失败、无可创建订单、部分可创建、混合阶段;
    • 默认待处理列表与「查看全部」分页;
    • 批量预检失败、部分创建失败、以及无权限时区块不可见。
  • 工具回退说明:本工单通过 Gitea API 创建;当前会话未提供 Gitea MCP 工具,按 AGENTS.md「Gitea 交互与工单最小读取」记录回退原因。

当前事实(行号按提交 5d85625 复核,2026-08-31 修正)

  1. 关联数据完整存在,三张表通过外键串联:
    • syb_product.shopee_product_id(models/schema.go:513)→ shopee_product
    • shopee_product.pdd_product_id(models/schema.go:416)→ pdd_product
    • 注释明确:一个虾皮商品最多关联一个 PDD 商品,但多个虾皮商品可共用同一个 PDD 商品(models/schema.go:413-415)。
  2. PDD 商品查询接口不返回任何关联信息:product/service.go:95-99 的 ListProductView 仅包含 ProductView 与采集相关字段(collectionSelectable、collectionDisabledReason、activeCollectionTaskId),无虾皮或 SYB 字段。
  3. 前端 web/src/views/goauto/pdd-products/index.vue 中不存在 shopee / syb 相关内容。详情接口的扩展点为 product/service.go:222 的 Detail。
  4. 现有链路均为正向:批量创建采购从 SYB 订单行出发解析虾皮商品与 PDD 商品(purchase/batch.go:314-324);规格映射配置在虾皮商品上并指向 PDD 规格值。
  5. SYBProduct 携带订单维度信息:OrderCode(虾皮订单号,:501)、TargetColor / TargetSize(:519)、数量等。

问题

采集是以 PDD 商品为中心的操作。采集完成后采购员站在 PDD 商品这一侧,需要回答「这个货源对应哪些虾皮/SYB 商品、接下来去哪继续采购」——这需要反向查询,而反向链路从未实现。

当前只能凭记忆回到 SYB 商品列表逐个查找,商品数量增加后不可行。

领域划分本身是合理的(PDD 商品作为货源档案不感知使用方,ShopeeProduct 注释亦强调商品域不存采购与订单字段),本工单不改变该划分,只补齐只读的反向查询与展示。

目标

  1. 在 PDD 商品详情可查看关联的虾皮商品,以及其下待采购的 SYB 订单行。
  2. 可直接从该处对选中的订单行发起采购,复用既有批量创建能力。
  3. 不修改任何数据模型与领域边界。

非目标

  • 不新增或修改数据库表与外键——关联关系已存在,本工单只做查询与展示。
  • 注意:本工单确实新增一个只读查询接口(见实施方案第 2 项),这不属于非目标;不新增的是数据表、外键与写入路径。
  • 不改变采购任务的创建逻辑、价格护栏、规格映射与并发约束。
  • 不改变 ShopeeProduct / PDDProduct 的领域边界,不在商品域写入采购或订单字段。
  • 不实现备货采购(#135 / #136 范围)。
  • 不实现支付;不放宽 #116 的门禁。

实施方案

一、服务端:反向关联查询

  1. 详情接口只增加有界摘要:GET /api/admin/v1/pdd-products/:productId(实现在 product/service.go:222 的 Detail)增加关联虾皮商品列表——商品标题、shopee_item_id、规格映射状态摘要、该虾皮商品下的待处理订单行数量。不在详情响应中嵌入任何订单行明细。

  2. 订单行走独立分页接口:新增 GET /api/admin/v1/pdd-products/:productId/related-syb-products。

    • 查询参数:page、pageSize(默认 20,最大 100)、scope=actionable|all(默认 actionable)、可选 shopeeProductId;
    • 不使用 pendingOnly:默认集合同时包含可创建、进行中、失败与待匹配四类状态,pendingOnly 名不副实,后续扩档也会产生兼容歧义;非法 scope 返回参数错误;
    • 响应为扁平的订单行列表加统一的 total / page / pageSize;
    • 不做「每个虾皮商品各自分页」的嵌套结构——一个 PDD 可关联多个虾皮商品、每个虾皮商品可能挂大量历史 SYB 明细,嵌套分页无法定义统一 total,也会拖慢详情打开。
    • 每行字段:order_code、目标颜色/尺码、数量、处理阶段与原因、当前采购任务及状态。
  3. 默认展示的处理阶段集合必须明确,不使用「待采购」统称:

    • 可创建采购:purchase_ready;
    • 采购前待处理:pdd_pending、pdd_collecting、pdd_collection_failed、color_mapping;
    • 已有任务、已完成、待人工核对(task_created、purchase_succeeded、order_review、manual_action、pdd_unlinked)默认隐藏,经「查看全部」(scope=all)进入。
    • scope=actionable 固定对应 purchase_ready、pdd_pending、pdd_collecting、pdd_collection_failed、color_mapping 这五个阶段,该集合由服务端定义,前端不得自行维护。
      阶段、标签、原因与下一步复用 purchase 包的统一入口(见第 4 项),不在 product 域复制一套判定。
  4. 阶段计算必须在 purchase 包统一重构,并对缺少当前采购规则容错(阻塞级约束):ProcessStages 在 purchase/process_stage.go:66 调用 purchaserule.CurrentRule,后者在未配置当前采购规则时返回错误(purchaserule/service.go:79),当前实现直接 return nil, err。若原样复用,未配置规则将导致 PDD 商品详情或关联列表整体失败。

    • 不得在 product 包复制 processStageFromDataset、捕获缺少规则的错误后另建简化判定,或维护第二套阶段原因;
    • 在 purchase 包提供统一的只读阶段计算入口:无 AI 调用;未配置或当前采购规则无效时仍返回基础关联、采集、已有任务等阶段事实;只有依赖规则与价格护栏才能决定的订单行标记为不可采购并给出明确原因;
    • SYB 商品列表/预检展示与 PDD 关联订单接口共同调用该入口,同一订单行在两个页面的 processStage、label、reason、nextAction 必须一致。
    • 这是对既有查询行为的有意变更,须显式声明并回归:今天未配置采购规则时 BatchPreview(purchase/batch.go:88)整体返回错误,SYB 商品页前端 catch 后把所有行标成「采购准备检查失败」(index.vue:222 附近)。重构后 SYB 页将变为「阶段正常显示、仅不可创建采购」。该变化是改进,但属于既有接口的行为变更,不是纯新增,必须纳入 SYB 页回归验证。
  5. 规格映射摘要由服务端返回明确结果。详情摘要返回:

{
  "specMappingStatus": "complete|incomplete|not_required",
  "specMappingConfirmedCount": 8,
  "specMappingTotalCount": 12,
  "specMappingReason": "已确认 8/12 个规格值"
}
  • 字段名不得使用 mappingStatus:该名称在本仓库已被占用两次且值域不同(models/replacement.go:52 为 matching|completed|completed_partial,:109 为 matching|matched|manual_required,另有 purchase/agent_history.go:62 的 replacementMappingStatus)。本工单讲的是规格值映射,统一用 specMapping* 前缀,避免同名字段第三种值域。
  • complete:所有需要映射的虾皮颜色/尺码规格值均有 confirmed 映射(shopeeproduct/specs.go:30 的 MappingStatusConfirmed),且映射目标仍属于当前 PDD 对应维度的可选规格;
  • incomplete:至少一个需要映射的规格值未映射、仍为 pending,或映射目标已不属于当前 PDD 可选规格;
  • not_required:虾皮商品不存在需要映射的颜色/尺码维度(RoleColor / RoleSize);不得用「某一订单行目标值为空」判定商品整体为 not_required;
  • 该字段是虾皮商品整体摘要,不等同于某一 SYB 订单行的采购资格;订单行是否可采购仍以第 4 项的统一阶段入口与采购预检结果为准。
  • 前端不得解析 specsJSON 自行推断,避免与采购预检的判定分叉。
  1. 列表接口不做逐行反查:ListProductView(product/service.go:95-99)每行反查会产生 N+1。如需在列表展示,只允许通过一次聚合查询附加「关联虾皮商品数」,不返回订单行明细。

  2. 查询必须有界且批量化:详情的基础查询与关联订单页查询的次数不得随订单行数量线性增长,不得逐行调用阶段计算。关联查询为只读,不得触发 AI 规格匹配(参照 retryQueryEligibility 的既有约束:查询路径永不调用外部 provider)。验收需记录实际查询次数或执行计划。

二、发起采购

  1. 选中订单行后调用既有的批量预检与批量创建接口(batch-preview / batch),不新写创建逻辑。
  2. 预检不通过的原因(规格未映射、价格缺失、已有进行中任务、已支付订单等)原样展示,不在前端二次解释或改写。

三、Admin 界面

  1. PDD 商品详情新增「关联商品」区块:上层为详情接口返回的虾皮商品摘要,下层订单行来自第 2 项的独立分页接口,可勾选订单行并发起采购。默认按第 3 项只展示 scope=actionable 的阶段,另提供「查看全部」入口。
    界面模型必须与扁平分页一致:
    • 每行携带所属 shopeeProductId、shopeeItemId 与虾皮商品标题;
    • 默认查看全部关联虾皮商品,可通过摘要项或筛选控件传 shopeeProductId 收窄;
    • 不得在统一分页结果上渲染成「每个虾皮商品各自拥有完整订单子表」的伪嵌套结构;
    • 允许同一虾皮商品的订单行跨页出现,total / page / pageSize 始终针对当前统一过滤条件。
  2. 与 #136 的备货入口明确区分:#136 的「创建备货采购」是脱离 SYB 订单的囤货;本工单是「为已有订单继续采购」。两者同处 PDD 商品详情,文案与视觉必须可分辨,避免误点。
  3. 无关联虾皮商品时显示明确空态(提示先在虾皮商品处完成关联),不报错、不显示空白区块。

四、权限

  1. 新增的关联查询接口必须显式加入 GoAuto 权限矩阵:管理员、采购员可读;其他角色不可读。不能只依赖前端隐藏区块,须有自动化测试覆盖「非管理员、非采购员角色读取该接口被拒绝」。
  2. 创建采购任务继续使用既有采购权限,不新增也不放宽。

安全边界

  • 只读反向查询,不写入任何数据。
  • 不改变商品域的领域边界。
  • 采购任务创建仍走既有路径与全部既有校验(价格护栏、规格映射有效性、并发互斥、不可逆边界)。
  • 查询路径不得调用外部 AI provider。
  • 跨域数据可见性变化需显式确认:本工单会把 SYB 订单号、目标颜色/尺码、数量等订单维度信息呈现在 PDD 商品详情页,这是该页面此前没有的数据面。虽不新增权限点,可见范围确有扩大;实施时须确认采购员之外的角色不会因此看到订单数据,并把确认结论回写工单。
  • 不实现支付;不新增下单或地址目标。

验收标准

  • PDD 商品详情可看到关联的虾皮商品;一个 PDD 商品被多个虾皮商品共用时全部列出。
  • 每个虾皮商品下可看到待采购的 SYB 订单行,含订单号、目标颜色尺码、数量与当前任务状态。
  • 详情响应中不含订单行明细,只含虾皮商品摘要与待处理数量。
  • 订单行由独立接口 GET /pdd-products/:productId/related-syb-products 返回,total / page / pageSize 准确,不是按每个虾皮商品各自伪分页;pageSize 默认 20、上限 100 生效。
  • 请求缺省 scope 时等价于 scope=actionable;非法 scope 被拒绝;前端未自行维护阶段集合(代码检查佐证)。
  • 按「全部虾皮商品」与「指定 shopeeProductId」两种过滤分别验证分页与 total;同一虾皮商品的订单行跨页出现时展示正确,无伪嵌套子表。
  • 默认列表明确区分「可创建采购」与「采购前待处理」,其余阶段默认隐藏并可经「查看全部」进入。
  • 未配置当前采购规则时,PDD 商品详情与关联列表仍能正常读取,订单行显示明确的不可创建原因,不报错、不空白。
  • specMappingStatus 三态与计数按第 5 项定义验证;字段名未与既有 mappingStatus 混用。
  • 商品整体为 incomplete 不会阻止其中已满足条件的订单行进入 purchase_ready。
  • 代码中只有 purchase 包拥有共享阶段计算入口,product 包无复制判定(代码检查佐证)。
  • 未配置当前采购规则时,SYB 商品页与 PDD 关联列表返回一致的阶段与原因。
  • SYB 商品页在采购规则缺失场景下的行为变更已回归:不再整页「采购准备检查失败」,而是阶段正常、仅不可创建采购。
  • 列表接口未引入逐行反查(以查询次数或执行计划佐证);如展示关联数量,为单次聚合查询。
  • 关联查询过程中不触发任何外部 AI 调用。
  • 勾选订单行可成功发起采购,走的是既有 batch-preview / batch 路径(代码检查佐证未新增创建实现)。
  • 预检不通过时原因原样展示,与在 SYB 商品页发起时一致。
  • 「关联商品继续采购」与 #136 的「创建备货采购」在文案与视觉上可区分。
  • 无关联虾皮商品时显示空态,不报错。
  • 未新增或修改任何数据库表与外键。
  • 采购员可查看并发起采购;非管理员、非采购员角色读取关联订单接口被拒绝,且有自动化测试佐证,不只依赖前端隐藏。
  • 查询规模固定:不随订单行数量线性增长、不触发 AI、无 N+1(以查询次数或执行计划佐证)。
  • 「待采购」判定与 SYB 商品页的处理阶段一致:同一条订单行在两个页面显示同一阶段与同一原因(以对照验证佐证)。

验证方式

  • go test ./app/goauto/product/... ./app/goauto/purchase/...
  • 构造数据集:一个 PDD 商品被两个虾皮商品共用、其中一个虾皮商品挂多条 SYB 订单行(含已完成与待采购),验证过滤与分页。
  • 查询次数断言:详情接口的关联查询不随订单行数量线性增长到不可接受;列表接口无 N+1。
  • 手工验证:从 PDD 商品详情发起一次采购,与从 SYB 商品页发起的结果一致。
  • 专项验证:删除/置空当前采购规则后,详情与关联列表仍可读取,且 SYB 商品页与关联列表显示一致的阶段与原因(含 SYB 页既有行为变更的回归)。
  • 权限验证:以非管理员、非采购员角色调用关联接口,断言被拒绝(自动化测试)。
  • 不涉及 Agent,不需要真机验证;如实记录该结论。
  • 未覆盖的浏览器与异常路径如实回写。

依赖、并行与风险

  • 无强前置依赖。
  • 与 #136 均修改 PDD 商品详情页,不建议同时进行,需先后实施并在第二个工单中确认两个入口的区分效果。
  • 与 #162 表面上文件不重叠,但两者都在扩大「多选 + 批量发起」的交互面。建议先统一「同一页面存在多种批量动作时的选择语义」这一交互规范(该问题已在 #162 实际暴露),再实施本工单的订单行勾选,避免两页做出不一致的多选语义。
  • 风险:订单行数量大时详情接口变慢。缓解:详情只返回有界摘要(第 1 项)、订单行独立分页(第 2 项)、禁止列表页反查(第 6 项)。
  • 风险:复用 ProcessStages 会引入对当前采购规则的硬依赖,未配置规则时整页失败。缓解:第 4 项在 purchase 包统一重构并容错,并在验收中专项检查。
  • 风险:第 4 项的重构会改变 SYB 商品页在规则缺失时的既有表现(从整页失败变为阶段可见、不可采购)。缓解:作为有意变更显式声明,并纳入 SYB 页回归;若回归发现依赖旧行为的用例,先记录再决定。
  • 风险:两个采购入口并存导致误点。缓解:第 8 项的文案与视觉区分,并在验收中检查。
  • 回退:还原提交即可,无数据影响(本工单不写数据)。

文档影响

  • Wiki Architecture-and-Code-Map:PDD 商品的反向关联查询路径与新增的关联订单查询接口。
  • 新增管理端接口 GET /api/admin/v1/pdd-products/:productId/related-syb-products 及其权限矩阵条目必须同步记录(接口路径、参数、默认值与上限、可读角色)。
  • Wiki Business-Rules-and-Glossary:从 PDD 商品发起采购与备货采购的区别、scope=actionable 的阶段集合定义、specMappingStatus 三态判定口径。
  • Wiki Android-Agent-API-Contract:不涉及 Agent 接口,实施时确认无需改动并在工单说明。
  • 按 Wiki-first 门禁:先改线上页面并回读 revision,再执行一轮 sync 与一轮 sync --check,把页面与 revision 写回本工单。

状态

待实施(界面需先取得设计证据)。

修订记录

  • 2026-08-31(三):合并二次评审——查询参数改为 scope=actionable|all(弃用 pendingOnly)、阶段计算在 purchase 包统一重构并对缺少采购规则容错、明确扁平分页的界面模型、定义规格映射摘要三态与计数。另按复核结论做两处调整:映射字段改名为 specMapping*(避免与 models/replacement.go 两处既有 mappingStatus 值域冲突),并把「SYB 商品页在规则缺失时的既有表现变更」显式声明为有意行为变更并纳入回归。
  • 2026-08-31(二):合并 2026-08-31 实施前评审意见——订单行改为独立分页接口、明确默认阶段集合、修复缺少当前采购规则导致整页失败的隐患、权限进矩阵并要求自动化测试、mappingStatus 由服务端返回、查询有界;相应更新非目标、验收、验证方式、风险与文档影响。评审结论已并入正文,正文为唯一实施依据。
  • 2026-08-31:按提交 5d85625 复核,修正 schema.go 行号引用(pdd_product_id 为 :416,注释为 :413-415),补充详情扩展点 product/service.go:222;将「待采购」判定改为复用采购预检既有处理阶段口径;在安全边界补充跨域数据可见性确认;相应增加两条验收与与 #162 的交互规范前置说明。
## 所属与来源 - 关联工单:#135 / #136 备货采购(同在 PDD 商品详情的另一类入口,需与本工单区分)、#127 采购规则落库。 - 来源:用户于 2026-08-31 反馈:「PDD 商品页面无法查看到选定的 PDD 商品和虾皮商品或 SYB 商品的关联关系,采集后不知道是哪个 SYB 商品继续采购。」 - 类型:Server + Admin / PDD 商品的反向关联查询与继续采购入口。 - 设计证据:在 PDD 商品详情新增一个「关联商品」区块与订单行选择操作,属现有页面的小范围扩展,可复用现有列表与批量创建的交互规范,提供标注截图即可,不需要完整原型。**设计证据经用户确认后方可编写生产代码。** 需覆盖的状态: - 「关联订单继续采购」与既有「创建备货采购」的位置、文案与视觉层级; - 无关联商品、加载中、加载失败、无可创建订单、部分可创建、混合阶段; - 默认待处理列表与「查看全部」分页; - 批量预检失败、部分创建失败、以及无权限时区块不可见。 - 工具回退说明:本工单通过 Gitea API 创建;当前会话未提供 Gitea MCP 工具,按 `AGENTS.md`「Gitea 交互与工单最小读取」记录回退原因。 ## 当前事实(行号按提交 5d85625 复核,2026-08-31 修正) 1. **关联数据完整存在**,三张表通过外键串联: - `syb_product.shopee_product_id`(`models/schema.go:513`)→ `shopee_product` - `shopee_product.pdd_product_id`(`models/schema.go:416`)→ `pdd_product` - 注释明确:一个虾皮商品最多关联一个 PDD 商品,但**多个虾皮商品可共用同一个 PDD 商品**(`models/schema.go:413-415`)。 2. **PDD 商品查询接口不返回任何关联信息**:`product/service.go:95-99` 的 `ListProductView` 仅包含 `ProductView` 与采集相关字段(`collectionSelectable`、`collectionDisabledReason`、`activeCollectionTaskId`),无虾皮或 SYB 字段。 3. 前端 `web/src/views/goauto/pdd-products/index.vue` 中不存在 shopee / syb 相关内容。详情接口的扩展点为 `product/service.go:222` 的 `Detail`。 4. 现有链路均为**正向**:批量创建采购从 SYB 订单行出发解析虾皮商品与 PDD 商品(`purchase/batch.go:314-324`);规格映射配置在虾皮商品上并指向 PDD 规格值。 5. `SYBProduct` 携带订单维度信息:`OrderCode`(虾皮订单号,`:501`)、`TargetColor` / `TargetSize`(`:519`)、数量等。 ## 问题 采集是**以 PDD 商品为中心**的操作。采集完成后采购员站在 PDD 商品这一侧,需要回答「这个货源对应哪些虾皮/SYB 商品、接下来去哪继续采购」——这需要反向查询,而反向链路从未实现。 当前只能凭记忆回到 SYB 商品列表逐个查找,商品数量增加后不可行。 **领域划分本身是合理的**(PDD 商品作为货源档案不感知使用方,`ShopeeProduct` 注释亦强调商品域不存采购与订单字段),本工单不改变该划分,只补齐**只读的反向查询与展示**。 ## 目标 1. 在 PDD 商品详情可查看关联的虾皮商品,以及其下待采购的 SYB 订单行。 2. 可直接从该处对选中的订单行发起采购,复用既有批量创建能力。 3. 不修改任何数据模型与领域边界。 ## 非目标 - **不新增或修改数据库表与外键**——关联关系已存在,本工单只做查询与展示。 - 注意:本工单**确实新增一个只读查询接口**(见实施方案第 2 项),这不属于非目标;不新增的是数据表、外键与写入路径。 - 不改变采购任务的创建逻辑、价格护栏、规格映射与并发约束。 - 不改变 `ShopeeProduct` / `PDDProduct` 的领域边界,不在商品域写入采购或订单字段。 - 不实现备货采购(#135 / #136 范围)。 - 不实现支付;不放宽 #116 的门禁。 ## 实施方案 ### 一、服务端:反向关联查询 1. **详情接口只增加有界摘要**:`GET /api/admin/v1/pdd-products/:productId`(实现在 `product/service.go:222` 的 `Detail`)增加关联虾皮商品列表——商品标题、`shopee_item_id`、规格映射状态摘要、该虾皮商品下的待处理订单行数量。**不在详情响应中嵌入任何订单行明细**。 2. **订单行走独立分页接口**:新增 `GET /api/admin/v1/pdd-products/:productId/related-syb-products`。 - 查询参数:`page`、`pageSize`(默认 20,最大 100)、`scope=actionable|all`(默认 `actionable`)、可选 `shopeeProductId`; - **不使用 `pendingOnly`**:默认集合同时包含可创建、进行中、失败与待匹配四类状态,`pendingOnly` 名不副实,后续扩档也会产生兼容歧义;非法 `scope` 返回参数错误; - 响应为扁平的订单行列表加统一的 `total` / `page` / `pageSize`; - **不做「每个虾皮商品各自分页」的嵌套结构**——一个 PDD 可关联多个虾皮商品、每个虾皮商品可能挂大量历史 SYB 明细,嵌套分页无法定义统一 total,也会拖慢详情打开。 - 每行字段:`order_code`、目标颜色/尺码、数量、处理阶段与原因、当前采购任务及状态。 3. **默认展示的处理阶段集合必须明确,不使用「待采购」统称**: - 可创建采购:`purchase_ready`; - 采购前待处理:`pdd_pending`、`pdd_collecting`、`pdd_collection_failed`、`color_mapping`; - 已有任务、已完成、待人工核对(`task_created`、`purchase_succeeded`、`order_review`、`manual_action`、`pdd_unlinked`)默认隐藏,经「查看全部」(`scope=all`)进入。 - `scope=actionable` **固定对应** `purchase_ready`、`pdd_pending`、`pdd_collecting`、`pdd_collection_failed`、`color_mapping` 这五个阶段,该集合由服务端定义,**前端不得自行维护**。 阶段、标签、原因与下一步**复用 purchase 包的统一入口**(见第 4 项),不在 product 域复制一套判定。 4. **阶段计算必须在 purchase 包统一重构,并对缺少当前采购规则容错(阻塞级约束)**:`ProcessStages` 在 `purchase/process_stage.go:66` 调用 `purchaserule.CurrentRule`,后者在未配置当前采购规则时返回错误(`purchaserule/service.go:79`),当前实现**直接 `return nil, err`**。若原样复用,未配置规则将导致 PDD 商品详情或关联列表整体失败。 - **不得**在 product 包复制 `processStageFromDataset`、捕获缺少规则的错误后另建简化判定,或维护第二套阶段原因; - 在 `purchase` 包提供**统一的只读阶段计算入口**:无 AI 调用;未配置或当前采购规则无效时仍返回基础关联、采集、已有任务等阶段事实;只有依赖规则与价格护栏才能决定的订单行标记为不可采购并给出明确原因; - SYB 商品列表/预检展示与 PDD 关联订单接口**共同调用该入口**,同一订单行在两个页面的 `processStage`、label、reason、nextAction 必须一致。 - **这是对既有查询行为的有意变更,须显式声明并回归**:今天未配置采购规则时 `BatchPreview`(`purchase/batch.go:88`)整体返回错误,SYB 商品页前端 catch 后把所有行标成「采购准备检查失败」(`index.vue:222` 附近)。重构后 SYB 页将变为「阶段正常显示、仅不可创建采购」。该变化是改进,但属于既有接口的行为变更,不是纯新增,必须纳入 SYB 页回归验证。 5. **规格映射摘要由服务端返回明确结果**。详情摘要返回: ```json { "specMappingStatus": "complete|incomplete|not_required", "specMappingConfirmedCount": 8, "specMappingTotalCount": 12, "specMappingReason": "已确认 8/12 个规格值" } ``` - **字段名不得使用 `mappingStatus`**:该名称在本仓库已被占用两次且值域不同(`models/replacement.go:52` 为 `matching|completed|completed_partial`,`:109` 为 `matching|matched|manual_required`,另有 `purchase/agent_history.go:62` 的 `replacementMappingStatus`)。本工单讲的是规格值映射,统一用 `specMapping*` 前缀,避免同名字段第三种值域。 - `complete`:所有需要映射的虾皮颜色/尺码规格值均有 `confirmed` 映射(`shopeeproduct/specs.go:30` 的 `MappingStatusConfirmed`),且映射目标仍属于当前 PDD 对应维度的可选规格; - `incomplete`:至少一个需要映射的规格值未映射、仍为 `pending`,或映射目标已不属于当前 PDD 可选规格; - `not_required`:虾皮商品不存在需要映射的颜色/尺码维度(`RoleColor` / `RoleSize`);**不得用「某一订单行目标值为空」判定商品整体为 `not_required`**; - 该字段是**虾皮商品整体摘要**,不等同于某一 SYB 订单行的采购资格;订单行是否可采购仍以第 4 项的统一阶段入口与采购预检结果为准。 - 前端**不得解析 `specsJSON` 自行推断**,避免与采购预检的判定分叉。 6. **列表接口不做逐行反查**:`ListProductView`(`product/service.go:95-99`)每行反查会产生 N+1。如需在列表展示,只允许通过一次聚合查询附加「关联虾皮商品数」,不返回订单行明细。 7. **查询必须有界且批量化**:详情的基础查询与关联订单页查询的**次数不得随订单行数量线性增长**,不得逐行调用阶段计算。关联查询为只读,不得触发 AI 规格匹配(参照 `retryQueryEligibility` 的既有约束:查询路径永不调用外部 provider)。验收需记录实际查询次数或执行计划。 ### 二、发起采购 8. 选中订单行后**调用既有的批量预检与批量创建接口**(`batch-preview` / `batch`),不新写创建逻辑。 9. 预检不通过的原因(规格未映射、价格缺失、已有进行中任务、已支付订单等)原样展示,不在前端二次解释或改写。 ### 三、Admin 界面 10. PDD 商品详情新增「关联商品」区块:上层为详情接口返回的虾皮商品摘要,下层订单行来自第 2 项的独立分页接口,可勾选订单行并发起采购。默认按第 3 项只展示 `scope=actionable` 的阶段,另提供「查看全部」入口。 **界面模型必须与扁平分页一致**: - 每行携带所属 `shopeeProductId`、`shopeeItemId` 与虾皮商品标题; - 默认查看全部关联虾皮商品,可通过摘要项或筛选控件传 `shopeeProductId` 收窄; - **不得在统一分页结果上渲染成「每个虾皮商品各自拥有完整订单子表」的伪嵌套结构**; - 允许同一虾皮商品的订单行跨页出现,`total` / `page` / `pageSize` 始终针对当前统一过滤条件。 11. **与 #136 的备货入口明确区分**:#136 的「创建备货采购」是脱离 SYB 订单的囤货;本工单是「为已有订单继续采购」。两者同处 PDD 商品详情,文案与视觉必须可分辨,避免误点。 12. 无关联虾皮商品时显示明确空态(提示先在虾皮商品处完成关联),不报错、不显示空白区块。 ### 四、权限 13. **新增的关联查询接口必须显式加入 GoAuto 权限矩阵**:管理员、采购员可读;其他角色不可读。**不能只依赖前端隐藏区块**,须有自动化测试覆盖「非管理员、非采购员角色读取该接口被拒绝」。 14. 创建采购任务继续使用既有采购权限,不新增也不放宽。 ## 安全边界 - 只读反向查询,不写入任何数据。 - 不改变商品域的领域边界。 - 采购任务创建仍走既有路径与全部既有校验(价格护栏、规格映射有效性、并发互斥、不可逆边界)。 - 查询路径不得调用外部 AI provider。 - **跨域数据可见性变化需显式确认**:本工单会把 SYB 订单号、目标颜色/尺码、数量等订单维度信息呈现在 PDD 商品详情页,这是该页面此前没有的数据面。虽不新增权限点,可见范围确有扩大;实施时须确认采购员之外的角色不会因此看到订单数据,并把确认结论回写工单。 - 不实现支付;不新增下单或地址目标。 ## 验收标准 - [ ] PDD 商品详情可看到关联的虾皮商品;一个 PDD 商品被多个虾皮商品共用时全部列出。 - [ ] 每个虾皮商品下可看到待采购的 SYB 订单行,含订单号、目标颜色尺码、数量与当前任务状态。 - [ ] 详情响应中不含订单行明细,只含虾皮商品摘要与待处理数量。 - [ ] 订单行由独立接口 `GET /pdd-products/:productId/related-syb-products` 返回,`total` / `page` / `pageSize` 准确,**不是按每个虾皮商品各自伪分页**;`pageSize` 默认 20、上限 100 生效。 - [ ] 请求缺省 `scope` 时等价于 `scope=actionable`;非法 `scope` 被拒绝;前端未自行维护阶段集合(代码检查佐证)。 - [ ] 按「全部虾皮商品」与「指定 `shopeeProductId`」两种过滤分别验证分页与 `total`;同一虾皮商品的订单行跨页出现时展示正确,无伪嵌套子表。 - [ ] 默认列表明确区分「可创建采购」与「采购前待处理」,其余阶段默认隐藏并可经「查看全部」进入。 - [ ] **未配置当前采购规则时,PDD 商品详情与关联列表仍能正常读取**,订单行显示明确的不可创建原因,不报错、不空白。 - [ ] `specMappingStatus` 三态与计数按第 5 项定义验证;字段名未与既有 `mappingStatus` 混用。 - [ ] 商品整体为 `incomplete` **不会**阻止其中已满足条件的订单行进入 `purchase_ready`。 - [ ] 代码中只有 purchase 包拥有共享阶段计算入口,product 包无复制判定(代码检查佐证)。 - [ ] 未配置当前采购规则时,SYB 商品页与 PDD 关联列表返回**一致**的阶段与原因。 - [ ] SYB 商品页在采购规则缺失场景下的行为变更已回归:不再整页「采购准备检查失败」,而是阶段正常、仅不可创建采购。 - [ ] 列表接口未引入逐行反查(以查询次数或执行计划佐证);如展示关联数量,为单次聚合查询。 - [ ] 关联查询过程中不触发任何外部 AI 调用。 - [ ] 勾选订单行可成功发起采购,走的是既有 `batch-preview` / `batch` 路径(代码检查佐证未新增创建实现)。 - [ ] 预检不通过时原因原样展示,与在 SYB 商品页发起时一致。 - [ ] 「关联商品继续采购」与 #136 的「创建备货采购」在文案与视觉上可区分。 - [ ] 无关联虾皮商品时显示空态,不报错。 - [ ] 未新增或修改任何数据库表与外键。 - [ ] 采购员可查看并发起采购;**非管理员、非采购员角色读取关联订单接口被拒绝,且有自动化测试佐证**,不只依赖前端隐藏。 - [ ] 查询规模固定:不随订单行数量线性增长、不触发 AI、无 N+1(以查询次数或执行计划佐证)。 - [ ] 「待采购」判定与 SYB 商品页的处理阶段一致:同一条订单行在两个页面显示同一阶段与同一原因(以对照验证佐证)。 ## 验证方式 - `go test ./app/goauto/product/... ./app/goauto/purchase/...` - 构造数据集:一个 PDD 商品被两个虾皮商品共用、其中一个虾皮商品挂多条 SYB 订单行(含已完成与待采购),验证过滤与分页。 - 查询次数断言:详情接口的关联查询不随订单行数量线性增长到不可接受;列表接口无 N+1。 - 手工验证:从 PDD 商品详情发起一次采购,与从 SYB 商品页发起的结果一致。 - 专项验证:删除/置空当前采购规则后,详情与关联列表仍可读取,且 SYB 商品页与关联列表显示一致的阶段与原因(含 SYB 页既有行为变更的回归)。 - 权限验证:以非管理员、非采购员角色调用关联接口,断言被拒绝(自动化测试)。 - 不涉及 Agent,不需要真机验证;如实记录该结论。 - 未覆盖的浏览器与异常路径如实回写。 ## 依赖、并行与风险 - 无强前置依赖。 - 与 #136 均修改 PDD 商品详情页,**不建议同时进行**,需先后实施并在第二个工单中确认两个入口的区分效果。 - 与 #162 表面上文件不重叠,但两者都在扩大「多选 + 批量发起」的交互面。**建议先统一「同一页面存在多种批量动作时的选择语义」这一交互规范**(该问题已在 #162 实际暴露),再实施本工单的订单行勾选,避免两页做出不一致的多选语义。 - 风险:订单行数量大时详情接口变慢。缓解:详情只返回有界摘要(第 1 项)、订单行独立分页(第 2 项)、禁止列表页反查(第 6 项)。 - **风险:复用 `ProcessStages` 会引入对当前采购规则的硬依赖**,未配置规则时整页失败。缓解:第 4 项在 purchase 包统一重构并容错,并在验收中专项检查。 - **风险:第 4 项的重构会改变 SYB 商品页在规则缺失时的既有表现**(从整页失败变为阶段可见、不可采购)。缓解:作为有意变更显式声明,并纳入 SYB 页回归;若回归发现依赖旧行为的用例,先记录再决定。 - 风险:两个采购入口并存导致误点。缓解:第 8 项的文案与视觉区分,并在验收中检查。 - 回退:还原提交即可,无数据影响(本工单不写数据)。 ## 文档影响 - Wiki `Architecture-and-Code-Map`:PDD 商品的反向关联查询路径与新增的关联订单查询接口。 - **新增管理端接口 `GET /api/admin/v1/pdd-products/:productId/related-syb-products` 及其权限矩阵条目必须同步记录**(接口路径、参数、默认值与上限、可读角色)。 - Wiki `Business-Rules-and-Glossary`:从 PDD 商品发起采购与备货采购的区别、`scope=actionable` 的阶段集合定义、`specMappingStatus` 三态判定口径。 - Wiki `Android-Agent-API-Contract`:不涉及 Agent 接口,实施时确认无需改动并在工单说明。 - 按 Wiki-first 门禁:先改线上页面并回读 revision,再执行一轮 `sync` 与一轮 `sync --check`,把页面与 revision 写回本工单。 ## 状态 待实施(界面需先取得设计证据)。 ## 修订记录 - 2026-08-31(三):合并二次评审——查询参数改为 `scope=actionable|all`(弃用 `pendingOnly`)、阶段计算在 purchase 包统一重构并对缺少采购规则容错、明确扁平分页的界面模型、定义规格映射摘要三态与计数。另按复核结论做两处调整:映射字段改名为 `specMapping*`(避免与 `models/replacement.go` 两处既有 `mappingStatus` 值域冲突),并把「SYB 商品页在规则缺失时的既有表现变更」显式声明为有意行为变更并纳入回归。 - 2026-08-31(二):合并 2026-08-31 实施前评审意见——订单行改为独立分页接口、明确默认阶段集合、修复缺少当前采购规则导致整页失败的隐患、权限进矩阵并要求自动化测试、`mappingStatus` 由服务端返回、查询有界;相应更新非目标、验收、验证方式、风险与文档影响。评审结论已并入正文,正文为唯一实施依据。 - 2026-08-31:按提交 5d85625 复核,修正 schema.go 行号引用(pdd_product_id 为 :416,注释为 :413-415),补充详情扩展点 product/service.go:222;将「待采购」判定改为复用采购预检既有处理阶段口径;在安全边界补充跨域数据可见性确认;相应增加两条验收与与 #162 的交互规范前置说明。
Author
Owner

2026-08-31 实施前评审补充

结论:现有目标、领域边界和复用既有采购接口的方向合理;进入生产代码前需补齐以下契约与设计细节。

必须纳入实施方案

  1. 关联订单行使用独立分页接口,不把全部历史订单嵌入现有详情响应。 建议:

    • GET /pdd-products/:productId 继续返回商品详情,并增加关联虾皮商品摘要、待处理数量等有界信息;
    • 新增 GET /pdd-products/:productId/related-syb-products,支持 page、pageSize、pendingOnly,可选 shopeeProductId;
    • 默认 pageSize=20,最大 100。
      原因:一个 PDD 可关联多个虾皮商品,每个虾皮商品可能存在大量历史 SYB 明细;嵌套的逐虾皮分页难以定义统一 total,也会拖慢详情打开。
  2. 明确默认展示的处理阶段集合,不使用模糊的“待采购”统称。 界面建议分为:

    • 可创建采购:purchase_ready;
    • 采购前待处理:pdd_pending、pdd_collecting、pdd_collection_failed、color_mapping;
    • 已有任务、已完成、待人工核对:默认隐藏,通过“查看全部”进入。
      阶段、标签、原因和下一步仍须复用 processStageFromDataset / ProcessStages 口径,不在 product 域复制判定。
  3. 缺少当前采购规则时,关联关系和历史订单仍必须可查看。 当前 ProcessStages 会读取当前采购规则;实施时不得让“未配置当前采购规则”导致整个 PDD 详情或关联列表失败。缺少规则应表现为订单行不可创建及明确原因。必要时拆分“基础关联/阶段事实”和“采购资格、价格护栏”计算。

  4. 权限落实到独立 API 和自动化测试。 关联接口应显式加入 GoAuto 权限矩阵:管理员、采购员可读;其他角色不可读。不能只依赖前端隐藏区块。采购创建继续使用既有采购权限。

  5. 规格映射状态由服务端返回明确结果。 如展示映射摘要,应返回例如 mappingStatus: complete|incomplete|not_required 与原因;前端不得解析 specsJSON 自行推断,避免与采购预检分叉。

  6. 查询必须有界且批量化。 详情基础查询和关联订单页查询次数不得随订单行数量线性增长;不得逐行调用阶段计算或 AI Provider。验收记录实际查询次数或执行计划。

设计证据需覆盖

  • “关联订单继续采购”与既有“创建备货采购”的位置、文案和视觉层级;
  • 无关联商品、加载、失败、无可创建订单、部分可创建、混合阶段;
  • 默认待处理与“查看全部”分页;
  • 批量预检失败、部分创建失败及权限不可见状态。

建议补充验收

  • 没有当前采购规则时,详情和关联列表仍能读取,订单行显示不可创建原因;
  • 分页 total/page/pageSize 准确,不按每个虾皮商品各自伪分页;
  • 普通非管理员、非采购员角色不能读取关联订单接口;
  • 默认列表明确区分“可创建采购”和“采购前待处理”;
  • 查询规模固定,不触发 AI,不产生 N+1。

建议先确认统一的多动作选择规范并完成 #162,再实施本工单;#161 涉及新接口、订单数据可见性和详情页两个采购入口,风险更高。

## 2026-08-31 实施前评审补充 结论:现有目标、领域边界和复用既有采购接口的方向合理;进入生产代码前需补齐以下契约与设计细节。 ### 必须纳入实施方案 1. **关联订单行使用独立分页接口,不把全部历史订单嵌入现有详情响应。** 建议: - `GET /pdd-products/:productId` 继续返回商品详情,并增加关联虾皮商品摘要、待处理数量等有界信息; - 新增 `GET /pdd-products/:productId/related-syb-products`,支持 `page`、`pageSize`、`pendingOnly`,可选 `shopeeProductId`; - 默认 `pageSize=20`,最大 100。 原因:一个 PDD 可关联多个虾皮商品,每个虾皮商品可能存在大量历史 SYB 明细;嵌套的逐虾皮分页难以定义统一 total,也会拖慢详情打开。 2. **明确默认展示的处理阶段集合,不使用模糊的“待采购”统称。** 界面建议分为: - 可创建采购:`purchase_ready`; - 采购前待处理:`pdd_pending`、`pdd_collecting`、`pdd_collection_failed`、`color_mapping`; - 已有任务、已完成、待人工核对:默认隐藏,通过“查看全部”进入。 阶段、标签、原因和下一步仍须复用 `processStageFromDataset` / `ProcessStages` 口径,不在 product 域复制判定。 3. **缺少当前采购规则时,关联关系和历史订单仍必须可查看。** 当前 `ProcessStages` 会读取当前采购规则;实施时不得让“未配置当前采购规则”导致整个 PDD 详情或关联列表失败。缺少规则应表现为订单行不可创建及明确原因。必要时拆分“基础关联/阶段事实”和“采购资格、价格护栏”计算。 4. **权限落实到独立 API 和自动化测试。** 关联接口应显式加入 GoAuto 权限矩阵:管理员、采购员可读;其他角色不可读。不能只依赖前端隐藏区块。采购创建继续使用既有采购权限。 5. **规格映射状态由服务端返回明确结果。** 如展示映射摘要,应返回例如 `mappingStatus: complete|incomplete|not_required` 与原因;前端不得解析 `specsJSON` 自行推断,避免与采购预检分叉。 6. **查询必须有界且批量化。** 详情基础查询和关联订单页查询次数不得随订单行数量线性增长;不得逐行调用阶段计算或 AI Provider。验收记录实际查询次数或执行计划。 ### 设计证据需覆盖 - “关联订单继续采购”与既有“创建备货采购”的位置、文案和视觉层级; - 无关联商品、加载、失败、无可创建订单、部分可创建、混合阶段; - 默认待处理与“查看全部”分页; - 批量预检失败、部分创建失败及权限不可见状态。 ### 建议补充验收 - 没有当前采购规则时,详情和关联列表仍能读取,订单行显示不可创建原因; - 分页 total/page/pageSize 准确,不按每个虾皮商品各自伪分页; - 普通非管理员、非采购员角色不能读取关联订单接口; - 默认列表明确区分“可创建采购”和“采购前待处理”; - 查询规模固定,不触发 AI,不产生 N+1。 建议先确认统一的多动作选择规范并完成 #162,再实施本工单;#161 涉及新接口、订单数据可见性和详情页两个采购入口,风险更高。
Author
Owner

2026-08-31 二次评审补充(实施前需并入正文)

现有修订方向合理。为消除 API 与共享阶段实现的剩余歧义,补充以下阻塞级约束:

1. 查询参数改为准确的范围语义

独立订单行接口不使用 pendingOnly。推荐契约:

GET /api/admin/v1/pdd-products/:productId/related-syb-products?scope=actionable|all
  • 默认 scope=actionable;
  • actionable 固定对应 purchase_ready、pdd_pending、pdd_collecting、pdd_collection_failed、color_mapping;
  • scope=all 返回全部处理阶段;
  • 非法 scope 返回参数错误。

原因:默认集合同时包含可创建、进行中、失败和待匹配状态,命名为 pendingOnly 不准确,未来也容易产生兼容歧义。前端不得自行维护该阶段集合。

2. 明确定义虾皮商品映射摘要

详情摘要建议返回:

{
  "mappingStatus": "complete|incomplete|not_required",
  "mappingConfirmedCount": 8,
  "mappingTotalCount": 12,
  "mappingReason": "已确认 8/12 个规格值"
}

判定口径:

  • complete:所有需要映射的虾皮颜色/尺码规格值均有 confirmed 映射,且映射目标仍属于当前 PDD 对应维度的可选规格;
  • incomplete:至少一个需要映射的规格值未映射、仍为 pending,或映射目标已不属于当前 PDD 可选规格;
  • not_required:虾皮商品不存在需要映射的颜色/尺码维度;不得用“某一订单行目标值为空”判定商品整体为 not_required;
  • 该字段是虾皮商品整体摘要,不等同于某一 SYB 订单行的采购资格。订单行是否可采购仍以共享阶段/采购预检结果为准。

3. 共享阶段计算必须在 purchase 包统一重构

不得在 product 包复制 processStageFromDataset、捕获缺少规则错误后另建简化判定,或维护第二套阶段原因。应在 purchase 包提供统一的只读阶段计算入口:

  • 无 AI 调用;
  • 未配置或当前采购规则无效时仍返回基础关联、采集、已有任务等阶段事实;
  • 只有依赖规则与价格护栏才能决定的订单行标记为不可采购,并返回明确原因;
  • SYB 商品列表/预检展示与 PDD 关联订单接口共同调用该入口;
  • 同一订单行在两个页面的 processStage、label、reason、nextAction 必须一致。

4. 明确扁平分页的界面模型

  • 分页接口返回扁平订单行,每行携带所属 shopeeProductId、shopeeItemId、虾皮商品标题;
  • 前端默认查看全部关联虾皮商品,可通过摘要项或筛选控件传 shopeeProductId;
  • 不在统一分页结果上渲染成“每个虾皮商品都拥有完整订单子表”的伪嵌套结构;
  • 允许同一虾皮商品跨页出现,total/page/pageSize 始终针对当前统一过滤条件。

补充验收

  • 请求缺省时等价于 scope=actionable;非法 scope 被拒绝;前端未自行维护阶段集合;
  • mapping 三态和计数按上述定义验证,且商品整体 incomplete 不阻止其中已满足条件的订单行进入 purchase_ready;
  • 无当前采购规则时,SYB 页与 PDD 关联列表仍返回一致的阶段与原因;
  • 代码中只有 purchase 包拥有共享阶段计算,product 包无复制判定;
  • 按全部虾皮商品和指定 shopeeProductId 两种过滤验证分页与 total。
## 2026-08-31 二次评审补充(实施前需并入正文) 现有修订方向合理。为消除 API 与共享阶段实现的剩余歧义,补充以下阻塞级约束: ### 1. 查询参数改为准确的范围语义 独立订单行接口不使用 `pendingOnly`。推荐契约: ```http GET /api/admin/v1/pdd-products/:productId/related-syb-products?scope=actionable|all ``` - 默认 `scope=actionable`; - `actionable` 固定对应 `purchase_ready`、`pdd_pending`、`pdd_collecting`、`pdd_collection_failed`、`color_mapping`; - `scope=all` 返回全部处理阶段; - 非法 scope 返回参数错误。 原因:默认集合同时包含可创建、进行中、失败和待匹配状态,命名为 `pendingOnly` 不准确,未来也容易产生兼容歧义。前端不得自行维护该阶段集合。 ### 2. 明确定义虾皮商品映射摘要 详情摘要建议返回: ```json { "mappingStatus": "complete|incomplete|not_required", "mappingConfirmedCount": 8, "mappingTotalCount": 12, "mappingReason": "已确认 8/12 个规格值" } ``` 判定口径: - `complete`:所有需要映射的虾皮颜色/尺码规格值均有 `confirmed` 映射,且映射目标仍属于当前 PDD 对应维度的可选规格; - `incomplete`:至少一个需要映射的规格值未映射、仍为 `pending`,或映射目标已不属于当前 PDD 可选规格; - `not_required`:虾皮商品不存在需要映射的颜色/尺码维度;不得用“某一订单行目标值为空”判定商品整体为 `not_required`; - 该字段是**虾皮商品整体摘要**,不等同于某一 SYB 订单行的采购资格。订单行是否可采购仍以共享阶段/采购预检结果为准。 ### 3. 共享阶段计算必须在 purchase 包统一重构 不得在 product 包复制 `processStageFromDataset`、捕获缺少规则错误后另建简化判定,或维护第二套阶段原因。应在 `purchase` 包提供统一的只读阶段计算入口: - 无 AI 调用; - 未配置或当前采购规则无效时仍返回基础关联、采集、已有任务等阶段事实; - 只有依赖规则与价格护栏才能决定的订单行标记为不可采购,并返回明确原因; - SYB 商品列表/预检展示与 PDD 关联订单接口共同调用该入口; - 同一订单行在两个页面的 `processStage`、label、reason、nextAction 必须一致。 ### 4. 明确扁平分页的界面模型 - 分页接口返回扁平订单行,每行携带所属 `shopeeProductId`、`shopeeItemId`、虾皮商品标题; - 前端默认查看全部关联虾皮商品,可通过摘要项或筛选控件传 `shopeeProductId`; - 不在统一分页结果上渲染成“每个虾皮商品都拥有完整订单子表”的伪嵌套结构; - 允许同一虾皮商品跨页出现,`total/page/pageSize` 始终针对当前统一过滤条件。 ### 补充验收 - 请求缺省时等价于 `scope=actionable`;非法 scope 被拒绝;前端未自行维护阶段集合; - mapping 三态和计数按上述定义验证,且商品整体 incomplete 不阻止其中已满足条件的订单行进入 `purchase_ready`; - 无当前采购规则时,SYB 页与 PDD 关联列表仍返回一致的阶段与原因; - 代码中只有 purchase 包拥有共享阶段计算,product 包无复制判定; - 按全部虾皮商品和指定 `shopeeProductId` 两种过滤验证分页与 total。
Author
Owner

实施完成,待验收

设计与可见性:用户于 2026-08-31 确认低保真设计,以及 SYB 订单号、目标颜色/尺码、数量和采购任务状态仅对 admin/purchaser 开放。

实现:

  • PDD 详情增加关联虾皮商品摘要,返回 specMappingStatus 三态、确认/总数及待处理订单数;详情不嵌订单明细。
  • 新增 GET /api/admin/v1/pdd-products/:productId/related-syb-products,支持统一扁平分页、scope=actionable|all、可选 shopeeProductId,默认 20、最大 100。
  • purchase 包统一只读阶段入口并对当前采购规则缺失/无效容错;SYB 批量预检和 PDD 关联订单共享阶段口径,查询不调用 AI。
  • 权限矩阵显式加入采购员只读策略;其他角色不获得该策略。
  • PDD 详情新增“关联商品”区块;“创建备货采购”保留顶部,“关联订单继续采购”位于订单区块。继续采购复用既有 batch-preview / batch。
  • 未新增或修改数据库表、外键;Android Agent 未修改。

验证:

  • go test ./app/goauto/product ./app/goauto/purchase ./app/goauto/access:通过。
  • pnpm exec eslint src/views/goauto/pdd-products/index.vue src/api/goauto/pdd-products.js:通过。
  • pnpm build:prod:通过;仅有仓库既有 CSS 伪类和 chunk-size 警告。
  • PLAYWRIGHT_TEST_BASE_URL=http://localhost:9530 PLAYWRIGHT_DISABLE_VIDEO=1 pnpm exec playwright test tests/e2e/pdd-related-products.spec.ts:1/1 通过,覆盖扁平关联订单展示、不可采购行禁选、仅向预检提交 purchase_ready 子集及入口安全文案。
  • 未执行真机:不涉及 Agent API、任务契约或设备执行。

长期文档:

  • Architecture-and-Code-Map revision de7e400503d6。
  • Business-Rules-and-Glossary revision 4ba1787539a8。
  • 已运行一次受影响页面 sync 和一次 sync --check,镜像一致。
  • Android Agent API 契约无变化,未更新。

提交:09d9d22,已推送 origin/main。

说明:工作区中与 #161 无关的 Android、SYB 文档及未跟踪文件均保留,未纳入提交。状态:待用户验收。

## 实施完成,待验收 设计与可见性:用户于 2026-08-31 确认低保真设计,以及 SYB 订单号、目标颜色/尺码、数量和采购任务状态仅对 admin/purchaser 开放。 实现: - PDD 详情增加关联虾皮商品摘要,返回 `specMappingStatus` 三态、确认/总数及待处理订单数;详情不嵌订单明细。 - 新增 `GET /api/admin/v1/pdd-products/:productId/related-syb-products`,支持统一扁平分页、`scope=actionable|all`、可选 `shopeeProductId`,默认 20、最大 100。 - purchase 包统一只读阶段入口并对当前采购规则缺失/无效容错;SYB 批量预检和 PDD 关联订单共享阶段口径,查询不调用 AI。 - 权限矩阵显式加入采购员只读策略;其他角色不获得该策略。 - PDD 详情新增“关联商品”区块;“创建备货采购”保留顶部,“关联订单继续采购”位于订单区块。继续采购复用既有 batch-preview / batch。 - 未新增或修改数据库表、外键;Android Agent 未修改。 验证: - `go test ./app/goauto/product ./app/goauto/purchase ./app/goauto/access`:通过。 - `pnpm exec eslint src/views/goauto/pdd-products/index.vue src/api/goauto/pdd-products.js`:通过。 - `pnpm build:prod`:通过;仅有仓库既有 CSS 伪类和 chunk-size 警告。 - `PLAYWRIGHT_TEST_BASE_URL=http://localhost:9530 PLAYWRIGHT_DISABLE_VIDEO=1 pnpm exec playwright test tests/e2e/pdd-related-products.spec.ts`:1/1 通过,覆盖扁平关联订单展示、不可采购行禁选、仅向预检提交 purchase_ready 子集及入口安全文案。 - 未执行真机:不涉及 Agent API、任务契约或设备执行。 长期文档: - `Architecture-and-Code-Map` revision `de7e400503d6`。 - `Business-Rules-and-Glossary` revision `4ba1787539a8`。 - 已运行一次受影响页面 sync 和一次 sync --check,镜像一致。 - Android Agent API 契约无变化,未更新。 提交:`09d9d22`,已推送 `origin/main`。 说明:工作区中与 #161 无关的 Android、SYB 文档及未跟踪文件均保留,未纳入提交。状态:待用户验收。
Author
Owner

重启后采购员新接口 403 修复

现象:使用 purchaser 打开 PDD 商品详情时,新关联接口返回“没有该接口访问权限”。

根因:启动时 Casbin Enforcer 先从数据库加载旧策略,随后 ReconcilePurchaserPermissions 才写入新策略;对账完成后未刷新内存 Enforcer,因此首次升级重启仍使用旧权限,通常第二次重启才会生效。权限矩阵条目本身正确。

修复:

  • cmd/api.run 在所有数据库采购员权限对账成功后、注册路由和监听端口前,对 sdk.Runtime.GetCasbin() 中每个 Enforcer 调用 LoadPolicy()。
  • 任一 Enforcer 为空或刷新失败时启动失败关闭,不以旧内存策略继续提供服务。
  • 新增启动策略刷新回归测试,覆盖全部 Enforcer 刷新和 nil/adapter 错误关闭。

验证:go test ./cmd/api ./app/goauto/access ./app/goauto/product ./app/goauto/purchase 全部通过。

提交:d90bf66,已推送 origin/main。部署该提交后只需正常重启一次即可同步数据库与内存 Casbin 策略。

## 重启后采购员新接口 403 修复 现象:使用 purchaser 打开 PDD 商品详情时,新关联接口返回“没有该接口访问权限”。 根因:启动时 Casbin Enforcer 先从数据库加载旧策略,随后 `ReconcilePurchaserPermissions` 才写入新策略;对账完成后未刷新内存 Enforcer,因此首次升级重启仍使用旧权限,通常第二次重启才会生效。权限矩阵条目本身正确。 修复: - `cmd/api.run` 在所有数据库采购员权限对账成功后、注册路由和监听端口前,对 `sdk.Runtime.GetCasbin()` 中每个 Enforcer 调用 `LoadPolicy()`。 - 任一 Enforcer 为空或刷新失败时启动失败关闭,不以旧内存策略继续提供服务。 - 新增启动策略刷新回归测试,覆盖全部 Enforcer 刷新和 nil/adapter 错误关闭。 验证:`go test ./cmd/api ./app/goauto/access ./app/goauto/product ./app/goauto/purchase` 全部通过。 提交:`d90bf66`,已推送 `origin/main`。部署该提交后只需正常重启一次即可同步数据库与内存 Casbin 策略。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#161