Files
AIclinicalresearch/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-23_数据库迁移状态说明.md
HaHafeng 66255368b7 feat(admin): Add user management and upgrade to module permission system
Features - User Management (Phase 4.1):
- Database: Add user_modules table for fine-grained module permissions
- Database: Add 4 user permissions (view/create/edit/delete) to role_permissions
- Backend: UserService (780 lines) - CRUD with tenant isolation
- Backend: UserController + UserRoutes (648 lines) - 13 API endpoints
- Backend: Batch import users from Excel
- Frontend: UserListPage (412 lines) - list/filter/search/pagination
- Frontend: UserFormPage (341 lines) - create/edit with module config
- Frontend: UserDetailPage (393 lines) - details/tenant/module management
- Frontend: 3 modal components (592 lines) - import/assign/configure
- API: GET/POST/PUT/DELETE /api/admin/users/* endpoints

Architecture Upgrade - Module Permission System:
- Backend: Add getUserModules() method in auth.service
- Backend: Login API returns modules array in user object
- Frontend: AuthContext adds hasModule() method
- Frontend: Navigation filters modules based on user.modules
- Frontend: RouteGuard checks requiredModule instead of requiredVersion
- Frontend: Remove deprecated version-based permission system
- UX: Only show accessible modules in navigation (clean UI)
- UX: Smart redirect after login (avoid 403 for regular users)

Fixes:
- Fix UTF-8 encoding corruption in ~100 docs files
- Fix pageSize type conversion in userService (String to Number)
- Fix authUser undefined error in TopNavigation
- Fix login redirect logic with role-based access check
- Update Git commit guidelines v1.2 with UTF-8 safety rules

Database Changes:
- CREATE TABLE user_modules (user_id, tenant_id, module_code, is_enabled)
- ADD UNIQUE CONSTRAINT (user_id, tenant_id, module_code)
- INSERT 4 permissions + role assignments
- UPDATE PUBLIC tenant with 8 module subscriptions

Technical:
- Backend: 5 new files (~2400 lines)
- Frontend: 10 new files (~2500 lines)
- Docs: 1 development record + 2 status updates + 1 guideline update
- Total: ~4900 lines of code

Status: User management 100% complete, module permission system operational
2026-01-16 13:42:10 +08:00

13 KiB
Raw Permalink Blame History

数据库迁移状态说明

文档版本: 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

迁移策略选择

方案APrisma Migrate被拒绝

npx prisma migrate dev --name add_fulltext_screening

拒绝原因

  • Prisma会尝试删除 public schema中的重复表
  • 可能影响其他模块的数据
  • 违反"管好自己"的原则

方案B手动SQL脚本已采用

# 创建手动迁移脚本
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_idscreening_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_idfulltext_screening_tasks(id) ON DELETE CASCADE
  • project_idscreening_projects(id) ON DELETE CASCADE
  • literature_idliteratures(id) ON DELETE CASCADE

迁移结果验证

-- 验证表创建
\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生成:

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表

代码示例(正确访问方式):

// ✅ 正确通过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. 便于代码审查和审计

操作流程

# 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脚本模板

-- 只操作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
  (标题摘要初筛任务)           (全文复筛任务)

外键约束验证

-- 验证所有外键都指向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

📚 相关文档


文档维护

  • 数据库结构变更时更新
  • 发现新问题时记录
  • 定期审查Schema隔离状况

最后更新2025-11-23
更新人ASL开发团队
下次审查:下次数据库迁移时