baodan/api/insurance/db/migrate_022.py

141 lines
5.5 KiB
Python
Raw Normal View History

"""迁移 022: 工作区与统一任务表。
docs/0728修复文件.md 阶段 1 实施
- 扩展 insurance_ppt_sessions新增工作区字段
- 扩展 poster_records新增工作区字段
- 新建 insurance_generation_tasks统一任务执行表
兼容 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
# ─── 扩展 PptSession ─────────────────────────────────────
ppt_columns = [
("title", "VARCHAR(200) DEFAULT ''"),
("workflow_step", "VARCHAR(20) DEFAULT 'upload'"),
("draft_options_json", "TEXT"),
("draft_revision", "INT DEFAULT 1"),
("generated_revision", "INT DEFAULT 0"),
("latest_task_id", "VARCHAR(36)"),
("latest_output_path", "VARCHAR(500)"),
("archived_at", "TIMESTAMP"),
]
for col_name, col_def in ppt_columns:
if not _column_exists(db, "insurance_ppt_sessions", col_name):
db.session.execute(text(
f"ALTER TABLE insurance_ppt_sessions ADD COLUMN {col_name} {col_def}"
))
logger.info(f"[migrate_022] 已为 insurance_ppt_sessions 添加 {col_name}")
# ─── 扩展 PosterRecord ───────────────────────────────────
poster_columns = [
("title", "VARCHAR(200) DEFAULT ''"),
("workflow_step", "VARCHAR(20) DEFAULT 'product'"),
("draft_status", "VARCHAR(20) DEFAULT 'active'"),
("draft_revision", "INT DEFAULT 1"),
("generated_revision", "INT DEFAULT 0"),
("latest_task_id", "VARCHAR(36)"),
("archived_at", "TIMESTAMP"),
]
for col_name, col_def in poster_columns:
if not _column_exists(db, "poster_records", col_name):
db.session.execute(text(
f"ALTER TABLE poster_records ADD COLUMN {col_name} {col_def}"
))
logger.info(f"[migrate_022] 已为 poster_records 添加 {col_name}")
# ─── 新建 insurance_generation_tasks ─────────────────────
if not _table_exists(db, "insurance_generation_tasks"):
db.session.execute(text("""
CREATE TABLE insurance_generation_tasks (
id VARCHAR(36) PRIMARY KEY,
user_id VARCHAR(64) NOT NULL,
artifact_type VARCHAR(10) NOT NULL,
operation VARCHAR(10) NOT NULL,
workspace_id VARCHAR(36) NOT NULL,
title_snapshot VARCHAR(200) DEFAULT '',
status VARCHAR(20) NOT NULL DEFAULT 'queued',
stage VARCHAR(30) DEFAULT '',
progress INT DEFAULT 0,
message VARCHAR(500) DEFAULT '',
error_code VARCHAR(50) DEFAULT '',
error_message TEXT,
input_revision INT DEFAULT 1,
input_snapshot_json TEXT,
output_json TEXT,
idempotency_key VARCHAR(100),
celery_task_id VARCHAR(200),
attempt_count INT DEFAULT 0,
heartbeat_at TIMESTAMP,
started_at TIMESTAMP,
finished_at TIMESTAMP,
viewed_at TIMESTAMP,
dock_hidden_at TIMESTAMP,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP
)
"""))
logger.info("[migrate_022] 已创建 insurance_generation_tasks 表")
# 创建索引PostgreSQL 兼容)
indexes = [
("idx_gen_tasks_user_status", "user_id, status"),
("idx_gen_tasks_user_artifact", "user_id, artifact_type"),
("idx_gen_tasks_workspace", "workspace_id"),
("idx_gen_tasks_idempotency", "idempotency_key"),
]
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_generation_tasks ({idx_cols})"
))
logger.info(f"[migrate_022] 已创建索引 {idx_name}")
db.session.commit()
logger.info("[migrate_022] 工作区与任务表迁移完成")