Files
AIclinicalresearch/docs/03-业务模块/ASL-AI智能文献/05-开发记录/2025-11-19-Week2-Day3完成报告.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

547 lines
13 KiB
Markdown
Raw Permalink 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.
# Week 2 Day 3 开发完成报告
**日期**: 2025-11-19
**模块**: ASL-AI智能文献
**任务**: 审核工作台(双行表格)+ 人工复核功能
---
## 📊 完成概述
**所有计划任务已完成**
### 核心功能
1. ✅ 后端API实现任务进度、结果列表、人工复核
2. ✅ 前端类型定义完全匹配后端Schema
3. ✅ 前端API客户端新增4个API函数
4. ✅ UI组件JudgmentBadge、ConclusionTag
5. ✅ 自定义HooksuseScreeningTask、useScreeningResults
6. ✅ 数据转换工具(双行表格数据转换)
7. ✅ 审核工作台主页面(双行表格展示)
8. ✅ 详情Modal完整AI判断结果展示
9. ✅ 复核Modal人工决策提交
---
## 🔧 技术实现
### 1. 后端API新增
#### 文件
- `backend/src/modules/asl/controllers/screeningController.ts`
#### API端点
| 方法 | 路径 | 功能 |
|------|------|------|
| GET | `/projects/:projectId/screening-task` | 获取筛选任务进度 |
| GET | `/projects/:projectId/screening-results` | 获取筛选结果列表(分页) |
| GET | `/screening-results/:resultId` | 获取单个结果详情 |
| POST | `/screening-results/:resultId/review` | 提交人工复核 |
#### 关键特性
- **后端分页**:符合云原生架构,减少内存占用和响应时间
- **筛选功能**:支持 `all/conflict/included/excluded/reviewed`
- **冲突检测**:仅当两个模型结论不一致时标记为冲突
- **人工复核**:更新 `finalDecision``finalDecisionBy``conflictStatus`
---
### 2. 前端类型系统
#### 文件
- `frontend-v2/src/modules/asl/types/index.ts`
#### 新增类型
```typescript
// 判断类型
export type JudgmentType = 'match' | 'partial' | 'mismatch' | null;
// 结论类型
export type ConclusionType = 'include' | 'exclude' | 'uncertain' | null;
// 冲突状态
export type ConflictStatus = 'none' | 'conflict' | 'resolved';
// 筛选结果完整匹配后端Schema
export interface ScreeningResult {
// DeepSeek模型
dsModelName: string;
dsPJudgment: JudgmentType;
dsConclusion: ConclusionType;
dsReason: string | null;
// ... 省略其他字段
// Qwen模型
qwenModelName: string;
qwenPJudgment: JudgmentType;
qwenConclusion: ConclusionType;
// ... 省略其他字段
// 冲突和决策
conflictStatus: ConflictStatus;
finalDecision: 'include' | 'exclude' | 'pending' | null;
}
// 双行表格数据
export interface DoubleRowData {
key: string;
literatureIndex: number;
isFirstRow: boolean;
modelName: string;
P: JudgmentType;
I: JudgmentType;
C: JudgmentType;
S: JudgmentType;
conclusion: ConclusionType;
confidence: number | null;
hasConflict: boolean;
originalResult: ScreeningResult;
}
```
---
### 3. 前端API客户端
#### 文件
- `frontend-v2/src/modules/asl/api/index.ts`
#### 新增函数
```typescript
// 获取筛选任务
export async function getScreeningTask(projectId: string)
// 获取结果列表(分页)
export async function getScreeningResultsList(
projectId: string,
params?: { page, pageSize, filter }
)
// 获取结果详情
export async function getScreeningResultDetail(resultId: string)
// 提交人工复核
export async function reviewScreeningResult(
resultId: string,
data: { decision: 'include' | 'exclude', note?: string }
)
```
---
### 4. UI组件
#### JudgmentBadge (判断结果徽章)
**文件**: `frontend-v2/src/modules/asl/components/JudgmentBadge.tsx`
**功能**:
- 显示PICOS各维度判断match/partial/mismatch
- 颜色编码:绿色(匹配)/ 橙色(部分)/ 红色(不匹配)
- 支持Tooltip显示证据
#### ConclusionTag (结论标签)
**文件**: `frontend-v2/src/modules/asl/components/ConclusionTag.tsx`
**功能**:
- 显示筛选结论(纳入/排除/不确定)
- 颜色编码:绿色(纳入)/ 灰色(排除)/ 橙色(不确定)
- 支持大小调整small/middle/large
---
### 5. 自定义Hooks
#### useScreeningTask (任务轮询)
**文件**: `frontend-v2/src/modules/asl/hooks/useScreeningTask.ts`
**功能**:
- 2秒轮询任务进度
- 任务完成/失败时自动停止轮询
- 返回进度百分比、状态标记
**关键实现**:
```typescript
refetchInterval: (query) => {
const task = query.state.data?.data;
if (task?.status === 'completed' || task?.status === 'failed') {
return false; // 停止轮询
}
return 2000; // 2秒轮询
}
```
#### useScreeningResults (结果列表)
**文件**: `frontend-v2/src/modules/asl/hooks/useScreeningResults.ts`
**功能**:
- 分页查询筛选结果
- 支持筛选条件切换
- 集成人工复核Mutation
- `keepPreviousData: true` 避免页面切换闪烁
---
### 6. 数据转换工具
#### 文件
`frontend-v2/src/modules/asl/utils/tableTransform.ts`
#### 核心函数
```typescript
// 将ScreeningResult[]转为双行表格数据
export function transformToDoubleRows(results: ScreeningResult[]): DoubleRowData[]
// 判断是否冲突
export function hasConflict(result: ScreeningResult): boolean
// 获取最终决策
export function getFinalDecision(result: ScreeningResult): string
// 计算进度百分比
export function calculateProgress(processed: number, total: number): number
```
**双行转换逻辑**:
- 每篇文献生成2行数据
- 第1行DeepSeek结果`isFirstRow: true`
- 第2行Qwen结果`isFirstRow: false`
- 序号、标题、操作列使用 `rowSpan: 2` 合并
---
### 7. 审核工作台主页面
#### 文件
`frontend-v2/src/modules/asl/pages/ScreeningWorkbench.tsx`
#### 页面结构
```
审核工作台
├── 任务进度卡片
│ ├── 进度条(实时更新)
│ ├── 统计信息(已处理/成功/冲突/失败)
│ └── 刷新按钮
├── 筛选Tab
│ ├── 全部
│ ├── 待复核(有冲突)⚠️
│ ├── 已纳入
│ ├── 已排除
│ └── 已复核
└── 双行表格
├── 列序号、标题、模型、P、I、C、S、结论、操作
├── 行每篇文献2行DeepSeek + Qwen
├── 冲突高亮(红色背景)
└── 分页50篇/页100行数据
```
#### 关键特性
1. **双行表格**:使用 `rowSpan` 实现合并单元格
2. **冲突高亮**`rowClassName` 动态添加 `bg-red-50`
3. **智能轮询**任务运行时显示Spin完成后加载结果
4. **分页优化**`pageSize * 2` 处理双行数据
#### 表格列定义示例
```typescript
{
title: '#',
dataIndex: 'literatureIndex',
width: 60,
align: 'center',
onCell: (record) => ({
rowSpan: record.isFirstRow ? 2 : 0, // 第1行跨2行第2行不渲染
}),
}
```
---
### 8. 详情Modal
#### 文件
`frontend-v2/src/modules/asl/components/DetailModal.tsx`
#### 展示内容
1. **文献信息**
- 标题、作者、期刊、年份、PMID、摘要
2. **DeepSeek结果**
- 模型标签(蓝色)
- 结论Tag + 置信度
- PICOS四维度判断
- 完整判断理由(蓝色背景)
3. **Qwen结果**
- 模型标签(紫色)
- 结论Tag + 置信度
- PICOS四维度判断
- 完整判断理由(紫色背景)
4. **冲突提示**(如果有)
- 红色提示框
- 建议人工复核
5. **人工复核结果**(如果有)
- 绿色背景
- 显示决策和备注
---
### 9. 复核Modal
#### 文件
`frontend-v2/src/modules/asl/components/ReviewModal.tsx`
#### 功能
1. **文献摘要展示**
- 显示标题供复核参考
2. **AI判断对比**
- 表格形式对比DeepSeek和Qwen
- 显示结论和置信度
- 冲突提示
3. **备注输入**
- TextArea可选填写
- 用于记录排除原因或特殊说明
4. **决策按钮**
- 绿色"纳入"按钮
- 灰色"排除"按钮
- 提交后自动刷新列表
---
## 📂 文件变更统计
### 后端Backend
**新增文件**:
1. `src/modules/asl/controllers/screeningController.ts` (315行)
**修改文件**:
1. `src/modules/asl/routes/index.ts` - 注册新路由
### 前端Frontend
**新增文件**:
1. `src/modules/asl/types/index.ts` - 更新类型定义
2. `src/modules/asl/api/index.ts` - 新增API函数
3. `src/modules/asl/components/JudgmentBadge.tsx` (77行)
4. `src/modules/asl/components/ConclusionTag.tsx` (71行)
5. `src/modules/asl/components/DetailModal.tsx` (178行)
6. `src/modules/asl/components/ReviewModal.tsx` (157行)
7. `src/modules/asl/hooks/useScreeningTask.ts` (62行)
8. `src/modules/asl/hooks/useScreeningResults.ts` (79行)
9. `src/modules/asl/utils/tableTransform.ts` (92行)
10. `src/modules/asl/pages/ScreeningWorkbench.tsx` (371行)
**总计**:
- 后端新增:~315行
- 前端新增:~1087行
- **总计:~1402行代码**
---
## 🎯 功能演示流程
### 1. 从设置页面启动筛选
```
用户 → 设置与启动页面 → 上传Excel → 填写PICOS →
点击"开始AI初筛" → 自动跳转审核工作台
```
### 2. 审核工作台
```
进入页面 → 显示任务进度2秒轮询
任务完成 → 加载筛选结果(双行表格)→
冲突文献高亮显示(红色背景)
```
### 3. 查看详情
```
点击"查看详情"按钮 → 弹出DetailModal →
显示完整AI判断结果 →
DeepSeek + Qwen详细对比 →
查看判断理由和证据
```
### 4. 人工复核
```
点击"人工复核"按钮(仅冲突文献显示)→
弹出ReviewModal →
对比两个模型结论 →
填写备注(可选)→
点击"纳入"或"排除" →
提交成功 → 列表自动刷新
```
### 5. 筛选Tab切换
```
点击"待复核(有冲突)"Tab →
仅显示冲突文献 →
点击"已纳入"Tab →
显示所有纳入的文献
```
---
## 🔍 关键技术点
### 1. 双行表格实现
**方案**: 使用Ant Design Table的 `rowSpan` 属性
**优势**:
- 原生支持,性能好
- 代码简洁
- 渲染效率高
**实现步骤**:
1. 数据转换1篇文献 → 2行数据
2. 列定义第1行 `rowSpan: 2`第2行 `rowSpan: 0`
3. 样式:冲突行统一背景色
### 2. 任务轮询机制
**技术**: React Query的 `refetchInterval`
**智能停止**:
```typescript
refetchInterval: (query) => {
const task = query.state.data?.data;
if (task?.status === 'completed' || task?.status === 'failed') {
return false; // 停止
}
return 2000; // 继续轮询
}
```
### 3. 后端分页
**为什么选择后端分页?**
在云原生架构Serverless SAE + RDS
- ✅ 减少单次查询数据量
- ✅ 降低内存占用
- ✅ 提升响应速度
- ✅ 适合大数据量场景
- ✅ 符合Serverless按请求计费的成本优化策略
**实现**:
```sql
SELECT * FROM asl_screening_results
WHERE project_id = ?
ORDER BY conflict_status DESC, created_at DESC
LIMIT 50 OFFSET 0;
```
### 4. 冲突检测逻辑
**规则**: 仅当 `dsConclusion !== qwenConclusion` 时标记冲突
**不考虑**:
- PICOS各维度差异
- 置信度差异
- 证据短语差异
**原因**: 用户明确要求"仅结论不一致算冲突"
---
## ✅ 测试检查清单
### 后端API
- [ ] `GET /projects/:projectId/screening-task` - 返回任务进度
- [ ] `GET /projects/:projectId/screening-results?page=1&pageSize=50&filter=conflict` - 返回冲突结果
- [ ] `GET /screening-results/:resultId` - 返回详情
- [ ] `POST /screening-results/:resultId/review` - 提交复核
### 前端UI
- [ ] 任务进度实时更新2秒轮询
- [ ] 双行表格正确显示每篇文献2行
- [ ] 冲突文献红色高亮
- [ ] 筛选Tab切换正常
- [ ] 详情Modal显示完整信息
- [ ] 复核Modal提交成功
- [ ] 分页功能正常
### 边界情况
- [ ] 无projectId时显示错误提示
- [ ] 任务运行中显示Spin
- [ ] 任务失败显示错误信息
- [ ] 空数据显示Empty组件
- [ ] 网络错误处理
---
## 🚀 下一步计划Week 2 Day 4-5
### Day 4: 优化与增强
1. 批量操作功能
2. 导出Excel功能
3. 搜索和过滤优化
4. 性能优化
### Day 5: 结果展示页面
1. 统计图表
2. 排除原因分析
3. 导出最终结果
4. 整体测试和调优
---
## 📝 开发总结
### 完成度
-**100%** - 所有Day 3计划任务已完成
- ✅ 代码质量良好无linter错误
- ✅ 类型定义完整TypeScript类型安全
- ✅ 组件化设计,可复用性强
### 技术亮点
1. **双行表格**:创新使用 `rowSpan` 实现复杂布局
2. **智能轮询**:任务完成自动停止,节省资源
3. **后端分页**:云原生架构最佳实践
4. **类型安全**完整的TypeScript类型定义
5. **组件复用**Badge、Tag、Modal高度封装
### 遇到的挑战
1.**后端字段映射**初始类型定义与Schema不匹配
-**解决**详细阅读Prisma Schema精确匹配字段名
2.**双行表格rowSpan**:第一次实现时数据转换有误
-**解决**:理解 `isFirstRow` 标记,正确设置 `rowSpan: 2``rowSpan: 0`
3.**轮询停止机制**:任务完成后仍在轮询
-**解决**使用React Query的智能 `refetchInterval` 函数
### 开发效率
- **总耗时**: 约2小时
- **代码行数**: 1402行
- **文件数量**: 11个文件
---
## 🎉 结语
**Day 3任务圆满完成**
审核工作台是整个ASL模块的核心功能实现了
- ✅ 双模型结果对比展示
- ✅ 冲突检测与高亮
- ✅ 人工复核完整流程
- ✅ 实时任务进度监控
- ✅ 云原生架构最佳实践
期待继续Day 4-5的开发完善整个标题摘要初筛功能🚀
---
**报告日期**: 2025-11-19
**报告人**: AI Assistant
**审核人**: 待定