2026-07-31 14:10:24 +08:00
|
|
|
|
"""迁移 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
|
|
|
|
|
|
)
|
|
|
|
|
|
"""))
|
2026-08-01 13:08:42 +08:00
|
|
|
|
# 先提交新表,确保后续 inspector 使用的独立连接能够看到它。
|
|
|
|
|
|
db.session.commit()
|
2026-07-31 14:10:24 +08:00
|
|
|
|
|
|
|
|
|
|
_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",
|
|
|
|
|
|
})
|
|
|
|
|
|
|
2026-08-01 13:08:42 +08:00
|
|
|
|
# 新增字段提交后再检查并创建索引,避免 DDL 对其他连接不可见。
|
|
|
|
|
|
db.session.commit()
|
|
|
|
|
|
|
2026-07-31 14:10:24 +08:00
|
|
|
|
_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] 品牌策略与软删除字段迁移完成")
|