baodan/api/insurance/db/migrate_026.py

101 lines
3.9 KiB
Python
Raw Normal View History

2026-07-31 09:50:46 +08:00
"""迁移 026用户产品小册子资料库与海报产品来源快照。"""
import logging
from sqlalchemy import inspect, text
logger = logging.getLogger(__name__)
def _primary_key_sql(dialect: str) -> str:
if dialect == "postgresql":
return "BIGSERIAL PRIMARY KEY"
if dialect in ("mysql", "mariadb"):
return "BIGINT AUTO_INCREMENT PRIMARY KEY"
return "INTEGER PRIMARY KEY AUTOINCREMENT"
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 migrate():
"""幂等创建用户资料表并扩展海报关联字段。"""
from insurance.db.compat import db
dialect = db.engine.dialect.name
inspector = inspect(db.engine)
if not inspector.has_table("insurance_user_product_materials"):
db.session.execute(text(f"""
CREATE TABLE insurance_user_product_materials (
id {_primary_key_sql(dialect)},
owner_user_id VARCHAR(64) NOT NULL,
tenant_id VARCHAR(64) NOT NULL DEFAULT 'default',
company_id VARCHAR(50),
company_name VARCHAR(100),
product_name VARCHAR(150),
plan_type VARCHAR(20),
original_name VARCHAR(200) NOT NULL,
file_key VARCHAR(500) NOT NULL,
file_size BIGINT NOT NULL DEFAULT 0,
page_count INTEGER,
sha256 VARCHAR(64) NOT NULL,
parse_status VARCHAR(20) NOT NULL DEFAULT 'pending',
parse_message VARCHAR(500) NOT NULL DEFAULT '',
parse_error TEXT,
parse_task_id VARCHAR(200),
parse_started_at TIMESTAMP,
parse_finished_at TIMESTAMP,
parsed_rules TEXT,
confirmed_rules TEXT,
confirmed_by VARCHAR(64),
confirmed_at TIMESTAMP,
review_status VARCHAR(20) NOT NULL DEFAULT 'private',
status SMALLINT NOT NULL DEFAULT 1,
deleted_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
"""))
db.session.commit()
logger.info("[migrate_026] 已创建 insurance_user_product_materials")
desired_indexes = {
"idx_user_material_owner_updated": "owner_user_id, updated_at",
"idx_user_material_owner_sha256": "owner_user_id, sha256",
"idx_user_material_parse_status": "parse_status",
}
existing_indexes = _index_names(db, "insurance_user_product_materials")
for index_name, columns in desired_indexes.items():
if index_name not in existing_indexes:
db.session.execute(text(
f"CREATE INDEX {index_name} "
f"ON insurance_user_product_materials ({columns})"
))
extra_columns = {
"product_source_type": "VARCHAR(20)",
"product_source_id": "VARCHAR(64)",
"product_snapshot_json": "TEXT",
}
for table_name in ("poster_case_uploads", "poster_records"):
current_columns = _column_names(db, table_name)
for column_name, column_type in extra_columns.items():
if column_name not in current_columns:
db.session.execute(text(
f"ALTER TABLE {table_name} ADD COLUMN {column_name} {column_type}"
))
db.session.execute(text(
f"UPDATE {table_name} "
"SET product_source_type = 'library_product', product_source_id = product_id "
"WHERE product_id IS NOT NULL AND product_id <> '' "
"AND (product_source_type IS NULL OR product_source_type = '')"
))
db.session.commit()
logger.info("[migrate_026] 用户产品资料库迁移完成")