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,39 +1,39 @@
# 2026-01-11 数据库事故总结
> 事故等级: **严重**
> 蜿醍函譌カ髣エ: 2026-01-11 郤?11:00
> 諱「螟肴慮髣エ: 2026-01-11 郤?13:00
> 蠖ア蜩崎激蝗エ: 豬玖ッ慕識蠅<E8AD98>焚謐ョ蠎?
> 发生时间: 2026-01-11 11:00
> 恢复时间: 2026-01-11 13:00
> 影响范围: 测试环境数据库
---
## 1. 事故概述
蝨ィ蠑€蜿題ソ占是邂。逅<EFBFBD>ォッ<EFBFBD><EFBFBD>DMIN<EFBFBD>画ィ。蝮玲慮<EFBFBD>御クコ莠<EFBFBD>峩譁ー逕ィ謌キ陦ィ扈捺桷<EFBFBD>域キサ蜉<EFBFBD>謇区惻蜿キ逋サ蠖輔€∫ァ滓姐蜈ウ閨皮ュ牙ュ玲ョオ<EFBFBD>会シ碁漠隸ッ菴ソ逕ィ莠?`prisma db push --force-reset` 蜻ス莉、<E88E89>悟ッシ閾エ謨ー謐ョ蠎謎クュ髱<EFBDAD> Prisma 邂。逅<EFBDA1>噪蟇ケ雎。陲ォ蛻<EFBDAB>髯、縲?
在开发运营管理端ADMIN模块时为了更新用户表结构添加手机号登录、租户关联等字段错误使用了 `prisma db push --force-reset` 命令,导致数据库中非 Prisma 管理的对象被删除。
---
## 2. 莠区腐譌カ髣エ郤?
## 2. 事故时间线
| 时间 | 事件 |
|------|------|
| 11:00 | 菫ョ謾ケ schema.prisma<EFBFBD>梧キサ蜉<EFBFBD>譁ー逧<EFBFBD>畑謌キ蟄玲ョオ蜥檎ァ滓姐陦?|
| 11:00 | 修改 schema.prisma,添加新的用户字段和租户表 |
| 11:05 | 执行 `prisma db push`,报错:现有数据与新 schema 冲突 |
| 11:10 | **髞呵ッッ蜀ウ遲<EFBFBD>**<EFBFBD>壽鴬陦?`prisma db push --force-reset` |
| 11:15 | 謨ー謐ョ蠎楢「ォ驥咲スョ<EFBFBD>碁撼 Prisma 邂。逅<EFBDA1>噪蟇ケ雎。荳「螟?|
| 11:10 | **错误决策**:执行 `prisma db push --force-reset` |
| 11:15 | 数据库被重置,非 Prisma 管理的对象丢失 |
| 11:20 | 执行 seed 脚本,补充基础数据 |
| 11:30 | 用户报告后端启动报错pg-boss 队列无法注册 |
| 11:45 | 诊断:`platform_schema.create_queue()` 函数丢失 |
| 12:00 | 从备份文件提取并恢复 pg-boss 函数 |
| 12:15 | 隸頑妙<EFBFBD>platform_schema.job_common` 陦ィ荳「螟?|
| 12:20 | 莉主、<EFBFBD>サス譁<EFBFBD>サカ謠仙叙蟷カ諱「螟<EFBFBD> job_common 陦?|
| 12:15 | 诊断:`platform_schema.job_common` 表丢失 |
| 12:20 | 从备份文件提取并恢复 job_common |
| 12:30 | 用户报告RVW 模块上传失败 |
| 12:35 | 诊断mock 用户 `user-mock-001` 丢失 |
| 12:40 | 蛻帛サコ mock 逕ィ謌キ蛻?public.users 蜥?platform_schema.users |
| 12:50 | 逕ィ謌キ謚・蜻奇シ啀KB 讓。蝮怜<E89DAE>蟒コ遏・隸<EFBDA5>コ灘、ア雍?|
| 12:55 | 隸頑妙<EFBFBD>壼、夜醗郤ヲ譚滂シ碁怙隕∝惠荳、荳ェ schema 逧?users 陦ィ驛ス譛?mock 逕ィ謌キ |
| 12:40 | 创建 mock 用户到 public.users platform_schema.users |
| 12:50 | 用户报告PKB 模块创建知识库失败 |
| 12:55 | 诊断:外键约束,需要在两个 schema users 表都有 mock 用户 |
| 13:00 | **系统恢复正常** |
| 13:15 | 螳梧紛螟<EFBFBD>サス蠖灘燕謨ー謐ョ蠎?|
| 13:15 | 完整备份当前数据库 |
---
@@ -41,41 +41,41 @@
### 3.1 直接原因
菴ソ逕ィ莠<EFBFBD>些髯ゥ蜻ス莉?`prisma db push --force-reset`<EFBFBD>瑚ッ・蜻ス莉、莨夲シ<EFBFBD>
使用了危险命令 `prisma db push --force-reset`,该命令会:
1. 删除数据库中所有表
2. 譬ケ謐ョ schema.prisma 驥肴眠蛻帛サコ陦?
3. **荳堺シ<EFBFBD>**諱「螟<EFBDA2> Prisma 荳咲ョ。逅<EFBDA1>噪蟇ケ雎。<E99B8E><EFBFBD>謨ー縲∵汾莠幄。ィ<EFBDA1>?
2. 根据 schema.prisma 重新创建表
3. **不会**恢复 Prisma 不管理的对象(函数、某些表)
### 3.2 深层原因
1. **遏・隸<EFBFBD>峇蛹コ**<EFBFBD>壻ク堺コ<EFBFBD>ァ」 Prisma 逧<>ョ。逅<EFBDA1>セケ逡?
- Prisma 蜿ェ邂。逅?schema.prisma 荳ュ螳壻ケ臥噪蟇ケ雎。
- pg-boss 霑占。梧慮蛻帛サコ逧<EFBFBD><EFBFBD>謨ー蜥瑚。ィ荳榊惠 Prisma 邂。逅<EFBDA1>激蝗エ蜀?
1. **知识盲区**:不了解 Prisma 的管理边界
- Prisma 只管理 schema.prisma 中定义的对象
- pg-boss 运行时创建的函数和表不在 Prisma 管理范围内
2. **郛コ荵丞、<EFBFBD>サス諢剰ッ<EFBFBD>**<EFBFBD>壽桃菴懷燕豐。譛牙、<EFBFBD>サス謨ー謐ョ蠎?
2. **缺乏备份意识**:操作前没有备份数据库
3. **缺乏规范文档**:没有数据库操作规范指导
### 3.3 Prisma 管理边界
```
笏娯楳笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏?
笏? 謨ー謐ョ蠎灘ョ梧紛蜀<EFBFBD>ョ? 笏?
笏? 笏娯楳笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏? 笏?
笏? 笏? Prisma 邂。逅<EFBFBD>噪蟇ケ雎? 笏? 笏?
笏? 笏? - schema.prisma 荳ュ螳壻ケ臥噪 model 笏? 笏?
笏? 笏? - schema.prisma 荳ュ螳壻ケ臥噪 enum 笏? 笏?
笏? 笏? - Prisma 蛻帛サコ逧<EFBFBD>エ「蠑募柱郤ヲ譚<EFBFBD> 笏? 笏?
笏? 笏披楳笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏? 笏?
笏? 笏?
笏? 笏娯楳笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏? 笏?
笏? 笏? Prisma 荳咲ョ。逅<EFBFBD>噪蟇ケ雎。 笞<><E7AC9E><EFBFBD> 笏? 笏?
笏? 笏? - pg-boss 蛻帛サコ逧?job_common 陦? 笏? 笏?
笏? 笏? - pg-boss 蛻帛サコ逧?create_queue() 蜃ス謨ー 笏? 笏?
笏? 笏? - pg-boss 蛻帛サコ逧?delete_queue() 蜃ス謨ー 笏? 笏?
笏? 笏? - 謇句勘蛻帛サコ逧<EFBDBA>ュ伜お霑<E3818A>ィ九€∬ァヲ蜿大勣縲∬ァ<E288AC><EFBDA7>? 笏? 笏?
笏? 笏披楳笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏? 笏?
笏披楳笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏€笏?
┌─────────────────────────────────────────────────────────────┐
数据库完整内容
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Prisma 管理的对象 │ │
│ │ - schema.prisma 中定义的 model │ │
│ │ - schema.prisma 中定义的 enum │ │
│ │ - Prisma 创建的索引和约束 │ │
│ └─────────────────────────────────────────────────────┘ │
│ ┌─────────────────────────────────────────────────────┐ │
│ │ Prisma 不管理的对象 ⚠️ │ │
│ │ - pg-boss 创建的 job_common │ │
│ │ - pg-boss 创建的 create_queue() 函数 │ │
│ │ - pg-boss 创建的 delete_queue() 函数 │ │
│ │ - 手动创建的存储过程、触发器、视图 │ │
│ └─────────────────────────────────────────────────────┘ │
└─────────────────────────────────────────────────────────────┘
```
---
@@ -84,23 +84,23 @@
### 4.1 数据丢失
| 鬘ケ逶ョ | 迥カ諤?| 隸エ譏<EFBDB4> |
| 项目 | 状态 | 说明 |
|------|------|------|
| 用户数据 | ⚠️ 丢失后通过 seed 恢复 | 测试数据,可接受 |
| 荳壼苅讓。蝮苓。ィ扈捺<EFBFBD>?| 笨?譛ェ蜿怜スア蜩<EFBDB1> | Prisma 豁」遑ョ驥榊サコ |
| 业务模块表结构 | ✅ 未受影响 | Prisma 正确重建 |
| 业务模块数据 | ⚠️ 清空 | 测试数据,可接受 |
| pg-boss 蜃ス謨ー | 笶?荳「螟ア | 髴€謇句勘諱「螟<EFBDA2> |
| job_common 陦?| 笶?荳「螟ア | 髴€謇句勘諱「螟<EFBDA2> |
| pg-boss 函数 | ❌ 丢失 | 需手动恢复 |
| job_common 表 | ❌ 丢失 | 需手动恢复 |
### 4.2 功能影响
| 功能 | 影响 | 恢复措施 |
|------|------|----------|
| 蜷守ォッ蜷ッ蜉ィ | 笶?螟ア雍・ | 諱「螟<EFBDA2> pg-boss 蜃ス謨ー蜥瑚。ィ |
| RVW <EFBFBD>ョ。遞?| 笶?500髞呵ッッ | 蛻帛サコ mock 逕ィ謌キ |
| PKB 遏・隸<EFBFBD>コ?| 笶?500髞呵ッッ | 蛻帛サコ mock 逕ィ謌キ |
| ASL <EFBFBD>鍵遲幃€?| 笨?豁」蟶ク | - |
| DC 謨ー謐ョ貂<EFBFBD><EFBFBD> | 笨?豁」蟶ク | - |
| 后端启动 | ❌ 失败 | 恢复 pg-boss 函数和表 |
| RVW 预审稿 | ❌ 500错误 | 创建 mock 用户 |
| PKB 知识库 | ❌ 500错误 | 创建 mock 用户 |
| ASL 文献筛选 | ✅ 正常 | - |
| DC 数据清洗 | ✅ 正常 | - |
---
@@ -109,7 +109,7 @@
### 5.1 恢复 pg-boss 对象
```bash
# 諱「螟<EFBFBD> job_common 陦?
# 恢复 job_common
npx prisma db execute --file restore_job_common.sql --schema prisma/schema.prisma
# 恢复 pg-boss 函数
@@ -130,9 +130,9 @@ VALUES ('user-mock-001', '13800000000', ..., 'tenant-mock-001', ...);
### 5.3 恢复文件清单
| <EFBFBD>サカ | 逕ィ騾?| 菴咲スョ |
| 文件 | 用途 | 位置 |
|------|------|------|
| `restore_job_common.sql` | 諱「螟<EFBFBD> job_common 陦?| backend/ |
| `restore_job_common.sql` | 恢复 job_common | backend/ |
| `restore_pgboss_functions.sql` | 恢复 pg-boss 函数 | backend/ |
| `create_mock_user.sql` | 创建 public.users mock 用户 | backend/ |
| `create_mock_user_platform.sql` | 创建 platform_schema.users mock 用户 | backend/ |
@@ -141,51 +141,51 @@ VALUES ('user-mock-001', '13800000000', ..., 'tenant-mock-001', ...);
## 6. 改进措施
### 6.1 遶句叉謗ェ譁ス<EFBFBD>亥キイ螳梧<EFBFBD><EFBFBD>?
### 6.1 立即措施(已完成)
- [x] 蛻帛サコ謨ー謐ョ蠎灘、<EFBFBD>サ?`backup_20260111_131506.sql`
- [x] 创建数据库备份 `backup_20260111_131506.sql`
- [x] 更新 schema.prisma 添加警告注释
- [x] 创建恢复脚本文件
- [x] 郛門<EFBFBD>謨ー謐ョ蠎灘シ€蜿題ァ<EFBFBD>激譁<EFBFBD>。?
- [x] 编写数据库开发规范文档
### 6.2 短期措施(本周)
- [ ] 将恢复脚本添加到版本控制
- [ ] 蝨?CI/CD 荳ュ豺サ蜉<EFBFBD>謨ー謐ョ蠎灘、<EFBFBD>サス豁・鬪、
- [ ] CI/CD 中添加数据库备份步骤
- [ ] 团队培训Prisma 使用规范
### 6.3 长期措施
- [ ] 建立自动备份机制
- [ ] 謨ー謐ョ蠎灘序譖エ螳。謇ケ豬∫ィ?
- [ ] 数据库变更审批流程
- [ ] 定期演练数据恢复
---
## 7. 经验教训
### 7.1 €譛ッ螻る<EFBFBD>?
### 7.1 技术层面
1. **<EFBFBD>ァ」蟾・蜈キ霎ケ逡<EFBFBD>**<EFBFBD>壽ッ丈クェ蟾・蜈キ驛ス譛牙<EFBFBD>邂。逅<EFBFBD>激蝗エ<EFBFBD>碁怙隕∽コ<EFBFBD>ァ」霎ケ逡?
2. **<EFBFBD>サス莨伜<EFBFBD>**<EFBFBD>壻ササ菴墓焚謐ョ蠎捺桃菴懷燕蠢<EFBFBD>。サ螟<EFBFBD>サ?
3. **蠅樣㍼蜿俶峩**<EFBFBD>壻シ伜<EFBFBD>菴ソ逕?`prisma migrate dev` 閠碁撼 `db push`
1. **了解工具边界**:每个工具都有其管理范围,需要了解边界
2. **备份优先**:任何数据库操作前必须备份
3. **增量变更**:优先使用 `prisma migrate dev` 而非 `db push`
### 7.2 流程层面
1. **荳画€晏錘陦?*<2A>壽鴬陦悟些髯ゥ蜻ス莉、蜑榊、夐琉蜃<E79089>荳ェ髣ョ鬚<EFBDAE>
1. **三思后行**:执行危险命令前多问几个问题
2. **文档先行**:操作规范文档要提前准备
3. **蠢ォ騾溷桃蠎?*<2A>壼書邇ー髣ョ鬚伜錘蠢ォ騾溯ッ頑妙蜥梧△螟<E296B3>
3. **快速响应**:发现问题后快速诊断和恢复
### 7.3 团队层面
1. **遏・隸<EFBFBD><EFBFBD>莠ォ**<EFBFBD>壽橿譛ッ扈城ェ碁怙隕∝所譌カ豐画キ€蜥悟<EFBFBD>莠?
1. **知识共享**:技术经验需要及时沉淀和分享
2. **Code Review**:数据库操作应有审批机制
---
## 8. 相关文档
- [謨ー謐ョ蠎灘シ€蜿題ァ<EFBFBD>](../04-蠑€蜿題ァ<E9A18C><EFBDA7>?09-謨ー謐ョ蠎灘シ€蜿題ァ<E9A18C><EFBDA7>?md)
- [数据库开发规范](../04-开发规范/09-数据库开发规范.md)
- [Prisma Schema 文件](../../backend/prisma/schema.prisma)
- [备份文件](../../backup_20260111_131506.sql)
@@ -200,8 +200,7 @@ VALUES ('user-mock-001', '13800000000', ..., 'tenant-mock-001', ...);
---
> **螢ー譏<EFBFBD>**<2A>壽悽谺。莠区腐蜿醍函蝨ィ豬玖ッ慕識蠅<E8AD98>シ梧悴蠖ア蜩咲函莠ァ謨ー謐ョ縲ゆス<E38286>垓髴イ逧<EFBDB2>琉鬚伜酔譬キ蜿ッ閭ス蝨ィ逕滉コァ邇ッ蠅<EFBDAF>書逕滂シ碁怙隕<E68099>ォ伜コヲ驥崎ァ<E5B48E>€?
> **声明**:本次事故发生在测试环境,未影响生产数据。但暴露的问题同样可能在生产环境发生,需要高度重视。