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

436 lines
13 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 数据库迁移状态说明
> **文档版本:** 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被拒绝**
```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开发团队
**下次审查**:下次数据库迁移时