修复 PPT 异步任务无法生成的问题,包括任务变量引用错误、失败状态回写、心跳缺失任务恢复。 脱敏改为保司/产品后台统一配置,生成端不再让用户选择;任务创建时保存策略快照。 保司支持独立控制 PPT、海报 Logo 显示。 PPT 核验新增吸烟状态、币种及三个条件字段。 利益演示、退保提取调整为警告,不再阻止生成。 PPT 生成完成后可以直接返回数据核验页修改。 建立不同险种、单图/长图共六套海报字段画像。 PPT“生成场景”支持后台新增、启停和删除。 保司、产品、PPT 模板、文案模板均支持安全删除。 内置模板禁止删除,只允许停用;存在关联数据时拒绝危险删除。 补充策略变更及删除审计日志。 更新 API 文档、部署文档及修复计划实施记录。 关键交付文件: [数据库迁移 migrate_027.py](D:/work/code/python/coding/baodanagent/api/insurance/db/migrate_027.py) [海报字段画像 field_profiles.py](D:/work/code/python/coding/baodanagent/api/insurance/poster/field_profiles.py) [动态场景服务 scenarios.py](D:/work/code/python/coding/baodanagent/api/insurance/ppt/scenarios.py) [新增回归测试](D:/work/code/python/coding/baodanagent/tests/ppt_poster_optimization_test.py) [优化修复计划书](D:/work/code/python/coding/baodanagent/docs/保险智能客服系统_PPT与海报优化修复计划书_20260731.md) 验证结果: 核心链路测试:37 passed,1 skipped 扩展回归测试:140 passed PPT 渲染器测试:6 passed 前端生产构建:通过 Python 编译检查:通过 完整测试集:190 passed,1 failed 唯一失败为 tests/test_chat_save.py::test_chat_logs_query 未建立 Flask application context,与本次 PPT/海报链路无关。
120 lines
4.3 KiB
Python
120 lines
4.3 KiB
Python
"""迁移 027:PPT/海报品牌策略与后台对象软删除字段。"""
|
||
import logging
|
||
|
||
from sqlalchemy import inspect, text
|
||
|
||
logger = logging.getLogger(__name__)
|
||
|
||
|
||
def _column_names(db, table_name: str) -> set[str]:
|
||
return {item["name"] for item in inspect(db.engine).get_columns(table_name)}
|
||
|
||
|
||
def _index_names(db, table_name: str) -> set[str]:
|
||
return {item["name"] for item in inspect(db.engine).get_indexes(table_name)}
|
||
|
||
|
||
def _add_columns(db, table_name: str, columns: dict[str, str]):
|
||
existing = _column_names(db, table_name)
|
||
for name, definition in columns.items():
|
||
if name not in existing:
|
||
db.session.execute(text(
|
||
f"ALTER TABLE {table_name} ADD COLUMN {name} {definition}"
|
||
))
|
||
|
||
|
||
def _add_index(db, table_name: str, index_name: str, columns: str):
|
||
if index_name not in _index_names(db, table_name):
|
||
db.session.execute(text(
|
||
f"CREATE INDEX {index_name} ON {table_name} ({columns})"
|
||
))
|
||
|
||
|
||
def migrate():
|
||
"""幂等增加品牌策略和软删除字段。"""
|
||
from insurance.db.compat import db
|
||
|
||
db.session.execute(text("""
|
||
CREATE TABLE IF NOT EXISTS insurance_ppt_scenarios (
|
||
code VARCHAR(50) PRIMARY KEY,
|
||
name VARCHAR(100) NOT NULL,
|
||
base_scenario VARCHAR(50),
|
||
generation_mode VARCHAR(20) NOT NULL DEFAULT 'single',
|
||
description TEXT,
|
||
status SMALLINT NOT NULL DEFAULT 1,
|
||
sort_order INTEGER NOT NULL DEFAULT 0,
|
||
is_builtin BOOLEAN NOT NULL DEFAULT FALSE,
|
||
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
|
||
deleted_at TIMESTAMP
|
||
)
|
||
"""))
|
||
|
||
_add_columns(db, "insurance_ppt_companies", {
|
||
"masking_enabled": "BOOLEAN NOT NULL DEFAULT FALSE",
|
||
"logo_enabled": "BOOLEAN NOT NULL DEFAULT TRUE",
|
||
"deleted_at": "TIMESTAMP",
|
||
})
|
||
_add_columns(db, "insurance_ppt_products", {
|
||
"masking_enabled": "BOOLEAN NOT NULL DEFAULT FALSE",
|
||
"deleted_at": "TIMESTAMP",
|
||
})
|
||
_add_columns(db, "insurance_ppt_templates", {
|
||
"deleted_at": "TIMESTAMP",
|
||
})
|
||
_add_columns(db, "poster_copy_templates", {
|
||
"deleted_at": "TIMESTAMP",
|
||
})
|
||
|
||
_add_index(
|
||
db, "insurance_ppt_companies",
|
||
"idx_ppt_companies_status_deleted", "status, deleted_at",
|
||
)
|
||
_add_index(
|
||
db, "insurance_ppt_scenarios",
|
||
"idx_ppt_scenarios_status_deleted", "status, deleted_at",
|
||
)
|
||
|
||
builtins = [
|
||
("single_savings", "单份储蓄计划", "single_savings", "single", 10),
|
||
("multi_savings_comparison", "双/多储蓄险对比", "multi_savings_comparison", "compare", 20),
|
||
("savings_iul_comprehensive", "储蓄险 + IUL 综合方案", "savings_iul_comprehensive", "portfolio", 30),
|
||
("generic_single", "通用单产品", "generic_single", "single", 40),
|
||
("generic_compare", "通用同险种对比", "generic_compare", "compare", 50),
|
||
("generic_portfolio", "通用组合方案", "generic_portfolio", "portfolio", 60),
|
||
]
|
||
for code, name, base_scenario, generation_mode, sort_order in builtins:
|
||
db.session.execute(text("""
|
||
INSERT INTO insurance_ppt_scenarios
|
||
(code, name, base_scenario, generation_mode, status, sort_order, is_builtin)
|
||
SELECT :code, :name, :base_scenario, :generation_mode, 1, :sort_order, TRUE
|
||
WHERE NOT EXISTS (
|
||
SELECT 1 FROM insurance_ppt_scenarios WHERE code = :code
|
||
)
|
||
"""), {
|
||
"code": code,
|
||
"name": name,
|
||
"base_scenario": base_scenario,
|
||
"generation_mode": generation_mode,
|
||
"sort_order": sort_order,
|
||
})
|
||
_add_index(
|
||
db, "insurance_ppt_products",
|
||
"idx_ppt_products_status_deleted", "status, deleted_at",
|
||
)
|
||
_add_index(
|
||
db, "insurance_ppt_products",
|
||
"idx_ppt_products_company_deleted", "company_id, deleted_at",
|
||
)
|
||
_add_index(
|
||
db, "insurance_ppt_templates",
|
||
"idx_ppt_templates_status_deleted", "status, deleted_at",
|
||
)
|
||
_add_index(
|
||
db, "poster_copy_templates",
|
||
"idx_copy_templates_status_deleted", "status, deleted_at",
|
||
)
|
||
|
||
db.session.commit()
|
||
logger.info("[migrate_027] 品牌策略与软删除字段迁移完成")
|