R4d:全量拉取时按货憨憨最新视频状态修正本地工作流状态 #16

Open
opened 2026-09-03 14:26:45 +08:00 by ila · 1 comment
Owner

基本信息

  • 类型:需求
  • 所属 Epic:#2
  • 所属 MVP / 版本:#5
  • 阶段:待确认

依赖与并行

  • 前置工单:#15(R4c)
  • 是否允许与前置工单并行:否
  • 原因:本工单修改的断点跳过逻辑建立在 #15 修正后的状态语义之上。

子项目影响

  • 仅影响的子项目 / 交付单元:cmsp 桌面端
  • 是否跨子项目:否
  • 是否修改共享接口或契约:否
  • 各子项目需要执行的验证:见「验证方式」

原始需求

  • 来源:用户对话
  • 提出时间:2026-09-03
  • 关键原话:
    「我的想法是每次全量拉取,是否有视频的状态覆盖本地,如果上次上传了,被删除,
    最新拉取是无视频,筛选出来就可以重新上传,你觉得合理吗」

要解决什么

现状

三个状态字段回答的是不同问题:

字段 回答什么 事实来源 谁更新
video_diagnosis Shopee 上现在有没有视频 货憨憨 每次全量拉取覆盖(已实现)
video_status 淘宝上有没有找到同款视频 我们自己搜的 取视频任务
upload_status 我们传没传上去 我们自己 上传任务

UpsertProducts 已经在每次全量拉取时用货憨憨的 video_diagnosis 覆盖本地
(internal/store/product.go:118),前端「类型 = 缺少视频」筛的就是它。
所以「视频被删 → 重新拉取 → 能筛出来」这条链路目前是通的。

问题

筛得出来,但跑起来什么也不会发生。

某商品此前已上传成功(upload_status = 'done'),后来视频在上游被删除。
重新全量拉取后 video_diagnosis 变回 missing,界面能筛出这个商品,
但批量任务按 upload_status = 'done' 判定为已完成,直接跳过它。

期望结果

全量拉取发现某商品的视频在上游消失后,本地的工作流状态要随之修正,
使它能被重新处理;且已经下载到本地的视频直接重传,不重新搜淘宝。

做什么 / 不做什么

  • 做:
    • 在全量拉取时识别 video_diagnosis 由 ok 变为 missing 的商品
    • 按本地是否已有可用视频,分别修正 upload_status 或 download_status
  • 不做:
    • 不因为 video_diagnosis = 'missing' 就把 video_status = 'none' 重置为 pending
      (原因见下,这是本工单最重要的边界)
    • 不改图搜、签名、详情提取、下载逻辑
    • 不改上传链路本身(那是 R5)
    • 不新增第二份任务状态来源

已确认方案

触发时机

在 UpsertProducts 写入过程中,对每条商品比较写入前的旧 video_diagnosis
与本次拉取的新值。只处理 ok → missing 这一个方向。

新增商品(本地原本不存在)不适用本规则,它们本来就是 pending。

处理规则

ok → missing 时:

  1. 本地已有下载完成的视频(videos 表存在该商品 status = 'downloaded' 的记录,
    且 local_path 指向的文件仍然存在):

    • upload_status → pending
    • 不动 video_status 和 download_status
    • 效果:下次批量任务直接重传本地文件,不打开淘宝,既快又不消耗风控额度
  2. 本地没有可用视频:

    • download_status → pending
    • upload_status → pending
    • video_status 保持原值不变(见下方边界说明)

★ 边界:为什么不重置 video_status = 'none' ★

video_status = 'none' 的含义是「我们拿这张主图去淘宝搜过,确实没有同款视频」。
这个结论是关于商品主图的,不是关于货憨憨状态的——
货憨憨那边的视频被删,不会让淘宝突然多出一个同款视频。

若因 diagnosis = missing 就把 none 重置为 pending,会导致:
货憨憨永远说缺视频 → 我们永远搜不到 → 每批任务都重新搜一遍,无限循环,
白白消耗风控额度。这正是 #14 让断点跳过 none 的初衷。

因此 none 只能通过 #15 提供的一次性重置工具、或主图变化来解除。

主图变化

如果本次拉取发现 main_image 也变了,说明是另一张图,
旧的「搜不到」结论不再适用,理论上 video_status = 'none' 可以重置为 pending。

2026-09-03 负责人确认:本工单不做。 本工单只处理 video_diagnosis
由 ok 变 missing 这一条规则,主图变化不触发任何状态修改。
将来确有需要时另开工单。

实现位置

  • internal/store/product.go:UpsertProducts 内在同一事务中完成比较与状态修正,
    避免出现「诊断已更新但状态没跟上」的中间态
  • 需要能查询某商品是否有可用本地视频,复用或扩展 internal/store/video.go
  • 不新增 migration(不需要新列)

预计修改文件:

  • internal/store/product.go、product_test.go
  • internal/store/video.go、video_test.go(若需新增查询方法)
  • app.go(下载数据完成后在运行日志中汇报「N 个商品的视频在上游消失,已退回待处理」)

需求变化记录

日期 变化内容 原因 用户确认
2026-09-03 建立本工单 上传后被上游删除的商品无法重新处理 是
2026-09-03 主图变化不重置 video_status='none',本工单不做 负责人确认暂不处理,避免扩大范围 是

设计与原型门禁

  • 修改类型:非 UI
  • 所需设计证据:无需 UI 原型
  • 无需 UI 原型的原因:纯后端状态流转规则,界面不新增控件,
    筛选与列表沿用现有已确认的实现。
  • 状态:无

文档影响

  • 更新业务规则与术语:补充三个状态字段的分工、
    video_diagnosis 由货憨憨每次全量拉取覆盖、
    以及 ok → missing 的状态修正规则和 none 不被重置的原因。

交付文档影响

  • 无交付文档影响,原因:内部工具,无外部使用者文档。

任务记录与可选快照

  • 单次任务事实来源:当前 Gitea 工单正文与评论
  • 默认不创建任务快照

验收标准

  • 商品此前 upload_status='done',重新拉取后诊断变 missing
    且本地有可用视频 → upload_status 变 pending,
    video_status / download_status 不变
  • 同上但本地无可用视频 → download_status 与 upload_status 变 pending
  • video_status='none' 的商品在任何情况下都不被本规则重置
  • 诊断保持 missing 不变(ok → missing 之外的方向不触发任何状态修改)
  • 诊断由 missing → ok 时不做任何状态修改
  • 新增商品不受本规则影响
  • 状态修正与诊断更新在同一事务内完成
  • 下载数据完成后运行日志汇报受影响商品数
  • 不新增 migration,不新增第三方依赖
  • internal/ 下不得 import wails

验证方式

go vet ./...
go build ./...
go test ./internal/... -v
go test ./...
cd frontend && npx vite build

单元测试需覆盖:

  • ok → missing + 本地有视频 → 只重置 upload_status
  • ok → missing + 本地无视频 → 重置 download_status 和 upload_status
  • ok → missing + video_status='none' → none 不被重置
  • missing → ok → 无任何状态修改
  • missing → missing → 无任何状态修改
  • 新增商品 → 不触发规则
  • 本地视频记录存在但文件已被删除 → 按「无可用视频」处理

真机验证(负责人执行):

  • 挑一个已上传成功的测试商品,在货憨憨侧删除其视频
  • 点「下载数据」全量拉取
  • 确认该商品 upload_status 回到待处理,且能被重新处理

风险和回退

  • 风险 1:状态修正写在 UpsertProducts 里,而「下载数据」会处理近万条商品。
    逐条查 videos 表会显著拖慢。缓解:先批量查出 ok → missing 的商品 ID
    (通常极少),再只对这批查视频,不要每条都查。
  • 风险 2:规则写错方向会大面积重置状态,导致整批商品被重新处理,
    消耗大量风控额度。缓解:只处理 ok → missing 单一方向;
    验收标准里对每个不该触发的方向都写了断言。
  • 回退:改动集中在 internal/store/product.go,git revert 即可。
    不新增列、不改结构,回退后数据无残留。
## 基本信息 - 类型:需求 - 所属 Epic:#2 - 所属 MVP / 版本:#5 - 阶段:待确认 ## 依赖与并行 - 前置工单:#15(R4c) - 是否允许与前置工单并行:否 - 原因:本工单修改的断点跳过逻辑建立在 #15 修正后的状态语义之上。 ## 子项目影响 - 仅影响的子项目 / 交付单元:cmsp 桌面端 - 是否跨子项目:否 - 是否修改共享接口或契约:否 - 各子项目需要执行的验证:见「验证方式」 ## 原始需求 - 来源:用户对话 - 提出时间:2026-09-03 - 关键原话: 「我的想法是每次全量拉取,是否有视频的状态覆盖本地,如果上次上传了,被删除, 最新拉取是无视频,筛选出来就可以重新上传,你觉得合理吗」 ## 要解决什么 ### 现状 三个状态字段回答的是不同问题: | 字段 | 回答什么 | 事实来源 | 谁更新 | |---|---|---|---| | `video_diagnosis` | Shopee 上现在有没有视频 | 货憨憨 | 每次全量拉取覆盖(已实现) | | `video_status` | 淘宝上有没有找到同款视频 | 我们自己搜的 | 取视频任务 | | `upload_status` | 我们传没传上去 | 我们自己 | 上传任务 | `UpsertProducts` 已经在每次全量拉取时用货憨憨的 `video_diagnosis` 覆盖本地 (`internal/store/product.go:118`),前端「类型 = 缺少视频」筛的就是它。 所以「视频被删 → 重新拉取 → 能筛出来」这条链路目前是通的。 ### 问题 **筛得出来,但跑起来什么也不会发生。** 某商品此前已上传成功(`upload_status = 'done'`),后来视频在上游被删除。 重新全量拉取后 `video_diagnosis` 变回 `missing`,界面能筛出这个商品, 但批量任务按 `upload_status = 'done'` 判定为已完成,直接跳过它。 ### 期望结果 全量拉取发现某商品的视频在上游消失后,本地的工作流状态要随之修正, 使它能被重新处理;且**已经下载到本地的视频直接重传,不重新搜淘宝**。 ## 做什么 / 不做什么 - 做: - 在全量拉取时识别 `video_diagnosis` 由 `ok` 变为 `missing` 的商品 - 按本地是否已有可用视频,分别修正 `upload_status` 或 `download_status` - 不做: - **不因为 `video_diagnosis = 'missing'` 就把 `video_status = 'none'` 重置为 `pending`** (原因见下,这是本工单最重要的边界) - 不改图搜、签名、详情提取、下载逻辑 - 不改上传链路本身(那是 R5) - 不新增第二份任务状态来源 ## 已确认方案 ### 触发时机 在 `UpsertProducts` 写入过程中,对每条商品比较**写入前的旧 `video_diagnosis`** 与**本次拉取的新值**。只处理 `ok → missing` 这一个方向。 新增商品(本地原本不存在)不适用本规则,它们本来就是 `pending`。 ### 处理规则 `ok → missing` 时: 1. **本地已有下载完成的视频**(`videos` 表存在该商品 `status = 'downloaded'` 的记录, 且 `local_path` 指向的文件仍然存在): - `upload_status` → `pending` - **不动 `video_status` 和 `download_status`** - 效果:下次批量任务直接重传本地文件,不打开淘宝,既快又不消耗风控额度 2. **本地没有可用视频**: - `download_status` → `pending` - `upload_status` → `pending` - `video_status` 保持原值不变(见下方边界说明) ### ★ 边界:为什么不重置 `video_status = 'none'` ★ `video_status = 'none'` 的含义是「我们拿这张主图去淘宝搜过,确实没有同款视频」。 这个结论是关于**商品主图**的,不是关于货憨憨状态的—— 货憨憨那边的视频被删,不会让淘宝突然多出一个同款视频。 若因 `diagnosis = missing` 就把 `none` 重置为 `pending`,会导致: 货憨憨永远说缺视频 → 我们永远搜不到 → **每批任务都重新搜一遍,无限循环**, 白白消耗风控额度。这正是 #14 让断点跳过 `none` 的初衷。 因此 `none` 只能通过 #15 提供的一次性重置工具、或主图变化来解除。 ### 主图变化 如果本次拉取发现 `main_image` 也变了,说明是另一张图, 旧的「搜不到」结论不再适用,理论上 `video_status = 'none'` 可以重置为 `pending`。 **2026-09-03 负责人确认:本工单不做。** 本工单只处理 `video_diagnosis` 由 `ok` 变 `missing` 这一条规则,主图变化不触发任何状态修改。 将来确有需要时另开工单。 ### 实现位置 - `internal/store/product.go`:`UpsertProducts` 内在同一事务中完成比较与状态修正, 避免出现「诊断已更新但状态没跟上」的中间态 - 需要能查询某商品是否有可用本地视频,复用或扩展 `internal/store/video.go` - 不新增 migration(不需要新列) 预计修改文件: - `internal/store/product.go`、`product_test.go` - `internal/store/video.go`、`video_test.go`(若需新增查询方法) - `app.go`(下载数据完成后在运行日志中汇报「N 个商品的视频在上游消失,已退回待处理」) ## 需求变化记录 | 日期 | 变化内容 | 原因 | 用户确认 | |---|---|---|---| | 2026-09-03 | 建立本工单 | 上传后被上游删除的商品无法重新处理 | 是 | | 2026-09-03 | 主图变化不重置 `video_status='none'`,本工单不做 | 负责人确认暂不处理,避免扩大范围 | 是 | ## 设计与原型门禁 - 修改类型:非 UI - 所需设计证据:无需 UI 原型 - 无需 UI 原型的原因:纯后端状态流转规则,界面不新增控件, 筛选与列表沿用现有已确认的实现。 - 状态:无 ## 文档影响 - [x] 更新业务规则与术语:补充三个状态字段的分工、 `video_diagnosis` 由货憨憨每次全量拉取覆盖、 以及 `ok → missing` 的状态修正规则和 `none` 不被重置的原因。 ## 交付文档影响 - [x] 无交付文档影响,原因:内部工具,无外部使用者文档。 ## 任务记录与可选快照 - 单次任务事实来源:当前 Gitea 工单正文与评论 - [x] 默认不创建任务快照 ## 验收标准 - [ ] 商品此前 `upload_status='done'`,重新拉取后诊断变 `missing` 且本地有可用视频 → `upload_status` 变 `pending`, `video_status` / `download_status` 不变 - [ ] 同上但本地无可用视频 → `download_status` 与 `upload_status` 变 `pending` - [ ] **`video_status='none'` 的商品在任何情况下都不被本规则重置** - [ ] 诊断保持 `missing` 不变(`ok → missing` 之外的方向不触发任何状态修改) - [ ] 诊断由 `missing → ok` 时不做任何状态修改 - [ ] 新增商品不受本规则影响 - [ ] 状态修正与诊断更新在同一事务内完成 - [ ] 下载数据完成后运行日志汇报受影响商品数 - [ ] 不新增 migration,不新增第三方依赖 - [ ] `internal/` 下不得 import wails ## 验证方式 ``` go vet ./... go build ./... go test ./internal/... -v go test ./... cd frontend && npx vite build ``` 单元测试需覆盖: - `ok → missing` + 本地有视频 → 只重置 `upload_status` - `ok → missing` + 本地无视频 → 重置 `download_status` 和 `upload_status` - `ok → missing` + `video_status='none'` → **`none` 不被重置** - `missing → ok` → 无任何状态修改 - `missing → missing` → 无任何状态修改 - 新增商品 → 不触发规则 - 本地视频记录存在但文件已被删除 → 按「无可用视频」处理 真机验证(负责人执行): - 挑一个已上传成功的测试商品,在货憨憨侧删除其视频 - 点「下载数据」全量拉取 - 确认该商品 `upload_status` 回到待处理,且能被重新处理 ## 风险和回退 - **风险 1**:状态修正写在 `UpsertProducts` 里,而「下载数据」会处理近万条商品。 逐条查 `videos` 表会显著拖慢。缓解:先批量查出 `ok → missing` 的商品 ID (通常极少),再只对这批查视频,不要每条都查。 - **风险 2**:规则写错方向会大面积重置状态,导致整批商品被重新处理, 消耗大量风控额度。缓解:只处理 `ok → missing` 单一方向; 验收标准里对每个不该触发的方向都写了断言。 - **回退**:改动集中在 `internal/store/product.go`,`git revert` 即可。 不新增列、不改结构,回退后数据无残留。
Author
Owner

范围扩充:拉取后增加本地视频磁盘对账(2026-09-03)

负责人提出:「拉取数据入库时去检测视频子目录和文件在不在」。

本工单原本就是「全量拉取后按货憨憨最新状态修正本地工作流状态」,
磁盘对账是同一时机、同一目的(拉取后修正本地状态),并入本工单。

触发时机:「下载数据」全量拉取完成之后,作为独立的对账步骤。
不得写进 store.UpsertProducts——那是数据层,不应该知道 video_dir 的存在,
也不应该 stat 文件系统。对账放在 app.go,日志单独汇报补回条数。

规则(只补不删):

情况 处理
磁盘有 <video_dir>/<蝦皮ID>/<蝦皮ID>_N.mp4,videos 表无对应记录 补一条记录
videos 表有记录,磁盘上文件不存在 什么都不做

第二条必须克制:video_dir 是可配置的,指向移动硬盘未挂载、或使用者改了路径时,
扫描会看到「文件全没了」,此时删记录等于一次性抹掉全部历史。
缺文件的场景已由上传路径的「取第一个 downloaded 且文件真实存在」检查兜住。

补回记录的残缺性必须如实标记:从磁盘只能还原 local_path 和 file_size,
还原不了 source_item(视频来自哪个淘宝同款),而那是负责人明确要求记库的信息。
补回记录的 source_item 只能留空,且要能与正常搜出来的记录区分开,不得冒充完整记录。

性能:目录名是确定的,一个商品一次 os.Stat,约 9300 个商品耗时可忽略,
不需要遍历搜索整个视频目录。

这不能替代根因修复:本次漂移的根因是 ReplaceVideos 先删后写,已另开 #18 修复。
对账是兜底,先写后删才是止血,两者都要做。

依赖调整:本工单前置增加 #18。

### 范围扩充:拉取后增加本地视频磁盘对账(2026-09-03) 负责人提出:「拉取数据入库时去检测视频子目录和文件在不在」。 本工单原本就是「全量拉取后按货憨憨最新状态修正本地工作流状态」, 磁盘对账是同一时机、同一目的(拉取后修正本地状态),并入本工单。 **触发时机**:「下载数据」全量拉取**完成之后**,作为独立的对账步骤。 **不得写进 `store.UpsertProducts`**——那是数据层,不应该知道 `video_dir` 的存在, 也不应该 stat 文件系统。对账放在 `app.go`,日志单独汇报补回条数。 **规则(只补不删)**: | 情况 | 处理 | |---|---| | 磁盘有 `<video_dir>/<蝦皮ID>/<蝦皮ID>_N.mp4`,`videos` 表无对应记录 | **补**一条记录 | | `videos` 表有记录,磁盘上文件不存在 | **什么都不做** | 第二条必须克制:`video_dir` 是可配置的,指向移动硬盘未挂载、或使用者改了路径时, 扫描会看到「文件全没了」,此时删记录等于一次性抹掉全部历史。 缺文件的场景已由上传路径的「取第一个 `downloaded` 且文件真实存在」检查兜住。 **补回记录的残缺性必须如实标记**:从磁盘只能还原 `local_path` 和 `file_size`, **还原不了 `source_item`**(视频来自哪个淘宝同款),而那是负责人明确要求记库的信息。 补回记录的 `source_item` 只能留空,且要能与正常搜出来的记录区分开,不得冒充完整记录。 **性能**:目录名是确定的,一个商品一次 `os.Stat`,约 9300 个商品耗时可忽略, 不需要遍历搜索整个视频目录。 **这不能替代根因修复**:本次漂移的根因是 `ReplaceVideos` 先删后写,已另开 #18 修复。 对账是兜底,先写后删才是止血,两者都要做。 **依赖调整**:本工单前置增加 #18。
Sign in to join this conversation.
No labels
1 Participants
Notifications
Due Date
No due date set.
Dependencies

No dependencies set.

Reference: chengma/cmsp#16