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

13 KiB
Raw Blame History

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
  • 冲突检测:仅当两个模型结论不一致时标记为冲突
  • 人工复核:更新 finalDecisionfinalDecisionByconflictStatus

2. 前端类型系统

文件

  • frontend-v2/src/modules/asl/types/index.ts

新增类型

// 判断类型
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

新增函数

// 获取筛选任务
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秒轮询任务进度
  • 任务完成/失败时自动停止轮询
  • 返回进度百分比、状态标记

关键实现:

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

核心函数

// 将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 处理双行数据

表格列定义示例

{
  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

智能停止:

refetchInterval: (query) => {
  const task = query.state.data?.data;
  if (task?.status === 'completed' || task?.status === 'failed') {
    return false; // 停止
  }
  return 2000; // 继续轮询
}

3. 后端分页

为什么选择后端分页?

在云原生架构Serverless SAE + RDS

  • 减少单次查询数据量
  • 降低内存占用
  • 提升响应速度
  • 适合大数据量场景
  • 符合Serverless按请求计费的成本优化策略

实现:

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: 2rowSpan: 0
  3. 轮询停止机制:任务完成后仍在轮询

    • 解决使用React Query的智能 refetchInterval 函数

开发效率

  • 总耗时: 约2小时
  • 代码行数: 1402行
  • 文件数量: 11个文件

🎉 结语

Day 3任务圆满完成

审核工作台是整个ASL模块的核心功能实现了

  • 双模型结果对比展示
  • 冲突检测与高亮
  • 人工复核完整流程
  • 实时任务进度监控
  • 云原生架构最佳实践

期待继续Day 4-5的开发完善整个标题摘要初筛功能🚀


报告日期: 2025-11-19
报告人: AI Assistant
审核人: 待定