feat: 素材片段区间持久化去重 + 区间用尽自动轮回 #1543

Merged
xiaoxia merged 3 commits from feat/persist-used-segments into develop 2026-08-29 21:19:16 +08:00
Owner

背景

片段级去重(used_segments)此前只在单次 from-assets 调用内存中生效,每次调用 used_segments = {} 空字典起步(clips.py),用完就丢。PR #1522 做过的跨任务去重在 #1524/#1525 重构中丢失。

导致的真实问题:

  • 用户反复对同一批素材 from-assets,新片段区间与历史片段重叠 → 观众看到重复内容
  • 本 PR 为 PR1(核心修复);批量生成各自独立 plan 为 PR2(后续单独提)

metadata.used_time_ranges schema

存储在素材 metadata(assets.classification_result JSON,即 domain Asset.metadata)中:

{
  "used_time_ranges": [
    {
      "start": 12.5,
      "end": 20.3,
      "plan_id": "plan-xxx",
      "created_at": "2026-08-29T12:00:00+00:00"
    }
  ]
}
  • start / end:片段在素材内的起止时间(秒,float,保留 3 位小数)
  • plan_id:使用该区间的剪辑计划 ID
  • created_at:记录写入时间(ISO8601 UTC)
  • 同一素材多条记录表示历史多次使用;区间轮完一圈后该数组被清空重置(轮回)
  • 与既有字段 generation_use_count / last_used_at / review_status 共存,互不影响

改动文件

1. apps/api/app/services/asset_segment_tracker.py(新增,171 行)

片段级使用记录追踪服务。所有函数不自行 commit,事务由调用方控制:

  • get_used_segments(db, asset_ids):批量聚合素材 metadata 中的历史区间,返回 {asset_id: [(start, end), ...]},格式与 _calc_random_start_timeused_segments 参数一致,可直接传入。一次 IN 查询,O(N)。
  • record_used_segments(db, asset_id, start, end, plan_id):追加一条区间记录(保留 metadata 其他字段)。
  • remove_used_segment(db, asset_id, start, end, plan_id, tolerance=0.5):按 plan_id + 起止时间(±0.5s 容差)匹配删除一条记录,供 MediaKit 移动片段后更新。
  • reset_used_segments(db, asset_id):清空单个素材的区间记录(轮回重置),其他 metadata 字段不动。
  • make_reset_callback(db, used_segments):构造给 _calc_random_start_time 的回调,同时清空持久化记录和内存 dict。

2. apps/api/app/api/routes/templates_editor/clips.py

  • create_clips_from_assets_editor
    • used_segments = {}get_used_segments(db, unique_asset_ids),读取跨任务历史区间
    • _calc_random_start_time 传入 on_exhausted=reset_cb(区间用尽自动轮回)
    • 每个片段创建时同步 record_used_segments(...) 写入 metadata;不 commit,与 replace_all_clips_transactional 共用同一请求级 session 和事务,任一步失败整体回滚,不留脏数据
  • _update_mediakit_recommendations_async:MediaKit 更新片段 start_time 成功后,remove_used_segment(旧区间,按 plan_id+旧 start 匹配)+ record_used_segments(新区间)并 commit;异步任务失败静默,不影响已创建片段

3. packages/domain/plan_generator_utils.py

  • _calc_random_start_time 新增可选参数 on_exhausted: Callable[[str], None]:100 次随机都找不到空闲区间时调用(轮回重置钩子),回调后再随机重试一次;仍失败才走原降级逻辑(素材末尾/0)。不传回调时行为与之前完全一致(向后兼容)。

4. 测试

  • tests/unit/test_asset_segment_tracker.py(新增,14 用例):聚合/追加/删除/容差匹配/plan_id 校验/轮回重置/回调双清/on_exhausted 触发与不触发
  • tests/unit/test_editor_clips_random_start.py:既有 from-assets 测试 mock 新 tracker 函数(保持原断言意图),fake_calc 兼容 on_exhausted 参数

不动的地方

  • smart_match.pyPOST /assets/smart-match(素材级评分)
  • mark_asset_used_for_generation(素材级计数)
  • _calc_random_start_time 核心避开逻辑(100 次尝试 + 重叠判定),只加了重置钩子
  • 前端:后端自动处理,前端无感知

验收对照

  • 跨任务去重:from-assets 读取持久化区间,新片段避开历史区间(单测覆盖聚合+避开)
  • 自动轮回:区间占满 → on_exhausted 清空该素材记录 → 重试随机成功(单测覆盖)
  • MediaKit 同步:移动起点后删旧记录写新记录(异步静默)
  • 失败回滚:metadata 写入与 replace_all_clips_transactional 同事务
  • 性能:get_used_segments 单次 IN 查询 O(N),无 N+1
  • 本地 203 个相关测试全过(14 新增 + 189 既有 clips/mediakit/plan_generator 相关)

后续 PR2(不在本 PR)

批量生成 count>1 时为每个 task 克隆独立 plan 并对 clip 重新随机起点,解决 N 条视频片段区间一模一样。

## 背景 片段级去重(used_segments)此前只在单次 from-assets 调用内存中生效,每次调用 `used_segments = {}` 空字典起步(clips.py),用完就丢。PR #1522 做过的跨任务去重在 #1524/#1525 重构中丢失。 导致的真实问题: - 用户反复对同一批素材 from-assets,新片段区间与历史片段重叠 → 观众看到重复内容 - 本 PR 为 PR1(核心修复);批量生成各自独立 plan 为 PR2(后续单独提) ## metadata.used_time_ranges schema 存储在素材 metadata(`assets.classification_result` JSON,即 domain `Asset.metadata`)中: ```json { "used_time_ranges": [ { "start": 12.5, "end": 20.3, "plan_id": "plan-xxx", "created_at": "2026-08-29T12:00:00+00:00" } ] } ``` - `start` / `end`:片段在素材内的起止时间(秒,float,保留 3 位小数) - `plan_id`:使用该区间的剪辑计划 ID - `created_at`:记录写入时间(ISO8601 UTC) - 同一素材多条记录表示历史多次使用;区间轮完一圈后该数组被清空重置(轮回) - 与既有字段 `generation_use_count` / `last_used_at` / `review_status` 共存,互不影响 ## 改动文件 ### 1. `apps/api/app/services/asset_segment_tracker.py`(新增,171 行) 片段级使用记录追踪服务。所有函数**不自行 commit**,事务由调用方控制: - `get_used_segments(db, asset_ids)`:批量聚合素材 metadata 中的历史区间,返回 `{asset_id: [(start, end), ...]}`,格式与 `_calc_random_start_time` 的 `used_segments` 参数一致,可直接传入。一次 IN 查询,O(N)。 - `record_used_segments(db, asset_id, start, end, plan_id)`:追加一条区间记录(保留 metadata 其他字段)。 - `remove_used_segment(db, asset_id, start, end, plan_id, tolerance=0.5)`:按 plan_id + 起止时间(±0.5s 容差)匹配删除一条记录,供 MediaKit 移动片段后更新。 - `reset_used_segments(db, asset_id)`:清空单个素材的区间记录(轮回重置),其他 metadata 字段不动。 - `make_reset_callback(db, used_segments)`:构造给 `_calc_random_start_time` 的回调,同时清空持久化记录和内存 dict。 ### 2. `apps/api/app/api/routes/templates_editor/clips.py` - `create_clips_from_assets_editor`: - `used_segments = {}` → `get_used_segments(db, unique_asset_ids)`,读取跨任务历史区间 - `_calc_random_start_time` 传入 `on_exhausted=reset_cb`(区间用尽自动轮回) - 每个片段创建时同步 `record_used_segments(...)` 写入 metadata;**不 commit**,与 `replace_all_clips_transactional` 共用同一请求级 session 和事务,任一步失败整体回滚,不留脏数据 - `_update_mediakit_recommendations_async`:MediaKit 更新片段 start_time 成功后,`remove_used_segment`(旧区间,按 plan_id+旧 start 匹配)+ `record_used_segments`(新区间)并 commit;异步任务失败静默,不影响已创建片段 ### 3. `packages/domain/plan_generator_utils.py` - `_calc_random_start_time` 新增可选参数 `on_exhausted: Callable[[str], None]`:100 次随机都找不到空闲区间时调用(轮回重置钩子),回调后再随机重试一次;仍失败才走原降级逻辑(素材末尾/0)。不传回调时行为与之前完全一致(向后兼容)。 ### 4. 测试 - `tests/unit/test_asset_segment_tracker.py`(新增,14 用例):聚合/追加/删除/容差匹配/plan_id 校验/轮回重置/回调双清/on_exhausted 触发与不触发 - `tests/unit/test_editor_clips_random_start.py`:既有 from-assets 测试 mock 新 tracker 函数(保持原断言意图),fake_calc 兼容 on_exhausted 参数 ## 不动的地方 - `smart_match.py`、`POST /assets/smart-match`(素材级评分) - `mark_asset_used_for_generation`(素材级计数) - `_calc_random_start_time` 核心避开逻辑(100 次尝试 + 重叠判定),只加了重置钩子 - 前端:后端自动处理,前端无感知 ## 验收对照 - 跨任务去重:from-assets 读取持久化区间,新片段避开历史区间(单测覆盖聚合+避开) - 自动轮回:区间占满 → on_exhausted 清空该素材记录 → 重试随机成功(单测覆盖) - MediaKit 同步:移动起点后删旧记录写新记录(异步静默) - 失败回滚:metadata 写入与 replace_all_clips_transactional 同事务 - 性能:get_used_segments 单次 IN 查询 O(N),无 N+1 - 本地 203 个相关测试全过(14 新增 + 189 既有 clips/mediakit/plan_generator 相关) ## 后续 PR2(不在本 PR) 批量生成 count>1 时为每个 task 克隆独立 plan 并对 clip 重新随机起点,解决 N 条视频片段区间一模一样。
xiaoxia added 1 commit 2026-08-29 20:15:27 +08:00
feat: 素材片段区间持久化去重 + 区间用尽自动轮回
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 / 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 / Check if frontend-only change (pull_request) Successful in 2m39s
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
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 3m43s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m8s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m15s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 5m30s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 8m33s
AI Code Review / AI Code Review (pull_request) Successful in 8m50s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 9m35s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration 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
Preview Deploy / Deploy Preview Environment (pull_request) Has been cancelled
2174e91c48
- 新增 asset_segment_tracker 服务:素材 metadata(used_time_ranges) 持久化片段级已用区间
- from-assets 创建片段时读取历史区间,新片段跨任务/跨调用自动避开
- 片段记录与 replace_all_clips_transactional 同事务,失败整体回滚
- MediaKit 异步移动片段起点后同步更新 metadata 区间记录(失败静默)
- _calc_random_start_time 新增 on_exhausted 回调:100次找不到时清空该素材历史区间再重试,实现轮完一圈自动循环

详见 PR body 的 metadata schema 说明
auto-approve-bot added 1 commit 2026-08-29 20:26:03 +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 Web Image (pull_request) Has been skipped
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 / 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 / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 2m36s
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 / PR Build API Image (pull_request) Successful in 1m31s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m35s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 4m36s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 4m54s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 5m45s
AI Code Review / AI Code Review (pull_request) Failing after 7m6s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 9m5s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 10m19s
CI/CD Pipeline / Validate - Code Quality (pull_request) Has been cancelled
CI/CD Pipeline / Unit Tests (pull_request) Has been cancelled
CI/CD Pipeline / Integration Tests (pull_request) Has been cancelled
CI/CD Pipeline / Staging API Integration 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
5d705307c1

🚀 预览环境已部署

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

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

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

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

🚀 **预览环境已部署** | 项目 | 详情 | |------|------| | PR号 | #1543 | | 预览链接 | [https://pr-1543.preview.xiaoxiajianji.com](https://pr-1543.preview.xiaoxiajianji.com) | | API环境 | staging | > 💡 预览环境使用 staging API 数据,请勿在预览环境中操作重要数据。 > > 🔄 每次提交新代码后预览环境会自动更新。 > > 🗑️ PR 关闭或合并后,预览环境会自动清理。
xiaoxia added 1 commit 2026-08-29 20:39:07 +08:00
fix: AI Code Review 阻塞问题修复
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 / 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 / ACR Image Cleanup (pull_request) Has been skipped
CI/CD Pipeline / Staging API Integration Tests (pull_request) Has been skipped
CI/CD Pipeline / Check if frontend-only change (pull_request) Successful in 3m10s
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
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 3m45s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 4m15s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 1m5s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 1m10s
AI Code Review / AI Code Review (pull_request) Successful in 5m8s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 5m24s
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 9m7s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 10m0s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 18m2s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 21m17s
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 / 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
CI/CD Pipeline / Integration Tests (pull_request) Successful in 19m16s
CI/CD Pipeline / CI Gate (pull_request) Successful in 28s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 4m5s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 4m30s
1b83ec9952
1. remove_used_segment 旧数据兼容:记录缺 plan_id(本功能上线前的旧数据)时
   退化为按时间匹配删除,避免旧区间永远删不掉导致素材容量泄漏
2. MediaKit 异步更新事务一致性:clip start_time 更新与 metadata 区间记录
   删旧/写新放入同一事务,metadata 失败时 rollback 本次 clip 更新,
   统一 commit,消除 clip 已提交、metadata 未更新的不一致
3. (建议) on_exhausted 回调异常增加 logger.warning,不再静默吞掉
4. 补充旧数据兼容单测
Collaborator

代码审查结果 - PR #1543

⚠️ 问题(1个需要修改)

  1. apps/api/app/api/routes/templates_editor/clips.py 第824-830行:逻辑错误导致数据不一致。
    • 问题描述:在 _update_mediakit_recommendations_async 函数中,只有当 remove_used_segment 返回 True 时才会调用 record_used_segments。如果 remove_used_segment 返回 False(例如:由于时间 tolerance 不匹配、旧数据无 plan_id 导致匹配失败,或记录不存在),record_used_segments 会被跳过。但是,plan_svc.update_clip 已经成功修改了片段的起始时间,且后续会执行 db.commit()。这会导致数据库中片段位置已更新,但素材 metadata 中的区间记录未更新
    • 后果:后续创建片段时,读取到的 metadata 是旧的,无法避开已移动的片段位置,导致片段时间重叠,违背了该功能的初衷。
    • 修改建议:无论 remove_used_segment 是否成功找到并删除旧记录,只要 plan_svc.update_clip 成功且未抛出异常,都应调用 record_used_segments 记录新的区间。建议修改逻辑为:先尝试 remove,然后无条件执行 record(除非 remove 抛出异常导致回滚)。

💡 建议(2个可选)

  1. apps/api/app/services/asset_segment_tracker.py 第38-41行:建议增加异常日志。
    • 描述_read_ranges 函数在 json.loads 失败时仅返回空字典,没有记录日志。如果 metadata 中存在脏数据导致解析失败,这将难以排查。建议在 except 块中增加 logger.warning 记录解析失败的原始数据或 asset_id。
  2. apps/api/app/services/asset_segment_tracker.py 第103-115行:建议清理所有匹配的重复记录。
    • 描述remove_used_segment 目前的逻辑是 if match and not removed: removed = True; continue,这意味着每次调用只会删除一条匹配的记录。如果 metadata 中存在重复的历史区间(虽然正常流程不应产生),该函数无法完全清理,会导致残留数据。建议改为删除所有匹配的记录,或者确认业务逻辑上绝对不会有重复记录。

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


🤖 由 AI 代码审查机器人自动生成 | 2026-08-29 12:44:16 | 模型:

## 代码审查结果 - PR #1543 ### ⚠️ 问题(1个需要修改) 1. **apps/api/app/api/routes/templates_editor/clips.py 第824-830行**:逻辑错误导致数据不一致。 - **问题描述**:在 `_update_mediakit_recommendations_async` 函数中,只有当 `remove_used_segment` 返回 `True` 时才会调用 `record_used_segments`。如果 `remove_used_segment` 返回 `False`(例如:由于时间 tolerance 不匹配、旧数据无 plan_id 导致匹配失败,或记录不存在),`record_used_segments` 会被跳过。但是,`plan_svc.update_clip` 已经成功修改了片段的起始时间,且后续会执行 `db.commit()`。这会导致**数据库中片段位置已更新,但素材 metadata 中的区间记录未更新**。 - **后果**:后续创建片段时,读取到的 metadata 是旧的,无法避开已移动的片段位置,导致**片段时间重叠**,违背了该功能的初衷。 - **修改建议**:无论 `remove_used_segment` 是否成功找到并删除旧记录,只要 `plan_svc.update_clip` 成功且未抛出异常,都应调用 `record_used_segments` 记录新的区间。建议修改逻辑为:先尝试 `remove`,然后无条件执行 `record`(除非 remove 抛出异常导致回滚)。 ### 💡 建议(2个可选) 1. **apps/api/app/services/asset_segment_tracker.py 第38-41行**:建议增加异常日志。 - **描述**:`_read_ranges` 函数在 `json.loads` 失败时仅返回空字典,没有记录日志。如果 metadata 中存在脏数据导致解析失败,这将难以排查。建议在 `except` 块中增加 `logger.warning` 记录解析失败的原始数据或 asset_id。 2. **apps/api/app/services/asset_segment_tracker.py 第103-115行**:建议清理所有匹配的重复记录。 - **描述**:`remove_used_segment` 目前的逻辑是 `if match and not removed: removed = True; continue`,这意味着每次调用只会删除**一条**匹配的记录。如果 metadata 中存在重复的历史区间(虽然正常流程不应产生),该函数无法完全清理,会导致残留数据。建议改为删除所有匹配的记录,或者确认业务逻辑上绝对不会有重复记录。 --- ✅ 格式检查通过 | ❌ 逻辑审查需修改 | ⚠️ 建议关注性能 --- <sub>🤖 由 AI 代码审查机器人自动生成 | 2026-08-29 12:44:16 | 模型: </sub> <!-- AI_CODE_REVIEW_AUTO_COMMENT -->
xiaoxia merged commit f655508f88 into develop 2026-08-29 21:19:16 +08:00

🗑️ 预览环境已清理

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

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

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