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)