baodan/api/insurance/db/migrate_023.py
wsb1224 fa67b83bbf 核心改进:
小册子解析:上传后3秒内返回,解析通过 Celery 异步执行,状态流转 pending → queued → parsing → parsed/failed,失败保留错误原因,启动时自动恢复卡住任务
保司Logo:支持多张图片上传、设主图、删除、排序,主图自动同步到 logo_url 保持向后兼容
文件存储:统一使用 get_storage_root() 持久化目录,Docker 重启不丢文件
2026-07-28 20:35:28 +08:00

115 lines
4.4 KiB
Python
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

"""迁移 023: 产品小册子异步解析 + 保司多Logo。
- 扩展 insurance_ppt_products新增小册子解析状态字段
- 新建 insurance_ppt_company_logos保司多Logo表
- 清理卡住的 parsing 数据
兼容 PostgreSQL 和 MySQL。
"""
import logging
from sqlalchemy import text
logger = logging.getLogger(__name__)
def _column_exists(db, table: str, column: str) -> bool:
"""检查列是否存在(兼容 PostgreSQL 和 MySQL"""
try:
result = db.session.execute(text(
"SELECT COUNT(*) FROM information_schema.columns "
"WHERE table_name = :table AND column_name = :column"
), {"table": table, "column": column})
return result.scalar() > 0
except Exception:
return False
def _table_exists(db, table_name: str) -> bool:
"""检查表是否存在(兼容 PostgreSQL 和 MySQL"""
try:
result = db.session.execute(text(
"SELECT COUNT(*) FROM information_schema.tables "
"WHERE table_name = :table"
), {"table": table_name})
return result.scalar() > 0
except Exception:
return False
def _index_exists(db, index_name: str) -> bool:
"""检查索引是否存在(兼容 PostgreSQL 和 MySQL"""
try:
result = db.session.execute(text(
"SELECT COUNT(*) FROM pg_indexes WHERE indexname = :name"
), {"name": index_name})
return result.scalar() > 0
except Exception:
return False
def migrate():
"""执行迁移。"""
from insurance.db.compat import db
# ─── 扩展 PptProduct 小册子字段 ───────────────────────────
manual_columns = [
("manual_parse_message", "VARCHAR(500) DEFAULT ''"),
("manual_parse_error", "TEXT"),
("manual_parse_task_id", "VARCHAR(200)"),
("manual_parse_started_at", "TIMESTAMP"),
("manual_parse_finished_at", "TIMESTAMP"),
]
for col_name, col_def in manual_columns:
if not _column_exists(db, "insurance_ppt_products", col_name):
db.session.execute(text(
f"ALTER TABLE insurance_ppt_products ADD COLUMN {col_name} {col_def}"
))
logger.info(f"[migrate_023] 已为 insurance_ppt_products 添加 {col_name}")
# ─── 清理卡住的 parsing 数据 ─────────────────────────────
# 将 manual_parse_status='parsing' 且无有效 manual_parsed_rules 的记录改为 failed
result = db.session.execute(text(
"UPDATE insurance_ppt_products "
"SET manual_parse_status = 'failed', "
" manual_parse_error = '任务因服务重启而中断,请重新解析', "
" manual_parse_finished_at = CURRENT_TIMESTAMP "
"WHERE manual_parse_status = 'parsing' "
"AND (manual_parsed_rules IS NULL OR manual_parsed_rules = '')"
))
if result.rowcount > 0:
logger.info(f"[migrate_023] 已恢复 {result.rowcount} 个卡住的 parsing 记录为 failed")
# ─── 新建 insurance_ppt_company_logos ────────────────────
if not _table_exists(db, "insurance_ppt_company_logos"):
db.session.execute(text("""
CREATE TABLE insurance_ppt_company_logos (
id VARCHAR(36) PRIMARY KEY,
company_id VARCHAR(50) NOT NULL,
file_path VARCHAR(500) NOT NULL,
file_url VARCHAR(500),
original_name VARCHAR(200),
mime_type VARCHAR(50),
file_size INT,
is_primary BOOLEAN DEFAULT false,
sort_order INT DEFAULT 0,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
"""))
logger.info("[migrate_023] 已创建 insurance_ppt_company_logos 表")
# 创建索引
indexes = [
("idx_company_logos_company", "company_id"),
("idx_company_logos_primary", "company_id, is_primary"),
]
for idx_name, idx_cols in indexes:
if not _index_exists(db, idx_name):
db.session.execute(text(
f"CREATE INDEX {idx_name} ON insurance_ppt_company_logos ({idx_cols})"
))
logger.info(f"[migrate_023] 已创建索引 {idx_name}")
db.session.commit()
logger.info("[migrate_023] 产品小册子异步解析 + 保司多Logo 迁移完成")