Completed: - Add 6 core database documents (docs/01-平台基础层/07-数据库/) Architecture overview, migration history, environment comparison, tech debt tracking, seed data management, PostgreSQL extensions - Restructure deployment docs: archive 20 legacy files to _archive-2025/ - Create unified daily operations manual (01-日常更新操作手册.md) - Add pending deployment change tracker (03-待部署变更清单.md) - Update database development standard to v3.0 (three iron rules) - Fix Prisma schema type drift: align @db.* annotations with actual DB IIT: UUID/Timestamptz(6), SSA: Timestamp(6)/VarChar(20/50/100) - Add migration: 20260227_align_schema_with_db_types (idempotent ALTER) - Add Cursor Rule for auto-reminding deployment change documentation - Update system status guide v6.4 with deployment and DB doc references - Add architecture consultation docs (Prisma guide, SAE deployment guide) Technical details: - Manual migration due to shadow DB limitation (TD-001 in tech debt) - Deployment docs reduced from 20+ scattered files to 3 core documents - Cursor Rule triggers on schema.prisma, package.json, Dockerfile changes Made-with: Cursor
25 KiB
数据库迁移方案:开发环境 → RDS 结构同步
日期:2026-02-27
目标:将开发环境(localhost Docker)的数据库结构和必要 Seed 数据同步到阿里云 RDS 测试环境
核心原则:只做增量,不动存量;结构先行,数据后补;每步可回滚
1. 环境信息
1.1 源环境(开发)
Host: localhost
Port: 5432
Database: ai_clinical_research
User: postgres
Password: postgres123
容器名: ai-clinical-postgres
表数量: 96
迁移数: 11(+ 1 个文件未 apply)
1.2 目标环境(RDS 测试)
Host: pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com(外网)
Host: pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com(内网/SAE)
Port: 5432
Database: ai_clinical_research_test
User: airesearch
Password: Xibahe@fengzhibo117
表数量: 66
迁移数: 6
1.3 DATABASE_URL(操作时使用)
# 外网(本地操作用)
postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test
# 内网(SAE 部署用)
postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5.pg.rds.aliyuncs.com:5432/ai_clinical_research_test?connection_limit=18&pool_timeout=10
2. 差异分析总览
2.1 Prisma 迁移差距
| # | 迁移文件 | 本地 | RDS | 内容 |
|---|---|---|---|---|
| 1 | 20251010_init | ✅ | ✅ | 初始化 |
| 2 | 20251010_conversation_metadata | ✅ | ✅ | AIA 对话 |
| 3 | 20251012_batch_processing | ✅ | ✅ | 批处理 |
| 4 | 20251014_review_tasks | ✅ | ✅ | 论文预审 |
| 5 | 20251208_column_mapping | ✅ | ✅ | 列映射 |
| 6 | 20260128_system_knowledge_base | ✅ | ✅ | 系统知识库 |
| 7 | 20260207_add_iit_manager_agent_tables | ✅ | ❌ | IIT Agent 8 张新表 |
| 8 | 20260208_add_cra_qc_engine_support | ✅ | ❌ | CRA 质控 + skills 扩展 |
| 9 | 20260219_add_ssa_module | ✅ | ❌ | SSA 模块 9 张表 |
| 10 | 20260223_add_deep_research_v2_fields | ✅ | ❌ | research_tasks 加 6 列 |
| 11 | 20260225_add_extraction_template_engine | ✅ | ❌ | ASL 提取引擎 4 张表 |
| 12 | 20260226_add_equery_critical_events_cron | ❌ (db push) | ❌ | eQuery + 重大事件 + cron |
RDS 需要执行迁移 #7 ~ #12,共 6 个迁移。
2.2 无迁移文件的变更(db push 造成的漂移)
以下 6 张表 + 3 列 + 1 索引 存在于开发数据库和 Prisma Schema 中,但没有对应的迁移文件:
| Schema | 对象 | 类型 | 说明 |
|---|---|---|---|
| iit_schema | field_metadata |
新表(16 列) | REDCap 字段元数据 |
| iit_schema | qc_logs |
新表(16 列) | 质控日志(附历史追溯) |
| iit_schema | qc_project_stats |
新表(10 列) | 项目级质控统计汇总 |
| iit_schema | record_summary |
新表(16 列) | 受试者记录汇总 |
| iit_schema | projects.knowledge_base_id |
新列 | 关联 EKB 知识库 |
| iit_schema | idx_iit_project_kb |
新索引 | knowledge_base_id 索引 |
| ssa_schema | ssa_workflows |
新表(11 列) | 统计分析工作流 |
| ssa_schema | ssa_workflow_steps |
新表(13 列) | 工作流步骤明细 |
2.3 表数量对比
| Schema | 开发环境 | RDS | 差异 |
|---|---|---|---|
| admin_schema | 2 | 2 | — |
| agent_schema | 6 | 6 | — |
| aia_schema | 3 | 3 | — |
| asl_schema | 12 | 7 | +4 表 +6 列 |
| capability_schema | 4 | 4 | — |
| dc_schema | 6 | 6 | — |
| ekb_schema | 3 | 3 | — |
| iit_schema | 20 | 5 | +15 表 +3 列 |
| pkb_schema | 5 | 5 | — |
| platform_schema | 19 | 19 | — |
| protocol_schema | 2 | 2 | — |
| public | 3 | 3 | — |
| rvw_schema | 1 | 1 | — |
| ssa_schema | 11 | 0 | +11 表 |
| 合计 | 96 | 66 | +30 表 |
2.4 列级差异(已有表)
| 表 | 开发环境 | RDS | 缺失列 |
|---|---|---|---|
asl_schema.research_tasks |
25 列 | 19 列 | target_sources, confirmed_requirement, ai_intent_summary, execution_logs, synthesis_report, result_list |
iit_schema.projects |
18 列 | 15 列 | knowledge_base_id, cron_enabled, cron_expression |
iit_schema.skills |
16 列 | — (新表) | 迁移 #8 中 ALTER ADD 4 列 |
3. Seed 数据同步需求
3.1 Prompt 管理数据对比
| 表 | 开发环境 | RDS | 分析 |
|---|---|---|---|
prompt_templates |
27 行 | 14 行 | RDS 缺 13 个 SSA 模板(ID 17-29) |
prompt_versions |
42 行 | 23 行 | RDS 缺 19 个 SSA 版本(ID 26-44) |
重要:RDS 的 AIA/ASL/DC/RVW 模板(ID 1-16)已经过独立迭代,部分版本比开发环境更丰富(如模板 7 在 RDS 有 v2/v3 内容各 1044 字符,开发环境是短内容测试版),绝对不能覆盖。
3.2 需要同步的 Seed 数据
| 表 | 行数 | 内容 | 操作 |
|---|---|---|---|
capability_schema.prompt_templates |
13 行 | SSA 模块 Prompt(ID 17-29) | INSERT,已有则跳过 |
capability_schema.prompt_versions |
19 行 | SSA Prompt 版本(ID 26-44) | INSERT,已有则跳过 |
asl_schema.extraction_templates |
3 行 | 系统提取模板(RCT/Cohort/QC) | INSERT(新表,迁移后) |
3.3 不需要同步的数据
| 表 | 原因 |
|---|---|
prompt_templates ID 1-16 |
RDS 版本更优,不覆盖 |
prompt_versions ID 1-25 |
RDS 版本更优,不覆盖 |
platform_schema.users/tenants/... |
RDS 有真实用户/租户数据 |
ekb_schema.* |
RDS 有真实知识库数据(5686 chunks) |
iit_schema.qc_logs 等 |
开发测试数据,不推向生产 |
4. 迁移方案
4.1 方案选择:补丁迁移 + prisma migrate deploy
采用 方案 B:先为 db push 漏网的变更创建补丁迁移文件,然后用 prisma migrate deploy 一次性应用。
风险评估:
| 风险项 | 评估 | 说明 |
|---|---|---|
| 数据丢失 | ✅ 零风险 | 6 个迁移全部是 CREATE TABLE / ALTER TABLE ADD COLUMN,无任何 DROP / DELETE / TRUNCATE |
| 现有表结构破坏 | ✅ 零风险 | 仅新增,不修改已有列 |
| 现有数据变更 | ✅ 零风险 | 纯 DDL 操作,不涉及 DML |
| Prompt 数据丢失 | ✅ 零风险 | Prompt 在 capability_schema,不受任何迁移影响 |
| 迁移执行失败 | 🟡 低风险 | 可能因 RDS 权限问题失败,事务回滚不影响数据 |
4.2 执行步骤总览
步骤 1 → 备份 RDS 数据库
步骤 2 → 创建补丁迁移文件(覆盖 db push 漏网变更)
步骤 3 → 本地标记补丁迁移为已应用(dev 已有这些表)
步骤 4 → 在 RDS 执行 prisma migrate deploy(7 个迁移)
步骤 5 → 同步 Seed 数据(SSA Prompt + 提取模板)
步骤 6 → 验证
5. 详细执行步骤
步骤 1:备份 RDS 数据库
⚠️ 安全第一:任何操作前必须备份!
使用 Docker 容器中的 pg_dump(避免 PowerShell 编码问题):
# 在 Docker 容器内执行备份(避免 PowerShell UTF-8 编码问题!)
docker exec ai-clinical-postgres pg_dump \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" \
-f /tmp/backup_rds_before_0227.sql
# 将备份文件从容器复制到本地
docker cp ai-clinical-postgres:/tmp/backup_rds_before_0227.sql ./backup_rds_before_0227.sql
# 验证备份文件大小和中文内容
docker exec ai-clinical-postgres head -20 /tmp/backup_rds_before_0227.sql
预期:备份文件应 > 1MB,且中文内容正常显示(非 ????)。
步骤 2:创建补丁迁移文件
将 db push 漏网的 6 张表 + 3 列 + 索引 补入迁移体系
创建文件 backend/prisma/migrations/20260227_patch_db_push_drift/migration.sql:
-- ================================================================
-- 补丁迁移:覆盖 db push 创建的 6 张表 + 3 列
-- 所有语句使用 IF NOT EXISTS 保证幂等(多次执行不报错)
-- ================================================================
-- ============ iit_schema: 4 张新表 + 1 列 + 1 索引 ============
-- 1. IIT 字段元数据表(REDCap 字段定义缓存)
CREATE TABLE IF NOT EXISTS "iit_schema"."field_metadata" (
"id" TEXT NOT NULL,
"project_id" TEXT NOT NULL,
"field_name" TEXT NOT NULL,
"field_label" TEXT NOT NULL,
"field_type" TEXT NOT NULL,
"form_name" TEXT NOT NULL,
"section_header" TEXT,
"validation" TEXT,
"validation_min" TEXT,
"validation_max" TEXT,
"choices" TEXT,
"required" BOOLEAN NOT NULL DEFAULT false,
"branching" TEXT,
"alias" TEXT,
"rule_source" TEXT,
"synced_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "field_metadata_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX IF NOT EXISTS "unique_iit_field_metadata"
ON "iit_schema"."field_metadata"("project_id", "field_name");
CREATE INDEX IF NOT EXISTS "idx_iit_field_metadata_project"
ON "iit_schema"."field_metadata"("project_id");
CREATE INDEX IF NOT EXISTS "idx_iit_field_metadata_form"
ON "iit_schema"."field_metadata"("project_id", "form_name");
-- 2. IIT 质控日志表
CREATE TABLE IF NOT EXISTS "iit_schema"."qc_logs" (
"id" TEXT NOT NULL,
"project_id" TEXT NOT NULL,
"record_id" TEXT NOT NULL,
"event_id" TEXT,
"qc_type" TEXT NOT NULL,
"form_name" TEXT,
"status" TEXT NOT NULL,
"issues" JSONB NOT NULL DEFAULT '[]',
"rules_evaluated" INTEGER NOT NULL DEFAULT 0,
"rules_skipped" INTEGER NOT NULL DEFAULT 0,
"rules_passed" INTEGER NOT NULL DEFAULT 0,
"rules_failed" INTEGER NOT NULL DEFAULT 0,
"rule_version" TEXT NOT NULL,
"inclusion_passed" BOOLEAN,
"exclusion_passed" BOOLEAN,
"triggered_by" TEXT NOT NULL,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
CONSTRAINT "qc_logs_pkey" PRIMARY KEY ("id")
);
CREATE INDEX IF NOT EXISTS "idx_iit_qc_log_record_time"
ON "iit_schema"."qc_logs"("project_id", "record_id", "created_at");
CREATE INDEX IF NOT EXISTS "idx_iit_qc_log_status_time"
ON "iit_schema"."qc_logs"("project_id", "status", "created_at");
CREATE INDEX IF NOT EXISTS "idx_iit_qc_log_type_time"
ON "iit_schema"."qc_logs"("project_id", "qc_type", "created_at");
-- 3. IIT 受试者记录汇总表
CREATE TABLE IF NOT EXISTS "iit_schema"."record_summary" (
"id" TEXT NOT NULL,
"project_id" TEXT NOT NULL,
"record_id" TEXT NOT NULL,
"enrolled_at" TIMESTAMP(3),
"enrolled_by" TEXT,
"last_updated_at" TIMESTAMP(3) NOT NULL,
"last_updated_by" TEXT,
"last_form_name" TEXT,
"form_status" JSONB NOT NULL DEFAULT '{}',
"total_forms" INTEGER NOT NULL DEFAULT 0,
"completed_forms" INTEGER NOT NULL DEFAULT 0,
"completion_rate" DOUBLE PRECISION NOT NULL DEFAULT 0,
"latest_qc_status" TEXT,
"latest_qc_at" TIMESTAMP(3),
"update_count" INTEGER NOT NULL DEFAULT 0,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT CURRENT_TIMESTAMP,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "record_summary_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX IF NOT EXISTS "unique_iit_record_summary"
ON "iit_schema"."record_summary"("project_id", "record_id");
CREATE INDEX IF NOT EXISTS "idx_iit_record_summary_enrolled"
ON "iit_schema"."record_summary"("project_id", "enrolled_at");
CREATE INDEX IF NOT EXISTS "idx_iit_record_summary_qc_status"
ON "iit_schema"."record_summary"("project_id", "latest_qc_status");
CREATE INDEX IF NOT EXISTS "idx_iit_record_summary_completion"
ON "iit_schema"."record_summary"("project_id", "completion_rate");
-- 4. IIT 项目级质控统计汇总表
CREATE TABLE IF NOT EXISTS "iit_schema"."qc_project_stats" (
"id" TEXT NOT NULL,
"project_id" TEXT NOT NULL,
"total_records" INTEGER NOT NULL DEFAULT 0,
"passed_records" INTEGER NOT NULL DEFAULT 0,
"failed_records" INTEGER NOT NULL DEFAULT 0,
"warning_records" INTEGER NOT NULL DEFAULT 0,
"inclusion_met" INTEGER NOT NULL DEFAULT 0,
"exclusion_met" INTEGER NOT NULL DEFAULT 0,
"avg_completion_rate" DOUBLE PRECISION NOT NULL DEFAULT 0,
"updated_at" TIMESTAMP(3) NOT NULL,
CONSTRAINT "qc_project_stats_pkey" PRIMARY KEY ("id")
);
CREATE UNIQUE INDEX IF NOT EXISTS "qc_project_stats_project_id_key"
ON "iit_schema"."qc_project_stats"("project_id");
-- 5. IIT projects 表新增 knowledge_base_id 列
ALTER TABLE "iit_schema"."projects"
ADD COLUMN IF NOT EXISTS "knowledge_base_id" TEXT;
CREATE INDEX IF NOT EXISTS "idx_iit_project_kb"
ON "iit_schema"."projects"("knowledge_base_id");
-- ============ ssa_schema: 2 张新表 ============
-- 6. SSA 工作流表
CREATE TABLE IF NOT EXISTS "ssa_schema"."ssa_workflows" (
"id" TEXT NOT NULL DEFAULT gen_random_uuid()::text,
"session_id" TEXT NOT NULL,
"message_id" TEXT,
"status" VARCHAR NOT NULL DEFAULT 'pending',
"total_steps" INTEGER NOT NULL,
"completed_steps" INTEGER NOT NULL DEFAULT 0,
"workflow_plan" JSONB NOT NULL,
"reasoning" TEXT,
"created_at" TIMESTAMP(3) NOT NULL DEFAULT now(),
"started_at" TIMESTAMP(3),
"completed_at" TIMESTAMP(3),
CONSTRAINT "ssa_workflows_pkey" PRIMARY KEY ("id")
);
CREATE INDEX IF NOT EXISTS "idx_ssa_workflow_session"
ON "ssa_schema"."ssa_workflows"("session_id");
CREATE INDEX IF NOT EXISTS "idx_ssa_workflow_status"
ON "ssa_schema"."ssa_workflows"("status");
-- 外键:ssa_workflows.session_id → ssa_sessions.id
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name = 'ssa_workflows_session_id_fkey'
) THEN
ALTER TABLE "ssa_schema"."ssa_workflows"
ADD CONSTRAINT "ssa_workflows_session_id_fkey"
FOREIGN KEY ("session_id")
REFERENCES "ssa_schema"."ssa_sessions"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
END IF;
END $$;
-- 7. SSA 工作流步骤表
CREATE TABLE IF NOT EXISTS "ssa_schema"."ssa_workflow_steps" (
"id" TEXT NOT NULL DEFAULT gen_random_uuid()::text,
"workflow_id" TEXT NOT NULL,
"step_order" INTEGER NOT NULL,
"tool_code" VARCHAR NOT NULL,
"tool_name" VARCHAR NOT NULL,
"status" VARCHAR NOT NULL DEFAULT 'pending',
"input_params" JSONB,
"guardrail_checks" JSONB,
"output_result" JSONB,
"error_info" JSONB,
"execution_ms" INTEGER,
"started_at" TIMESTAMP(3),
"completed_at" TIMESTAMP(3),
CONSTRAINT "ssa_workflow_steps_pkey" PRIMARY KEY ("id")
);
CREATE INDEX IF NOT EXISTS "idx_ssa_workflow_step_workflow"
ON "ssa_schema"."ssa_workflow_steps"("workflow_id");
CREATE INDEX IF NOT EXISTS "idx_ssa_workflow_step_status"
ON "ssa_schema"."ssa_workflow_steps"("status");
-- 外键:ssa_workflow_steps.workflow_id → ssa_workflows.id
DO $$
BEGIN
IF NOT EXISTS (
SELECT 1 FROM information_schema.table_constraints
WHERE constraint_name = 'ssa_workflow_steps_workflow_id_fkey'
) THEN
ALTER TABLE "ssa_schema"."ssa_workflow_steps"
ADD CONSTRAINT "ssa_workflow_steps_workflow_id_fkey"
FOREIGN KEY ("workflow_id")
REFERENCES "ssa_schema"."ssa_workflows"("id")
ON DELETE CASCADE ON UPDATE CASCADE;
END IF;
END $$;
步骤 3:本地标记补丁迁移为已应用
开发环境已经有这些表(db push 创建的),只需告诉 Prisma "这个迁移已经执行过了"。
cd backend
# 标记补丁迁移为已应用(不实际执行 SQL)
npx prisma migrate resolve --applied 20260227_patch_db_push_drift
# 同时标记 20260226 迁移为已应用(dev 已有 equery/critical_events 表)
npx prisma migrate resolve --applied 20260226_add_equery_critical_events_cron
验证:
# 本地应该显示 13 个迁移全部已应用
npx prisma migrate status
步骤 4:在 RDS 执行迁移
核心操作:一次性应用 7 个迁移(#7 ~ #12 + 补丁)
cd backend
# 临时切换 DATABASE_URL 指向 RDS 外网
# Windows PowerShell:
$env:DATABASE_URL="postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test"
# 执行迁移(只应用未执行的迁移,不会重新执行已有的)
npx prisma migrate deploy
预期输出:
7 migrations found in prisma/migrations
6 migrations have been already applied
Applying migration `20260207112544_add_iit_manager_agent_tables`
Applying migration `20260208134925_add_cra_qc_engine_support`
Applying migration `20260219_add_ssa_module`
Applying migration `20260223_add_deep_research_v2_fields`
Applying migration `20260225_add_extraction_template_engine`
Applying migration `20260226_add_equery_critical_events_cron`
Applying migration `20260227_patch_db_push_drift`
All migrations have been successfully applied.
注意:迁移 #7 中包含
CREATE EXTENSION IF NOT EXISTS vector;,需要 RDS 已安装 pgvector 插件(已在 0126 部署中确认安装)。
步骤 5:同步 Seed 数据
仅同步 SSA Prompt 和提取模板,不动 RDS 已有数据
方法:从开发环境导出 → 插入 RDS
# 5a. 导出 SSA Prompt 模板(ID >= 17,仅 SSA 新增)
docker exec ai-clinical-postgres psql -U postgres -d ai_clinical_research -c "\
COPY (SELECT * FROM capability_schema.prompt_templates WHERE id >= 17) \
TO '/tmp/seed_prompt_templates.csv' WITH CSV HEADER;"
# 5b. 导出 SSA Prompt 版本(ID >= 26)
docker exec ai-clinical-postgres psql -U postgres -d ai_clinical_research -c "\
COPY (SELECT * FROM capability_schema.prompt_versions WHERE id >= 26) \
TO '/tmp/seed_prompt_versions.csv' WITH CSV HEADER;"
# 5c. 导出提取模板(新表,全部 3 行)
docker exec ai-clinical-postgres psql -U postgres -d ai_clinical_research -c "\
COPY (SELECT * FROM asl_schema.extraction_templates) \
TO '/tmp/seed_extraction_templates.csv' WITH CSV HEADER;"
# 5d. 导入到 RDS(在 Docker 容器内操作,避免 PowerShell 编码问题)
docker exec ai-clinical-postgres psql \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" \
-c "\COPY capability_schema.prompt_templates FROM '/tmp/seed_prompt_templates.csv' WITH CSV HEADER;"
docker exec ai-clinical-postgres psql \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" \
-c "\COPY capability_schema.prompt_versions FROM '/tmp/seed_prompt_versions.csv' WITH CSV HEADER;"
docker exec ai-clinical-postgres psql \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" \
-c "\COPY asl_schema.extraction_templates FROM '/tmp/seed_extraction_templates.csv' WITH CSV HEADER;"
⚠️ 关键:所有操作通过
docker exec在容器内执行,避免 PowerShell 的 UTF-8 编码问题(参考 0126 部署教训)。
步骤 6:验证
# 6a. 验证表数量(应从 66 → 96)
docker exec ai-clinical-postgres psql \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" \
-c "SELECT count(*) as total_tables FROM information_schema.tables WHERE table_type='BASE TABLE' AND table_schema NOT IN ('pg_catalog','information_schema');"
# 6b. 验证迁移记录(应为 13 条)
docker exec ai-clinical-postgres psql \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" \
-c "SELECT count(*) FROM public._prisma_migrations;"
# 6c. 验证 SSA Prompt 数据(应为 27 个模板)
docker exec ai-clinical-postgres psql \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" \
-c "SELECT count(*) FROM capability_schema.prompt_templates;"
# 6d. 验证 SSA 表存在(应有 11 张表)
docker exec ai-clinical-postgres psql \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" \
-c "SELECT count(*) FROM information_schema.tables WHERE table_schema='ssa_schema';"
# 6e. 验证关键列存在
docker exec ai-clinical-postgres psql \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" \
-c "SELECT column_name FROM information_schema.columns WHERE table_schema='asl_schema' AND table_name='research_tasks' AND column_name IN ('target_sources','synthesis_report','result_list');"
# 6f. 验证 RDS 原有数据未受影响(Prompt ID 1-16 + 用户数据)
docker exec ai-clinical-postgres psql \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" \
-c "SELECT id, code, name FROM capability_schema.prompt_templates WHERE id <= 16 ORDER BY id LIMIT 5;"
docker exec ai-clinical-postgres psql \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" \
-c "SELECT count(*) as user_count FROM platform_schema.users;"
验证通过标准:
| 检查项 | 预期值 |
|---|---|
| 总表数 | 96 |
| 迁移记录数 | 13 |
| prompt_templates 总数 | 27 |
| ssa_schema 表数 | 11 |
| research_tasks 新列 | 3 列存在 |
| 原有 Prompt(ID 1-16) | 数据完整、中文正常 |
| 用户数 | 22(不变) |
6. 回滚方案
6.1 迁移执行失败
prisma migrate deploy 每个迁移在事务中执行,失败自动回滚,不影响已有数据。
处理方式:查看错误信息,修复 SQL 后重新执行。
6.2 需要完全回滚
使用步骤 1 的备份文件恢复:
# 在 Docker 容器内操作(避免编码问题)
# 1. 删除测试数据库
docker exec ai-clinical-postgres psql \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/postgres" \
-c "DROP DATABASE ai_clinical_research_test WITH (FORCE);"
# 2. 重新创建
docker exec ai-clinical-postgres psql \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/postgres" \
-c "CREATE DATABASE ai_clinical_research_test ENCODING='UTF8';"
# 3. 安装插件
docker exec ai-clinical-postgres psql \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" \
-c "CREATE EXTENSION IF NOT EXISTS pg_bigm; CREATE EXTENSION IF NOT EXISTS vector;"
# 4. 恢复备份
docker exec ai-clinical-postgres psql \
"postgresql://airesearch:Xibahe%40fengzhibo117@pgm-2zex1m2y3r23hdn5so.pg.rds.aliyuncs.com:5432/ai_clinical_research_test" \
-f /tmp/backup_rds_before_0227.sql
7. 注意事项
7.1 PowerShell 编码问题
⚠️ 0126 部署的惨痛教训:PowerShell 会破坏 UTF-8 编码
所有涉及中文数据的操作(备份、导入 Seed 数据)必须在 Docker 容器内直接执行(docker exec),绝不能通过 PowerShell 管道传输。
7.2 RDS 权限
CREATE EXTENSION需要 RDS 超级用户权限(已在 0126 部署中通过 RDS 控制台安装插件)CREATE SCHEMA需要 OWNER 权限(airesearch 用户已具备)
7.3 执行顺序
补丁迁移 20260227 必须排在 20260219_add_ssa_module 之后,因为 ssa_workflows 表的外键引用 ssa_sessions 表(由 #9 迁移创建)。当前文件名的字母序保证了正确顺序。
7.4 Prompt ID 序列
导入 Seed 数据后需要重置 prompt_templates 和 prompt_versions 表的 ID 序列:
-- 重置序列到当前最大值
SELECT setval(pg_get_serial_sequence('capability_schema.prompt_templates', 'id'),
(SELECT MAX(id) FROM capability_schema.prompt_templates));
SELECT setval(pg_get_serial_sequence('capability_schema.prompt_versions', 'id'),
(SELECT MAX(id) FROM capability_schema.prompt_versions));
8. 后续改进:防止 Schema 漂移
8.1 根因
当前 6 表 + 3 列无迁移文件的根因是开发中使用了 prisma db push 而非 prisma migrate dev。
8.2 四层防线
| 层级 | 时机 | 方法 | 实施成本 |
|---|---|---|---|
| 开发规范 | 写代码时 | 禁止 db push,统一用 migrate dev |
零 |
| pre-commit Hook | git commit 前 | 检测 schema.prisma 变更但无新迁移文件 | 一次性 |
| CI 漂移检测 | PR 合并前 | prisma migrate diff --exit-code |
一次性 |
| 部署前闸门 | 部署前 | 对比 schema vs 迁移历史 | 一次性 |
详细实施方案见开发规范更新(后续单独文档)。
9. 时间估算
| 步骤 | 预计耗时 | 说明 |
|---|---|---|
| 步骤 1:备份 | 5 分钟 | pg_dump 约 20MB |
| 步骤 2:创建补丁迁移 | 5 分钟 | 创建文件 |
| 步骤 3:本地标记 | 2 分钟 | prisma resolve |
| 步骤 4:RDS 迁移 | 3 分钟 | migrate deploy |
| 步骤 5:Seed 数据 | 5 分钟 | CSV 导出导入 |
| 步骤 6:验证 | 5 分钟 | 7 项检查 |
| 总计 | ~25 分钟 | 含缓冲时间 |
更新日志
| 版本 | 日期 | 内容 |
|---|---|---|
| v1.0 | 2026-02-27 | 初始版本:完整迁移方案 |
文档结束