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,55 +1,63 @@
# **CTO 代码审查报告AI临床研究平台部署架构**
螳。譬ク蟇ケ雎。<EFBFBD>?莉ス迢ャ遶矩Κ鄂イ譁<EFBDB2>。?(Dify/Python/Node/Frontend/PostgreSQL)
螳。譬ク莠コ<EFBFBD>夊劒諡滓楔譫<EFBFBD>ク?
扈楢ョコ<EFBFBD>哂- (莨倡ァ€)縲よ楔譫<E6A594>ョセ隶。隨ヲ蜷井コ大次逕溽炊蠢オ<E8A0A2><EFBFBD>譛ャ荳取黄螻墓€ァ蟷ウ陦。濶ッ螂ス縲ゆス<E38286>ュ伜惠鄂醍サ懷<EFBDBB>蜿」蜥御セ晁オ夜溜邇ッ荳、荳ェ蜈ウ髞ョ鬟朱勦轤ケ髴€隗」蜀ウ縲?
## **1\. 讓。蝮礼コァ豺ア蠎ヲ螳。隶?(Module-Level Audit)**
审核对象5份独立部署文档 (Dify/Python/Node/Frontend/PostgreSQL)
审核人:虚拟架构师
结论A- (优秀)。架构设计符合云原生理念,成本与扩展性平衡良好。但存在网络出口和依赖闭环两个关键风险点需解决。
### **笨?1.1 蜑咲ォッ Nginx (SAE)**
## **1\. 模块级深度审计 (Module-Level Audit)**
* **莨倡せ**<2A>壼、夐亳谿オ譫<EFBDB5>サコ莨倡ァ€<EFBDA7>井ス鍋ァッ蟆擾シ会シ帷識蠅<E8AD98>序驥乗ウィ蜈・譁ケ譯茨シ<E88CA8>nvsubst<73>蛾撼蟶ク荳謎ク夲シ瑚ァ」蜀ウ莠<EFBDB3>撕諤<E69295>。オ髱「譌<EFBDA2>豕募勘諤<E58B98><E8ABA4>鄂ョ蜷守ォッ逧<EFBDAF>埓鬚倥€?
* **菫ョ豁」蟒コ隶ョ**<2A>?
* 遑ョ隶、 nginx.conf 荳ュ蠑€蜷?gzip<69>瑚ソ吝ッ?React 螟ァ菴鍋ァ?JS 譁<>サカ閾ウ蜈ウ驥崎ヲ√€?
* 譽€譟?Nginx 逧?client\_max\_body\_size 驟咲スョ縲ょ現逍?PDF/Excel 蜿ッ閭ス雜<EFBDBD>ソ<EFBFBD>サ倩ョ、逧?1MB<4D>悟サコ隶ョ隶セ鄂ョ荳コ 50M縲?
### **笨?1.2 蜷守ォッ Node.js (SAE)**
### **✅ 1.1 前端 Nginx (SAE)**
* **莨倡せ**<2A>啀risma "蜿榊髄蜷梧ュ・" 豬∫ィ矩撼蟶ク蜉。螳橸シ瑚ァ」蜀ウ莠<EFBDB3>€蜿台ケ<E58FB0>諠ッ荳崎ァ<E5B48E>激逧<E6BF80>琉鬚倥€ostgres-Only 逧<>楔譫<E6A594>栫螟ァ蝨ー髯堺ス惹コ<E683B9>ソ千サエ雍滓球縲?
* **菫ョ豁」蟒コ隶ョ**<2A>?
* **霑樊磁豕<E7A381>シ城」朱勦**<2A>啀ython 譛榊苅螯よ棡蜩榊コ疲<EFBDBA><E796B2>悟錘遶ッ逧?HTTP Client 隶セ鄂ョ雜<EFBDAE>慮莠<E685AE><EFBFBD>溷サコ隶ョ隶セ鄂?timeout: 30s<30>碁亟豁「蜷守ォッ霑樊磁謨ー蝣<EFBDB0>ァッ縲?
### **笨?1.3 Python 蠕ョ譛榊<E8AD9B>?(SAE)**
* **优点**多阶段构建优秀体积小环境变量注入方案envsubst非常专业解决了静态页面无法动态配置后端的难题。
* **修正建议**
* 确认 nginx.conf 中开启 gzip这对 React 大体积 JS 文件至关重要。
* 检查 Nginx 的 client\_max\_body\_size 配置。医疗 PDF/Excel 可能超过默认的 1MB建议设置为 50M。
* **莨倡せ**<2A><EFBFBD>遑ョ謖<EFBDAE><E8AC96><EFBFBD> libGL 遲臥ウサ扈滉セ晁オ夜琉鬚假シ瑚ソ呎弍 Python 螳ケ蝎ィ蛹匁怙螟ァ逧<EFBDA7><EFBFBD>梧枚譯」蟾イ謠仙燕隗<E78795>∩縲?
* **菫ョ豁」蟒コ隶ョ**<2A>?
* **OOM 鬟朱勦**<2A>啀ython 霑帷ィ具シ亥ー、蜈カ譏ッ PyMuPDF/OCR<43>蛾撼蟶ク蜷<EFBDB8><E89CB7>蟄倥€ょ惠 2G 蜀<>ュ倬剞蛻カ荳具シ悟苅蠢<E88B85>剞蛻カ蟷カ蜿第焚<E7ACAC><E7849A>unicorn Workers 荳崎ヲ∬カ<E288AC>ソ<EFBFBD> 2 荳ェ<E88DB3>€?
### **笨?1.4 Dify (ECS)**
### **✅ 1.2 后端 Node.js (SAE)**
* **莨倡せ**<2A>€画叫莠?ECS 驛ィ鄂イ莉・菫晁ッ∵焚謐ョ遘∵怏蛹厄シ悟酔譌カ菴ソ逕?Swap 髦イ豁「 OOM<4F>碁撼蟶ク諛り。後€?
* **菫ョ豁」蟒コ隶ョ**<2A>?
* **螳牙<EFBFBD>諤?*<2A>哘CS 逧?Redis 遶ッ蜿」 (6379) 蜥?Weaviate 遶ッ蜿」 (8080) **扈晏ッケ荳崎ヲ<E5B48E>**蟇ケ蜈ャ鄂大シ€謾セ縲ゆサ<E38286><EFBDBB>隶ク localhost 蜥?VPC 蜀<>ス題ョソ髣ョ縲?
### **笨?1.5 謨ー謐ョ蠎?(RDS)**
* **优点**Prisma "反向同步" 流程非常务实解决了开发习惯不规范的问题。Postgres-Only 的架构极大地降低了运维负担。
* **修正建议**
* **连接泄漏风险**Python 服务如果响应慢,后端的 HTTP Client 设置超时了吗?建议设置 timeout: 30s防止后端连接数堆积。
* **莨倡せ**<2A>售chema 髫皮ヲサ隶セ隶。譫∽スウ縲?
* **菫ョ豁」蟒コ隶ョ**<2A>?
* **Dify 謨ー謐ョ蠎馴囈遖?*<2A>夂。ョ隶?Dify 菴ソ逕ィ逧<EFBDA8>弍迢ャ遶狗<E981B6>?dify\_prod 蠎難シ御ク崎ヲ∝柱荳壼苅陦ィ豺キ蝨ィ ai\_clinical\_research 蠎謎クュ<EFBDB8>碁亟豁?Dify 蜊<>コァ閼壽悽隸ッ蛻<EFBDAF>荳壼苅陦ィ縲?
## **2\. 霍ィ讓。蝮鈴寔謌宣」朱<EFBDA3>?(Integration Risks)**
### **✅ 1.3 Python 微服务 (SAE)**
### **<2A>圷 鬟朱勦荳€<E88DB3>售AE 逧?蟄、蟯帶譜蠎<E8AD9C>" (Internet Access)**
* **优点**:明确指出了 libGL 等系统依赖问题,这是 Python 容器化最大的坑,文档已提前规避。
* **修正建议**
* **OOM 风险**Python 进程(尤其是 PyMuPDF/OCR非常吃内存。在 2G 内存限制下务必限制并发数Gunicorn Workers 不要超过 2 个)。
* **髣ョ鬚<EFBDAE>**<2A>售AE 驛ィ鄂イ蝨?VPC 蜀<>シ碁サ倩ョ、**豐。譛牙<E8AD9B>鄂大<E98482>蜿」**縲?
* **蝨コ譎ッ**<2A>壼錘遶ッ隹<EFBDAF><E99AB9>?DeepSeek API縲 ̄ython 荳玖スス蜈ャ鄂<EFBDAC> PDF縲¨PM 螳芽」<E88ABD>セ晁オ厄シ域桷蟒コ譌カ<E8AD8C>€?
* **蟇ケ遲<EFBFBD>**<2A>壼ソ<E5A3BC>。サ蝨ィ VPC 荳ュ驟咲ス?**NAT 鄂大<E98482>** (謗ィ闕<EFBDA8>) 謌也。ョ菫?SAE 譛臥サ大ョ壼<EFBDAE>鄂?IP 逧<><E980A7>蜉帙€?*蜷ヲ蛻吩ク顔コソ蠖灘、ゥ謇€譛?AI 蜉溯<E89C89>驛ス莨夊カ<E5A48A>慮縲?*
### **✅ 1.4 Dify (ECS)**
* **优点**:选择了 ECS 部署以保证数据私有化,同时使用 Swap 防止 OOM非常懂行。
* **修正建议**
* **安全性**ECS 的 Redis 端口 (6379) 和 Weaviate 端口 (8080) **绝对不要**对公网开放。仅允许 localhost 和 VPC 内网访问。
### **✅ 1.5 数据库 (RDS)**
* **优点**Schema 隔离设计极佳。
* **修正建议**
* **Dify 数据库隔离**:确认 Dify 使用的是独立的 dify\_prod 库,不要和业务表混在 ai\_clinical\_research 库中,防止 Dify 升级脚本误删业务表。
## **2\. 跨模块集成风险 (Integration Risks)**
### **🚨 风险一SAE 的"孤岛效应" (Internet Access)**
* **问题**SAE 部署在 VPC 内,默认**没有公网出口**。
* **场景**:后端调用 DeepSeek API、Python 下载公网 PDF、NPM 安装依赖(构建时)。
* **对策**:必须在 VPC 中配置 **NAT 网关** (推荐) 或确保 SAE 有绑定公网 IP 的能力。**否则上线当天所有 AI 功能都会超时。**
### **🔄 风险二:部署依赖死锁 (Deployment Deadlock)**
* **邇ー雎。**<2A>?
1. 蜷守ォッ蜷ッ蜉ィ髴€隕?DIFY\_API\_KEY縲?
2. DIFY\_API\_KEY 髴€隕?Dify 蜷ッ蜉ィ蟷カ莠コ蟾・逋サ蠖募錘謇崎<EFBFBD>逕滓<EFBFBD>縲?
3. 蜷守ォッ螯よ棡驟咲スョ莠?蛛・蠎キ譽€譟・螟ア雍・蛻咎㍾蜷ッ"<22>悟惠蝪ォ蜈・ Key 荵句燕莨壽裏髯宣㍾蜷ッ縲?
* **蟇ケ遲<EFBFBD>**<2A>夐ヲ匁ャ。驛ィ鄂イ譌カ<E8AD8C>悟錘遶ッ邇ッ蠅<EFBDAF>序驥?DIFY\_API\_KEY 蜿ッ莉・蜈亥。ォ荳ェ蛛<EFBDAA>€<C280>亥ヲ?temp<6D>会シ瑚ョゥ譛榊苅霍題オキ譚・縲らュ<E38289> Dify 驛ィ鄂イ螂ス諡ソ蛻ー逵<EFBDB0> Key 蜷趣シ梧峩譁ー SAE 驟咲スョ蟷カ驥榊星縲?
### **<2A>倹 鬟朱勦荳会シ壼燕遶ッ荳?Dify 逧<>キィ蝓?(CORS)**
* **现象**
1. 后端启动需要 DIFY\_API\_KEY
2. DIFY\_API\_KEY 需要 Dify 启动并人工登录后才能生成。
3. 后端如果配置了"健康检查失败则重启",在填入 Key 之前会无限重启。
* **对策**:首次部署时,后端环境变量 DIFY\_API\_KEY 可以先填个假值(如 temp让服务跑起来。等 Dify 部署好拿到真 Key 后,更新 SAE 配置并重启。
### **🌐 风险三:前端与 Dify 的跨域 (CORS)**
* **问题**:前端直接调用后端(通过 Nginx 代理)没问题。但如果前端需要**直接嵌入** Dify 的 Web UI如 iframe或直接调用 Dify API绕过后端会遇到 CORS。
* **对策**:坚持\*\*"所有请求走后端"\*\*的原则。前端 \-\> Nginx \-\> 后端 \-\> Dify。不要让前端直连 Dify既安全又避免 CORS。
* **髣ョ鬚<EFBDAE>**<2A>壼燕遶ッ逶エ謗・隹<EFBDA5>畑蜷守ォッ<EFBDAB>€夊ソ<E5A48A> Nginx 莉」逅<EFBDA3>シ画イ。髣ョ鬚倥€ゆス<E38286>ヲよ棡蜑咲ォッ髴€隕?*逶エ謗・蠏悟<E8A08F>** Dify 逧?Web UI<55>亥ヲ<E4BAA5> iframe<6D><EFBFBD>逶エ謗・隹<EFBDA5>畑 Dify API<50>育サ戊ソ<E6888A>錘遶ッ<E981B6>会シ御シ夐∞蛻ー CORS縲?
* **蟇ケ遲<EFBDB9>**<2A>壼撓謖―*\*"謇€譛芽ッキ豎りオー蜷守ォッ"\*\*逧<>次蛻吶€ょ燕遶?\-\> Nginx \-\> 蜷守ォッ \-\> Dify縲ゆク崎ヲ∬ョゥ蜑咲ォッ逶エ霑<EFBDB4> Dify<66>梧里螳牙<E89EB3>蜿磯∩蜈?CORS縲?
## **3\. 架构关系图谱**
\[浏览器\]
@@ -69,18 +77,19 @@
|
\+--(HTTPS)--\> \[OSS 对象存储\]
|
\+--(NAT鄂大<EFBFBD>)--\> \[莠定#鄂? DeepSeek/OpenAI\]
\+--(NAT网关)--\> \[互联网: DeepSeek/OpenAI\]
## **4\. 扈?1-2 莠コ蝗「髦溽噪逕溷ュ伜サコ隶ョ**
## **4\. 1-2 人团队的生存建议**
1. **<EFBFBD>蝶鮟醍ァ第橿**<EFBFBD>?
* SAE 2.0 譛?*髣イ鄂ョ隶。雍ケ**蜉溯<E89C89>縲ょシ€蜿醍識蠅<E8AD98>シ域オ玖ッ慕識蠅<E8AD98>シ牙苅蠢<E88B85>€蜷ッ<E89CB7>梧イ。莠コ逕ィ譌カ荳肴噺 CPU/蜀<><EFBFBD> 雍ケ縲?
* RDS 雍ュ荵ー**騾夂畑蝙?* (2譬?G) 蜊ウ蜿ッ<EFBFBD>御ク崎ヲ∽ケー迢ャ莠ォ蝙具シ悟、溽畑蠕井ケ<EFBFBD>€?
2. **荳崎ヲ∬<EFBFBD>蟒コ逶第而**<EFBFBD>?
* 逶エ謗・逕ィ髦ソ驥御コ<EFBFBD> **ARMS** (蠎皮畑螳樊慮逶第而譛榊苅) 逧<><E980A7>雍ケ鬚晏コヲ謌門渕遑€迚医€ゆク崎ヲ∬<EFBDA6>蟾ア謳ュ Prometheus \+ Grafana<EFBFBD>檎サエ謚、謌先悽螟ェ鬮倥€?
3. **謨ー謐ョ螟<EFBFBD>サス譏ッ蠎慕コ?*<2A>?
* RDS €蜷ッ閾ェ蜉ィ螟<EFBFBD>サス<EFBFBD>井ソ晉蕗7螟ゥ<EFBFBD>€?
* ECS 荳顔噪 Dify docker-compose.yaml 蜥?.env <EFBFBD>サカ<EFBFBD>悟苅蠢<EFBFBD>惠譛ャ蝨ー謌?Git 遘∵怏莉灘コ灘、<E78198>サス荳€莉ス縲<EFBDBD>CS 豐。莠<EFBDA1>庄莉・驥堺ケー<EFBDB9><EFBFBD>鄂ョ譁<EFBDAE>サカ豐。莠<EFBDA1>ーア蠕鈴㍾驟阪€?
4. **蠑€蜿第譜邇?*<2A>?
* 蛻ゥ逕ィ ECS 蛛?*霍ウ譚ソ譛?*<2A>梧悽蝨ー逶エ霑?RDS 蠑€蜿代€ゆク崎ヲ∵ッ乗ャ。驛ス蜀吩サ」遐∝悉譟・謨ー謐ョ縲?
**譛€扈育サ楢ョ?*<2A>夊ソ吝・玲楔譫<E6A594>ョセ隶。蠕鈴撼蟶ク謇主ョ橸シ悟ョ悟<EFBDAE>蜿ッ莉・謾ッ謦台サ<E58FB0> 0 蛻?10 荳<>畑謌キ逧<EFBDB7><EFBFBD>ィ。縲りッキ驥咲せ隗」蜀ウ **"SAE 隶ソ髣ョ蜈ャ鄂<EFBDAC> (NAT)"** 霑吩クェ髣ョ鬚假シ悟叉蜿ッ蠑€蟋矩Κ鄂イ縲
1. **省钱黑科技**
* SAE 2.0 有**闲置计费**功能。开发环境(测试环境)务必开启,没人用时不收 CPU/内存 费。
* RDS 购买**通用型** (2核4G) 即可,不要买独享型,够用很久。
2. **不要自建监控**
* 直接用阿里云 **ARMS** (应用实时监控服务) 的免费额度或基础版。不要自己搭 Prometheus \+ Grafana,维护成本太高。
3. **数据备份是底线**
* RDS 开启自动备份保留7天
* ECS 上的 Dify docker-compose.yaml .env 文件,务必在本地或 Git 私有仓库备份一份。ECS 没了可以重买,配置文件没了就得重配。
4. **开发效率**
* 利用 ECS 做**跳板机**,本地直连 RDS 开发。不要每次都写代码去查数据。
**最终结论**:这套架构设计得非常扎实,完全可以支撑从 0 到 10 万用户的规模。请重点解决 **"SAE 访问公网 (NAT)"** 这个问题,即可开始部署。