阶段 1:数据库与工作区 ✅ 完成 100% 阶段 2:Celery 后台任务 ✅ 完成 100% 阶段 3:前端刷新恢复与多工作区 ✅ 完成 100% 阶段 4:任务坞与任务中心 ✅ 完成 100% 阶段 5:版本化编辑 ✅ 完成 100% 阶段 6:测试与灰度 ❌ 未开始 0%
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] 工作区与任务表迁移完成")
|