# 数据库迁移状态说明 > **文档版本:** v1.0 > **创建日期:** 2025-11-23 > **维护者:** ASL开发团队 > **文档目的:** 记录ASL模块数据库迁移状态,为未来开发人员提供清晰的上下文 --- ## 📋 当前数据库状态总览 ### ✅ ASL模块(asl_schema)- 完全正确 | 表名 | 状态 | 用途 | 记录数 | |-----|------|------|--------| | `literatures` | ✅ 已更新 | 文献基础信息(含全文字段) | - | | `screening_projects` | ✅ 正常 | 筛选项目 | - | | `screening_tasks` | ✅ 正常 | 标题摘要初筛任务 | - | | `screening_results` | ✅ 正常 | 标题摘要初筛结果 | - | | `fulltext_screening_tasks` | ✅ 新建 | 全文复筛任务 | 0 | | `fulltext_screening_results` | ✅ 新建 | 全文复筛结果 | 0 | **核心结论**: - ✅ ASL模块所有数据完全位于 `asl_schema` - ✅ 没有数据泄漏到 `public` schema - ✅ Schema隔离策略执行正确 - ✅ 代码访问路径正确(`prisma.aslLiterature`, `prisma.aslScreeningProject` 等) --- ## 🔴 Public Schema历史遗留问题(与ASL无关) ### 问题描述 在项目早期开发中,部分模块的表被错误地创建在 `public` schema 中,违反了Schema隔离策略: | 错误表名 | 应在Schema | 当前状态 | |---------|-----------|---------| | `public.users` | `platform_schema` | ⚠️ 重复存在 | | `public.projects` | `aia_schema` | ⚠️ 重复存在 | | `public.conversations` | `aia_schema` | ⚠️ 重复存在 | | `public.messages` | `aia_schema` | ⚠️ 重复存在 | | `public.knowledge_bases` | `pkb_schema` | ⚠️ 重复存在 | | `public.documents` | `pkb_schema` | ⚠️ 重复存在 | | `public.batch_tasks` | `pkb_schema` | ⚠️ 重复存在 | | `public.batch_results` | `pkb_schema` | ⚠️ 重复存在 | **数据对比(2025-11-23快照)**: ``` platform_schema.users: 3条记录 public.users: 2条记录 aia_schema.projects: 2条记录 public.projects: 2条记录 pkb_schema.knowledge_bases: 2条记录 public.knowledge_bases: 2条记录 ``` **影响范围**: - 🟢 **不影响ASL模块**(ASL完全隔离在asl_schema) - ⚠️ 影响AIA模块(AI助手) - ⚠️ 影响PKB模块(知识库) - ⚠️ 影响Platform模块(用户系统) **责任归属**: - 🔵 ASL团队:无责任,数据管理完全正确 - 🟡 其他模块团队:需自行清理public schema数据 --- ## 🛠️ 2025-11-23迁移操作记录 ### 迁移目标 为全文复筛功能(Day 4开发)添加数据库支持: 1. 修改 `literatures` 表(添加全文相关字段) 2. 创建 `fulltext_screening_tasks` 表 3. 创建 `fulltext_screening_results` 表 ### 迁移策略选择 **❌ 方案A:Prisma Migrate(被拒绝)** ```bash npx prisma migrate dev --name add_fulltext_screening ``` **拒绝原因**: - Prisma会尝试删除 `public` schema中的重复表 - 可能影响其他模块的数据 - 违反"管好自己"的原则 **✅ 方案B:手动SQL脚本(已采用)** ```bash # 创建手动迁移脚本 backend/prisma/migrations/manual_fulltext_screening.sql # 执行迁移(仅操作asl_schema) Get-Content manual_fulltext_screening.sql | docker exec -i ai-clinical-postgres psql ... ``` **优势**: - ✅ 只操作 `asl_schema`,不动其他schema - ✅ 不删除任何 `public` 数据 - ✅ 安全、可控、可审计 - ✅ 符合"管好自己"原则 ### 迁移内容详情 #### 1. 修改 `literatures` 表 新增字段(13个): **文献生命周期**: - `stage TEXT DEFAULT 'imported'` - 阶段标记(imported → title_screened → fulltext_pending → fulltext_screened) **PDF管理**: - `has_pdf BOOLEAN DEFAULT false` - 是否有PDF - `pdf_storage_type TEXT` - 存储类型(oss/dify/local) - `pdf_storage_ref TEXT` - 存储引用(key或ID) - `pdf_status TEXT DEFAULT 'pending'` - 状态(pending/extracting/completed/failed) - `pdf_uploaded_at TIMESTAMP(3)` - 上传时间 **全文管理(云原生)**: - `full_text_storage_type TEXT` - 存储类型(oss/dify) - `full_text_storage_ref TEXT` - 存储引用 - `full_text_url TEXT` - 访问URL **全文元数据**: - `full_text_format TEXT` - 格式(markdown/plaintext) - `full_text_source TEXT` - 提取方式(nougat/pymupdf) - `full_text_token_count INTEGER` - Token数量 - `full_text_extracted_at TIMESTAMP(3)` - 提取时间 **新增索引**: - `idx_literatures_stage` - `idx_literatures_has_pdf` - `idx_literatures_pdf_status` #### 2. 创建 `fulltext_screening_tasks` 表 任务管理表,字段包括: - 基础信息:`id`, `project_id` - 模型配置:`model_a`, `model_b`, `prompt_version` - 进度跟踪:`total_count`, `processed_count`, `success_count`, `failed_count`, `degraded_count` - 成本统计:`total_tokens`, `total_cost` - 状态管理:`status`, `started_at`, `completed_at`, `estimated_end_at` - 错误记录:`error_message`, `error_stack` **索引**: - `idx_fulltext_tasks_project_id` - `idx_fulltext_tasks_status` - `idx_fulltext_tasks_created_at` **外键约束**: - `project_id` → `screening_projects(id)` ON DELETE CASCADE #### 3. 创建 `fulltext_screening_results` 表 结果存储表(12字段模板),字段包括: - 关联信息:`task_id`, `project_id`, `literature_id` - Model A结果:`model_a_name`, `model_a_fields` (JSONB), `model_a_tokens`, `model_a_cost` 等 - Model B结果:`model_b_name`, `model_b_fields` (JSONB), `model_b_tokens`, `model_b_cost` 等 - 验证结果:`medical_logic_issues` (JSONB), `evidence_chain_issues` (JSONB) - 冲突检测:`is_conflict`, `conflict_severity`, `conflict_fields`, `review_priority` - 人工复核:`final_decision`, `final_decision_by`, `exclusion_reason`, `review_notes` - 处理状态:`processing_status`, `is_degraded`, `degraded_model` - 可追溯性:`raw_output_a` (JSONB), `raw_output_b` (JSONB), `prompt_version` **索引**: - `idx_fulltext_results_task_id` - `idx_fulltext_results_project_id` - `idx_fulltext_results_literature_id` - `idx_fulltext_results_is_conflict` - `idx_fulltext_results_final_decision` - `idx_fulltext_results_review_priority` **唯一约束**: - `unique_project_literature_fulltext (project_id, literature_id)` **外键约束**: - `task_id` → `fulltext_screening_tasks(id)` ON DELETE CASCADE - `project_id` → `screening_projects(id)` ON DELETE CASCADE - `literature_id` → `literatures(id)` ON DELETE CASCADE ### 迁移结果验证 ```sql -- 验证表创建 \dt asl_schema.* -- 结果:6个表 -- ✅ literatures (已更新) -- ✅ screening_projects -- ✅ screening_tasks -- ✅ screening_results -- ✅ fulltext_screening_tasks (新建) -- ✅ fulltext_screening_results (新建) -- 验证新字段 \d asl_schema.literatures -- 结果: -- ✅ stage -- ✅ has_pdf -- ✅ full_text_storage_type -- ✅ full_text_storage_ref -- ✅ full_text_url -- ✅ full_text_format -- ... 等13个新字段 ``` **Prisma Client生成**: ```bash cd backend npx prisma generate # 结果:✅ 生成成功 # 代码可访问: # - prisma.aslLiterature # - prisma.aslFulltextScreeningTask # - prisma.aslFulltextScreeningResult ``` --- ## 📐 Schema隔离策略执行情况 ### 设计原则(来自系统架构文档) ``` 各模块数据逻辑隔离: ├── admin_schema (系统管理) ├── platform_schema (用户系统) ├── aia_schema (AI助手) ├── asl_schema (AI智能文献) ✅ 执行正确 ├── pkb_schema (知识库) ├── rvw_schema (审阅协作) ├── st_schema (统计分析) ├── dc_schema (数据采集) ├── ssa_schema (样本量分析) └── common_schema (公共数据) ``` ### ASL模块执行情况 ✅ | 检查项 | 状态 | 说明 | |-------|------|------| | Schema命名 | ✅ 正确 | `asl_schema` | | 所有表都在正确Schema | ✅ 正确 | 6个表全部在 `asl_schema` | | 没有表在public | ✅ 正确 | 无泄漏 | | Prisma Model映射正确 | ✅ 正确 | `@@schema("asl_schema")` | | 代码访问路径正确 | ✅ 正确 | `prisma.aslXxx` | | 外键约束内部化 | ✅ 正确 | 所有FK指向同schema表 | **代码示例**(正确访问方式): ```typescript // ✅ 正确:通过Prisma Client访问asl_schema const project = await prisma.aslScreeningProject.findUnique({ where: { id: projectId }, }); const literatures = await prisma.aslLiterature.findMany({ where: { projectId }, }); const task = await prisma.aslFulltextScreeningTask.create({ data: { ... }, }); // ❌ 错误:直接SQL访问public(不会发生,因为表不在public) await prisma.$queryRaw`SELECT * FROM public.literatures`; ``` --- ## 🔮 未来迁移策略 ### 对于ASL模块 **推荐策略**:继续使用手动SQL脚本 **原因**: 1. ✅ Public schema的历史遗留问题短期无法解决 2. ✅ 手动脚本更安全、可控 3. ✅ 避免意外影响其他模块 4. ✅ 便于代码审查和审计 **操作流程**: ```bash # 1. 修改 Prisma Schema # backend/prisma/schema.prisma # 2. 编写手动SQL脚本 # backend/prisma/migrations/manual_xxx.sql # 3. 执行脚本(只操作asl_schema) Get-Content manual_xxx.sql | docker exec -i ai-clinical-postgres psql ... # 4. 验证结果 docker exec ai-clinical-postgres psql ... -c "\dt asl_schema.*" # 5. 生成Prisma Client npx prisma generate # 6. 提交Git git add . git commit -m "feat(asl): add xxx tables for xxx feature" ``` **SQL脚本模板**: ```sql -- 只操作asl_schema,不影响其他schema ALTER TABLE asl_schema.xxx ADD COLUMN IF NOT EXISTS ...; CREATE TABLE IF NOT EXISTS asl_schema.xxx (...); CREATE INDEX IF NOT EXISTS idx_xxx ON asl_schema.xxx(...); ``` ### 对于其他模块 **问题所有者**:各模块开发团队 **建议操作**(由各模块团队自行决定): 1. 检查 `public` schema中是否有本模块的表 2. 对比数据差异(`public` vs 正确schema) 3. 决策是否需要数据迁移或清理 4. 执行清理操作(风险自负) **ASL团队立场**: - 🔵 不主动清理其他模块的public表 - 🔵 不对其他模块数据安全负责 - 🔵 专注于asl_schema的质量和稳定性 --- ## 📊 数据完整性验证 ### ASL模块数据关系图 ``` asl_schema.screening_projects (项目) ↓ 1:N asl_schema.literatures (文献) ↓ 1:1 ↓ 1:1 asl_schema.screening_results asl_schema.fulltext_screening_results (标题摘要初筛结果) (全文复筛结果) ↑ N:1 ↑ N:1 asl_schema.screening_tasks asl_schema.fulltext_screening_tasks (标题摘要初筛任务) (全文复筛任务) ``` ### 外键约束验证 ```sql -- 验证所有外键都指向asl_schema内部 SELECT tc.constraint_name, tc.table_name, kcu.column_name, ccu.table_name AS foreign_table_name, ccu.column_name AS foreign_column_name FROM information_schema.table_constraints AS tc JOIN information_schema.key_column_usage AS kcu ON tc.constraint_name = kcu.constraint_name JOIN information_schema.constraint_column_usage AS ccu ON ccu.constraint_name = tc.constraint_name WHERE tc.constraint_type = 'FOREIGN KEY' AND tc.table_schema = 'asl_schema' ORDER BY tc.table_name; -- 预期结果: -- ✅ 所有FK的 foreign_table_name 都在 asl_schema 中 -- ✅ 没有跨schema引用 ``` --- ## 🎯 关键结论 ### ✅ ASL模块:完全健康 1. **Schema隔离**:100%正确,所有表都在 `asl_schema` 2. **数据管理**:无数据泄漏到 `public` 3. **代码规范**:所有访问路径正确 4. **迁移策略**:手动SQL脚本,安全可控 ### ⚠️ 系统级问题:Public Schema污染 1. **问题性质**:历史遗留,与ASL无关 2. **影响范围**:AIA、PKB、Platform模块 3. **解决责任**:各模块团队自行处理 4. **ASL策略**:不动public,管好自己 ### 📋 开发人员指南 **如果你是ASL模块开发者**: - ✅ 继续保持当前的Schema隔离实践 - ✅ 使用手动SQL脚本进行数据库迁移 - ✅ 所有表都创建在 `asl_schema` - ✅ 不要尝试清理 `public` schema **如果你是其他模块开发者**: - 🟡 检查自己模块的Schema隔离状况 - 🟡 决定是否需要清理 `public` 中的重复表 - 🟡 参考ASL的迁移策略(手动SQL) - 🟡 不要依赖ASL团队清理public --- ## 📚 相关文档 - [系统总体设计 - 数据库架构说明](../../../../00-系统总体设计/03-数据库架构说明.md) - [ASL模块 - 数据库设计](../../02-技术设计/01-数据库设计.md) - [云原生开发规范](../../../../04-开发规范/08-云原生开发规范.md) - [Day 2-3开发记录](./2025-11-22_Day2-Day3_LLM服务与验证系统开发.md) --- **文档维护**: - 数据库结构变更时更新 - 发现新问题时记录 - 定期审查Schema隔离状况 **最后更新**:2025-11-23 **更新人**:ASL开发团队 **下次审查**:下次数据库迁移时