"""海报生成记录模型。""" from sqlalchemy import Column, String, Text, BigInteger, Integer, TIMESTAMP, func from insurance.db.compat import db class PosterRecord(db.Model): """海报生成记录表。""" __tablename__ = "poster_records" id = Column(BigInteger, primary_key=True, autoincrement=True) user_id = Column(String(50), nullable=False, comment="用户 ID") product_id = Column(String(50), nullable=True, comment="产品 ID") product_source_type = Column(String(20), nullable=True, comment="library_product/user_material") product_source_id = Column(String(64), nullable=True, comment="产品来源 ID") product_snapshot_json = Column(Text, nullable=True, comment="提交生成时的产品快照 JSON") case_upload_id = Column(BigInteger, nullable=True, comment="关联计划书上传 ID") template_id = Column(BigInteger, nullable=True, comment="海报模板 ID") copy_mode = Column(String(20), nullable=True, comment="文案模式: template/ai") copy_content = Column(Text, nullable=True, comment="最终文案 JSON") ai_raw_content = Column(Text, nullable=True, comment="AI 原始文案 JSON(合规留痕)") export_url = Column(String(500), nullable=True, comment="导出文件地址") export_format = Column(String(10), nullable=True, comment="导出格式: png/jpg") export_size = Column(String(20), nullable=True, comment="导出尺寸") reference_image_used = Column(String(500), nullable=True, comment="使用的参考图(合规留痕)") prompt_used = Column(Text, nullable=True, comment="完整 prompt(合规留痕)") generation_mode = Column(String(20), nullable=True, comment="生成方式: ai/fallback") image_provider = Column(String(50), nullable=True, comment="图片供应商") image_model = Column(String(100), nullable=True, comment="图片模型名称") task_status = Column(String(20), default="pending", comment="任务状态: pending/queued/generating/background_ready/done/failed") task_progress = Column(db.Integer, default=0, comment="任务进度 0-100") task_error = Column(Text, nullable=True, comment="任务错误信息") extra_data = Column(Text, nullable=True, comment="扩展数据 JSON(如品牌策略、输出模式)") document_json = Column(Text, nullable=True, comment="可编辑海报文档 JSON") document_revision = Column(Integer, default=1, comment="海报文档版本") background_file_url = Column(String(500), nullable=True, comment="AI 背景图文件地址") compliance_json = Column(Text, nullable=True, comment="服务端合规检查结果 JSON") compliance_revision = Column(String(64), nullable=True, comment="合规文案哈希") # 工作区字段(migrate_022) title = Column(String(200), default="", comment="工作区名称") workflow_step = Column(String(20), default="product", comment="当前业务步骤") draft_status = Column(String(20), default="active", comment="active/archived") draft_revision = Column(Integer, default=1, comment="当前草稿版本") generated_revision = Column(Integer, default=0, comment="最新成品版本") latest_task_id = Column(String(36), nullable=True, comment="最近任务 ID") archived_at = Column(TIMESTAMP, nullable=True, comment="归档时间") started_at = Column(TIMESTAMP, nullable=True, comment="任务开始时间") finished_at = Column(TIMESTAMP, nullable=True, comment="任务完成时间") created_at = Column(TIMESTAMP, server_default=func.now()) def to_dict(self): import json def _safe_json(text): if not text: return None try: return json.loads(text) except (json.JSONDecodeError, TypeError): return None return { "id": self.id, "userId": self.user_id, "productId": self.product_id, "productSource": { "type": self.product_source_type or "library_product", "id": self.product_source_id or self.product_id, }, "productSnapshot": _safe_json(self.product_snapshot_json), "caseUploadId": self.case_upload_id, "templateId": self.template_id, "copyMode": self.copy_mode, "copyContent": _safe_json(self.copy_content), "aiRawContent": _safe_json(self.ai_raw_content), "exportUrl": self.export_url, "exportFormat": self.export_format, "exportSize": self.export_size, "referenceImageUsed": self.reference_image_used, "promptUsed": self.prompt_used, "generationMode": self.generation_mode, "imageProvider": self.image_provider, "imageModel": self.image_model, "taskStatus": self.task_status, "taskProgress": self.task_progress, "taskError": self.task_error, "extraData": _safe_json(self.extra_data), "document": _safe_json(self.document_json), "documentRevision": self.document_revision or 1, "backgroundUrl": ( f"/insurance/poster/records/{self.id}/background" if self.background_file_url else None ), "compliance": _safe_json(self.compliance_json), "complianceRevision": self.compliance_revision or "", "title": self.title or "", "workflowStep": self.workflow_step or "product", "draftStatus": self.draft_status or "active", "draftRevision": self.draft_revision or 1, "generatedRevision": self.generated_revision or 0, "latestTaskId": self.latest_task_id, "archivedAt": self.archived_at.isoformat() if self.archived_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, "createdAt": self.created_at.isoformat() if self.created_at else None, }