主要结果: PPT: 已完成步骤可自由返回,刷新后保留当前步骤。 结果页改为固定三栏视口,缩略图和质量面板独立滚动。 修复画布宽度计算和宽高自适应缩放。 返回配置页时恢复上次模板。 Worker 禁止静默替换模板,不兼容时明确失败。 结果页显示实际应用的模板。 海报解析与合规: 修复 insured/policy 嵌套字段映射。 增加 partial/failed 解析质量判断和缺失字段提示。 传入险种、产品、保司和别名上下文。 修复性别、币种归一化和错误字段计数。 新增服务端字符级合规接口与生成前复检。 不合规文字可高亮、点击定位并选中对应文字。 海报编辑: AI 图片现在作为背景资产,不再替换整个海报。 单图、长图生成后仍可编辑标题、正文、CTA、数据和卖点。 背景生成失败不会清空当前编辑内容。 下载时合成背景、文字、数据、图表和免责声明。 增加可编辑文档快照、最终 PNG 保存和历史继续编辑。 新增数据库迁移:[migrate_028.py](/D:/work/code/python/coding/baodanagent/api/insurance/db/migrate_028.py) 验证结果: 专项及相关后端测试:26 passed, 1 skipped Python 全模块编译检查:通过 前端生产构建:通过 git diff --check:通过 全量测试收集受本机缺少 python-pptx 依赖影响,报错为 ModuleNotFoundError: pptx,不是本次修改产生的测试失败。
106 lines
5.9 KiB
Python
106 lines
5.9 KiB
Python
"""海报生成记录模型。"""
|
||
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/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,
|
||
}
|