fix: 上传素材后视频库立即显示(创建 PROCESSING 状态 Asset) #1644

Merged
auto-approve-bot merged 2 commits from fix/upload-asset-not-visible-v2 into develop 2026-09-03 14:56:35 +08:00
Owner

问题

上传视频/图片素材后,前端视频库页面不会立即显示新素材。需要手动刷新才能看到。

根因

/upload/direct/completeupload_asset 端点只创建 IngestJob 记录并派发 Celery 任务。Asset 记录由 worker 异步创建。前端上传完成后 invalidate query 刷新列表时,新素材还不存在于数据库中。

修复

  1. API 端:上传完成后立即创建 Asset(status=PROCESSING) 记录,前端刷新后即可看到(显示为"处理中")
  2. Worker 端:通过 find_by_storage_key 找到已有 Asset 记录,更新元数据(时长、分辨率、编码等)+ 状态改为 READY
  3. 兜底:若找不到已有记录则创建新记录(兼容旧版本)

改动文件

  • apps/api/app/api/routes/upload.py:添加 _infer_mime_type_from_storage_key + _create_pending_asset
  • apps/worker/worker_app/tasks/ingest.py:先查找已有 Asset,有则更新,无则创建
  • packages/ports/asset_repository.py:新增 find_by_storage_key 抽象方法
  • packages/adapters/in_memory/asset_repository.py:实现 find_by_storage_key
  • packages/adapters/sqlalchemy_impl/asset_repository.py:实现 find_by_storage_key
  • tests/unit/test_form_upload_routes.py:更新测试 mock

注:此 PR 替换原 #1642(base 分支从 main 改为 develop)

## 问题 上传视频/图片素材后,前端视频库页面不会立即显示新素材。需要手动刷新才能看到。 ## 根因 `/upload/direct/complete` 和 `upload_asset` 端点只创建 IngestJob 记录并派发 Celery 任务。Asset 记录由 worker 异步创建。前端上传完成后 invalidate query 刷新列表时,新素材还不存在于数据库中。 ## 修复 1. **API 端**:上传完成后立即创建 `Asset(status=PROCESSING)` 记录,前端刷新后即可看到(显示为"处理中") 2. **Worker 端**:通过 `find_by_storage_key` 找到已有 Asset 记录,更新元数据(时长、分辨率、编码等)+ 状态改为 READY 3. **兜底**:若找不到已有记录则创建新记录(兼容旧版本) ## 改动文件 - `apps/api/app/api/routes/upload.py`:添加 `_infer_mime_type_from_storage_key` + `_create_pending_asset` - `apps/worker/worker_app/tasks/ingest.py`:先查找已有 Asset,有则更新,无则创建 - `packages/ports/asset_repository.py`:新增 `find_by_storage_key` 抽象方法 - `packages/adapters/in_memory/asset_repository.py`:实现 `find_by_storage_key` - `packages/adapters/sqlalchemy_impl/asset_repository.py`:实现 `find_by_storage_key` - `tests/unit/test_form_upload_routes.py`:更新测试 mock 注:此 PR 替换原 #1642(base 分支从 main 改为 develop)
xiaoxia added 1 commit 2026-09-03 14:42:47 +08:00
fix: 上传素材后立即在视频库显示(上传时创建PROCESSING状态Asset)
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 / Build 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 / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 31s
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 / PR Build Worker Image (pull_request) Successful in 15s
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
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m11s
AI Code Review / AI Code Review (pull_request) Successful in 1m52s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 1m53s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m59s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 2m7s
CI/CD Pipeline / Validate - Style (pull_request) Failing after 2m44s
CI/CD Pipeline / Validate - Security (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (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
PR Automation / Auto Approve on CI Green (pull_request) Has been cancelled
0deccf0314
Root cause: /upload/direct/complete 和 upload_asset 只创建 IngestJob,
Asset 记录由 worker 异步创建。前端上传后 invalidate query 刷新列表时,
新素材还不存在于 DB 中,所以看不到。

Fix:
- API 端上传完成后立即创建 Asset(status=PROCESSING),前端刷新后即可看到
- Worker 通过 find_by_storage_key 找到已有记录,更新元数据+改状态为 READY
- 兜底:若无已有记录则创建新记录(旧版本兼容)
- 新增 find_by_storage_key 到 AssetRepository port + 两个实现
- 更新测试 mock 适配新逻辑

🚀 预览环境已部署

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

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

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

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

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1644 | | 预览链接 | [https://pr-1644.preview.xiaoxiajianji.com](https://pr-1644.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
auto-approve-bot added 1 commit 2026-09-03 14:45:34 +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 / Check if frontend-only change (pull_request) Successful in 2s
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 / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Build 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 / 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 / PR Build API Image (pull_request) Successful in 19s
CI/CD Pipeline / Deploy Staging (Watchtower auto-deploy) (pull_request) Has been skipped
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 20s
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
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m11s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 1m47s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 1m49s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m46s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 2m16s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 2m56s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 3m13s
AI Code Review / AI Code Review (pull_request) Failing after 4m16s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 5m27s
CI/CD Pipeline / Build Production Web Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Production Worker Image (pull_request) Has been skipped
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
CI/CD Pipeline / CI Gate (pull_request) Successful in 1s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 19s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 37s
07d0ddc386
Collaborator

代码审查结果 - PR #1644

【阻塞级判定】

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

📊 审查概览

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

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

  1. [apps/worker/worker_app/tasks/ingest.py:661] 异常捕获过于宽泛导致数据重复风险

    • 问题类型:数据损坏风险 / 逻辑bug
    • 问题描述:find_by_storage_key 外部使用了裸的 except Exception。如果数据库查询抛出异常(如网络抖动、超时),代码会捕获异常并继续执行,导致 existing_assetNone,进而触发“创建新 Asset”的逻辑。这会导致同一个 storage_key 对应多条 Asset 记录(一条由 API 创建的 PROCESSING 状态,一条由 Worker 创建的 READY 状态),造成数据重复。
    • 修改建议:不应捕获所有异常。如果 find_by_storage_key 可能不存在(旧版本兼容),应捕获 NotImplementedError 或特定异常;对于数据库连接等异常,应该向上抛出让任务重试,而不是静默失败并创建脏数据。
  2. [apps/api/app/api/routes/upload.py:244] 缺少去重检查导致数据一致性风险

    • 问题类型:逻辑bug / 数据损坏风险
    • 问题描述:在 _create_pending_asset 中直接创建 Asset 记录,未先检查 file_hash 是否已存在。如果用户重复上传相同文件,会创建新的 Asset 记录。如果数据库有唯一索引约束,这里会直接报错导致 500;如果没有约束,则会生成重复的素材记录,破坏业务去重逻辑。
    • 修改建议:在调用 _create_pending_asset 之前,必须调用 asset_repository.find_by_library_and_file_hash 检查文件是否已存在。如果存在,应直接返回已有的 Asset ID 或抛出 409 冲突错误,而不是创建新记录。
  3. [packages/adapters/sqlalchemy_impl/asset_repository.py:429] 字段映射逻辑错误导致查询失效

    • 问题类型:逻辑bug
    • 问题描述:find_by_storage_key 实现中,使用 AssetModel.file_url == storage_key 进行查询。通常 storage_key 是对象存储的路径(如 uploads/a.mp4),而 file_url 往往是完整的访问地址(如 https://bucket.oss.com/uploads/a.mp4)。如果两者格式不一致,该查询将永远返回 None,导致 Worker 无法更新 API 创建的记录,而是创建新的重复记录。
    • 修改建议:需确认 AssetModel.file_url 存储的是纯 Key 还是完整 URL。如果是完整 URL,应修改查询逻辑(例如使用 .endswith(storage_key) 或确保存入时只存 Key)。目前的实现存在极高的逻辑隐患。

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

  1. [apps/api/app/api/routes/upload.py:89] 默认 MIME 类型设置不当

    • 具体内容:_infer_mime_type_from_storage_key 函数在无法识别后缀时默认返回 "video/mp4"。如果用户上传了系统不支持的后缀文件(如 .psd, .doc),会被错误标记为视频,可能导致前端播放器尝试加载非视频文件而报错。建议默认返回 "application/octet-stream" 或抛出错误。
  2. [apps/worker/worker_app/tasks/ingest.py:680] 领域模型直接修改属性

    • 具体内容:Worker 中直接通过 asset.mime_type = ... 等方式修改领域对象属性。如果 Asset 是富领域模型,建议封装 update_metadatamark_as_ready 等方法来保证状态流转的一致性和业务逻辑封装。

良好实践

  • 接口设计良好,在 AssetRepository 端口中新增抽象方法,并同步更新了所有实现类(SQLAlchemy 和 InMemory),符合开闭原则。
  • 测试文件及时更新了 Stub 以适配新的依赖,保证了单元测试的覆盖。

格式检查通过 | 逻辑审查需修改 | 性能无明显问题


🤖 由 AI 代码审查机器人自动生成 | 2026-09-03 06:50:01 | 模型:

## 代码审查结果 - PR #1644 ### 【阻塞级判定】 - 是否存在阻塞级问题:是 - 阻塞级问题数量:3 个 ### 📊 审查概览 - 整体评价:需修改 - 建议级问题数量:2 个 ### 🔴 阻塞级问题(必须修复) 1. **[apps/worker/worker_app/tasks/ingest.py:661] 异常捕获过于宽泛导致数据重复风险** - 问题类型:数据损坏风险 / 逻辑bug - 问题描述:`find_by_storage_key` 外部使用了裸的 `except Exception`。如果数据库查询抛出异常(如网络抖动、超时),代码会捕获异常并继续执行,导致 `existing_asset` 为 `None`,进而触发“创建新 Asset”的逻辑。这会导致同一个 `storage_key` 对应多条 Asset 记录(一条由 API 创建的 PROCESSING 状态,一条由 Worker 创建的 READY 状态),造成数据重复。 - 修改建议:不应捕获所有异常。如果 `find_by_storage_key` 可能不存在(旧版本兼容),应捕获 `NotImplementedError` 或特定异常;对于数据库连接等异常,应该向上抛出让任务重试,而不是静默失败并创建脏数据。 2. **[apps/api/app/api/routes/upload.py:244] 缺少去重检查导致数据一致性风险** - 问题类型:逻辑bug / 数据损坏风险 - 问题描述:在 `_create_pending_asset` 中直接创建 Asset 记录,未先检查 `file_hash` 是否已存在。如果用户重复上传相同文件,会创建新的 Asset 记录。如果数据库有唯一索引约束,这里会直接报错导致 500;如果没有约束,则会生成重复的素材记录,破坏业务去重逻辑。 - 修改建议:在调用 `_create_pending_asset` 之前,必须调用 `asset_repository.find_by_library_and_file_hash` 检查文件是否已存在。如果存在,应直接返回已有的 Asset ID 或抛出 409 冲突错误,而不是创建新记录。 3. **[packages/adapters/sqlalchemy_impl/asset_repository.py:429] 字段映射逻辑错误导致查询失效** - 问题类型:逻辑bug - 问题描述:`find_by_storage_key` 实现中,使用 `AssetModel.file_url == storage_key` 进行查询。通常 `storage_key` 是对象存储的路径(如 `uploads/a.mp4`),而 `file_url` 往往是完整的访问地址(如 `https://bucket.oss.com/uploads/a.mp4`)。如果两者格式不一致,该查询将永远返回 `None`,导致 Worker 无法更新 API 创建的记录,而是创建新的重复记录。 - 修改建议:需确认 `AssetModel.file_url` 存储的是纯 Key 还是完整 URL。如果是完整 URL,应修改查询逻辑(例如使用 `.endswith(storage_key)` 或确保存入时只存 Key)。目前的实现存在极高的逻辑隐患。 ### 💡 改进建议(不阻塞合并) 1. **[apps/api/app/api/routes/upload.py:89] 默认 MIME 类型设置不当** - 具体内容:`_infer_mime_type_from_storage_key` 函数在无法识别后缀时默认返回 `"video/mp4"`。如果用户上传了系统不支持的后缀文件(如 .psd, .doc),会被错误标记为视频,可能导致前端播放器尝试加载非视频文件而报错。建议默认返回 `"application/octet-stream"` 或抛出错误。 2. **[apps/worker/worker_app/tasks/ingest.py:680] 领域模型直接修改属性** - 具体内容:Worker 中直接通过 `asset.mime_type = ...` 等方式修改领域对象属性。如果 `Asset` 是富领域模型,建议封装 `update_metadata` 或 `mark_as_ready` 等方法来保证状态流转的一致性和业务逻辑封装。 ### ✅ 良好实践 - 接口设计良好,在 `AssetRepository` 端口中新增抽象方法,并同步更新了所有实现类(SQLAlchemy 和 InMemory),符合开闭原则。 - 测试文件及时更新了 Stub 以适配新的依赖,保证了单元测试的覆盖。 --- ✅ 格式检查通过 | ❌ 逻辑审查需修改 | ✅ 性能无明显问题 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-09-03 06:50:01 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
auto-approve-bot approved these changes 2026-09-03 14:56:03 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot approved these changes 2026-09-03 14:56:04 +08:00
auto-approve-bot left a comment
Collaborator

CI全绿,自动审批通过。

CI全绿,自动审批通过。
auto-approve-bot merged commit ec45d71d2c into develop 2026-09-03 14:56:35 +08:00
auto-approve-bot deleted branch fix/upload-asset-not-visible-v2 2026-09-03 14:56:36 +08:00

🗑️ 预览环境已清理

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

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

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