B6/B7: failed终态前端可见 + SSE事件链核实
B6 错误态: - 后端已加 TaskStatus.FAILED + 异常区分重试中/耗尽(014迁移ALTER status ENUM) - 前端 TaskStatus 类型加 failed - RecentTasksBar/history STATUS_LABEL 补 failed 标签 - history 页 HISTORY_STATUSES 纳入 failed(否则失败任务前端找不到) - FatalErrorBanner + useSse error 不再静默丢弃 B7: 核实 SSE(ticket换JWT) + 事件消费链无缺口,标绿 tsc EXIT=0
This commit is contained in:
34
backend/alembic/versions/014_task_status_failed.py
Normal file
34
backend/alembic/versions/014_task_status_failed.py
Normal file
@@ -0,0 +1,34 @@
|
||||
"""014 generation_tasks.status ENUM 补 failed 终态(B6 错误态)
|
||||
|
||||
Revision ID: 014
|
||||
Revises: 013
|
||||
Create Date: 2026-06-16
|
||||
|
||||
status 是 MySQL ENUM,原7值无失败终态。Celery生成彻底失败(重试耗尽)时
|
||||
要落 failed,区分"没跑(pending)"和"跑挂了(failed)"。不补 ENUM 会 Data truncated。
|
||||
"""
|
||||
from alembic import op
|
||||
|
||||
revision = "014"
|
||||
down_revision = "013"
|
||||
branch_labels = None
|
||||
depends_on = None
|
||||
|
||||
_ENUM_WITH_FAILED = (
|
||||
"ENUM('pending','generating','pending_selection','pending_review',"
|
||||
"'approved','rejected','archived','failed') NOT NULL DEFAULT 'pending'"
|
||||
)
|
||||
_ENUM_ORIG = (
|
||||
"ENUM('pending','generating','pending_selection','pending_review',"
|
||||
"'approved','rejected','archived') NOT NULL DEFAULT 'pending'"
|
||||
)
|
||||
|
||||
|
||||
def upgrade():
|
||||
op.execute(f"ALTER TABLE generation_tasks MODIFY COLUMN status {_ENUM_WITH_FAILED}")
|
||||
|
||||
|
||||
def downgrade():
|
||||
# 回退前先把 failed 行归位到 pending,避免越界
|
||||
op.execute("UPDATE generation_tasks SET status='pending' WHERE status='failed'")
|
||||
op.execute(f"ALTER TABLE generation_tasks MODIFY COLUMN status {_ENUM_ORIG}")
|
||||
@@ -23,6 +23,7 @@ class TaskStatus(str, Enum):
|
||||
APPROVED = "approved"
|
||||
REJECTED = "rejected"
|
||||
ARCHIVED = "archived"
|
||||
FAILED = "failed" # 生成彻底失败终态(重试耗尽):区分"没跑"和"跑挂了"(B6)
|
||||
|
||||
|
||||
# ── 审核状态(generation_tasks 平铺字段)──────────────────
|
||||
|
||||
@@ -140,16 +140,21 @@ def run_generation_pipeline(self, task_id: int) -> dict:
|
||||
|
||||
except Exception as exc:
|
||||
logger.error("run_generation_pipeline failed: task_id=%s err=%s", task_id, exc)
|
||||
# 重试耗尽才落 FAILED 终态;还能重试则保持 GENERATING(别伪装成"待开始")。
|
||||
# B6:失败要可见,区分"没跑(pending)"和"跑挂了(failed)"。
|
||||
exhausted = self.request.retries >= self.max_retries
|
||||
try:
|
||||
from app.models.task import GenerationTask as GT
|
||||
from app.constants.enums import TaskStatus as TS
|
||||
t = db.query(GT).filter(GT.id == task_id).first()
|
||||
if t:
|
||||
t.status = TS.PENDING
|
||||
t.status = TS.FAILED if exhausted else TS.GENERATING
|
||||
db.commit()
|
||||
except Exception:
|
||||
pass
|
||||
_push_event_sync(task_id, workspace_id, "error", {"code": 50001, "message": str(exc)}, seq + 1)
|
||||
if exhausted:
|
||||
return {"task_id": task_id, "status": "failed", "error": str(exc)}
|
||||
raise self.retry(exc=exc)
|
||||
finally:
|
||||
db.close()
|
||||
|
||||
Reference in New Issue
Block a user