fix(smart-match): 过滤前置+余量/高频全过滤时空结果回退保留素材 #1590

Open
xiaoxia wants to merge 0 commits from fix/smart-match-empty-results into develop
Owner

问题

前端调用 POST /assets/smart-match 返回空 items,前端回退全选。

根因

路由旧实现顺序错误:先 smart_select_assets(limit=N) 截取 Top-N,再对这 N 条做 usable/高频过滤,过滤后不回补:

  1. 排名靠前素材恰好全部 usable=False(可切区间耗尽)→ 返回空
  2. usable 过滤在 limit 之后,被排除的名额不从排名靠后的候选回补
  3. 高频排除分支有「不足保留全部」回退,但余量过滤没有回退

修复

  • 余量过滤 + 高频排除全部前置到评分/limit 之前,在全量候选上执行
  • 余量过滤后为空(全部耗尽)→ 回退保留全部候选(耗尽素材仍可走复用区间;空结果反而让前端回退全选、绕过评分)
  • 高频排除后为空或不足 limit → 回退保留全部可用素材
  • 高频查询异常 → 跳过排除(保持不变)

测试

  • 新增 test_smart_match_fallback.py 7 用例:全耗尽回退、混合过滤排除耗尽、limit 回补、全高频回退、部分高频排除、不足 limit 回退、查询异常跳过
  • 更新 test_asset_availability.py 旧断言(全耗尽返回空 → 回退保留)
  • 相关 1792 测试全过
## 问题 前端调用 POST /assets/smart-match 返回空 items,前端回退全选。 ## 根因 路由旧实现顺序错误:先 smart_select_assets(limit=N) 截取 Top-N,再对这 N 条做 usable/高频过滤,过滤后不回补: 1. 排名靠前素材恰好全部 usable=False(可切区间耗尽)→ 返回空 2. usable 过滤在 limit 之后,被排除的名额不从排名靠后的候选回补 3. 高频排除分支有「不足保留全部」回退,但余量过滤没有回退 ## 修复 - 余量过滤 + 高频排除全部前置到评分/limit 之前,在全量候选上执行 - 余量过滤后为空(全部耗尽)→ 回退保留全部候选(耗尽素材仍可走复用区间;空结果反而让前端回退全选、绕过评分) - 高频排除后为空或不足 limit → 回退保留全部可用素材 - 高频查询异常 → 跳过排除(保持不变) ## 测试 - 新增 test_smart_match_fallback.py 7 用例:全耗尽回退、混合过滤排除耗尽、limit 回补、全高频回退、部分高频排除、不足 limit 回退、查询异常跳过 - 更新 test_asset_availability.py 旧断言(全耗尽返回空 → 回退保留) - 相关 1792 测试全过
xiaoxia added 1 commit 2026-09-01 00:44:18 +08:00
fix(smart-match): 过滤前置+余量/高频全过滤时空结果回退保留素材
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 4s
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 / Check if frontend-only change (pull_request) Successful in 7s
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
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 4m53s
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 API Image (pull_request) Successful in 5m8s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 3m50s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 6m59s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 6m56s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 7m31s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 8m38s
CI/CD Pipeline / Validate - Security (pull_request) Successful in 9m16s
AI Code Review / AI Code Review (pull_request) Successful in 10m24s
CI/CD Pipeline / Validate - Style (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
8e962b3af2
根因:路由旧实现先 smart_select_assets(limit=N) 截取 Top-N,再对这 N 条
做 usable/高频过滤,过滤后不回补。排名靠前素材恰好全部被排除时返回空
items,前端回退全选(smart-match 名存实亡);usable 过滤在 limit 之后,
被排除名额也不从排名靠后候选回补。

修复:
- 余量过滤、高频排除全部前置到评分/limit 之前,在全量候选上过滤
- 余量过滤后为空(全部可切区间耗尽)→ 回退保留全部候选
- 高频排除后为空或不足 limit → 回退保留全部可用素材(与旧逻辑一致)
- 高频查询异常 → 跳过排除(不变)
- 更新 test_all_exhausted_returns_empty → 回退语义
- 新增 test_smart_match_fallback.py 7 用例覆盖各回退路径

🚀 预览环境已部署

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

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

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

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

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1590 | | 预览链接 | [https://pr-1590.preview.xiaoxiajianji.com](https://pr-1590.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
auto-approve-bot added 1 commit 2026-09-01 00:57:09 +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 3s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Failing after 1s
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
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 3m48s
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 API Image (pull_request) Successful in 3m6s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m51s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 4m28s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 5m3s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 5m3s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 6m20s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 6m30s
AI Code Review / AI Code Review (pull_request) Failing after 7m37s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 18m27s
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) Failing after 0s
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
7e266d5d64
Collaborator

【阻塞级判定】

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

📊 审查概览

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

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

  1. [apps/api/api/routes/assets.py: 653-658] 逻辑错误:响应构建时丢失计算字段

    • 问题类型:逻辑bug
    • 问题描述:代码重构将 compute_asset_availability 的调用移到了前置过滤阶段(第593行),计算结果 avail 仅用于判断是否过滤,未保存。在后续构建 SmartMatchItem 时(第653-658行),直接尝试从 smart_select_assets 返回的对象 r 中读取 usableavailability 字段。由于 smart_select_assets 未在此PR中修改,且通常返回原始Asset对象或仅包含评分的包装对象,这些对象上不存在动态计算的 usable/availability 属性。这将导致 AttributeError 或字段为空,使得前端无法获取素材余量信息,且测试用例 assert all(item.usable ...) 将失败。
    • 修改建议:需要将前置过滤计算出的 usable/availability 数据传递到最终响应构建环节。建议在过滤循环中将计算结果附加到 Asset 对象上(如 a._computed_avail = avail),或在最终循环中重新调用 compute_asset_availability(需权衡性能),或者修改 smart_select_assets 以支持携带这些元数据。
  2. [apps/api/api/routes/assets.py: 653] 逻辑错误:对象属性访问不匹配

    • 问题类型:逻辑bug
    • 问题描述:旧代码中访问 r.asset.id,表明 smart_select_assets 返回的是一个包装对象(Wrapper),其中包含 .asset 属性指向原始素材。新代码改为直接访问 r.idr.name 等,而 smart_select_assets 函数并未在此PR中修改。这意味着调用方式与函数返回值的结构不兼容,极大概率会导致运行时出现 AttributeError: 'ScoredAsset' object has no attribute 'id'
    • 修改建议:确认 smart_select_assets 的返回值结构。如果它返回包装对象,应保持使用 r.asset.id;如果它返回原始对象,则需确认为何旧代码使用了 r.asset.id。在未修改 smart_select_assets 实现的前提下,恢复为 r.asset.id 访问方式更为安全。

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

良好实践

  • 回退策略设计合理:针对“余量耗尽”和“高频过滤”场景,实现了完善的回退逻辑,避免了过滤过严导致结果为空、前端降级全选的问题,提升了用户体验。
  • 测试覆盖全面:新增的 test_smart_match_fallback.py 详细覆盖了各种边界情况(全耗尽、部分耗尽、高频回退等),能有效验证回退逻辑的正确性。
  • 代码注释清晰:关键逻辑节点(如过滤前置、回退策略)均有详细的中文注释,解释了修改原因和预期行为,便于后续维护。

🤖 由 AI 代码审查机器人自动生成 | 2026-08-31 17:06:17 | 模型:

### 【阻塞级判定】 - 是否存在阻塞级问题:是 - 阻塞级问题数量:2 个 ### 📊 审查概览 - 整体评价:需修改 - 建议级问题数量:0 个 ### 🔴 阻塞级问题(必须修复) 1. **[apps/api/api/routes/assets.py: 653-658] 逻辑错误:响应构建时丢失计算字段** - 问题类型:逻辑bug - 问题描述:代码重构将 `compute_asset_availability` 的调用移到了前置过滤阶段(第593行),计算结果 `avail` 仅用于判断是否过滤,未保存。在后续构建 `SmartMatchItem` 时(第653-658行),直接尝试从 `smart_select_assets` 返回的对象 `r` 中读取 `usable` 和 `availability` 字段。由于 `smart_select_assets` 未在此PR中修改,且通常返回原始Asset对象或仅包含评分的包装对象,这些对象上不存在动态计算的 `usable`/`availability` 属性。这将导致 `AttributeError` 或字段为空,使得前端无法获取素材余量信息,且测试用例 `assert all(item.usable ...)` 将失败。 - 修改建议:需要将前置过滤计算出的 `usable`/`availability` 数据传递到最终响应构建环节。建议在过滤循环中将计算结果附加到 Asset 对象上(如 `a._computed_avail = avail`),或在最终循环中重新调用 `compute_asset_availability`(需权衡性能),或者修改 `smart_select_assets` 以支持携带这些元数据。 2. **[apps/api/api/routes/assets.py: 653] 逻辑错误:对象属性访问不匹配** - 问题类型:逻辑bug - 问题描述:旧代码中访问 `r.asset.id`,表明 `smart_select_assets` 返回的是一个包装对象(Wrapper),其中包含 `.asset` 属性指向原始素材。新代码改为直接访问 `r.id`、`r.name` 等,而 `smart_select_assets` 函数并未在此PR中修改。这意味着调用方式与函数返回值的结构不兼容,极大概率会导致运行时出现 `AttributeError: 'ScoredAsset' object has no attribute 'id'`。 - 修改建议:确认 `smart_select_assets` 的返回值结构。如果它返回包装对象,应保持使用 `r.asset.id`;如果它返回原始对象,则需确认为何旧代码使用了 `r.asset.id`。在未修改 `smart_select_assets` 实现的前提下,恢复为 `r.asset.id` 访问方式更为安全。 ### 💡 改进建议(不阻塞合并) 无 ### ✅ 良好实践 - **回退策略设计合理**:针对“余量耗尽”和“高频过滤”场景,实现了完善的回退逻辑,避免了过滤过严导致结果为空、前端降级全选的问题,提升了用户体验。 - **测试覆盖全面**:新增的 `test_smart_match_fallback.py` 详细覆盖了各种边界情况(全耗尽、部分耗尽、高频回退等),能有效验证回退逻辑的正确性。 - **代码注释清晰**:关键逻辑节点(如过滤前置、回退策略)均有详细的中文注释,解释了修改原因和预期行为,便于后续维护。 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-08-31 17:06:17 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
Some required checks failed
CI/CD Pipeline / Check push changed paths (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3s
CI/CD Pipeline / Dedup Check - skip PR tests when covered by push pipeline (pull_request) Successful in 3s
CI/CD Pipeline / Build Staging API Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Worker Image (pull_request) Has been skipped
CI/CD Pipeline / Build Staging Web Image (pull_request) Has been skipped
CI/CD Pipeline / Validate - Security (pull_request) Failing after 1s
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
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 3m48s
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 API Image (pull_request) Successful in 3m6s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 2m51s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 4m28s
CI/CD Pipeline / Validate - Style (pull_request) Successful in 5m3s
CI/CD Pipeline / Validate - Python (mypy + alembic) (pull_request) Successful in 5m3s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 6m20s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 6m30s
AI Code Review / AI Code Review (pull_request) Failing after 7m37s
Required
Details
CI/CD Pipeline / Unit Tests (pull_request) Successful in 18m27s
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) Failing after 0s
Required
Details
CI/CD Pipeline / Deploy Production (pull_request) Has been skipped
CI/CD Pipeline / Canary Release to Production (pull_request) Has been skipped
CI/CD Pipeline / Production Browser E2E (pull_request) Has been skipped
This branch is already included in the target branch. There is nothing to merge.
View command line instructions

Checkout

From your project repository, check out a new branch and test the changes.
git fetch -u origin fix/smart-match-empty-results:fix/smart-match-empty-results
git checkout fix/smart-match-empty-results
Sign in to join this conversation.