141 lines
5.5 KiB
Python
141 lines
5.5 KiB
Python
|
|
"""迁移 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] 工作区与任务表迁移完成")
|