feat(server/web): 图搜自动关联标记与人工覆盖清除 #279

Open
opened 2026-09-14 10:17:48 +08:00 by ila · 2 comments
Owner

所属 MVP:#275 | 来源:用户 2026-09-14 需求讨论(原话摘要见 #275)

目标

图搜自动建立的 shopee↔PDD 关联带「图搜」标注;采购员手动改过关联后标注自动消失;可按标注筛选待核关联。

当前事实

  • 关联存放在 shopee_product.pdd_product_id(可空、非唯一)。
  • 「关联 PDD」入口已存在于 web/src/views/goauto/syb-products/index.vue 与 shopee-products/index.vue。

前置依赖

#278。

方案

  1. shopee_product 新增标注字段(布尔),默认 false。
  2. 图搜建立关联时置 true;采购员手动关联或手动确认时置 false。标注是"没人核过"的意思,人碰过一次就消失。
  3. Web:虾皮商品页与 SYB 商品页的关联列旁显示标记;新增「只看图搜关联」筛选。
  4. Agent:采集任务列表行显示来源标记,让采购员在手机上也能看出这条的 PDD 商品是机器找的。
  5. 采购侧可见(2026-09-14 追加):把标注带到采购发起视图,只显示、不拦截。

为什么采购侧也要显示

现有链路里,AI 规格匹配发生在采购时(purchase/lifecycle.go、purchase/batch.go),采集完成不触发任何匹配——图搜任务与现有临时任务在这一点行为一致,本 MVP 不改变。

但由此产生一条无声路径:

图搜取第一个结果(可能错) -> 建立关联(带标注)
  -> 采购时 AI 在这个商品上匹配规格 -> 匹配成功 -> 创建 PDD 订单

商品若搜错,AI 依然会匹配成功(同款商品的颜色尺码当然对得上)。标注只显示在虾皮/SYB 商品页,采购流程里看不见,于是一个没人核过的关联会一路走到创建订单——而创建订单是不可逆操作。

标注的意义本就是"提醒人这个没核过",不送到该看见的人眼前等于没有提醒。

实现位置

purchase.BatchPreviewItem 已经携带 PDDProductID / PDDGoodsID / PDDTitle,在此追加一个布尔字段即可把标注带到批量采购预览与确认视图;采购任务列表同样显示。

明确不做

  • 不拦截。带标注的关联仍可直接采购。硬拦截会把本功能省下的时间还回去,违背需求初衷。
  • 不在采集完成时自动触发 AI 规格匹配。批量图搜一次可能几百个,逐个调用 AI provider 的成本与限流不可控,且其中相当一部分商品并不会真的去采购;按需匹配(采购时)比预先匹配更省。

设计证据

现有界面的小范围调整(一个标记 + 一个筛选)。需提供标注截图或明确复用现有规范后再实施。

验收

  • 图搜建立的关联带标注。
  • 手动改关联后标注消失。
  • 筛选能列出全部未核关联。
  • Agent 采集列表能区分图搜来源。
  • 既有手动关联的历史数据不带标注,行为不变。
  • 批量采购预览与确认视图能看到该关联是图搜建立、未经人工核对。
  • 采购任务列表能看到同一标注。
  • 带标注的关联不被拦截,采购流程与现状一致。

验证

Go 单元测试 + Web 构建 + .\scripts erify.ps1 -Component all。

风险

  • 新增字段需迁移(加列,风险低于 check 约束变更,仍需按迁移流程处理)。
  • 本工单只读地触及采购视图(新增一个展示字段),不改变采购的任何门禁、状态机或下单条件。实施时必须确认既有采购行为逐字节不变,并有回归测试证明。

可能的调整

本工单跨 server / web / android 三个交付单元,且现在还覆盖采购侧的只读显示,偏胖。若实施时验收困难,可把 Agent 列表标记拆到 #277,或把采购侧显示单独拆一单。

文档影响

数据结构与业务规则变化,需更新对应 Wiki 页面。

> 所属 MVP:#275 | 来源:用户 2026-09-14 需求讨论(原话摘要见 #275) ## 目标 图搜自动建立的 shopee↔PDD 关联带「图搜」标注;采购员手动改过关联后标注自动消失;可按标注筛选待核关联。 ## 当前事实 - 关联存放在 `shopee_product.pdd_product_id`(可空、非唯一)。 - 「关联 PDD」入口已存在于 `web/src/views/goauto/syb-products/index.vue` 与 `shopee-products/index.vue`。 ## 前置依赖 #278。 ## 方案 1. `shopee_product` 新增标注字段(布尔),默认 false。 2. 图搜建立关联时置 true;采购员手动关联或手动确认时置 false。**标注是"没人核过"的意思,人碰过一次就消失。** 3. Web:虾皮商品页与 SYB 商品页的关联列旁显示标记;新增「只看图搜关联」筛选。 4. Agent:采集任务列表行显示来源标记,让采购员在手机上也能看出这条的 PDD 商品是机器找的。 5. **采购侧可见(2026-09-14 追加)**:把标注带到采购发起视图,**只显示、不拦截**。 ### 为什么采购侧也要显示 现有链路里,AI 规格匹配发生在采购时(`purchase/lifecycle.go`、`purchase/batch.go`),采集完成**不触发**任何匹配——图搜任务与现有临时任务在这一点行为一致,本 MVP 不改变。 但由此产生一条无声路径: ``` 图搜取第一个结果(可能错) -> 建立关联(带标注) -> 采购时 AI 在这个商品上匹配规格 -> 匹配成功 -> 创建 PDD 订单 ``` 商品若搜错,AI 依然会匹配成功(同款商品的颜色尺码当然对得上)。标注只显示在虾皮/SYB 商品页,采购流程里看不见,于是一个没人核过的关联会一路走到创建订单——而创建订单是不可逆操作。 标注的意义本就是"提醒人这个没核过",不送到该看见的人眼前等于没有提醒。 ### 实现位置 `purchase.BatchPreviewItem` 已经携带 `PDDProductID` / `PDDGoodsID` / `PDDTitle`,在此追加一个布尔字段即可把标注带到批量采购预览与确认视图;采购任务列表同样显示。 ### 明确不做 - **不拦截**。带标注的关联仍可直接采购。硬拦截会把本功能省下的时间还回去,违背需求初衷。 - 不在采集完成时自动触发 AI 规格匹配。批量图搜一次可能几百个,逐个调用 AI provider 的成本与限流不可控,且其中相当一部分商品并不会真的去采购;按需匹配(采购时)比预先匹配更省。 ## 设计证据 现有界面的小范围调整(一个标记 + 一个筛选)。**需提供标注截图或明确复用现有规范后再实施。** ## 验收 - [ ] 图搜建立的关联带标注。 - [ ] 手动改关联后标注消失。 - [ ] 筛选能列出全部未核关联。 - [ ] Agent 采集列表能区分图搜来源。 - [ ] 既有手动关联的历史数据不带标注,行为不变。 - [ ] 批量采购预览与确认视图能看到该关联是图搜建立、未经人工核对。 - [ ] 采购任务列表能看到同一标注。 - [ ] **带标注的关联不被拦截**,采购流程与现状一致。 ## 验证 Go 单元测试 + Web 构建 + `.\scripts erify.ps1 -Component all`。 ## 风险 - 新增字段需迁移(加列,风险低于 check 约束变更,仍需按迁移流程处理)。 - 本工单**只读地**触及采购视图(新增一个展示字段),不改变采购的任何门禁、状态机或下单条件。实施时必须确认既有采购行为逐字节不变,并有回归测试证明。 ## 可能的调整 本工单跨 server / web / android 三个交付单元,且现在还覆盖采购侧的只读显示,偏胖。若实施时验收困难,可把 Agent 列表标记拆到 #277,或把采购侧显示单独拆一单。 ## 文档影响 数据结构与业务规则变化,需更新对应 Wiki 页面。
Author
Owner

核对范围与方法(2026-09-15)

  • 被核对分支:release/283 @ c80d0c9;基线 main @ c14f5f5。
  • 该分支是打包分支,同时含 #272 #273 #274 #276 #278 #279 #280 #282 #283,本次只就 #275~#280 的验收项作结论。
  • 执行内容:go build ./...、go test ./...(server)、npm run build:prod(web)、逐条对照工单验收项读取 diff。
  • 未执行:真机验证、Android 构建与单测(见 #277 结论,Android 侧无可验证的实现)。

结论:部分完成,不达标

已完成

  • shopee_product.image_search_linked 字段 + 迁移 1789400100000_image_search_link_marker.go。
  • task/image_search_link.go 自动关联的并发保护写得正确:
Where("id = ? AND pdd_product_id IS NULL AND image_search_linked = ?", product.ID, false)

Agent 执行期间发生的人工改动会因该 CAS 谓词而胜出,不会被覆盖。

  • shopeeproduct.LinkPDD 手动关联时写入 "image_search_linked": false,清除逻辑正确。
  • 虾皮商品列表「图搜关联」筛选(全部/仅图搜关联/非图搜关联)、详情抽屉「图搜自动关联」标签。
  • npm run build:prod 通过;app/goauto/shopeeproduct、app/goauto/purchase 包测试通过。

未完成的验收项

  • 采购侧可见(方案第 5 条 / 2026-09-14 追加)。 purchase.BatchPreviewItem 中没有任何图搜相关字段,批量采购预览与确认视图看不到该关联是机器建立、未经人工核对。

    对应验收第 5、6 条未满足。本条的存在理由已写在工单正文:标注只显示在商品页而不显示在采购页,等于提醒没有送到该看见的人眼前,一个没人核过的关联会无声走到创建订单。

  • Agent 采集任务列表行显示来源标记(方案第 4 条)。Android 侧本分支无相关改动。

需要澄清的一处

release/283 中 purchase/batch.go 的 8 行改动(改写 ReasonCode / Reason / NextAction 为 CodeMappingRequired)属于 #283,不属于本工单。不应被认作方案第 5 条已完成;且本工单明确要求只读地触及采购视图、不改变采购门禁,该改动与本工单无关。

状态维持待实施。

## 核对范围与方法(2026-09-15) - 被核对分支:`release/283` @ `c80d0c9`;基线 `main` @ `c14f5f5`。 - 该分支是打包分支,同时含 #272 #273 #274 #276 #278 #279 #280 #282 #283,本次只就 #275~#280 的验收项作结论。 - 执行内容:`go build ./...`、`go test ./...`(server)、`npm run build:prod`(web)、逐条对照工单验收项读取 diff。 - **未执行**:真机验证、Android 构建与单测(见 #277 结论,Android 侧无可验证的实现)。 ## 结论:部分完成,不达标 ### 已完成 - `shopee_product.image_search_linked` 字段 + 迁移 `1789400100000_image_search_link_marker.go`。 - `task/image_search_link.go` 自动关联的并发保护写得正确: ```go Where("id = ? AND pdd_product_id IS NULL AND image_search_linked = ?", product.ID, false) ``` Agent 执行期间发生的人工改动会因该 CAS 谓词而胜出,不会被覆盖。 - `shopeeproduct.LinkPDD` 手动关联时写入 `"image_search_linked": false`,清除逻辑正确。 - 虾皮商品列表「图搜关联」筛选(全部/仅图搜关联/非图搜关联)、详情抽屉「图搜自动关联」标签。 - `npm run build:prod` 通过;`app/goauto/shopeeproduct`、`app/goauto/purchase` 包测试通过。 ### 未完成的验收项 - [ ] **采购侧可见(方案第 5 条 / 2026-09-14 追加)。** `purchase.BatchPreviewItem` 中没有任何图搜相关字段,批量采购预览与确认视图看不到该关联是机器建立、未经人工核对。 对应验收第 5、6 条未满足。本条的存在理由已写在工单正文:标注只显示在商品页而不显示在采购页,等于提醒没有送到该看见的人眼前,一个没人核过的关联会无声走到创建订单。 - [ ] Agent 采集任务列表行显示来源标记(方案第 4 条)。Android 侧本分支无相关改动。 ### 需要澄清的一处 `release/283` 中 `purchase/batch.go` 的 8 行改动(改写 `ReasonCode` / `Reason` / `NextAction` 为 `CodeMappingRequired`)**属于 #283,不属于本工单**。不应被认作方案第 5 条已完成;且本工单明确要求只读地触及采购视图、不改变采购门禁,该改动与本工单无关。 状态维持待实施。
Author
Owner

上线信息(共用)

  • 合并提交:1d01186,已推送 be03972..1d01186 到 main。
  • 发布目录:/home/goauto/releases/20260915-1d01186,二进制 SHA-256 前缀 65e404c4721d592a(本地与服务器一致)。
  • systemctl is-active goauto.service = active,监听 127.0.0.1:8010。
  • 数据库迁移:新执行 2 个,跳过 45 个已应用。迁移前已做全库逻辑备份(66 张表,gzip 校验通过),路径在运维备份目录,不在此列出。
  • 迁移前后 collection_task 222 行、shopee_product 26901 行,逐一相符。

外部入口验证:

检查 结果
GET / HTTP 200
GET /api/admin/v1/collection-tasks {"code":401,"msg":"cookie token is empty"}
POST /api/admin/v1/collection-tasks/image-search/batch {"code":401,...}
不存在路由对照 HTTP 404

最后两行是关键对照:图搜接口返回 401 而非 404,证明新路由已注册。

回退方式:把 current 软链切回 20260914-be03972-r2 后重启。两条迁移均为加列与放宽约束,不阻碍回退到旧二进制。

实施

提交:b4dc7fc、1c99891、36d1f82、5a7e219、804448e、90c1668、a3cf989、c2c0bae。

  • shopee_product.image_search_linked 字段与迁移 1789400100000。
  • task/image_search_link.go 自动关联,CAS 谓词保证 Agent 执行期间的人工改动胜出:WHERE id = ? AND pdd_product_id IS NULL AND image_search_linked = false。
  • shopeeproduct.LinkPDD 手动关联时写入 image_search_linked: false。
  • 虾皮商品列表「图搜关联」筛选、详情抽屉「图搜自动关联」标签。
  • Agent 采集列表行来源标记(实现在 #277 的提交中)。

上一轮评论提出的缺口已补:采购侧可见

purchase.BatchPreviewItem 新增 ImageSearchLinked,从 loadBatchPreviewDataset 已加载的 ShopeeProduct 行读取,无新增查询;purchase.AdminTaskItem 新增同名字段,loadImageSearchLinked 批量查询接入 AdminList 与 AdminDetail。Web 侧在批量采购确认对话框与采购任务列表显示「图搜未核」标记,并给出「其中 N 条为图搜自动关联,尚未人工核对」汇总。

[必须] 该字段只读展示,未修改 Eligible / ReasonCode / Reason / NextAction 的任何现有赋值。新增三个测试,其中一个是反向回归:已不合格的行在 ImageSearchLinked=true 时结论逐字不变。

另澄清:release/283 中 purchase/batch.go 改写 ReasonCode 的那段属于 #283,不属于本工单。

验证

go test ./app/goauto/purchase/... ./app/goauto/shopeeproduct/... ./app/goauto/task/... 通过;npm run build:prod 通过。

状态

已上线,保持待验收。界面变化的标注截图或复用规范说明仍未提供(工单设计证据要求),验收时需补。

## 上线信息(共用) - 合并提交:`1d01186`,已推送 `be03972..1d01186` 到 `main`。 - 发布目录:`/home/goauto/releases/20260915-1d01186`,二进制 SHA-256 前缀 `65e404c4721d592a`(本地与服务器一致)。 - `systemctl is-active goauto.service` = `active`,监听 `127.0.0.1:8010`。 - 数据库迁移:新执行 2 个,跳过 45 个已应用。迁移前已做全库逻辑备份(66 张表,gzip 校验通过),路径在运维备份目录,不在此列出。 - 迁移前后 `collection_task` 222 行、`shopee_product` 26901 行,逐一相符。 外部入口验证: | 检查 | 结果 | |---|---| | `GET /` | HTTP 200 | | `GET /api/admin/v1/collection-tasks` | `{"code":401,"msg":"cookie token is empty"}` | | `POST /api/admin/v1/collection-tasks/image-search/batch` | `{"code":401,...}` | | 不存在路由对照 | HTTP 404 | 最后两行是关键对照:图搜接口返回 401 而非 404,证明新路由已注册。 回退方式:把 `current` 软链切回 `20260914-be03972-r2` 后重启。两条迁移均为加列与放宽约束,不阻碍回退到旧二进制。 ## 实施 提交:`b4dc7fc`、`1c99891`、`36d1f82`、`5a7e219`、`804448e`、`90c1668`、`a3cf989`、`c2c0bae`。 - `shopee_product.image_search_linked` 字段与迁移 `1789400100000`。 - `task/image_search_link.go` 自动关联,CAS 谓词保证 Agent 执行期间的人工改动胜出:`WHERE id = ? AND pdd_product_id IS NULL AND image_search_linked = false`。 - `shopeeproduct.LinkPDD` 手动关联时写入 `image_search_linked: false`。 - 虾皮商品列表「图搜关联」筛选、详情抽屉「图搜自动关联」标签。 - Agent 采集列表行来源标记(实现在 #277 的提交中)。 ### 上一轮评论提出的缺口已补:采购侧可见 `purchase.BatchPreviewItem` 新增 `ImageSearchLinked`,从 `loadBatchPreviewDataset` 已加载的 `ShopeeProduct` 行读取,无新增查询;`purchase.AdminTaskItem` 新增同名字段,`loadImageSearchLinked` 批量查询接入 `AdminList` 与 `AdminDetail`。Web 侧在批量采购确认对话框与采购任务列表显示「图搜未核」标记,并给出「其中 N 条为图搜自动关联,尚未人工核对」汇总。 `[必须]` 该字段只读展示,未修改 `Eligible` / `ReasonCode` / `Reason` / `NextAction` 的任何现有赋值。新增三个测试,其中一个是反向回归:已不合格的行在 `ImageSearchLinked=true` 时结论逐字不变。 另澄清:`release/283` 中 `purchase/batch.go` 改写 `ReasonCode` 的那段属于 #283,不属于本工单。 ## 验证 `go test ./app/goauto/purchase/... ./app/goauto/shopeeproduct/... ./app/goauto/task/...` 通过;`npm run build:prod` 通过。 ## 状态 已上线,保持待验收。界面变化的标注截图或复用规范说明仍未提供(工单设计证据要求),验收时需补。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: OPC/goauto#279