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:
2026-01-16 13:42:10 +08:00
parent 98d862dbd4
commit 66255368b7
560 changed files with 70424 additions and 52353 deletions

View File

@@ -1,23 +1,23 @@
# SAE环境变量配置指南
> **<EFBFBD><EFBFBD><EFBFBD><EFBFBD>𧋦嚗?* v1.0
> **<EFBFBD>𥕦遣<EFBFBD><EFBFBD>嚗?* 2025-12-11
> **<EFBFBD><EFBFBD><EFBFBD>箸艶嚗?* <20><EFBFBD>鈭騌AE<41>函蔡<E587BD><EFBFBD><E887AC><EFBFBD><E3979B>滨蔭
> **雿輻鍂<EFBFBD><EFBFBD>嚗?* <20>沒AE<41><EFBFBD><E689B9><EFBFBD><EFBFBD><E99EB1>滨蔭
> **文档版本:** v1.0
> **创建日期:** 2025-12-11
> **适用场景:** 阿里云SAE部署环境变量配置
> **使用方法:** 在SAE控制台逐行配置
---
## 📋 配置说明
<EFBFBD>券燵<EFBFBD><EFBFBD>SAE<EFBFBD><EFBFBD><EFBFBD><EFBFBD>蝵桃㴓憓<EFBFBD><EFBFBD><EFBFBD>𤩺𧒄嚗峕<EFBFBD><EFBFBD>找誑銝钅◇摨誯<EFBFBD><EFBFBD>瘛餃<EFBFBD>嚗?
在阿里云SAE控制台配置环境变量时按照以下顺序逐行添加
### 操作步骤
1. 登录阿里云控制台
2. 进入 Serverless应用引擎SAE
3. <EFBFBD>㗇𥋘摨𠉛鍂 <20>?<3F>滨蔭蝞∠<E89D9E> <20>?<3F><EFBFBD><E887AC><EFBFBD>
4. <EFBFBD>孵稬<EFBFBD>峕溶<EFBFBD>删㴓憓<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
5. <EFBFBD><EFBFBD>憭滚<EFBFBD>隞乩<EFBFBD><EFBFBD><EFBFBD>捆嚗<EFBFBD>𤜯<EFBFBD><EFBFBD><EFBFBD>?雿删<E99BBF>XXX"嚗?
3. 选择应用 → 配置管理 → 环境变量
4. 点击「添加环境变量」
5. 逐行复制以下内容(替换所有"你的XXX"
---
@@ -32,21 +32,21 @@ SERVICE_NAME=aiclinical-backend-dev
LOG_LEVEL=debug
```
### <EFBFBD>唳旿摨㯄<EFBFBD>蝵?
### 数据库配置
```bash
# <EFBFBD><EFBFBD>嚗䮝ostgresql://<EFBFBD><EFBFBD><EFBFBD>?撖<><E69296>@<40><EFBFBD>:蝡臬藁/<2F>唳旿摨枏<E691A8>
# 格式postgresql://用户名:密码@地址:端口/数据库名
# 示例postgresql://aiclinical:MyPass123@rm-bp1xxxx.mysql.rds.aliyuncs.com:5432/aiclinical_dev
DATABASE_URL=postgresql://aiclinical:你的密码@你的RDS内网地址:5432/aiclinical_dev
# Serverless餈墧𦻖瘙牐<EFBFBD><EFBFBD>?
# Serverless连接池优化
DB_MAX_CONNECTIONS=400
MAX_INSTANCES=10
```
**<EFBFBD><EFBFBD>RDS<EFBFBD><EFBFBD>嚗?*
1. RDS<EFBFBD><EFBFBD><EFBFBD>?<3F>?摰硺<E691B0><E7A1BA>𡑒” <20>?<3F>孵稬摰硺<E691B0>ID
2. <EFBFBD>箸𧋦靽⊥<EFBFBD> <20>?<3F><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E59A97><EFBFBD><EFBFBD>
**获取RDS地址**
1. RDS控制台 → 实例列表 → 点击实例ID
2. 基本信息 → 内网地址(复制)
3. 示例:`rm-bp1abcd1234.mysql.rds.aliyuncs.com`
### OSS存储配置
@@ -59,10 +59,10 @@ OSS_ACCESS_KEY_ID=你的AccessKeyId
OSS_ACCESS_KEY_SECRET=你的AccessKeySecret
```
**<EFBFBD><EFBFBD>OSS撖<EFBFBD>𤨎嚗?*
1. 霈輸䔮<EFBFBD><EFBFBD>RAM <20>?<3F><EFBFBD> <20>?`aiclinical-oss`
**获取OSS密钥**
1. 访问控制RAM → 用户 → `aiclinical-oss`
2. 如果忘记密钥需要重新创建AccessKey
3. **<EFBFBD><EFBFBD>嚗?* 撖<>𤨎<EFBFBD>芣遬蝷箔<E89DB7>甈∴<E79488>蝡见朖靽嘥<E99DBD>嚗?
3. **重要:** 密钥只显示一次,立即保存!
### LLM API配置
@@ -71,7 +71,7 @@ OSS_ACCESS_KEY_SECRET=你的AccessKeySecret
DEEPSEEK_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
DEEPSEEK_BASE_URL=https://api.deepseek.com
# <EFBFBD><EFBFBD><EFBFBD><EFBFBD>䔮嚗<EFBFBD><EFBFBD><EFBFBD>嚗?
# 通义千问(阿里云)
DASHSCOPE_API_KEY=sk-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
# CloseAI代理可选
@@ -80,14 +80,14 @@ CLOSEAI_OPENAI_BASE_URL=https://api.openai-proxy.org/v1
CLOSEAI_CLAUDE_BASE_URL=https://api.openai-proxy.org/anthropic
```
**<EFBFBD><EFBFBD><EFBFBD>滨蔭銝<EFBFBD>銝林LM API Key嚗?*
**至少配置一个LLM API Key**
### 安全配置
```bash
# JWT<EFBFBD>𤨎嚗<EFBFBD><EFBFBD>憿颱耨<EFBFBD><EFBFBD>嚗?
# JWT密钥(必须修改!)
# 生成工具https://www.random.org/strings/
JWT_SECRET=霂瑟㺿銝?2雿滢誑銝𢠃<E98A9D><F0A2A083><EFBFBD>蝚虫葡abcdefg123456
JWT_SECRET=请改为32位以上随机字符串abcdefg123456
JWT_EXPIRES_IN=7d
# CORS配置
@@ -100,16 +100,16 @@ CORS_ORIGIN=*
## ⚙️ 推荐配置
### 蝻枏<EFBFBD><EFBFBD>滨蔭嚗<EFBFBD><EFBFBD><EFBFBD><EFBFBD>雿輻鍂Redis嚗?
### 缓存配置(初期不使用Redis
```bash
CACHE_TYPE=memory
QUEUE_TYPE=memory
```
**霂湔<EFBFBD>嚗?* <20><EFBFBD><E884B2><EFBFBD><E586BD><EFBFBD>嚗䔶蝙<E494B6><EFBFBD>摮条<E691AE>摮䁅雲憭?
**说明:** 初期用户量小,使用内存缓存足够
**<EFBFBD>芣䔉<EFBFBD><EFBFBD><EFBFBD>edis<EFBFBD><EFBFBD><EFBFBD>嫣蛹嚗?*
**未来需要Redis时改为**
```bash
CACHE_TYPE=redis
REDIS_HOST=r-bp1xxxx.redis.rds.aliyuncs.com
@@ -125,9 +125,9 @@ DIFY_API_KEY=app-xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx
DIFY_API_URL=https://api.dify.ai/v1
```
**霂湔<EFBFBD>嚗?* 憒<><E68692>雿輻鍂Dify<66>𣂷<EFBFBD>RAG<41>滚𦛚嚗屸<E59A97><EFBFBD><E996AC>蝵?
**说明:** 如果使用Dify提供RAG服务需要配置
### Python敺格<EFBFBD><EFBFBD><EFBFBD>蝵?
### Python微服务配置
```bash
# 临时方案:先用公网地址
@@ -143,47 +143,47 @@ EXTRACTION_SERVICE_URL=http://你的临时地址:8000
UPLOAD_MAX_SIZE=104857600
```
**霂湔<EFBFBD>嚗?* 100MB = 104857600 bytes
**说明:** 100MB = 104857600 bytes
---
## <EFBFBD>?<3F>滨蔭璉<E894AD><E79289><EFBFBD><E4BAA4>?
## ✅ 配置检查清单
### 蝚砌<EFBFBD>甇伐<EFBFBD>憭滚<EFBFBD>蝎䁅斐璉<EFBFBD><EFBFBD>?
### 第一步:复制粘贴检查
- [ ] 所有环境变量已添加到SAE
- [ ] <EFBFBD><EFBFBD><EFBFBD>?雿删<E99BBF>XXX"撌脫𤜯<E884AB><EFBCB6><EFBFBD><E7AC94>?
- [ ] 瘝⊥<EFBFBD><EFBFBD><EFBFBD>隞颱<EFBFBD><EFBFBD>憿?
- [ ] 所有"你的XXX"已替换为真实值
- [ ] 没有遗漏任何必填项
### 蝚砌<EFBFBD>甇伐<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
### 第二步:格式检查
- [ ] DATABASE_URL 格式正确
```
postgresql://<EFBFBD><EFBFBD><EFBFBD>?撖<><E69296>@<40><EFBFBD>:蝡臬藁/<2F>唳旿摨?
<EFBFBD>?甇<>嚗䮝ostgresql://aiclinical:MyPass@rm-xxx.com:5432/db
<EFBFBD>?<3F>躰秤嚗䮝ostgresql://aiclinical@rm-xxx.com:5432/db<EFBFBD>撩撠穃<EFBFBD><EFBFBD><EFBFBD><EFBFBD>
postgresql://用户名:密码@地址:端口/数据库
✅ 正确postgresql://aiclinical:MyPass@rm-xxx.com:5432/db
❌ 错误postgresql://aiclinical@rm-xxx.com:5432/db(缺少密码)
```
- [ ] <EFBFBD><EFBFBD>銝剜瓷<EFBFBD>厩鸌畾𠰴<EFBFBD>蝚佗<EFBFBD>`@ # $ % & 蝛箸聢`嚗?
- [ ] 密码中没有特殊字符(`@ # $ % & 空格`
```
<EFBFBD>?<3F><EFBFBD>嚗鐝yPassword123
<EFBFBD>?<3F><EFBFBD>嚗鐝y@Pass#123<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?嚗?
✅ 推荐MyPassword123
❌ 避免My@Pass#123(包含@和#
```
- [ ] JWT_SECRET 已修改(不是默认值)
- [ ] OSS_REGION <EFBFBD><EFBFBD><EFBFBD><EFBFBD> `oss-` <EFBFBD><EFBFBD>嚗?
- [ ] OSS_REGION 格式正确(带 `oss-` 前缀)
```
<EFBFBD>?甇<>嚗駅ss-cn-hangzhou
<EFBFBD>?<3F>躰秤嚗䬙n-hangzhou
✅ 正确oss-cn-hangzhou
❌ 错误cn-hangzhou
```
### 蝚砌<EFBFBD>甇伐<EFBFBD><EFBFBD>𤨎<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
### 第三步:密钥有效性检查
- [ ] RDS<EFBFBD><EFBFBD><EFBFBD><EFBFBD>虾隞亦鍂<EFBFBD>唳旿摨枏恥<EFBFBD>瑞垢瘚贝<EFBFBD>餈墧𦻖嚗?
- [ ] RDS密码正确(可以用数据库客户端测试连接)
- [ ] OSS AccessKey有效在RAM控制台确认
- [ ] LLM API Key<EFBFBD><EFBFBD><EFBFBD>虾隞亦鍂curl瘚贝<EFBFBD>嚗?
- [ ] LLM API Key有效可以用curl测试
**瘚贝<EFBFBD>LLM API Key嚗?*
**测试LLM API Key**
```bash
curl https://api.deepseek.com/v1/models \
-H "Authorization: Bearer sk-你的密钥"
@@ -191,7 +191,7 @@ curl https://api.deepseek.com/v1/models \
---
## <EFBFBD><EFBFBD> <20>滨蔭蝷箔<E89DB7><EFBFBD><E59A97><EFBFBD><EFBFBD>嚗?
## 📝 配置示例(脱敏版)
```bash
NODE_ENV=development
@@ -228,21 +228,21 @@ UPLOAD_MAX_SIZE=104857600
---
## <EFBFBD><EFBFBD> 摰匧<E691B0><E58CA7><EFBFBD>雿喳<E99BBF>頝?
## 🔐 安全最佳实践
### 密钥管理
1. **不要将密钥提交到Git**
- <EFBFBD>?銝滩<E98A9D><E6BBA9>𥕦遣 `.env.production` <EFBFBD><EFBFBD>
- <EFBFBD>?銝滩<E98A9D><E6BBA9>其誨<E585B6><E8AAA8>葉蝖祉<E89D96><E7A589><EFBFBD><EFBFBD><EFBFBD>?
- <EFBFBD>?<3F>芸銁SAE<41><EFBFBD><E689B9><EFBFBD>蝵?
- ❌ 不要创建 `.env.production` 文件
- ❌ 不要在代码中硬编码密钥
- ✅ 只在SAE控制台配置
2. **定期更换密钥**
- 瘥?-6銝芣<E98A9D><E88AA3>湔揢銝<E68FA2>甈?
- 每3-6个月更换一次
- 发现泄露立即更换
3. **雿輻鍂撖<EFBFBD><EFBFBD>蝞∠<EFBFBD><EFBFBD>?*
- <EFBFBD><EFBFBD>嚗?Password<EFBFBD><EFBFBD>astPass<EFBFBD><EFBFBD>itwarden
3. **使用密码管理器**
- 推荐1Password、LastPass、Bitwarden
- 保存所有密钥和配置信息
### 环境隔离
@@ -251,15 +251,15 @@ UPLOAD_MAX_SIZE=104857600
开发环境:
- Bucket: aiclinical-dev
- Database: aiclinical_dev
- JWT_SECRET: <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
- JWT_SECRET: 独立的密钥
<EFBFBD>煺漣<EFBFBD><EFBFBD>嚗?
生产环境:
- Bucket: aiclinical-prod
- Database: aiclinical_prod
- JWT_SECRET: 銝滚<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
- JWT_SECRET: 不同的密钥
```
**瘞貉<EFBFBD>銝滩<EFBFBD><EFBFBD><EFBFBD>鈭抒㴓憓<EFBFBD><EFBFBD><EFBFBD><EFBFBD>𤑳㴓憓<EFBFBD><EFBFBD><EFBFBD>𤨎嚗?*
**永远不要在生产环境使用开发环境的密钥!**
---
@@ -267,56 +267,56 @@ UPLOAD_MAX_SIZE=104857600
### Q1: 忘记了RDS密码怎么办
**<EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗?*
1. RDS<EFBFBD><EFBFBD><EFBFBD>?<3F>?韐血噡蝞∠<E89D9E>
**解决方法:**
1. RDS控制台 → 账号管理
2. 找到用户 `aiclinical`
3. <EFBFBD>孵稬<EFBFBD><EFBFBD>蝵桀<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
4. 霈曄蔭<EFBFBD><EFBFBD><EFBFBD>?
3. 点击「重置密码」
4. 设置新密码
5. 更新SAE环境变量中的 `DATABASE_URL`
### Q2: OSS AccessKey泄露了怎么办
**<EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗?*
1. RAM<EFBFBD><EFBFBD><EFBFBD>?<3F>?<3F><EFBFBD> <20>?`aiclinical-oss`
**解决方法:**
1. RAM控制台 → 用户 → `aiclinical-oss`
2. 禁用或删除泄露的AccessKey
3. 创建新的AccessKey
4. 更新SAE环境变量
### Q3: <EFBFBD><EFBFBD>撉諹<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>滨蔭甇<EFBFBD>嚗?
### Q3: 如何验证环境变量配置正确?
**<EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗?*
**解决方法:**
1. 部署应用后,查看实时日志
2. <EFBFBD><EFBFBD>隞乩<EFBFBD><EFBFBD><EFBFBD>銵函內<EFBFBD>滨蔭甇<EFBFBD>嚗?
2. 看到以下日志表示配置正确:
```
<EFBFBD>?[Config] Environment validation passed
<EFBFBD>?[Database] <EFBFBD>唳旿摨栞<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
<EFBFBD>𣑐 [Storage] 雿輻鍂<EFBFBD><EFBFBD>鈭?OSS 摮睃<EFBFBD>
[Config] Environment validation passed
[Database] 数据库连接成功
📦 [Storage] 使用阿里云 OSS 存储
```
### Q4: DATABASE_URL中密码包含特殊字符怎么办
**<EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗?*
<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD> `@ # $ % & 蝛箸聢` 蝑厩鸌畾𠰴<E795BE>蝚佗<E89D9A><E4BD97><EFBFBD><EFBFBD>RL蝻𣇉<E89DBB>嚗?
**解决方法:**
如果密码包含 `@ # $ % & 空格` 等特殊字符需要URL编码
```
原密码My@Pass#123
编码后My%40Pass%23123
摰峕㟲URL嚗?
完整URL
postgresql://aiclinical:My%40Pass%23123@rm-xxx.com:5432/aiclinical_dev
```
**编码对照表:**
```
@ <EFBFBD>?%40
# <EFBFBD>?%23
$ <EFBFBD>?%24
% <EFBFBD>?%25
& <EFBFBD>?%26
蝛箸聢 <20>?%20
@ %40
# %23
$ %24
% %25
& %26
空格 → %20
```
**<EFBFBD><EFBFBD>嚗?* <20>齿鰵霈曄蔭銝滚<E98A9D><E6BB9A>怎鸌畾𠰴<E795BE>蝚衣<E89D9A><EFBFBD><E69296><EFBFBD><EFBFBD><E6B8A1>?
**推荐:** 重新设置不包含特殊字符的密码更简单
---
@@ -331,9 +331,9 @@ $
// 会自动检查所有必填项
```
**<EFBFBD><EFBFBD>颲枏枂蝷箔<EFBFBD>嚗?*
**日志输出示例:**
```
<EFBFBD>?[Config] Environment validation passed
[Config] Environment validation passed
[Config] Application configuration:
- Environment: development
- Port: 3001
@@ -346,7 +346,7 @@ $
### 手动验证
```bash
# 霈輸䔮<EFBFBD>亙熒璉<EFBFBD><EFBFBD>交𦻖<EFBFBD>?
# 访问健康检查接口
curl http://你的SAE地址:3001/health
# 预期返回
@@ -361,11 +361,10 @@ curl http://你的SAE地址:3001/health
---
**<EFBFBD><EFBFBD><EFBFBD><EFBFBD>𧋦嚗?* v1.0
**文档版本:** v1.0
**最后更新:** 2025-12-11
**维护者:** 技术架构师
**<EFBFBD><EFBFBD><EFBFBD><EFBFBD>﹝嚗?* [SAE<EFBFBD>函蔡摰<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>](../05-<EFBFBD>函蔡<EFBFBD><EFBFBD>/02-SAE<EFBFBD>函蔡摰<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>(鈭批<E988AD>蝏讐<E89D8F><E8AE90>?.md)
**相关文档:** [SAE部署完全指南](../05-部署文档/02-SAE部署完全指南(产品经理版).md)