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
This commit is contained in:
@@ -1,16 +1,17 @@
|
||||
# **技术设计文档:工具 A \- 医疗数æ<C2B0>®è¶…级å<C2A7>ˆå¹¶å™?(The Super Merger)**
|
||||
# **技术设计文档:工具 A \- 医疗数据超级合并器 (The Super Merger)**
|
||||
|
||||
| 文档类型 | Technical Design Document (TDD) |
|
||||
| :---- | :---- |
|
||||
| **对应 PRD** | **PRD\_工具A\_超级合并器\_V2.md** |
|
||||
| **版本** | **V2.0** (æž¶æž„å<EFBFBD>‡çº§ï¼šè®¿è§†åŸºå‡?\+ æ—¶é—´çª? |
|
||||
| **状�* | Draft |
|
||||
| **æ ¸å¿ƒç›®æ ‡** | 构建一个基äº?Web çš?ETL å·¥å…·ï¼Œè§£å†³ä¸´åºŠç§‘ç ”ä¸â€œä¸€å¯¹å¤šâ€<EFBFBD>æ•°æ<EFBFBD>®å¯¹é½<EFBFBD>难题,实现基于时间窗的精准å<EFBFBD>ˆå¹¶ã€?|
|
||||
| **版本** | **V2.0** (架构升级:访视基准 \+ 时间窗) |
|
||||
| **状态** | Draft |
|
||||
| **核心目标** | 构建一个基于 Web 的 ETL 工具,解决临床科研中“一对多”数据对齐难题,实现基于时间窗的精准合并。 |
|
||||
|
||||
## **1\. 总体架构设计 (Architecture Overview)**
|
||||
|
||||
鉴于处ç<EFBFBD>† Excel 文件(解æž<C3A6>ã€<C3A3>å<EFBFBD>ˆå¹¶ã€<C3A3>写入)æ˜?CPU å¯†é›†åž‹å’Œå†…å˜æ•<C3A6>感型æ“<C3A6>作,为了é<E280A0>¿å…<C3A5>阻塞 Node.js 主线程,我们采用 **“异æ¥ä»»åŠ¡é˜Ÿåˆ?\+ æµ<C3A6>å¼<C3A5>处ç<E2809E>†â€?* 的架构模å¼<C3A5>ã€?
|
||||
### **1.1 系统架构�*
|
||||
鉴于处理 Excel 文件(解析、合并、写入)是 CPU 密集型和内存敏感型操作,为了避免阻塞 Node.js 主线程,我们采用 **“异步任务队列 \+ 流式处理”** 的架构模式。
|
||||
|
||||
### **1.1 系统架构图**
|
||||
|
||||
graph TD
|
||||
Client\[React 前端 (Wizard UI)\]
|
||||
@@ -29,7 +30,7 @@ graph TD
|
||||
end
|
||||
|
||||
subgraph Storage \[数据存储\]
|
||||
PG\[(PostgreSQL 业务�\]
|
||||
PG\[(PostgreSQL 业务库)\]
|
||||
FileSys\[临时文件存储 (Local/S3)\]
|
||||
Redis\[(Redis 缓存/队列)\]
|
||||
end
|
||||
@@ -42,36 +43,38 @@ graph TD
|
||||
BullMQ \--消费任务--\> Merger
|
||||
Merger \--读取辅表(全量)--\> FileSys
|
||||
Merger \--读取主表(流式)--\> FileSys
|
||||
Merger \--æµ<EFBFBD>å¼<EFBFBD>å<EFBFBD>ˆå¹¶ä¸Žå†™å…?-\> FileSys
|
||||
Merger \--更新状�-\> PG
|
||||
Merger \--流式合并与写入--\> FileSys
|
||||
Merger \--更新状态--\> PG
|
||||
Client \--3.轮询/WS 进度--\> TaskAPI
|
||||
Client \--4.下载结果--\> API\_Server
|
||||
|
||||
## **2\. 技术选型 (Tech Stack)**
|
||||
|
||||
åŸºäºŽçŽ°æœ‰æŠ€æœ¯æ ˆçš„é’ˆå¯¹æ€§é€‰æ‹©ï¼?
|
||||
| 层级 | 技术组ä»?| 选型ç<E280B9>†ç”± |
|
||||
基于现有技术栈的针对性选择:
|
||||
|
||||
| 层级 | 技术组件 | 选型理由 |
|
||||
| :---- | :---- | :---- |
|
||||
| **å‰<EFBFBD>端** | **React 19 \+ Ant Design 5** | 利用 AntD çš?Steps, Upload, Tree (æ ‘çŠ¶é€‰æ‹©å™? 快速构å»?UIã€?|
|
||||
| **å<EFBFBD>Žç«¯æ¡†æž¶** | **Fastify 5.x** | 高性能 HTTP 框架,适å<E2809A>ˆé«˜å¹¶å<C2B6>?I/Oã€?|
|
||||
| **Excel 处ç<EFBFBD>†** | **ExcelJS** | **æ ¸å¿ƒç»„ä»¶**。支æŒ<C3A6>æµ<C3A6>å¼<C3A5>读å†?(Streaming I/O),这是处ç<E2809E>†å¤§æ•°æ<C2B0>®é‡<C3A9>ä¸<C3A4>崩的关键ã€?|
|
||||
| **日期处ç<EFBFBD>†** | **Day.js \+ CustomParseFormat** | **新增**。处ç<E2809E>†â€œæ—¶é—´åœ°ç‹±â€<C3A2>çš„æ ¸å¿ƒåº“ï¼Œéœ€è¦<C3A8>æž<C3A6>强的容错解æž<C3A6>能力ã€?|
|
||||
| **任务队列** | **BullMQ \+ Redis** | 必须异æ¥å¤„ç<EFBFBD>†ã€‚å<EFBFBD>ˆå¹¶é€»è¾‘å¤<EFBFBD>æ<EFBFBD>‚,耗时较长,必须用队列ã€?|
|
||||
| **æ•°æ<EFBFBD>®åº?* | **PostgreSQL 15 \+ Prisma** | å˜å‚¨ä»»åŠ¡çŠ¶æ€<EFBFBD>ã€<EFBFBD>文件元数æ<EFBFBD>®ã€?*ä¸<C3A4>建议将原始 Excel æ•°æ<C2B0>®å˜å…¥ PG**ã€?|
|
||||
| **验è¯<EFBFBD>åº?* | **Zod** | ç”¨äºŽæ ¡éªŒå‰<EFBFBD>端æ<EFBFBD><EFBFBD>交的å¤<EFBFBD>æ<EFBFBD>‚æ˜ å°„é…<EFBFBD>置结构ã€?|
|
||||
| **前端** | **React 19 \+ Ant Design 5** | 利用 AntD 的 Steps, Upload, Tree (树状选择器) 快速构建 UI。 |
|
||||
| **后端框架** | **Fastify 5.x** | 高性能 HTTP 框架,适合高并发 I/O。 |
|
||||
| **Excel 处理** | **ExcelJS** | **核心组件**。支持流式读写 (Streaming I/O),这是处理大数据量不崩的关键。 |
|
||||
| **日期处理** | **Day.js \+ CustomParseFormat** | **新增**。处理“时间地狱”的核心库,需要极强的容错解析能力。 |
|
||||
| **任务队列** | **BullMQ \+ Redis** | 必须异步处理。合并逻辑复杂,耗时较长,必须用队列。 |
|
||||
| **数据库** | **PostgreSQL 15 \+ Prisma** | 存储任务状态、文件元数据。**不建议将原始 Excel 数据存入 PG**。 |
|
||||
| **验证库** | **Zod** | 用于校验前端提交的复杂映射配置结构。 |
|
||||
|
||||
### **2.1 关键技术决ç?(ADR): 为什么ä¸<C3A4>ç”?Python (Pandas)?**
|
||||
### **2.1 关键技术决策 (ADR): 为什么不用 Python (Pandas)?**
|
||||
|
||||
虽然 Python Pandas 在数æ<EFBFBD>®å<EFBFBD>ˆå¹¶ä¸Šä»£ç <EFBFBD>更简æ´<EFBFBD>,但针å¯?*本工å…?*的场景,我们决定å<C5A1>šæŒ<C3A6>使用 **Node.js**,ç<C592>†ç”±å¦‚下:
|
||||
虽然 Python Pandas 在数据合并上代码更简洁,但针对**本工具**的场景,我们决定坚持使用 **Node.js**,理由如下:
|
||||
|
||||
1. **æµ<EFBFBD>å¼<EFBFBD>处ç<EFBFBD>†ä¼˜åŠ¿ï¼?* Pandas 倾å<C2BE>‘于全é‡<C3A9>åŠ è½½å†…å˜ï¼Œå®¹æ˜“ OOM。Node.js çš?Stream API 天然支æŒ<EFBFBD>背压,能稳定处ç<EFBFBD>†â€œæ•°æ<EFBFBD>®è†¨èƒ€â€<EFBFBD>问题ã€?
|
||||
2. **架构一致性:** é<>¿å…<C3A5>引入 Python Runtime 带æ<C2A6>¥çš„è¿<C3A8>ç»´æˆ<C3A6>本和 IPC 开销ã€?
|
||||
3. **结论ï¼?* 对于精确匹é…<C3A9>和逻辑清洗,Node.js 性能足够且更å<C2B4>¯æŽ§ã€?
|
||||
## **3\. æ•°æ<C2B0>®åº“设è®?(Database Schema)**
|
||||
1. **流式处理优势:** Pandas 倾向于全量加载内存,容易 OOM。Node.js 的 Stream API 天然支持背压,能稳定处理“数据膨胀”问题。
|
||||
2. **架构一致性:** 避免引入 Python Runtime 带来的运维成本和 IPC 开销。
|
||||
3. **结论:** 对于精确匹配和逻辑清洗,Node.js 性能足够且更可控。
|
||||
|
||||
## **3\. 数据库设计 (Database Schema)**
|
||||
|
||||
### **Prisma Schema 定义**
|
||||
|
||||
// 任务状æ€<EFBFBD>æžšä¸?
|
||||
// 任务状态枚举
|
||||
enum TaskStatus {
|
||||
PENDING
|
||||
PROCESSING
|
||||
@@ -79,7 +82,7 @@ enum TaskStatus {
|
||||
FAILED
|
||||
}
|
||||
|
||||
// å<EFBFBD>ˆå¹¶ä»»åŠ¡è¡?
|
||||
// 合并任务表
|
||||
model MergeTask {
|
||||
id String @id @default(uuid())
|
||||
userId String
|
||||
@@ -89,9 +92,9 @@ model MergeTask {
|
||||
// 核心配置字段 (V2 更新)
|
||||
// 结构: {
|
||||
// anchorFileId: string,
|
||||
// anchorKeys: { id: "ä½<EFBFBD>院å<EFBFBD>?, time: "入院日期" },
|
||||
// anchorKeys: { id: "住院号", time: "入院日期" },
|
||||
// window: { daysBefore: 7, daysAfter: 7 },
|
||||
// files: \[{ id: "f2", timeCol: "报告时间", columns: \["白细�\] }\]
|
||||
// files: \[{ id: "f2", timeCol: "报告时间", columns: \["白细胞"\] }\]
|
||||
// }
|
||||
config Json?
|
||||
|
||||
@@ -110,7 +113,7 @@ model SourceFile {
|
||||
task MergeTask @relation(fields: \[taskId\], references: \[id\])
|
||||
filename String
|
||||
filepath String
|
||||
headers Json // \["ä½<EFBFBD>院å<EFBFBD>?, "å§“å<E2809C><C3A5>", "入院日期"\]
|
||||
headers Json // \["住院号", "姓名", "入院日期"\]
|
||||
rowCount Int
|
||||
fileSize Int
|
||||
uploadedAt DateTime @default(now())
|
||||
|
||||
Reference in New Issue
Block a user