# 问题 修复 文件 1 前端构建失败(引号错误) size="small type=" → size="small" type=" PosterHistoryPage.vue 2 migrate_014 ORM vs 缺失列 全部改为原始 SQL,不再引用 ORM 模型 migrate_014.py 3 cleanup 字段名错误 output_path → ppt_path cleanup.py 4 文案生成 case 越权 添加 case.user_id != user_id 校验 poster/service.py 5 存储路径未接通持久化卷 全部改用 get_storage_root()(默认 /app/api/storage/insurance) config.py, ppt/routes.py, poster/service.py, poster/tasks.py 高风险问题修复 # 问题 修复 文件 6 migrate_019 rollback 撤销成功字段 每个 ALTER 后立即 commit,失败只回滚当前语句 migrate_019.py 7 迁移锁 Windows 不兼容 + 句柄未持久化 全局变量保存锁句柄,支持 Windows msvcrt api/insurance/db/__init__.py 8 PDF 校验异常时放行 异常返回 False(文件损坏) security.py 9 健康检查始终返回成功 缺少关键资源时返回 503 + missing 列表 poster/routes.py 10 短密钥掩码泄露原值 ≤4 字符返回 **** ppt_admin_service.py 11 设置无键名白名单 添加 _ALLOWED_SETTING_KEYS 白名单 ppt_admin_service.py 12 容器重启任务永久 stuck 添加 recover_stale_tasks() 启动恢复函数 poster/tasks.py, ppt/parse_worker.py
100 lines
4.6 KiB
Python
100 lines
4.6 KiB
Python
"""迁移 020: 初始化海报模板和文案模板种子数据。
|
||
|
||
仅在表为空时插入,不覆盖管理员已修改的数据。
|
||
解决 POSTER-P0-02: 新环境没有海报基础数据。
|
||
"""
|
||
import json
|
||
import logging
|
||
from sqlalchemy import text
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
# 海报模板种子数据
|
||
POSTER_TEMPLATES = [
|
||
{
|
||
"name": "专业商务风",
|
||
"scenario_tag": "business",
|
||
"style_description": "专业保险营销海报,深蓝色背景搭配金色点缀,突出公司品牌和产品数据,排版整洁大方,适合储蓄险和寿险产品",
|
||
"color_scheme": json.dumps({"primary": "#1a3a5c", "accent": "#d4a843", "bg": "#f5f7fa"}, ensure_ascii=False),
|
||
},
|
||
{
|
||
"name": "温馨家庭风",
|
||
"scenario_tag": "family",
|
||
"style_description": "温馨家庭保障主题海报,暖色调渐变背景,包含家庭剪影元素,突出保障金额和家庭关怀,适合重疾险和家庭保障产品",
|
||
"color_scheme": json.dumps({"primary": "#e8734a", "accent": "#f5a623", "bg": "#fef9f3"}, ensure_ascii=False),
|
||
},
|
||
]
|
||
|
||
# 文案模板种子数据
|
||
COPY_TEMPLATES = [
|
||
{
|
||
"name": "储蓄险标准文案",
|
||
"scenario_tag": "savings",
|
||
"content": "{{product_name}} — 稳健增值,安心传承\n\n年缴 {{annual_premium}} {{currency}},缴费 {{coverage_period}},让财富在时间中复利增长。\n\n{{feature_1_title}}:{{feature_1_summary}}\n{{feature_2_title}}:{{feature_2_summary}}\n\n立即咨询您的专属顾问,获取个性化方案 →",
|
||
"variables": json.dumps([
|
||
{"name": "product_name", "desc": "产品名称"},
|
||
{"name": "annual_premium", "desc": "年缴保费"},
|
||
{"name": "currency", "desc": "货币"},
|
||
{"name": "coverage_period", "desc": "保障期限"},
|
||
{"name": "feature_1_title", "desc": "产品亮点1标题"},
|
||
{"name": "feature_1_summary", "desc": "产品亮点1摘要"},
|
||
{"name": "feature_2_title", "desc": "产品亮点2标题"},
|
||
{"name": "feature_2_summary", "desc": "产品亮点2摘要"},
|
||
], ensure_ascii=False),
|
||
},
|
||
{
|
||
"name": "重疾险标准文案",
|
||
"scenario_tag": "ci",
|
||
"content": "{{product_name}} — 守护家人,抵御风险\n\n{{age}} 岁投保,保障 {{coverage_period}},年缴 {{annual_premium}} {{currency}}。\n\n{{feature_1_title}}:{{feature_1_summary}}\n{{feature_2_title}}:{{feature_2_summary}}\n\n为家人撑起保护伞,立即了解详情 →",
|
||
"variables": json.dumps([
|
||
{"name": "product_name", "desc": "产品名称"},
|
||
{"name": "age", "desc": "投保年龄"},
|
||
{"name": "coverage_period", "desc": "保障期限"},
|
||
{"name": "annual_premium", "desc": "年缴保费"},
|
||
{"name": "currency", "desc": "货币"},
|
||
{"name": "feature_1_title", "desc": "产品亮点1标题"},
|
||
{"name": "feature_1_summary", "desc": "产品亮点1摘要"},
|
||
{"name": "feature_2_title", "desc": "产品亮点2标题"},
|
||
{"name": "feature_2_summary", "desc": "产品亮点2摘要"},
|
||
], ensure_ascii=False),
|
||
},
|
||
]
|
||
|
||
|
||
def _table_has_rows(db, table_name: str) -> bool:
|
||
"""检查表是否有数据。"""
|
||
try:
|
||
result = db.session.execute(text(f"SELECT COUNT(*) FROM {table_name}"))
|
||
return result.scalar() > 0
|
||
except Exception:
|
||
return True # 表不存在时返回 True,避免插入
|
||
|
||
|
||
def migrate():
|
||
"""执行迁移。"""
|
||
from insurance.db.compat import db
|
||
|
||
# 海报模板种子
|
||
if not _table_has_rows(db, "poster_templates"):
|
||
for tpl in POSTER_TEMPLATES:
|
||
db.session.execute(text(
|
||
"INSERT INTO poster_templates (name, scenario_tag, style_description, color_scheme, status) "
|
||
"VALUES (:name, :scenario_tag, :style_description, :color_scheme, 1)"
|
||
), tpl)
|
||
logger.info(f"已插入 {len(POSTER_TEMPLATES)} 个海报模板种子")
|
||
else:
|
||
logger.info("poster_templates 表已有数据,跳过种子插入")
|
||
|
||
# 文案模板种子
|
||
if not _table_has_rows(db, "poster_copy_templates"):
|
||
for tpl in COPY_TEMPLATES:
|
||
db.session.execute(text(
|
||
"INSERT INTO poster_copy_templates (name, scenario_tag, content, variables, status) "
|
||
"VALUES (:name, :scenario_tag, :content, :variables, 1)"
|
||
), tpl)
|
||
logger.info(f"已插入 {len(COPY_TEMPLATES)} 个文案模板种子")
|
||
else:
|
||
logger.info("poster_copy_templates 表已有数据,跳过种子插入")
|
||
|
||
db.session.commit()
|