From ed849efbabe78dd00fe4f3644f0b1a2a4b683e43 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Thu, 16 Jul 2026 09:52:58 +0800 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=E7=94=9F=E6=88=90=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E5=8F=96=E6=B6=88=E6=8E=A5=E5=8F=A3=20-=20POST=20/tas?= =?UTF-8?q?ks/{task=5Fid}/cancel?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 新增生成任务取消接口: - 支持 pending / running 状态的任务取消 - 取消后状态变为 cancelled,记录取消日志 - 终态(completed/failed/cancelled)不可取消,返回 409 - 权限校验:只能取消自己创建的任务 - 复用领域模型已有的 mark_cancelled 方法 --- apps/api/app/api/routes/generation_tasks.py | 50 +++++++++++++++++++++ 1 file changed, 50 insertions(+) mode change 100644 => 100755 apps/api/app/api/routes/generation_tasks.py diff --git a/apps/api/app/api/routes/generation_tasks.py b/apps/api/app/api/routes/generation_tasks.py old mode 100644 new mode 100755 index 7f7b3fa60..4f625ec53 --- a/apps/api/app/api/routes/generation_tasks.py +++ b/apps/api/app/api/routes/generation_tasks.py @@ -427,3 +427,53 @@ def retry_generation_task( detail="系统繁忙,请稍后再试", ) from None return _to_generation_task_response(retried) + + +@router.post("/tasks/{task_id}/cancel", response_model=GenerationTaskResponse) +def cancel_generation_task( + task_id: str, + authenticated_user: AuthenticatedUser = Depends(get_current_user), + generation_task_repository: Any = Depends(get_generation_task_repository), +) -> GenerationTaskResponse: + """取消生成任务。 + + 仅 pending / running 状态的任务可取消;取消后状态变为 cancelled。 + 对于已在运行的 Celery 任务,标记为 cancelled 后,worker 在下次检查点会中止执行。 + """ + task = generation_task_repository.get(task_id) + if task is None: + raise HTTPException(status_code=404, detail="Generation task not found") + + # 权限校验 + if task.created_by_user_id and task.created_by_user_id != authenticated_user.user.id: + raise HTTPException(status_code=403, detail="Access denied to this task") + + status_val = task.status.value if hasattr(task.status, "value") else str(task.status) + + # 终态不可取消 + if status_val in ("completed", "failed", "cancelled"): + raise HTTPException( + status_code=409, + detail=f"Cannot cancel task in {status_val} status", + ) + + # 执行取消 + try: + task.mark_cancelled() + task.append_log( + stage="cancelled", + message="用户主动取消任务", + level="INFO", + cancelled_by=authenticated_user.user.id, + ) + generation_task_repository.update(task) + logger.info( + "生成任务已取消: task_id=%s user_id=%s previous_status=%s", + task_id, + authenticated_user.user.id, + status_val, + ) + except ValueError as e: + raise HTTPException(status_code=400, detail=str(e)) from e + + return _to_generation_task_response(task) -- 2.54.0 From 9c2ffb32fc8bab7a7fa338499bef5ae262c45338 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Thu, 16 Jul 2026 10:06:02 +0800 Subject: [PATCH 2/3] =?UTF-8?q?feat:=20worker=E6=B8=B2=E6=9F=93=E4=BB=BB?= =?UTF-8?q?=E5=8A=A1=E5=8F=96=E6=B6=88=E6=A3=80=E6=9F=A5=E7=82=B9=20-=20?= =?UTF-8?q?=E7=B4=A0=E6=9D=90=E4=B8=8B=E8=BD=BD=E5=90=8E=E6=A3=80=E6=B5=8B?= =?UTF-8?q?cancelled=E7=8A=B6=E6=80=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 在渲染主流程中增加取消检查点: - 素材下载完成后、启动FFmpeg前,检查generation_task状态 - 如果已被取消(cancelled),立即中止,不启动渲染 - 计划状态从 rendering 切回 editing,用户可继续编辑 - 与 API 层的取消接口配套 不修改 FFmpeg 运行时取消(后续迭代),覆盖排队/下载期间取消的场景 --- .../worker_app/tasks/edit_plan_generation.py | 17 +++++++++++++++++ 1 file changed, 17 insertions(+) diff --git a/apps/worker/worker_app/tasks/edit_plan_generation.py b/apps/worker/worker_app/tasks/edit_plan_generation.py index 3eb54cc93..19ce9d826 100755 --- a/apps/worker/worker_app/tasks/edit_plan_generation.py +++ b/apps/worker/worker_app/tasks/edit_plan_generation.py @@ -491,6 +491,23 @@ def render_edit_plan(self, plan_id: str) -> dict: gen_task_repo.update(gen_task) # 4. 根据引擎选择渲染方式 + # 取消检查:素材下载完后,确认任务没有被用户取消 + if generation_task_id: + current_task = gen_task_repo.get(generation_task_id) + if current_task: + task_status = current_task.status.value if hasattr(current_task.status, "value") else str(current_task.status) + if task_status == "cancelled": + logger.info("任务已被取消,中止渲染: plan_id=%s task_id=%s", plan_id, generation_task_id) + # 计划回到 editing 状态,用户可以继续编辑 + from packages.domain.edit_plan import EditPlanStatus + if plan.status.value == "rendering": + try: + plan.resume_editing() + plan_repo.update(plan) + except ValueError: + pass + return {"status": "cancelled", "plan_id": plan_id, "message": "任务已取消"} + if engine == "unified": result = _render_with_unified( plan=plan, -- 2.54.0 From 216a02005b568221881e23de5c55af477b4f5371 Mon Sep 17 00:00:00 2001 From: CI Bot Date: Thu, 16 Jul 2026 14:09:58 +0800 Subject: [PATCH 3/3] style: auto-format with black + isort (runner env) --- apps/worker/worker_app/tasks/edit_plan_generation.py | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/apps/worker/worker_app/tasks/edit_plan_generation.py b/apps/worker/worker_app/tasks/edit_plan_generation.py index 19ce9d826..b671df4b5 100755 --- a/apps/worker/worker_app/tasks/edit_plan_generation.py +++ b/apps/worker/worker_app/tasks/edit_plan_generation.py @@ -495,11 +495,16 @@ def render_edit_plan(self, plan_id: str) -> dict: if generation_task_id: current_task = gen_task_repo.get(generation_task_id) if current_task: - task_status = current_task.status.value if hasattr(current_task.status, "value") else str(current_task.status) + task_status = ( + current_task.status.value + if hasattr(current_task.status, "value") + else str(current_task.status) + ) if task_status == "cancelled": logger.info("任务已被取消,中止渲染: plan_id=%s task_id=%s", plan_id, generation_task_id) # 计划回到 editing 状态,用户可以继续编辑 from packages.domain.edit_plan import EditPlanStatus + if plan.status.value == "rendering": try: plan.resume_editing() -- 2.54.0