fix: AI Code Review 阻塞级问题修复 — 分辨率一致性校验+空指针防护
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 / 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 34s
CI/CD Pipeline / PR Build Web Image (pull_request) Has been skipped
CI/CD Pipeline / Frontend Unit Tests (pull_request) Has been skipped
CI/CD Pipeline / Frontend Lint (pull_request) Has been skipped
CI/CD Pipeline / Validate - Migration (alembic) (pull_request) Successful in 1m31s
CI/CD Pipeline / Validate - Type Check (mypy) (pull_request) Successful in 1m39s
Preview Deploy / Deploy Preview Environment (pull_request) Successful in 1m42s
PR Automation / Auto Merge on CI Green + Approved (pull_request) Successful in 1m56s
CI/CD Pipeline / PR Build API Image (pull_request) Successful in 2m58s
PR Automation / Auto Approve on CI Green (pull_request) Successful in 3m44s
CI/CD Pipeline / Validate - Code Quality (pull_request) Successful in 4m30s
CI/CD Pipeline / PR Build Worker Image (pull_request) Successful in 4m35s
AI Code Review / AI Code Review (pull_request) Successful in 6m58s
CI/CD Pipeline / Integration Tests (pull_request) Successful in 4m8s
CI/CD Pipeline / Unit Tests (pull_request) Successful in 8m13s
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 / Build Production Web 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 / CI Gate (pull_request) Successful in 12s
Preview Cleanup / Cleanup Preview Environment (pull_request) Successful in 51s
ACR Cleanup / ACR Image Cleanup (pull_request_target) Successful in 1m4s

This commit is contained in:
CI Bot
2026-08-11 06:44:32 +08:00
parent ed80fd7205
commit 142d4331cc
4 changed files with 78 additions and 16 deletions
+31 -14
View File
@@ -369,23 +369,40 @@ def confirm_generation(
if source_task.project_id:
check_project_access(source_task.project_id, authenticated_user.user.id, project_repository)
# 3. 如果预览任务已完成,直接复用产物(秒出)
# 3. 如果预览任务已完成,检查分辨率一致性后复用产物(秒出)
if source_task.is_completed and getattr(source_task, "is_preview", False):
source_task.mark_confirmed(
cover_url=request.cover_url,
custom_title=request.custom_title,
output_width=request.output_width,
output_height=request.output_height,
)
generation_task_repository.update(source_task)
# 校验请求的分辨率是否与预览实际渲染的分辨率一致
req_w = request.output_width or 0
req_h = request.output_height or 0
src_w = getattr(source_task, "output_width", 0) or 0
src_h = getattr(source_task, "output_height", 0) or 0
resolution_match = (req_w == 0 or req_w == src_w) and (req_h == 0 or req_h == src_h)
if resolution_match:
source_task.mark_confirmed(
cover_url=request.cover_url,
custom_title=request.custom_title,
output_width=request.output_width,
output_height=request.output_height,
)
generation_task_repository.update(source_task)
logger.info(
"[确认生成] 复用预览产物: task_id=%s, user_id=%s",
task_id,
authenticated_user.user.id,
)
return BatchGenerationTaskResponse(
items=[_to_generation_task_response(source_task)],
total=1,
)
# 分辨率不一致,跳过复用,走新建任务流程
logger.info(
"[确认生成] 复用预览产物: task_id=%s, user_id=%s",
"[确认生成] 分辨率不一致,跳过复用: task_id=%s, src=%sx%s, req=%sx%s",
task_id,
authenticated_user.user.id,
)
return BatchGenerationTaskResponse(
items=[_to_generation_task_response(source_task)],
total=1,
src_w,
src_h,
req_w,
req_h,
)
# 4. 预览任务未完成,创建新的正式任务走渲染流程
@@ -206,6 +206,9 @@ def _find_reusable_preview_task(gen_task_repo, plan_id: str, plan) -> "object |
completed_at = getattr(task, "completed_at", None)
if completed_at and hasattr(plan, "updated_at"):
plan_updated = plan.updated_at
# 如果 plan.updated_at 为空,无法判断是否修改过,跳过
if plan_updated is None:
continue
# 如果 plan 在预览完成后又被修改了,不能复用
if plan_updated > completed_at:
continue
+27 -2
View File
@@ -198,8 +198,8 @@ def _make_preview_task(**kwargs: Any) -> GenerationTask:
asset_select_mode="all",
is_preview=True,
source_task_id="",
output_width=1280,
output_height=720,
output_width=1080,
output_height=1920,
cover_url="",
custom_title="",
video_title="",
@@ -405,3 +405,28 @@ class TestConfirmGenerationErrors:
item = resp.json()["items"][0]
assert item["output_width"] == 1080
assert item["output_height"] == 1920
def test_confirm_skips_reuse_when_resolution_mismatch(
self,
client: TestClient,
gen_task_repo: StubGenerationTaskRepository,
) -> None:
"""请求的分辨率与预览渲染的分辨率不一致时,跳过复用,创建新任务"""
preview = _make_preview_task(output_width=1080, output_height=1920)
gen_task_repo.create(preview)
initial_count = len(gen_task_repo._store)
with patch("app.api.routes.generation_tasks.safe_enqueue_generation_task", return_value=True):
resp = client.post(
f"/api/v1/tasks/{preview.id}/confirm",
json={"output_width": 720, "output_height": 1280},
)
assert resp.status_code == 200
item = resp.json()["items"][0]
# 创建了新任务(而非复用)
assert item["id"] != preview.id
assert item["is_preview"] is False
assert item["source_task_id"] == preview.id
assert len(gen_task_repo._store) == initial_count + 1
+17
View File
@@ -268,3 +268,20 @@ class TestMarkConfirmed:
task.mark_confirmed(output_width=0, output_height=0)
assert task.output_width == 1920
assert task.output_height == 1080
def test_skips_when_plan_updated_at_is_none(self):
from app.api.routes.templates_editor.generation import _find_reusable_preview_task
repo = StubGenTaskRepo()
now = datetime.now(timezone.utc)
task = _make_task(
source_edit_plan_id="plan-1",
is_preview=True,
status=GenerationTaskStatus.COMPLETED,
completed_at=now - timedelta(minutes=5),
)
repo.create(task)
plan = _make_plan(updated_at=None)
result = _find_reusable_preview_task(repo, "plan-1", plan)
assert result is None