baodan/api/insurance/models/generation_task.py
wsb1224 c7df34d0cd 07-27 阶段 状态 进度
阶段 1:数据库与工作区	 完成	100%
阶段 2:Celery 后台任务	 完成	100%
阶段 3:前端刷新恢复与多工作区	 完成	100%
阶段 4:任务坞与任务中心	 完成	100%
阶段 5:版本化编辑	 完成	100%
阶段 6:测试与灰度	 未开始	0%
2026-07-28 17:53:14 +08:00

98 lines
4.9 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""统一生成任务模型。"""
import uuid
from sqlalchemy import Column, String, Text, Integer, TIMESTAMP, func
from insurance.db.compat import db
class GenerationTask(db.Model):
"""统一生成任务表PPT + 海报)。
职责分离:
- 工作区PptSession/PosterRecord保存可编辑数据
- 任务表保存一次执行的不可变快照、状态和结果
"""
__tablename__ = "insurance_generation_tasks"
id = Column(String(36), primary_key=True, default=lambda: uuid.uuid4().hex)
user_id = Column(String(64), nullable=False, comment="用户 ID")
artifact_type = Column(String(10), nullable=False, comment="ppt/poster")
operation = Column(String(10), nullable=False, comment="parse/generate")
workspace_id = Column(String(36), nullable=False, comment="PptSession.id 或 PosterRecord.id")
title_snapshot = Column(String(200), default="", comment="提交时任务名称")
status = Column(String(20), nullable=False, default="queued",
comment="queued/running/done/failed/cancelled")
stage = Column(String(30), default="", comment="当前执行阶段")
progress = Column(Integer, default=0, comment="进度 0-100")
message = Column(String(500), default="", comment="用户可读进度")
error_code = Column(String(50), default="", comment="结构化错误代码")
error_message = Column(Text, nullable=True, comment="用户可读错误")
input_revision = Column(Integer, default=1, comment="本次生成使用的草稿版本")
input_snapshot_json = Column(Text, nullable=True, comment="不可变输入快照")
output_json = Column(Text, nullable=True, comment="输出路径、页数、预览等")
idempotency_key = Column(String(100), nullable=True, comment="防止重复提交")
celery_task_id = Column(String(200), nullable=True, comment="Celery ID")
attempt_count = Column(Integer, default=0, comment="重试次数")
heartbeat_at = Column(TIMESTAMP, nullable=True, comment="Worker 心跳")
started_at = Column(TIMESTAMP, nullable=True, comment="开始时间")
finished_at = Column(TIMESTAMP, nullable=True, comment="完成时间")
viewed_at = Column(TIMESTAMP, nullable=True, comment="用户查看时间")
dock_hidden_at = Column(TIMESTAMP, nullable=True, comment="从任务坞隐藏时间")
created_at = Column(TIMESTAMP, server_default=func.now())
updated_at = Column(TIMESTAMP, server_default=func.now(), onupdate=func.now())
def to_dict(self):
import json
return {
"id": self.id,
"userId": self.user_id,
"artifactType": self.artifact_type,
"operation": self.operation,
"workspaceId": self.workspace_id,
"titleSnapshot": self.title_snapshot,
"status": self.status,
"stage": self.stage,
"progress": self.progress,
"message": self.message,
"errorCode": self.error_code,
"errorMessage": self.error_message,
"inputRevision": self.input_revision,
"inputSnapshot": json.loads(self.input_snapshot_json) if self.input_snapshot_json else None,
"output": json.loads(self.output_json) if self.output_json else None,
"idempotencyKey": self.idempotency_key,
"celeryTaskId": self.celery_task_id,
"attemptCount": self.attempt_count,
"heartbeatAt": self.heartbeat_at.isoformat() if self.heartbeat_at else None,
"startedAt": self.started_at.isoformat() if self.started_at else None,
"finishedAt": self.finished_at.isoformat() if self.finished_at else None,
"viewedAt": self.viewed_at.isoformat() if self.viewed_at else None,
"dockHiddenAt": self.dock_hidden_at.isoformat() if self.dock_hidden_at else None,
"createdAt": self.created_at.isoformat() if self.created_at else None,
"updatedAt": self.updated_at.isoformat() if self.updated_at else None,
}
@staticmethod
def create_task(user_id: str, artifact_type: str, operation: str,
workspace_id: str, title: str = "",
input_snapshot: dict = None, idempotency_key: str = None):
"""创建新任务(幂等检查)。"""
import json
if idempotency_key:
existing = GenerationTask.query.filter_by(
idempotency_key=idempotency_key,
).filter(GenerationTask.status.in_(["queued", "running", "done"])).first()
if existing:
return existing
task = GenerationTask(
user_id=user_id,
artifact_type=artifact_type,
operation=operation,
workspace_id=workspace_id,
title_snapshot=title,
input_snapshot_json=json.dumps(input_snapshot, ensure_ascii=False) if input_snapshot else None,
idempotency_key=idempotency_key,
)
db.session.add(task)
db.session.commit()
return task