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
230 lines
8.4 KiB
SQL
230 lines
8.4 KiB
SQL
-- ================================================================
|
||
-- 补丁迁移:覆盖 db push 创建的 6 张表 + 3 列
|
||
-- 所有语句使用 IF NOT EXISTS 保证幂等(多次执行不报错)
|
||
-- 日期:2026-02-27
|
||
-- ================================================================
|
||
|
||
-- ============ 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: 1 列 + 2 张新表 ============
|
||
|
||
-- 6a. SSA Session 新增 data_profile 列(Phase 2A)
|
||
ALTER TABLE "ssa_schema"."ssa_sessions"
|
||
ADD COLUMN IF NOT EXISTS "data_profile" JSONB;
|
||
|
||
-- 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 $$;
|
||
|
||
-- ============ rvw_schema: review_tasks 缺失列 ============
|
||
|
||
-- 8. review_tasks 后续 prisma db push 新增的 8 个列
|
||
ALTER TABLE "rvw_schema"."review_tasks"
|
||
ADD COLUMN IF NOT EXISTS "selected_agents" TEXT[] DEFAULT ARRAY['editorial','methodology'];
|
||
ALTER TABLE "rvw_schema"."review_tasks"
|
||
ADD COLUMN IF NOT EXISTS "editorial_score" DOUBLE PRECISION;
|
||
ALTER TABLE "rvw_schema"."review_tasks"
|
||
ADD COLUMN IF NOT EXISTS "methodology_score" DOUBLE PRECISION;
|
||
ALTER TABLE "rvw_schema"."review_tasks"
|
||
ADD COLUMN IF NOT EXISTS "methodology_status" TEXT;
|
||
ALTER TABLE "rvw_schema"."review_tasks"
|
||
ADD COLUMN IF NOT EXISTS "pico_extract" JSONB;
|
||
ALTER TABLE "rvw_schema"."review_tasks"
|
||
ADD COLUMN IF NOT EXISTS "context_data" JSONB;
|
||
ALTER TABLE "rvw_schema"."review_tasks"
|
||
ADD COLUMN IF NOT EXISTS "is_archived" BOOLEAN DEFAULT false;
|
||
ALTER TABLE "rvw_schema"."review_tasks"
|
||
ADD COLUMN IF NOT EXISTS "archived_at" TIMESTAMP(3);
|
||
|
||
CREATE INDEX IF NOT EXISTS "review_tasks_is_archived_idx"
|
||
ON "rvw_schema"."review_tasks"("is_archived");
|