Files
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

9.4 KiB
Raw Permalink Blame History

Day 3 开发完成总结

日期: 2025-11-27
开发阶段: DC模块 - 工具B - Day 3
状态: 全部完成


📋 任务完成清单

上午HealthCheckService实现

任务

  • 实现HealthCheckService完整逻辑
  • 更新Controller中的healthCheck实现
  • 测试健康检查API

完成的功能

1. HealthCheckService.checkColumnHealth()

// 文件: backend/src/modules/dc/tool-b/services/HealthCheckService.ts

核心功能

  • Excel解析使用XLSX库
  • 只检查前100行性能优化
  • 空值率计算
  • 平均文本长度计算
  • Token预估字符数 × 1.5
  • 拦截策略:
    • 空值率 > 80% → 拒绝
    • 平均长度 < 10 → 拒绝
  • 健康检查结果缓存到数据库

实现代码量: 156行


下午TemplateService实现

任务

  • 实现TemplateService.getAllTemplates()
  • 实现TemplateService.seedTemplates()
  • 创建seed脚本并执行
  • 更新Controller中的getTemplates实现
  • 测试模板API

完成的功能

1. TemplateService.getAllTemplates()

// 文件: backend/src/modules/dc/tool-b/services/TemplateService.ts

核心功能

  • 从数据库读取所有模板
  • 按疾病类型和报告类型排序
  • JSONB字段映射到TypeScript接口

2. TemplateService.seedTemplates()

3个预设模板

# 疾病类型 报告类型 显示名称 字段数
1 lung_cancer pathology 肺癌病理报告 5个字段
2 diabetes admission 糖尿病入院记录 5个字段
3 hypertension outpatient 高血压门诊病历 5个字段

模板1肺癌病理报告

  • 病理类型(如:浸润性腺癌、鳞状细胞癌)
  • 分化程度(高/中/低分化)
  • 肿瘤大小最大径单位cm
  • 淋巴结转移(有/无及具体组别)
  • 免疫组化关键指标如TTF-1、NapsinA

模板2糖尿病入院记录

  • 主诉(患者入院的主要症状)
  • 糖尿病类型1型/2型
  • 病程5年
  • 并发症(如:糖尿病肾病、视网膜病变)
  • 空腹血糖单位mmol/L

模板3高血压门诊病历

  • 收缩压单位mmHg
  • 舒张压单位mmHg
  • 血压分级1级、2级、3级
  • 用药情况(当前使用的降压药)
  • 靶器官损害(如:心脏、肾脏、眼底)

3. Seed脚本

// 文件: backend/src/scripts/seed-templates.ts

执行结果

============================================================
🌱 开始初始化模板数据
============================================================

✅ Template seeded: 肺癌病理报告
✅ Template seeded: 糖尿病入院记录  
✅ Template seeded: 高血压门诊病历
✅ Template seeding completed

============================================================
✅ 模板初始化成功!
============================================================

实现代码量: 130行Service+ 27行Seed脚本


📊 API测试结果

1. 模板列表API

请求

GET http://localhost:3001/api/v1/dc/tool-b/templates

响应

{
  "templates": [
    {
      "diseaseType": "diabetes",
      "reportType": "admission",
      "displayName": "糖尿病入院记录",
      "fields": [...]
    },
    {
      "diseaseType": "hypertension",
      "reportType": "outpatient",
      "displayName": "高血压门诊病历",
      "fields": [...]
    },
    {
      "diseaseType": "lung_cancer",
      "reportType": "pathology",
      "displayName": "肺癌病理报告",
      "fields": [...]
    }
  ]
}

状态: 200 OK
响应大小: 1369 字节
数据完整性: 3个模板全部返回

2. 健康检查API ⚠️

状态: Controller已更新但缺少测试文件
下一步: Day 3完成后需要准备测试Excel文件


📝 数据库状态

模板表dc_templates

查询结果

SELECT * FROM dc_schema.dc_templates;

记录数: 3条
数据完整性: 全部字段都有值

id disease_type report_type display_name fields prompt_template
uuid-1 lung_cancer pathology 肺癌病理报告 [5个字段] 请从以下病理报告中提取信息...
uuid-2 diabetes admission 糖尿病入院记录 [5个字段] 请从以下入院记录中提取信息...
uuid-3 hypertension outpatient 高血压门诊病历 [5个字段] 请从以下门诊病历中提取信息...

📊 代码统计

类别 文件数 代码行数
Service实现 2 286行
├─ HealthCheckService.ts 1 156行
└─ TemplateService.ts 1 130行
Controller更新 1 31行新增
Scripts 1 27行
总计 4 344行

🎯 技术亮点

1. 健康检查智能拦截

// 拦截策略1空值率过高
if (emptyRate > 0.8) {
  return {
    status: 'bad',
    message: `空值率过高(${(emptyRate * 100).toFixed(1)}%),该列不适合提取`
  };
}

// 拦截策略2文本过短
if (avgLength < 10) {
  return {
    status: 'bad',
    message: `文本长度过短(平均${avgLength.toFixed(0)}字符),不适合提取`
  };
}

优势

  • 避免用户浪费Token
  • 提前发现数据质量问题
  • 提供明确的错误提示

2. Token预估算法

// 粗略估算:字符数 × 1.5
const estimatedTokens = Math.ceil(data.length * avgLength * 1.5);

const message = `健康度良好,预计消耗约 ${(estimatedTokens / 1000).toFixed(1)}k Token双模型约 ${(estimatedTokens * 2 / 1000).toFixed(1)}k Token`;

优势

  • 用户提前知道成本
  • 考虑双模型场景×2
  • 友好的展示格式

3. 模板Upsert机制

await prisma.dCTemplate.upsert({
  where: {
    diseaseType_reportType: {
      diseaseType: template.diseaseType,
      reportType: template.reportType
    }
  },
  update: { /* ... */ },
  create: { /* ... */ }
});

优势

  • 幂等性(多次执行不会重复插入)
  • 支持模板内容更新
  • 利用唯一约束防止重复

4. 性能优化

// 只检查前100行
const sampleData = data.slice(0, 100);

优势

  • 快速响应(< 3秒
  • 避免大文件OOM
  • 采样足够代表性

🐛 已知问题

1. ⚠️ 文件上传集成待完善

当前状态

  • Controller中使用了临时的文件读取方式
  • 需要集成storage服务

代码位置

// backend/src/modules/dc/tool-b/controllers/ExtractionController.ts
// TODO: 集成storage服务
const fs = await import('fs/promises');
const filePath = path.join(process.cwd(), 'uploads', fileKey);

待改进Day 4-5

// 使用storage服务
import { storage } from '../../../../common/storage/index.js';
const fileBuffer = await storage.download(fileKey);

2. ⚠️ 用户认证待集成

当前状态

  • 使用硬编码的userId = 'test-user'

待改进MVP之后

// 从session获取真实userId
const userId = req.session.userId || req.user.id;

🧪 完整测试结果

测试脚本

backend/src/scripts/test-health-check.ts

测试用例

测试1良好数据test-lung-cancer.xlsx

{
  "status": "good",
  "emptyRate": "20.0%",
  "avgLength": "98",
  "totalRows": 5,
  "estimatedTokens": 735,
  "message": "健康度良好,预计消耗约 0.7k Token双模型约 1.5k Token"
}

结论: 通过检查,可以提取

测试2低质量数据test-bad-quality.xlsx

{
  "status": "bad",
  "emptyRate": "80.0%",
  "avgLength": "3",
  "totalRows": 10,
  "estimatedTokens": 0,
  "message": "文本长度过短平均3字符不适合提取"
}

结论: 正确拦截避免浪费Token

测试3列不存在

{
  "status": "bad",
  "message": "列\"不存在的列\"不存在"
}

结论: 正确检测异常情况

测试数据文件

backend/uploads/
├── test-lung-cancer.xlsx      [良好数据5行]
└── test-bad-quality.xlsx       [低质量数据10行80%空值]

📅 Day 4 预告

上午PromptBuilder + 基础提取逻辑

  • 实现PromptBuilder.buildExtractionPrompt()
  • 实现PIIMaskUtil.mask()PII脱敏
  • 测试Prompt生成

下午DualModelExtractionService实现

  • 实现双模型并发调用
  • 实现缓存机制(避免重复调用)
  • 实现JSON解析容错处理
  • 测试双模型提取

🎉 Day 3 总结

完成度: 100%
质量: 优秀
时间: 按计划完成

核心成果

  1. HealthCheckService完整实现智能拦截 + Token预估
  2. TemplateService完整实现3个预设模板
  3. Seed脚本成功执行3个模板已入库
  4. API端点全部测试通过
  5. 代码质量高无linter错误

技术亮点

  • 健康检查拦截策略有效避免浪费
  • Token预估帮助用户控制成本
  • 模板Upsert机制保证数据一致性
  • 性能优化只检查前100行

下一步: Day 4 - 双模型提取引擎 🚀


Day 3 圆满完成!