fix: 查重流程全面核实修复 — 两阶段持久化 + 重新计算查重API (#1664) #1684

Merged
auto-approve-bot merged 2 commits from fix/dedup-flow-verification-1664 into develop 2026-09-04 13:00:12 +08:00
Owner

问题根因

全量排查了查重流程从后端→API→前端的全链路代码,发现以下问题:

后端

  1. dedup_helpers.py 事务问题(核心 bug):视频记录先 commit 入库,然后再算指纹/查重率。如果指纹计算(cv2异常)或查重率计算(DB异常)中途失败,视频已经在库里但 duplicate_rate=None/visual_similarity=None,且没有任何重试机制
  2. 没有重跑机制:已存在的视频如果没有查重率,没有任何 API 可以触发重新计算

前端(需前端工程师配套修复)

  1. visual_similarity 显示 bug:后端返回 0~1(归一化),ProductInfoPanel.tsx:59 直接 .toFixed(1)% 显示成 "0.7%" 而非 "70%"
  2. 类型注释错误types.ts 注释写的 "0-100",实际后端返回 "0-1"

修复

后端(本 PR)

  • dedup_helpers.py 重构为两阶段持久化:Phase 1 构建内存对象 → Phase 2 计算指纹+查重(全部在内存) → Phase 3 一次性 create() + commit()。消除中间态,保证要么完整写入要么整体回滚
  • videos.py 新增 POST /videos/recompute-dedup:对缺少 duplicate_rate / video_fingerprint 的视频触发异步 check_duplicate 任务重新计算。支持指定 video_ids 或全用户范围。含安全校验(只处理当前用户的视频)

前端(需前端工程师配套 PR)

  • ProductInfoPanel.tsx:59visual_similarity 显示需 ×100
  • products/types.ts:27,80:注释 "0-100" 改为 "0-1"

新增测试

  • test_dedup_two_phase_commit.py: 5 个测试覆盖两阶段提交场景
  • test_recompute_dedup_api.py: 5 个测试覆盖重新计算 API
  • 更新 test_dedup_helpers_user_id.py 适配新持久化模式
  • 全部 93 个查重相关测试通过
## 问题根因 全量排查了查重流程从后端→API→前端的全链路代码,发现以下问题: ### 后端 1. **`dedup_helpers.py` 事务问题(核心 bug)**:视频记录先 `commit` 入库,然后再算指纹/查重率。如果指纹计算(cv2异常)或查重率计算(DB异常)中途失败,视频已经在库里但 `duplicate_rate=None`/`visual_similarity=None`,且没有任何重试机制 2. **没有重跑机制**:已存在的视频如果没有查重率,没有任何 API 可以触发重新计算 ### 前端(需前端工程师配套修复) 3. **`visual_similarity` 显示 bug**:后端返回 0~1(归一化),`ProductInfoPanel.tsx:59` 直接 `.toFixed(1)%` 显示成 "0.7%" 而非 "70%" 4. **类型注释错误**:`types.ts` 注释写的 "0-100",实际后端返回 "0-1" ## 修复 ### 后端(本 PR) - **`dedup_helpers.py` 重构为两阶段持久化**:Phase 1 构建内存对象 → Phase 2 计算指纹+查重(全部在内存) → Phase 3 一次性 `create()` + `commit()`。消除中间态,保证要么完整写入要么整体回滚 - **`videos.py` 新增 `POST /videos/recompute-dedup`**:对缺少 `duplicate_rate` / `video_fingerprint` 的视频触发异步 `check_duplicate` 任务重新计算。支持指定 video_ids 或全用户范围。含安全校验(只处理当前用户的视频) ### 前端(需前端工程师配套 PR) - `ProductInfoPanel.tsx:59`:`visual_similarity` 显示需 `×100` - `products/types.ts:27,80`:注释 "0-100" 改为 "0-1" ## 新增测试 - `test_dedup_two_phase_commit.py`: 5 个测试覆盖两阶段提交场景 - `test_recompute_dedup_api.py`: 5 个测试覆盖重新计算 API - 更新 `test_dedup_helpers_user_id.py` 适配新持久化模式 - 全部 93 个查重相关测试通过
xiaoxia added 1 commit 2026-09-04 12:38:15 +08:00
fix: 查重流程全面核实修复 — 两阶段持久化 + 重新计算查重API (#1664)
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 2s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2s
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 24s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 26s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 1m46s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m53s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 1m52s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m5s
CI/CD Pipeline / Validate - Style (pull_request) Has been cancelled
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Build Production API Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Web Image (pull_request) Has been cancelled
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been cancelled
CI/CD Pipeline / Deploy Production (pull_request) Has been cancelled
CI/CD Pipeline / Production Browser E2E (pull_request) Has been cancelled
CI/CD Pipeline / Canary Release to Production (pull_request) Has been cancelled
CI/CD Pipeline / CI Gate (pull_request) Has been cancelled
AI Code Review / AI Code Review (pull_request) Has been cancelled
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
PR Automation / Auto Merge on CI Green + Approved (pull_request) Has been cancelled
aee29a45a4
问题根因:
1. dedup_helpers.py 先 commit 视频记录再算查重率 — 如果指纹计算
   或查重率计算中途异常,视频已入库但 duplicate_rate=None,且无重试
2. 没有 API 可以触发已有视频重新计算查重率
3. 前端 visual_similarity 显示 bug(后端返 0~1,前端直接 toFixed%)

修复:
- dedup_helpers.py 重构为两阶段持久化:先计算全部指纹/查重数据
  (内存),再一次性 create + commit,消除中间态
- videos.py 新增 POST /videos/recompute-dedup 端点:对缺少查重数据
  的视频触发异步 check_duplicate 任务重新计算
- 更新 test_dedup_helpers_user_id 适配新持久化模式

新增测试:
- test_dedup_two_phase_commit.py: 5 个测试覆盖两阶段提交场景
- test_recompute_dedup_api.py: 5 个测试覆盖重新计算 API

🚀 预览环境已部署

项目 详情
PR号 #1684
预览链接 https://pr-1684.preview.xiaoxiajianji.com
API环境 staging

💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。

🔄 每次提交新代码后预览环境会自动更新。

🗑️ PR 关闭或合并后,预览环境会自动清理。

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1684 | | 预览链接 | [https://pr-1684.preview.xiaoxiajianji.com](https://pr-1684.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
auto-approve-bot added 1 commit 2026-09-04 12:41:04 +08:00
style: auto-format with black + isort + prettier [skip ci-format-check]
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 6s
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 6s
CI/CD Pipeline / Retag skipped Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Retag skipped Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / Staging E2E Tests (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 31s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 31s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 54s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 1m37s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m41s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 1m43s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 2m9s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m43s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 4m23s
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 2m25s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 10s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 23s
AI Code Review / AI Code Review (pull_request) Successful in 6m17s
61e9262c0a
Collaborator

【阻塞级判定】

  • 是否存在阻塞级问题:是
  • 阻塞级问题数量:2 个

📊 审查概览

  • 整体评价:需修改
  • 建议级问题数量:2 个

🔴 阻塞级问题(必须修复)

  1. [apps/api/app/api/routes/videos.py: 第298行] 缺少必要的导入导致运行时错误

    • 问题类型:语法错误/逻辑bug
    • 问题描述:在新增的 recompute_dedup 函数中使用了 celery_app(第298行)和 logger(第301行),但在文件头部的导入块中未看到这两个变量的导入声明(celery_app 通常从 app.core.celery 或类似模块导入,logger 需实例化)。这会导致函数运行时抛出 NameError
    • 修改建议:请在文件头部添加 celery_applogger 的正确导入语句。
  2. [apps/worker/video_processing/dedup_helpers.py: 第134行] 异常处理逻辑缺失

    • 问题类型:数据损坏风险/逻辑bug
    • 问题描述:create_video_record_and_dedup 函数末尾的 except Exception as e: 块在 diff 中显示为空。如果 session.commit()video_repo.create() 抛出异常,代码将进入此块但什么都不做。这会导致:
      1. 数据库事务未被回滚(session.rollback() 未调用),可能导致连接泄漏或锁死。
      2. 函数隐式返回 None,而调用方期望返回 0(失败)或 1(成功),可能导致调用方逻辑错误。
      3. 单元测试 test_commit_not_called_on_total_failure 明确断言了 session.rollback 会被调用且返回值为 0,当前代码会导致该测试失败。
    • 修改建议:在 except 块中添加 session.rollback() 并返回 0,或者重新抛出异常(视业务需求而定),确保与单元测试预期一致。

💡 改进建议(不阻塞合并)

  1. [apps/api/app/api/routes/videos.py: 第268行] 潜在的内存溢出风险

    • 具体内容:当 request.video_ids 为空时,调用 repo.list_by_user(user_id) 获取当前用户所有视频。如果用户视频量巨大(如数万条),这将一次性加载所有数据到内存,可能导致 OOM。建议使用迭代器或分页处理。
  2. [apps/worker/video_processing/dedup_helpers.py: 第124行] 移除 update_thumbnail 的逻辑确认

    • 具体内容:旧代码中显式调用了 video_repo.update_thumbnail(...),新代码中仅在 GeneratedVideo 构造时传入了 thumbnail_url 并直接 create。请确认 update_thumbnail 方法中没有包含额外的副作用逻辑(如额外的日志、通知或外部存储操作),如果仅仅是更新 DB 字段,当前修改是合理的优化。

良好实践

  • 两阶段提交模式dedup_helpers.py 的重构很好地解决了“部分写入”的问题,先在内存计算完所有查重数据再一次性提交,避免了计算失败导致数据库中出现只有记录无查重数据的脏数据。
  • 权限校验videos.py 中的 recompute_dedup 接口正确地过滤了 user_id,防止用户越权操作他人的视频。
  • 测试覆盖:新增的单元测试(test_dedup_two_phase_commit.pytest_recompute_dedup_api.py)非常详尽,覆盖了正常流程、边界条件和异常场景,特别是对 rollback 的测试非常有价值。

格式检查通过 | 逻辑审查需修改 | ⚠️ 建议关注性能


🤖 由 AI 代码审查机器人自动生成 | 2026-09-04 04:46:07 | 模型:

### 【阻塞级判定】 - 是否存在阻塞级问题:是 - 阻塞级问题数量:2 个 ### 📊 审查概览 - 整体评价:需修改 - 建议级问题数量:2 个 ### 🔴 阻塞级问题(必须修复) 1. **[apps/api/app/api/routes/videos.py: 第298行] 缺少必要的导入导致运行时错误** - 问题类型:语法错误/逻辑bug - 问题描述:在新增的 `recompute_dedup` 函数中使用了 `celery_app`(第298行)和 `logger`(第301行),但在文件头部的导入块中未看到这两个变量的导入声明(`celery_app` 通常从 `app.core.celery` 或类似模块导入,`logger` 需实例化)。这会导致函数运行时抛出 `NameError`。 - 修改建议:请在文件头部添加 `celery_app` 和 `logger` 的正确导入语句。 2. **[apps/worker/video_processing/dedup_helpers.py: 第134行] 异常处理逻辑缺失** - 问题类型:数据损坏风险/逻辑bug - 问题描述:`create_video_record_and_dedup` 函数末尾的 `except Exception as e:` 块在 diff 中显示为空。如果 `session.commit()` 或 `video_repo.create()` 抛出异常,代码将进入此块但什么都不做。这会导致: 1. 数据库事务未被回滚(`session.rollback()` 未调用),可能导致连接泄漏或锁死。 2. 函数隐式返回 `None`,而调用方期望返回 `0`(失败)或 `1`(成功),可能导致调用方逻辑错误。 3. 单元测试 `test_commit_not_called_on_total_failure` 明确断言了 `session.rollback` 会被调用且返回值为 0,当前代码会导致该测试失败。 - 修改建议:在 `except` 块中添加 `session.rollback()` 并返回 `0`,或者重新抛出异常(视业务需求而定),确保与单元测试预期一致。 ### 💡 改进建议(不阻塞合并) 1. **[apps/api/app/api/routes/videos.py: 第268行] 潜在的内存溢出风险** - 具体内容:当 `request.video_ids` 为空时,调用 `repo.list_by_user(user_id)` 获取当前用户所有视频。如果用户视频量巨大(如数万条),这将一次性加载所有数据到内存,可能导致 OOM。建议使用迭代器或分页处理。 2. **[apps/worker/video_processing/dedup_helpers.py: 第124行] 移除 `update_thumbnail` 的逻辑确认** - 具体内容:旧代码中显式调用了 `video_repo.update_thumbnail(...)`,新代码中仅在 `GeneratedVideo` 构造时传入了 `thumbnail_url` 并直接 `create`。请确认 `update_thumbnail` 方法中没有包含额外的副作用逻辑(如额外的日志、通知或外部存储操作),如果仅仅是更新 DB 字段,当前修改是合理的优化。 ### ✅ 良好实践 - **两阶段提交模式**:`dedup_helpers.py` 的重构很好地解决了“部分写入”的问题,先在内存计算完所有查重数据再一次性提交,避免了计算失败导致数据库中出现只有记录无查重数据的脏数据。 - **权限校验**:`videos.py` 中的 `recompute_dedup` 接口正确地过滤了 `user_id`,防止用户越权操作他人的视频。 - **测试覆盖**:新增的单元测试(`test_dedup_two_phase_commit.py` 和 `test_recompute_dedup_api.py`)非常详尽,覆盖了正常流程、边界条件和异常场景,特别是对 `rollback` 的测试非常有价值。 --- ✅ 格式检查通过 | ❌ 逻辑审查需修改 | ⚠️ 建议关注性能 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-09-04 04:46:07 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
xiaoxia force-pushed fix/dedup-flow-verification-1664 from 058a8d7307 to 61e9262c0a 2026-09-04 12:54:55 +08:00 Compare
auto-approve-bot merged commit 452a484c5b into develop 2026-09-04 13:00:12 +08:00
auto-approve-bot deleted branch fix/dedup-flow-verification-1664 2026-09-04 13:00:12 +08:00

🗑️ 预览环境已清理

PR #1684 已关闭或合并,对应的预览环境已被清理。

如有需要,可以重新打开 PR 来重新生成预览环境。

🗑️ **预览环境已清理** PR #1684 已关闭或合并,对应的预览环境已被清理。 > 如有需要,可以重新打开 PR 来重新生成预览环境。
Sign in to join this conversation.