AGENTS.md
5d85625
server/app/goauto/task/router.go:33
admin.POST("/batch", handler.AdminBatchCreate)
task/admin_handler.go:63
service.BatchCreate
web/src/views/goauto/pdd-products/index.vue:14
@selection-change
:34-42
:37
index.vue:17
<el-table-column type="selection" :selectable="rowSelectable">
:6
:14
:33-49
:52
:28
nextAction
:210
open_pdd
:207
rowSelectable(row) { return this.canPurchase && this.purchaseReady(row).eligible }
pdd_pending
eligible
processStage
previewPurchaseTasks
purchase/batch.go:57-59
:101
processStageFromDataset
canPurchase
index.vue:213
:16
task/admin_service.go:144
len(request.PDDProductIDs) > 100
:147-153
pddProductId
purchase/batch.go:40
:145
pdd_collection_failed
purchase_ready
models/schema.go:413-416
pending
running
SYB 商品页以「订单行」为中心,每行处理阶段不同,因此做成了「逐行给出下一步动作」的形态——这一形态本身合理。但处于「PDD 待采集」阶段的行无法一起处理:页面已有的多选与批量入口只服务于采购(事实 3、4),采集仍只能逐行跳转。
新订单批量同步进来时,往往几十行同时停留在「PDD 待采集」,只能逐行点击跳转,操作成本与实际业务量不匹配。而批量能力在服务端与 PDD 商品页均已具备,缺的只是 SYB 商品页到该能力的入口。
rowSelectable
isPurchaseCandidate(row) { const ready = this.purchaseReady(row) return ready.processStage === 'purchase_ready' && ready.eligible } isCollectionCandidate(row) { const ready = this.purchaseReady(row) return ready.processStage === 'pdd_pending' && Number.isInteger(ready.pddProductId) && ready.pddProductId > 0 } rowSelectable(row) { return this.isPurchaseCandidate(row) || this.isCollectionCandidate(row) }
ready.eligible === true && ready.processStage === 'purchase_ready'
web/tests/e2e/syb-network-retry.spec.ts:53
{ sybProductId: 73, eligible: true, minUnitPriceCent: 100, maxUnitPriceCent: 300 }
:98
{ sybProductId: 75, eligible: true }
processStageLabel
processStageReason
syb-product-layout.spec.ts:13
两个批量动作只能提交各自的候选子集(阻塞级):当前 openPurchaseBatch()(index.vue:260)直接把全部 selectedProducts 的 id 送去预检,submitPurchaseBatch()(:279)再原样提交 purchaseDialog.ids。放宽选择后必须改为只提交 isPurchaseCandidate 的行;采集动作只提取 isCollectionCandidate 行的 pddProductId。不得把不适用的行发到服务端后依赖逐项跳过。
openPurchaseBatch()
index.vue:260
selectedProducts
submitPurchaseBatch()
:279
purchaseDialog.ids
isPurchaseCandidate
isCollectionCandidate
同步修正 :16 的表头提示(事实 6):现文案只描述采购,加入采集动作后即为错误说明,须一并改写为同时覆盖两种批量动作。
统一混选语义:允许混选、按动作分流、数量透明(取代早期「混选禁用」的表述,二者互相冲突,以本项为准):
创建采购(N)
创建 PDD 采集(N)
按 PDD 商品去重:选中的多行可能指向同一个 PDD 商品(事实 10)。提交前必须按 PDD 商品去重,否则会撞上「同一商品不能同时存在多个 pending/running 采集任务」的既有约束(事实 11),产生大量看似失败实为重复提交的报错。去重结果需在确认对话框中体现(例如「已选择 12 行,去重后 8 个商品」)。去重键直接取预检项已返回的 pddProductId(事实 8)。服务端 :147-153 亦会拒绝重复 ID,前端去重只是避免无意义报错。
上限按去重后的 PDD 商品数判断,顺序不可颠倒:筛选 pdd_pending → 提取有效 pddProductId → 去重 → 判断是否超过 100 → 打开确认框/发送请求。不能按原始 SYB 行数判断。确认框展示「选中 M 行,去重后 N 个 PDD 商品」。
处理异步 readiness 与选择状态的时序;readiness 只禁用动作,不阻塞列表浏览:rowSelectable 依赖 loadPurchaseReadiness(index.vue:213)的异步结果,而该请求本就是列表加载后独立发起的,须保持「列表先展示、准备状态独立加载」的既有体验。
loadPurchaseReadiness
nextAction: 'refresh'
调用既有的 POST /api/admin/v1/collection-tasks/batch 接口,不新写任何创建逻辑。
POST /api/admin/v1/collection-tasks/batch
复用 PDD 商品页的批量对话框交互:优先复用而非复制第二套实现,可提取的最小共享部分为规则与设备加载、确认/结果对话框、batchCreateCollectionTasks 提交与逐条结果展示;页面各自保留候选集合计算与完成后的刷新方式,不抽象整个表格选择模型。若抽取成本超出本工单范围,允许各自实现并在工单记录原因。原有要求不变:选择采集规则、可选指定 Android 设备、逐条展示创建结果、部分失败不影响其他。视觉与文案与 PDD 商品页保持一致,不另设一套。
batchCreateCollectionTasks
批量完成后刷新列表与处理阶段,使已创建任务的行更新为对应阶段。
服务端返回的失败原因原样展示,不在前端二次解释或改写。
确认并记录权限耦合(事实 5):processStage 来自采购预检接口且仅在 canPurchase 时加载,因此按当前实现,批量采集入口实际被采购权限门控。本期接受该耦合并在工单与界面说明中写明「本入口对采购员开放」;不通过新增服务端接口解耦——若后续确需对非采购角色开放,另建工单,届时「服务端零改动」不再成立。
POST /collection-tasks/batch
Common-Changes
待实施(界面需先取得设计证据)。
syb-network-retry.spec.ts:53
结论:修订后的根因和“服务端零改动”范围合理;实施前需统一混选语义并补齐浏览器行为验证。
两个批量动作只能提交各自候选子集。 当前 openPurchaseBatch() 直接提交全部 selectedProducts;放宽选择后必须改为只提交 isPurchaseCandidate 行。采集动作只提取 isCollectionCandidate 行的 pddProductId。不得把不适用行发到服务端后依赖逐项跳过。
统一混选语义:允许混选,按动作分流,数量透明。 删除/修正现有“混选被禁用”表述,因为它与“两个按钮各自只作用于自己的子集”冲突。建议按钮:
采集上限按去重后的 PDD 商品数判断。 顺序必须为:筛选 pdd_pending → 提取有效 pddProductId → 去重 → 判断是否超过 100 → 打开确认/发送请求。不能按原始 SYB 行数判断。确认框展示“选中 M 行,去重后 N 个 PDD 商品”。
处理异步 readiness 的选择状态。 loadPurchaseReadiness 完成后需要清除或校正已失效选择,并确保表格 selectable 状态刷新;readiness 加载期间禁止选择。查询、分页、阶段刷新和批量完成后均清空选择,避免旧选择提交。
复用批量采集对话框逻辑,避免复制第二套实现。 可提取最小共享组件/逻辑:规则与设备加载、确认/结果对话框、batchCreateCollectionTasks 提交、逐条结果展示。页面各自保留候选集合计算和完成后的刷新方式;不抽象整个表格选择模型。
现有构建不足以证明多动作选择正确。至少覆盖:
建议先实施本工单验证统一选择语义,再实施 #161。
现有方案已可实施;补充以下防歧义约束:
采购候选必须严格满足:
浏览器测试和 mock fixture 必须补齐真实 API 字段,至少包括 processStage、processStageLabel、processStageReason 与本场景需要的 pddProductId。现有仅返回 eligible: true 的 fixture 需要更新。不得为兼容不完整测试数据而放宽生产判断。
eligible: true
保持现有“列表先展示、准备状态独立加载”的体验:
purchase_ready 并非完全不在范围。准确边界为:
本工单不新增或改变 purchase_ready 的采购业务能力;仅为支持混选,调整其前端候选过滤、独立计数、按钮启用状态和提交子集,并完成既有采购批量回归。
设计证据:2026-08-31 用户确认本工单低保真交互。采用一套选择集、采购与采集按动作分流、按钮独立计数;采集确认框展示 SYB 行数与去重后的 PDD 商品数。
实现:
/api/admin/v1/collection-tasks/batch
验证:
pnpm build:prod
pnpm exec eslint src/views/goauto/syb-products/index.vue
PLAYWRIGHT_TEST_BASE_URL=http://localhost:9530 PLAYWRIGHT_DISABLE_VIDEO=1 pnpm exec playwright test tests/e2e/syb-product-layout.spec.ts tests/e2e/syb-network-retry.spec.ts
文档影响:无长期文档影响。未改变 API、数据结构、业务规则或权限边界,仅新增现有管理端能力入口,因此跳过 Wiki 更新与同步。
提交:4e05048,已推送 origin/main。
4e05048
origin/main
状态:待用户验收。
评审结论合理,已接受并补齐测试证据;生产代码无需修改。
补充内容:
提交:5270390,已推送 origin/main。
5270390
#162 继续保持待用户验收;未关闭工单。
No dependencies set.
The note is not visible to the blocked user.
所属与来源
AGENTS.md「Gitea 交互与工单最小读取」记录回退原因。当前事实(行号按提交
5d85625复核,2026-08-31 修正)server/app/goauto/task/router.go:33注册admin.POST("/batch", handler.AdminBatchCreate),对应task/admin_handler.go:63→service.BatchCreate。web/src/views/goauto/pdd-products/index.vue:14的表格带@selection-change,:34-42为「批量创建采集任务」对话框,可选择采集规则与 Android 设备,并逐条展示结果(:37明确「部分商品失败不会影响其他商品」)。index.vue:17已有<el-table-column type="selection" :selectable="rowSelectable">;:6已有「已选择 N 条」计数,:14已有「创建采购」批量按钮;:33-49为批量创建采购确认对话框,:52为批量结果对话框。:28每行按处理阶段渲染nextAction按钮,:210映射中open_pdd为「查看 PDD 商品」,需跳转到 PDD 商品详情后再采集。:207rowSelectable(row) { return this.canPurchase && this.purchaseReady(row).eligible }。pdd_pending的行eligible必为 false,当前根本无法被勾选。processStage由采购预检接口previewPurchaseTasks返回(purchase/batch.go:57-59,判定在:101的processStageFromDataset),且仅在canPurchase为真时加载(index.vue:213)。:16的表头提示当前写死为「表头全选只选择当前页中可以创建采购任务的商品」。task/admin_service.go:144校验len(request.PDDProductIDs) > 100即拒绝,同时:147-153已拒绝重复商品 ID。pddProductId(purchase/batch.go:40),前端可直接用它作为去重键,无需额外接口。:145),其中pdd_pending(PDD 待采集)、pdd_collection_failed(PDD 采集失败)、purchase_ready(可创建采购)在业务上天然可批量。models/schema.go:413-416:多个虾皮商品可共用同一个 PDD 商品)。pending或running采集任务(AGENTS.md第 1 节)。问题
SYB 商品页以「订单行」为中心,每行处理阶段不同,因此做成了「逐行给出下一步动作」的形态——这一形态本身合理。但处于「PDD 待采集」阶段的行无法一起处理:页面已有的多选与批量入口只服务于采购(事实 3、4),采集仍只能逐行跳转。
新订单批量同步进来时,往往几十行同时停留在「PDD 待采集」,只能逐行点击跳转,操作成本与实际业务量不匹配。而批量能力在服务端与 PDD 商品页均已具备,缺的只是 SYB 商品页到该能力的入口。
目标
非目标
pdd_pending一种处理阶段;pdd_collection_failed(批量重新采集)不在本工单范围,待本期效果验证后再评估。purchase_ready的准确边界(原表述「不在范围」不准确):本工单不新增或改变purchase_ready的采购业务能力;仅为支持混选,调整其前端候选过滤、独立计数、按钮启用状态与提交子集,并完成既有采购批量回归。nextAction的既有行为。实施方案
rowSelectable当前只放行采购可用的行。需改为「一套选择集、两个批量动作、各自判定资格」:rowSelectable放宽到「采购可用 或 处于pdd_pending」;eligible当成通用选择资格,实现等价于:ready.eligible === true && ready.processStage === 'purchase_ready',比现有rowSelectable多了一层阶段判断。web/tests/e2e/syb-network-retry.spec.ts:53({ sybProductId: 73, eligible: true, minUnitPriceCent: 100, maxUnitPriceCent: 300 })与:98({ sybProductId: 75, eligible: true })都没有processStage,严格判定下这些行将不可勾选、用例会失败。所有批量选择相关 fixture 至少补齐processStage、processStageLabel、processStageReason与本场景需要的pddProductId,参照syb-product-layout.spec.ts:13的既有写法。两个批量动作只能提交各自的候选子集(阻塞级):当前
openPurchaseBatch()(index.vue:260)直接把全部selectedProducts的 id 送去预检,submitPurchaseBatch()(:279)再原样提交purchaseDialog.ids。放宽选择后必须改为只提交isPurchaseCandidate的行;采集动作只提取isCollectionCandidate行的pddProductId。不得把不适用的行发到服务端后依赖逐项跳过。同步修正
:16的表头提示(事实 6):现文案只描述采购,加入采集动作后即为错误说明,须一并改写为同时覆盖两种批量动作。统一混选语义:允许混选、按动作分流、数量透明(取代早期「混选禁用」的表述,二者互相冲突,以本项为准):
创建采购(N)与创建 PDD 采集(N),各自只按自己的候选数计数与启用;本期采集侧只对
pdd_pending开放。按 PDD 商品去重:选中的多行可能指向同一个 PDD 商品(事实 10)。提交前必须按 PDD 商品去重,否则会撞上「同一商品不能同时存在多个 pending/running 采集任务」的既有约束(事实 11),产生大量看似失败实为重复提交的报错。去重结果需在确认对话框中体现(例如「已选择 12 行,去重后 8 个商品」)。去重键直接取预检项已返回的
pddProductId(事实 8)。服务端:147-153亦会拒绝重复 ID,前端去重只是避免无意义报错。上限按去重后的 PDD 商品数判断,顺序不可颠倒:筛选
pdd_pending→ 提取有效pddProductId→ 去重 → 判断是否超过 100 → 打开确认框/发送请求。不能按原始 SYB 行数判断。确认框展示「选中 M 行,去重后 N 个 PDD 商品」。处理异步 readiness 与选择状态的时序;readiness 只禁用动作,不阻塞列表浏览:
rowSelectable依赖loadPurchaseReadiness(index.vue:213)的异步结果,而该请求本就是列表加载后独立发起的,须保持「列表先展示、准备状态独立加载」的既有体验。nextAction: 'refresh');调用既有的
POST /api/admin/v1/collection-tasks/batch接口,不新写任何创建逻辑。复用 PDD 商品页的批量对话框交互:优先复用而非复制第二套实现,可提取的最小共享部分为规则与设备加载、确认/结果对话框、
batchCreateCollectionTasks提交与逐条结果展示;页面各自保留候选集合计算与完成后的刷新方式,不抽象整个表格选择模型。若抽取成本超出本工单范围,允许各自实现并在工单记录原因。原有要求不变:选择采集规则、可选指定 Android 设备、逐条展示创建结果、部分失败不影响其他。视觉与文案与 PDD 商品页保持一致,不另设一套。批量完成后刷新列表与处理阶段,使已创建任务的行更新为对应阶段。
服务端返回的失败原因原样展示,不在前端二次解释或改写。
确认并记录权限耦合(事实 5):
processStage来自采购预检接口且仅在canPurchase时加载,因此按当前实现,批量采集入口实际被采购权限门控。本期接受该耦合并在工单与界面说明中写明「本入口对采购员开放」;不通过新增服务端接口解耦——若后续确需对非采购角色开放,另建工单,届时「服务端零改动」不再成立。安全边界
canPurchase门控(事实 5、方案第 9 项),这是本期已知并接受的耦合,须在工单回写。验收标准
pdd_pending的行可以被勾选(放宽rowSelectable后),工具栏出现「批量创建采集任务」入口。pdd_pending行时,两个按钮各自显示正确数量,各自只作用于自己那部分,界面上可见数量差异,无静默丢弃。pdd_pending对应的去重后 PDD ID(以浏览器网络面板佐证)。:16表头提示已改写为同时覆盖两种批量动作,与实际行为一致。pddProductId去重,确认对话框展示去重后的数量。POST /collection-tasks/batch(代码检查佐证未新增创建实现)。nextAction的既有行为未发生变化。验证方式
purchase_ready与pdd_pending的行均可勾选(fixture 已补齐processStage等字段);pdd_pending对应的去重后 PDD ID;nextAction行为保持不变。依赖、并行与风险
task/admin_service.go:144),前端按该数字给出明确提示。rowSelectable会改变既有采购批量的可选行范围,可能让采购按钮收到不适用的行。缓解:方案第 1 项的按动作分别判定资格,并在验收中回归采购批量。processStage的既有 E2E fixture 失败。缓解:实施第一步先补齐 fixture,再改生产判断;禁止反向放宽判定。文档影响
Common-Changes中记录了采集任务的创建路径,则一并补充该入口。状态
待实施(界面需先取得设计证据)。
修订记录
processStage的既有 E2E fixture(syb-network-retry.spec.ts:53、:98)、readiness 加载只禁用动作而不阻塞列表浏览(修正上一版「加载期间禁止选择」的过头表述)、修正非目标中purchase_ready的边界表述;相应补充验收、浏览器验证项与风险。rowSelectable放宽与采购权限耦合的处理,并相应调整验收、风险与并行说明。2026-08-31 实施前评审补充
结论:修订后的根因和“服务端零改动”范围合理;实施前需统一混选语义并补齐浏览器行为验证。
必须纳入实施方案
eligible当成通用选择资格。 建议实现等价于:两个批量动作只能提交各自候选子集。 当前
openPurchaseBatch()直接提交全部selectedProducts;放宽选择后必须改为只提交isPurchaseCandidate行。采集动作只提取isCollectionCandidate行的pddProductId。不得把不适用行发到服务端后依赖逐项跳过。统一混选语义:允许混选,按动作分流,数量透明。 删除/修正现有“混选被禁用”表述,因为它与“两个按钮各自只作用于自己的子集”冲突。建议按钮:
创建采购(N)创建 PDD 采集(N)确认框明确显示“选中 M 条 SYB 明细,其中 N 条适用于当前动作”。不适用项不进入请求。
采集上限按去重后的 PDD 商品数判断。 顺序必须为:筛选
pdd_pending→ 提取有效pddProductId→ 去重 → 判断是否超过 100 → 打开确认/发送请求。不能按原始 SYB 行数判断。确认框展示“选中 M 行,去重后 N 个 PDD 商品”。处理异步 readiness 的选择状态。
loadPurchaseReadiness完成后需要清除或校正已失效选择,并确保表格 selectable 状态刷新;readiness 加载期间禁止选择。查询、分页、阶段刷新和批量完成后均清空选择,避免旧选择提交。复用批量采集对话框逻辑,避免复制第二套实现。 可提取最小共享组件/逻辑:规则与设备加载、确认/结果对话框、
batchCreateCollectionTasks提交、逐条结果展示。页面各自保留候选集合计算和完成后的刷新方式;不抽象整个表格选择模型。必须增加浏览器行为验证
现有构建不足以证明多动作选择正确。至少覆盖:
purchase_ready与pdd_pending均可选择;pdd_pending对应的去重 PDD ID;nextAction行为保持不变。设计证据需覆盖
建议先实施本工单验证统一选择语义,再实施 #161。
2026-08-31 二次评审补充(实施前需并入正文)
现有方案已可实施;补充以下防歧义约束:
1. 生产资格判断使用完整预检契约
采购候选必须严格满足:
浏览器测试和 mock fixture 必须补齐真实 API 字段,至少包括
processStage、processStageLabel、processStageReason与本场景需要的pddProductId。现有仅返回eligible: true的 fixture 需要更新。不得为兼容不完整测试数据而放宽生产判断。2. readiness 加载只禁用动作,不阻塞列表浏览
保持现有“列表先展示、准备状态独立加载”的体验:
3. 修正非目标中 purchase_ready 的表述
purchase_ready并非完全不在范围。准确边界为:补充验收
实施完成,待验收
设计证据:2026-08-31 用户确认本工单低保真交互。采用一套选择集、采购与采集按动作分流、按钮独立计数;采集确认框展示 SYB 行数与去重后的 PDD 商品数。
实现:
purchase_ready与pdd_pending;资格严格按完整processStage契约判断。创建采购(N)只提交采购候选 SYB ID;创建 PDD 采集(N)只提交pdd_pending对应的去重 PDD ID。/api/admin/v1/collection-tasks/batch、采集规则、设备选择和逐项结果结构;失败原因原样展示。验证:
pnpm build:prod:通过。构建仍输出仓库既有 CSS 伪类和 chunk-size 警告,无构建错误。pnpm exec eslint src/views/goauto/syb-products/index.vue:通过。PLAYWRIGHT_TEST_BASE_URL=http://localhost:9530 PLAYWRIGHT_DISABLE_VIDEO=1 pnpm exec playwright test tests/e2e/syb-product-layout.spec.ts tests/e2e/syb-network-retry.spec.ts:11/11 通过。覆盖混选、动作子集、PDD 去重、部分失败原文、readiness 延迟/失败、旧预检不覆盖新查询及逐行下一步回归。文档影响:无长期文档影响。未改变 API、数据结构、业务规则或权限边界,仅新增现有管理端能力入口,因此跳过 Wiki 更新与同步。
提交:
4e05048,已推送origin/main。状态:待用户验收。
Claude Code 评审闭环
评审结论合理,已接受并补齐测试证据;生产代码无需修改。
补充内容:
验证:
PLAYWRIGHT_TEST_BASE_URL=http://localhost:9530 PLAYWRIGHT_DISABLE_VIDEO=1 pnpm exec playwright test tests/e2e/syb-product-layout.spec.ts tests/e2e/syb-network-retry.spec.ts提交:
5270390,已推送origin/main。#162 继续保持待用户验收;未关闭工单。