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,6 +1,7 @@
# <EFBFBD>唳旿摨栞<EFBFBD><EFBFBD>仿<EFBFBD>蝵?
> **<2A><><EFBFBD><EFB99D>𧋦嚗?* v1.0
> **<EFBFBD>𥕦遣<EFBFBD><EFBFBD>嚗?* 2025-11-09
# 数据库连接配置
> **文档版本:** v1.0
> **创建日期:** 2025-11-09
> **维护者:** 架构团队
> **最后更新:** 2025-11-09
@@ -8,20 +9,26 @@
## 📋 文档说明
<EFBFBD><EFBFBD><EFBFBD>扇敶笌chema<EFBFBD>𠉛氖<EFBFBD><EFBFBD>摰墧鴌<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>餈墧𦻖<EFBFBD><EFBFBD>蝵桐縑<EFBFBD><EFBFBD><EFBFBD><EFBFBD>𡠺嚗?- 敶枏<E695B6><E69E8F>唳旿摨梶𠶖<E6A2B6>?- Schema<6D>𠉛氖餈<E6B096><EFBFBD>滨蔭
- <EFBFBD>宏餈<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>交䲮撘?- 撉諹<E69289><E8ABB9><EFBFBD><EFBFBD>皛𡁏䲮獢?
本文档记录Schema隔离架构实施相关的数据库连接和配置信息包括
- 当前数据库状态
- Schema隔离迁移配置
- 迁移过程的连接方式
- 验证和回滚方案
---
## <EFBFBD><EFBFBD>儭?敶枏<E695B6><E69E8F>唳旿摨梶𠶖<E6A2B6>?
### <20>唳旿摨枏抅<E69E8F>砌縑<E7A08C>?
**餈墧𦻖靽⊥<E99DBD>嚗?*
## 🗄️ 当前数据库状态
### 数据库基本信息
**连接信息:**
```
数据库类型PostgreSQL 15+
数据库名称ai_clinical_research
銝餅㦤<EFBFBD><EFBFBD>嚗? localhost
蝡臬藁嚗? 5432
主机地址: localhost
端口: 5432
用户名: postgres
<EFBFBD><EFBFBD>嚗? postgres
密码: postgres
```
**完整连接字符串:**
@@ -33,16 +40,19 @@ DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ai_clinical_research
### 当前Schema结构
**<EFBFBD><EFBFBD>Schema嚗?*
- <EFBFBD>?**public** - <EFBFBD><EFBFBD><EFBFBD>?3銝芾”<E88ABE>賢銁餈䠷<E9A488>
**现有Schema**
- **public** - 所有13个表都在这里
**銵冽<EFBFBD><EFBFBD>𤏪<EFBFBD>13銝迎<EFBFBD>嚗?*
**表清单13个**
#### 1. 撟喳蝱<EFBFBD><EFBFBD>嚗?銝芾”嚗?```sql
public.users -- <20><EFBFBD>銵?public.admin_logs -- 蝞∠<E89D9E><E288A0><EFBFBD>
#### 1. 平台基础2个表
```sql
public.users -- 用户表
public.admin_logs -- 管理日志
```
#### 2. AI<EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗?銝芾”嚗?```sql
#### 2. AI智能问答5个表
```sql
public.projects -- 项目管理
public.conversations -- 项目对话
public.messages -- 对话消息
@@ -50,29 +60,42 @@ public.general_conversations -- 通用对话
public.general_messages -- 通用消息
```
#### 3. 銝芯犖<EFBFBD><EFBFBD>摨橒<EFBFBD>2銝芾”嚗?```sql
public.knowledge_bases -- <20><EFBFBD>摨?public.documents -- <20><>
#### 3. 个人知识库2个表
```sql
public.knowledge_bases -- 知识库
public.documents -- 文档
```
#### 4. <EFBFBD><EFBFBD><EFBFBD><EFBFBD>頂蝏<EFBFBD><EFBFBD>3銝芾”嚗?```sql
public.batch_tasks -- <20><EFBFBD><E5ADB5><EFBFBD><EFBFBD>?public.batch_results -- <20><EFBFBD><E5ADB5><EFBFBD><EFBFBD><EFBFBD>?public.task_templates -- 隞餃𦛚璅⊥踎
#### 4. 批处理系统3个表
```sql
public.batch_tasks -- 批处理任务
public.batch_results -- 批处理结果
public.task_templates -- 任务模板
```
#### 5. 蝔蹂辣摰⊥䰻嚗?銝芾”嚗?```sql
#### 5. 稿件审查1个表
```sql
public.review_tasks -- 审查任务
```
---
### <EFBFBD>唳旿摨梶<EFBFBD>霈∩縑<EFBFBD>?
**銵冽㺭<E586BD>𧶏<EFBFBD>** 13銝?
**<EFBFBD><EFBFBD><EFBFBD>嚗?* 蝥?0銝?
**憭㚚睸蝥行<EFBFBD>嚗?* 蝥?5銝?
### 数据库统计信息
**表数量:** 13个
**索引数量:** 约40个
**外键约束:** 约15个
**当前数据量(估算):**
- users: ~10<EFBFBD>?- projects: ~20<32>?- conversations: ~50<35>?- messages: ~500<30>?- knowledge_bases: ~5<>?- documents: ~30<33>?- <20><EFBFBD>銵剁<E98AB5>瘚贝<E7989A><E8B49D>唳旿
- users: ~10
- projects: ~20条
- conversations: ~50条
- messages: ~500条
- knowledge_bases: ~5条
- documents: ~30条
- 其他表:测试数据
**<EFBFBD>餅㺭<EFBFBD><EFBFBD>嚗?* < 10MB嚗<42><E59A97>霂閧㴓憓<E3B493><E68693>
**总数据量:** < 10MB测试环境
---
@@ -80,13 +103,13 @@ public.review_tasks -- 审查任务
### 10个独立Schema
#### <EFBFBD><EFBFBD><EFBFBD>祕蝏<EFBFBD>挽霈?<3F>唳旿餈<E697BF>宏嚗?銝迎<E98A9D>
#### 需要详细设计+数据迁移5个
**1. platform_schema平台基础层**
```sql
-- 迁移表:
public.users <EFBFBD>?platform_schema.users
public.admin_logs <EFBFBD>?platform_schema.admin_logs<><E59A97>蝻橒<E89DBB><EFBFBD>霈歹<E99C88>
public.users platform_schema.users
public.admin_logs platform_schema.admin_logs
```
**2. common_schema通用能力层**
@@ -96,7 +119,7 @@ common_schema.llm_usage -- LLM使用记录
common_schema.feature_flags -- Feature Flags
```
**3. asl_schema嚗㇁I<EFBFBD><EFBFBD><EFBFBD><EFBFBD>讃嚗?*
**3. asl_schemaAI智能文献**
```sql
-- 新建表:
asl_schema.literature_projects -- 文献项目
@@ -105,36 +128,38 @@ asl_schema.literature_items -- 文献条目
-- 更多表见ASL设计文档
```
**4. aia_schema嚗㇁I<EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗?*
**4. aia_schemaAI智能问答**
```sql
-- 迁移表:
public.projects <EFBFBD>?aia_schema.projects
public.conversations <EFBFBD>?aia_schema.conversations
public.messages <EFBFBD>?aia_schema.messages
public.general_conversations <EFBFBD>?aia_schema.general_conversations
public.general_messages <EFBFBD>?aia_schema.general_messages
public.projects aia_schema.projects
public.conversations aia_schema.conversations
public.messages aia_schema.messages
public.general_conversations aia_schema.general_conversations
public.general_messages aia_schema.general_messages
```
**5. pkb_schema<EFBFBD>葵鈭箇䰻霂<EFBFBD><EFBFBD>嚗?*
**5. pkb_schema(个人知识库)**
```sql
-- 迁移表:
public.knowledge_bases <EFBFBD>?pkb_schema.knowledge_bases
public.documents <EFBFBD>?pkb_schema.documents
public.knowledge_bases pkb_schema.knowledge_bases
public.documents pkb_schema.documents
```
#### <EFBFBD><EFBFBD>撱箇征Schema嚗?銝迎<E98A9D>
#### 只创建空Schema5个
**6. dc_schema数据清洗** - 暂无表结构
**7. rvw_schema审稿系统** - 暂无表结构
**8. admin_schema运营管理** - 暂无表结构
**9. ssa_schema智能统计分析** - 暂无表结构
**10. st_schema统计分析工具** - 暂无表结构
**6. dc_schema嚗<61><EFBFBD><EFBFBD>瘣梹<E798A3>** - <20><><EFBFBD>銵函<E98AB5><E587BD>?
**7. rvw_schema嚗<61>恣蝔輻頂蝏<E9A082><E89D8F>** - <20><><EFBFBD>銵函<E98AB5><E587BD>?
**8. admin_schema嚗<61><E59A97><EFBFBD>亦恣<E4BAA6><E681A3><EFBFBD>** - <20><><EFBFBD>銵函<E98AB5><E587BD>?
**9. ssa_schema嚗<61><EFBFBD><EFBFBD><E99C88><E288AA><EFBFBD>** - <20><><EFBFBD>銵函<E98AB5><E587BD>?
**10. st_schema嚗<61><E59A97><E99C88><E288AA>𣂼極<F0A382BC><EFBFBD>** - <20><><EFBFBD>銵函<E98AB5><E587BD>?
---
## 🔄 迁移过程连接配置
### <EFBFBD><EFBFBD><EFBFBD>憭?
**1. 憭<>遢敶枏<E695B6><E69E8F>唳旿摨?*
### 迁移前准备
**1. 备份当前数据库**
```bash
# Windows PowerShell
cd D:\MyCursor\AIclinicalresearch\backend
@@ -148,9 +173,11 @@ pg_dump -h localhost -U postgres -d ai_clinical_research > backup_before_schema_
**2. 验证备份**
```bash
# <EFBFBD><EFBFBD><EFBFBD>隞賣<EFBFBD>隞嗅之撠?ls -lh backup_*.dump
# 检查备份文件大小
ls -lh backup_*.dump
# 撉諹<EFBFBD><EFBFBD><EFBFBD><EFBFBD>捆嚗𠄎QL<EFBFBD><EFBFBD>嚗?head -n 50 backup_*.sql
# 验证备份内容SQL格式
head -n 50 backup_*.sql
```
---
@@ -159,40 +186,43 @@ pg_dump -h localhost -U postgres -d ai_clinical_research > backup_before_schema_
#### 方式1使用Prisma Migrate推荐
**餈墧𦻖<EFBFBD>滨蔭嚗?*
**连接配置:**
```env
# backend/.env
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ai_clinical_research
```
**<EFBFBD><EFBFBD><EFBFBD>宏嚗?*
**执行迁移:**
```bash
cd backend
# 创建新的迁移
npx prisma migrate dev --name schema_isolation_10_schemas
# <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?npx prisma migrate status
# 查看迁移状态
npx prisma migrate status
```
---
#### 方式2直接执行SQL脚本
**餈墧𦻖<EFBFBD><EFBFBD>嚗?*
**连接方式:**
```bash
# 使用psql连接
psql -h localhost -U postgres -d ai_clinical_research
# <EFBFBD>碶蝙<EFBFBD>函㴓憓<EFBFBD><EFBFBD><EFBFBD>?export PGPASSWORD=postgres
# 或使用环境变量
export PGPASSWORD=postgres
psql -h localhost -U postgres -d ai_clinical_research -f migration_script.sql
```
**<EFBFBD><EFBFBD><EFBFBD><EFBFBD>𡁏𧋦嚗?*
**执行迁移脚本:**
```bash
cd docs/09-架构实施/migration-scripts
# <EFBFBD>厰◇摨𤩺<EFBFBD>銵?psql -h localhost -U postgres -d ai_clinical_research -f 001-create-all-10-schemas.sql
# 按顺序执行
psql -h localhost -U postgres -d ai_clinical_research -f 001-create-all-10-schemas.sql
psql -h localhost -U postgres -d ai_clinical_research -f 002-migrate-platform.sql
psql -h localhost -U postgres -d ai_clinical_research -f 003-migrate-aia.sql
psql -h localhost -U postgres -d ai_clinical_research -f 004-migrate-pkb.sql
@@ -203,28 +233,31 @@ psql -h localhost -U postgres -d ai_clinical_research -f 004-migrate-pkb.sql
#### 方式3使用可视化工具
**pgAdmin 4嚗?*
1. 餈墧𦻖靽⊥<EFBFBD>嚗? - 銝餅㦤嚗饝ocalhost
- 蝡臬藁嚗?432
**pgAdmin 4**
1. 连接信息:
- 主机localhost
- 端口5432
- 数据库ai_clinical_research
- 用户postgres
- 密码postgres
2. <EFBFBD><EFBFBD><EFBFBD>宏嚗? - <20><EFBFBD>Query Tool
2. 执行迁移:
- 打开Query Tool
- 加载SQL脚本
- 执行
**DataGrip / DBeaver嚗?*
- <EFBFBD>峕甅<EFBFBD><EFBFBD><EFBFBD><EFBFBD>乩縑<EFBFBD>?- <20><EFBFBD>鈭见𦛚蝞∠<E89D9E>
**DataGrip / DBeaver**
- 同样的连接信息
- 支持事务管理
- 方便回滚
---
## <EFBFBD>?餈<>宏撉諹<E69289>
## ✅ 迁移验证
### 1. 检查Schema创建
**SQL撉諹<EFBFBD>嚗?*
**SQL验证:**
```sql
-- 查看所有Schema
SELECT schema_name
@@ -232,7 +265,8 @@ FROM information_schema.schemata
WHERE schema_name NOT IN ('pg_catalog', 'information_schema')
ORDER BY schema_name;
-- <EFBFBD><EFBFBD>蝏𤘪<EFBFBD>嚗?-- admin_schema
-- 预期结果:
-- admin_schema
-- aia_schema
-- asl_schema
-- common_schema
@@ -249,29 +283,32 @@ ORDER BY schema_name;
### 2. 检查表迁移
**撉諹<EFBFBD>platform_schema嚗?*
**验证platform_schema**
```sql
-- <EFBFBD><EFBFBD>銵?SELECT table_name
-- 查看表
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'platform_schema';
-- 验证数据
SELECT COUNT(*) FROM platform_schema.users;
-- <EFBFBD><EFBFBD>嚗帋<EFBFBD><EFBFBD>ublic.users<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>?```
-- 预期与原public.users的数量一致
```
**<EFBFBD>aia_schema嚗?*
**验证aia_schema**
```sql
-- 查看所有表
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'aia_schema';
-- 撉諹<EFBFBD><EFBFBD>唳旿摰峕㟲<EFBFBD>?SELECT COUNT(*) FROM aia_schema.projects;
-- 验证数据完整性
SELECT COUNT(*) FROM aia_schema.projects;
SELECT COUNT(*) FROM aia_schema.conversations;
SELECT COUNT(*) FROM aia_schema.messages;
```
**撉諹<EFBFBD>pkb_schema嚗?*
**验证pkb_schema**
```sql
SELECT COUNT(*) FROM pkb_schema.knowledge_bases;
SELECT COUNT(*) FROM pkb_schema.documents;
@@ -281,9 +318,10 @@ SELECT COUNT(*) FROM pkb_schema.documents;
### 3. 验证外键关系
**<EFBFBD><EFBFBD>亥楊Schema憭㚚睸嚗?*
**检查跨Schema外键:**
```sql
-- <EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>桃漲<EFBFBD>?SELECT
-- 查看所有外键约束
SELECT
tc.table_schema,
tc.table_name,
kcu.column_name,
@@ -306,7 +344,8 @@ ORDER BY tc.table_schema, tc.table_name;
**检查索引是否正确迁移:**
```sql
-- <EFBFBD><EFBFBD><EFBFBD>𣂷葵Schema<EFBFBD><EFBFBD><EFBFBD><EFBFBD>厩揣撘?SELECT
-- 查看某个Schema的所有索引
SELECT
schemaname,
tablename,
indexname,
@@ -337,7 +376,7 @@ npx ts-node -e "import { PrismaClient } from '@prisma/client'; const p = new Pri
### 场景1迁移过程中发现问题
**蝡见朖<EFBFBD><EFBFBD>嚗?*
**立即回滚:**
```bash
# 如果使用Prisma Migrate
npx prisma migrate reset
@@ -352,7 +391,8 @@ psql -h localhost -U postgres -d ai_clinical_research < backup_before_schema_mig
**方案A从备份恢复推荐**
```bash
# 1. <EFBFBD>𣳇膄敶枏<EFBFBD><EFBFBD>唳旿摨?dropdb -h localhost -U postgres ai_clinical_research
# 1. 删除当前数据库
dropdb -h localhost -U postgres ai_clinical_research
# 2. 重新创建
createdb -h localhost -U postgres ai_clinical_research
@@ -363,7 +403,8 @@ pg_restore -h localhost -U postgres -d ai_clinical_research backup_before_schema
**方案B手动删除新Schema保留public**
```sql
-- <EFBFBD>𣳇膄<EFBFBD>啣遣<EFBFBD><EFBFBD>chema嚗<EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗?DROP SCHEMA IF EXISTS platform_schema CASCADE;
-- 删除新建的Schema慎重
DROP SCHEMA IF EXISTS platform_schema CASCADE;
DROP SCHEMA IF EXISTS aia_schema CASCADE;
DROP SCHEMA IF EXISTS pkb_schema CASCADE;
DROP SCHEMA IF EXISTS asl_schema CASCADE;
@@ -379,24 +420,35 @@ DROP SCHEMA IF EXISTS st_schema CASCADE;
---
## <EFBFBD><EFBFBD><>宏璉<E5AE8F><E79289><EFBFBD><E4BAA4>?
### 餈<><EFBFBD>齿<EFBFBD><E9BDBF>?
- [ ] <20>?<3F>唳旿摨枏歇憭<E6AD87>遢嚗Ê̄ackup_before_schema_migration_*.dump嚗?- [ ] <20>?憭<><EFBFBD><E981A2>辣撌脤<E6928C>霂?- [ ] <20>?Prisma<6D>滨蔭撌脫凒<E884AB><EFBFBD>datasource.schemas嚗?- [ ] <20>?餈<><EFBFBD>𡁏𧋦撌脣<E6928C><EFBFBD><E686AD>001-010.sql嚗?- [ ] <20>?瘚贝<E7989A><E8B49D><EFBFBD>撌脤<E6928C><EFBFBD><E99C82>蝘餅郊撉?
---
## 📊 迁移检查清单
### <EFBFBD>宏銝剜<EFBFBD><EFBFBD>?
- [ ] <20>?10銝杵chema<6D>𥕦遣<F0A595A6>𣂼<EFBFBD>
- [ ] <EFBFBD>?銵刻<E98AB5>蝘餅<E89D98><E9A485>躰秤嚗?銝芣<E98A9D><E88AA3>唳旿<E594B3><E697BF>chema嚗?- [ ] <20>?<3F>唳旿摰峕㟲<E5B395><EFBFBD><EFBFBD><E99C82><EFBFBD>
- [ ] <EFBFBD>?憭㚚睸蝥行<E89DA5><EFBFBD>
- [ ] <EFBFBD>?蝝<E89D9D><EFBFBD><EFBFBD>𥕦遣
### 迁移前检查
- [ ] ✅ 数据库已备份backup_before_schema_migration_*.dump
- [ ] ✅ 备份文件已验证
- [ ] ✅ Prisma配置已更新datasource.schemas
- [ ] ✅ 迁移脚本已准备001-010.sql
- [ ] ✅ 测试环境已验证迁移步骤
---
### <EFBFBD><EFBFBD><EFBFBD><EFBFBD>?
- [ ] <20>?Prisma Client<6E><74><EFBFBD><EFBFBD>𣂼<EFBFBD>
- [ ] <EFBFBD>?<3F>𡒊垢<F0A1928A>滚𦛚<E6BB9A>臬𢆡甇<F0A286A1>
- [ ] <EFBFBD>?<3F><EFBFBD><E594B3><EFBFBD>瘚贝<E7989A><E8B49D><EFBFBD>嚗㇁IA<49><41>KB嚗?- [ ] <20>?API靚<49>鍂甇<E98D82>
- [ ] <EFBFBD>?<3F>滨垢<E6BBA8><EFBFBD><EFBFBD>
### 迁移中检查
- [ ] ✅ 10个Schema创建成功
- [ ] ✅ 表迁移无错误5个有数据的Schema
- [ ] ✅ 数据完整性验证通过
- [ ] ✅ 外键约束正确
- [ ] ✅ 索引正确创建
---
### 迁移后检查
- [ ] ✅ Prisma Client生成成功
- [ ] ✅ 后端服务启动正常
- [ ] ✅ 现有功能测试通过AIA、PKB
- [ ] ✅ API调用正常
- [ ] ✅ 前端功能正常
---
@@ -404,67 +456,84 @@ DROP SCHEMA IF EXISTS st_schema CASCADE;
### 问题1Schema创建失败
**<EFBFBD>躰秤嚗?*
**错误:**
```
ERROR: schema "platform_schema" already exists
```
**<EFBFBD><EFBFBD>嚗?*
**解决:**
```sql
-- 检查Schema是否存在
SELECT schema_name FROM information_schema.schemata
WHERE schema_name = 'platform_schema';
-- <EFBFBD><EFBFBD>摮睃銁雿<EFBFBD>蛹蝛綽<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>撱?DROP SCHEMA platform_schema CASCADE;
-- 如果存在但为空,可删除重建
DROP SCHEMA platform_schema CASCADE;
CREATE SCHEMA platform_schema;
```
---
### <EFBFBD><EFBFBD>2嚗𡁏㺭<EFBFBD><EFBFBD>蝘餃仃韐?
**<2A>躰秤嚗?*
### 问题2数据迁移失败
**错误:**
```
ERROR: duplicate key value violates unique constraint
```
**<EFBFBD><EFBFBD>嚗?*
**解决:**
1. 检查数据是否已部分迁移
2. 清理目标Schema
3. 重新执行迁移
---
### <EFBFBD><EFBFBD>3嚗𡁜<EFBFBD><EFBFBD>桃漲<EFBFBD><EFBFBD>霂?
**<2A>躰秤嚗?*
### 问题3外键约束错误
**错误:**
```
ERROR: foreign key constraint "fk_user_id" cannot be implemented
```
**<EFBFBD><EFBFBD>嚗?*
1. 蝖桐<EFBFBD>鋡怠<EFBFBD><EFBFBD><EFBFBD>銵典歇<EFBFBD><EFBFBD><EFBFBD>蝘?2. 璉<><E79289><EFBFBD><E4BA99><EFBFBD>畾萇掩<E89087>见龪<E8A781>?3. <20><><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><E585B8><EFBFBD><EFBFBD><EFBFBD>𡡞<EFBFBD>撱?
**解决:**
1. 确保被引用的表已先迁移
2. 检查外键字段类型匹配
3. 考虑先禁用外键,迁移后重建
---
## <EFBFBD>圲 鈭穃<E988AD><E7A983><EFBFBD><E8A098><EFBFBD><E4BAA4>滨蔭嚗?025-11-16 <EFBFBD><EFBFBD>嚗?
> **潃?<3F><EFBFBD><E6BBA9>湔鰵**嚗帋蛹<E5B88B><EFBFBD><E88880><EFBFBD>鈭?Serverless <20>函蔡嚗峕鰵憓噼<E68693><E599BC><EFBFBD><E4BAA4>滨蔭
## 🔧 云原生连接池配置2025-11-16 新增)
> **⭐ 重要更新**:为支持阿里云 Serverless 部署,新增连接池配置
> **详细文档**[平台基础设施规划](./04-平台基础设施规划.md)
### <EFBFBD>峕艶嚗帋蛹隞<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>嚗?
**<2A><EFBFBD><E6A185>箸艶**嚗?```
<EFBFBD><EFBFBD>鈭?SAE <20>芸𢆡<E88AB8>拙捆嚗?- <20><EFBFBD>嚗?銝芸<E98A9D>靘页<E99D98>10銝芾<E98A9D><E88ABE>?- 擃睃陸嚗?00銝芸<E98A9D>靘页<E99D98>1000銝芾<E98A9D><E88ABE>?- RDS<44><53>憭扯<E686AD><E689AF>交㺭嚗?00 <20>?頞<><E9A09E>嚗?
蝏𤘪<EFBFBD>嚗𡁏㺭<EFBFBD><EFBFBD>餈墧𦻖<EFBFBD>堒偷嚗<EFBFBD><EFBFBD><EFBFBD>典援皞?```
### 背景:为什么需要连接池?
**问题场景**
```
阿里云 SAE 自动扩容:
- 初始1个实例10个连接
- 高峰100个实例1000个连接
- RDS最大连接数400 ❌ 超限!
结果:数据库连接耗尽,应用崩溃
```
**解决方案**:动态计算每实例连接数
**閫<><E996AB><EFBFBD><EFBFBD>**嚗𡁜𢆡<F0A1819C><F0A286A1>恣蝞埈<E89D9E>摰硺<E691B0>餈墧𦻖<E5A2A7>?
```typescript
= RDS最大连接数 / SAE最大实例数
蝷箔<EFBFBD>嚗?00餈墧𦻖 / 20摰硺<E691B0> = 20餈墧𦻖/摰硺<E691B0>
400 / 20 = 20/
```
---
### Prisma餈墧𦻖瘙𣳇<EFBFBD>蝵?
### Prisma连接池配置
**文件位置**`backend/src/config/database.ts`
**<EFBFBD>滨蔭隞<EFBFBD><EFBFBD>**嚗?
**配置代码**
```typescript
import { PrismaClient } from '@prisma/client'
@@ -473,7 +542,7 @@ const dbMaxConnections = Number(process.env.DB_MAX_CONNECTIONS) || 400
const maxInstances = Number(process.env.MAX_INSTANCES) || 20
const connectionLimit = Math.floor(dbMaxConnections / maxInstances)
console.log(`<EFBFBD><EFBFBD> <20>唳旿摨栞<E691A8><E6A09E><EFBFBD><E4BAA4>滨蔭嚗𡁏<E59A97>摰硺<E691B0><E7A1BA><EFBFBD>憭?{connectionLimit}銝芾<E98A9D><E88ABE>匝)
console.log(`📊 数据库连接池配置:每实例最多${connectionLimit}个连接`)
// 创建全局Prisma Client
export const prisma = new PrismaClient({
@@ -490,23 +559,24 @@ export const prisma = new PrismaClient({
// 优雅关闭连接
process.on('SIGTERM', async () => {
console.log('<27><><><EFBFBD>喲𡡒<E596B2>唳旿摨栞<E691A8><E6A09E>?..')
console.log('📊 正在关闭数据库连接...')
await prisma.$disconnect()
console.log('<27>?<3F>唳旿摨栞<E691A8><E6A09E>亙歇<E4BA99>喲𡡒')
console.log('✅ 数据库连接已关闭')
process.exit(0)
})
process.on('SIGINT', async () => {
console.log('<27><><><EFBFBD>喲𡡒<E596B2>唳旿摨栞<E691A8><E6A09E>?..')
console.log('📊 正在关闭数据库连接...')
await prisma.$disconnect()
console.log('<27>?<3F>唳旿摨栞<E691A8><E6A09E>亙歇<E4BA99>喲𡡒')
console.log('✅ 数据库连接已关闭')
process.exit(0)
})
// <EFBFBD>臬𢆡<EFBFBD><EFBFBD>霂閗<EFBFBD><EFBFBD>?prisma.$connect()
.then(() => console.log('<27>?<3F>唳旿摨栞<E691A8><E6A09E><EFBFBD><E4BAA4>?))
// 启动时测试连接
prisma.$connect()
.then(() => console.log('✅ 数据库连接成功'))
.catch((err) => {
console.error('<27>?<3F>唳旿摨栞<E691A8><E6A09E>亙仃韐?', err)
console.error('❌ 数据库连接失败:', err)
process.exit(1)
})
```
@@ -515,7 +585,8 @@ process.on('SIGINT', async () => {
### 环境变量配置
**<EFBFBD>砍𧑐撘<EFBFBD><EFBFBD>𤑳㴓憓?*嚗?
**本地开发环境**
```bash
# backend/.env.development
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ai_clinical_research
@@ -525,33 +596,37 @@ DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ai_clinical_research
# MAX_INSTANCES=N/A
```
**鈭𤑳垢<EFBFBD>煺漣<EFBFBD><EFBFBD>**嚗?
**云端生产环境**
```bash
# SAE<EFBFBD><EFBFBD><EFBFBD>?-> <20><EFBFBD><E887AC><EFBFBD><E3979B>滨蔭
# SAE控制台 -> 环境变量配置
DATABASE_URL=postgresql://user:password@rm-xxx.aliyuncs.com:5432/prod_db
DB_MAX_CONNECTIONS=400 # 阿里云RDS最大连接数
MAX_INSTANCES=20 # SAE最大实例数
```
**銝滚<EFBFBD>RDS閫<EFBFBD><EFBFBD><EFBFBD><EFBFBD><EFBFBD>交㺭**嚗?
| RDS閫<53>聢 | <20><>憭扯<E686AD><E689AF>交㺭 | 撱箄悅SAE摰硺<E691B0><E7A1BA>?| 瘥誩<E798A5>靘贝<E99D98><E8B49D>交㺭 |
**不同RDS规格的连接数**
| RDS规格 | 最大连接数 | 建议SAE实例数 | 每实例连接数 |
|---------|-----------|--------------|-------------|
| 2<EFBFBD>?GB | 200 | 10 | 20 |
| 4<EFBFBD>?GB | 400 | 20 | 20 |
| 8<EFBFBD>?6GB | 800 | 40 | 20 |
| 2核4GB | 200 | 10 | 20 |
| 4核8GB | 400 | 20 | 20 |
| 8核16GB | 800 | 40 | 20 |
---
### 监控数据库连接数
**摰墧𧒄<EFBFBD>亥砭餈墧𦻖<EFBFBD>?*嚗?
**实时查询连接数**
```typescript
// backend/src/common/monitoring/metrics.ts
import { prisma } from '@/config/database'
import { logger } from '@/common/logging'
export class DatabaseMetrics {
// <EFBFBD>亥砭敶枏<EFBFBD>餈墧𦻖<EFBFBD>? static async getConnectionCount(): Promise<number> {
// 查询当前连接数
static async getConnectionCount(): Promise<number> {
const result = await prisma.$queryRaw<Array<{ count: bigint }>>`
SELECT count(*) as count
FROM pg_stat_activity
@@ -560,12 +635,13 @@ export class DatabaseMetrics {
return Number(result[0].count)
}
// <EFBFBD>烐綉撟嗅<EFBFBD>霅? static async monitorConnections() {
// 监控并告警
static async monitorConnections() {
const currentConnections = await this.getConnectionCount()
const maxConnections = Number(process.env.DB_MAX_CONNECTIONS) || 400
const usagePercent = (currentConnections / maxConnections) * 100
logger.info('<EFBFBD>唳旿摨栞<EFBFBD><EFBFBD><EFBFBD><EFBFBD>?, {
logger.info('数据库连接监控', {
current: currentConnections,
max: maxConnections,
usage: `${usagePercent.toFixed(1)}%`
@@ -577,7 +653,7 @@ export class DatabaseMetrics {
current: currentConnections,
max: maxConnections,
usage: `${usagePercent.toFixed(1)}%`,
action: '𧼮<EFBFBD>RDS閫<EFBFBD><EFBFBD><EFBFBD>AE摰硺<EFBFBD><EFBFBD>?
action: '建议增加RDS规格或减少SAE实例数'
})
}
@@ -586,12 +662,14 @@ export class DatabaseMetrics {
}
```
**摰𡁏𧒄<EFBFBD>烐綉**嚗<><EFBFBD><EFBFBD>嚗?
**定时监控**(可选):
```typescript
// backend/src/index.ts
import { DatabaseMetrics } from '@/common/monitoring/metrics'
// 瘥?<3F><><EFBFBD><EFBFBD>烐綉銝<E7B689>甈?setInterval(async () => {
// 每5分钟监控一次
setInterval(async () => {
await DatabaseMetrics.monitorConnections()
}, 5 * 60 * 1000)
```
@@ -602,16 +680,21 @@ import { DatabaseMetrics } from '@/common/monitoring/metrics'
**问题1连接数耗尽**
**<EFBFBD><EFBFBD>𠶖**嚗?```
**症状**
```
Error: P1001: Can't reach database server
Error: remaining connection slots are reserved
```
**<EFBFBD><EFBFBD>**嚗?- SAE摰硺<E691B0><E7A1BA><EFBFBD>憭?- 瘥誩<E798A5>靘贝<E99D98><E8B49D>交㺭<E4BAA4>滨蔭餈<E894AD><E9A488>
**原因**
- SAE实例数过多
- 每实例连接数配置过高
- 存在连接泄漏
**<EFBFBD><EFBFBD><EFBFBD><EFBFBD>**嚗?```bash
# 1. <20><EFBFBD>敶枏<E695B6>餈墧𦻖<E5A2A7>?SELECT count(*) FROM pg_stat_activity
**解决方案**
```bash
# 1. 查看当前连接数
SELECT count(*) FROM pg_stat_activity
WHERE datname = 'ai_clinical_research';
# 2. 查看连接来源
@@ -622,24 +705,29 @@ GROUP BY client_addr;
# 3. 调整配置
# 方案A减少SAE最大实例数
MAX_INSTANCES=10 # 隞?0<>嫣蛹10
MAX_INSTANCES=10 # 从20改为10
# 方案B升级RDS规格
# 隞?<3F>?GB嚗?00餈墧𦻖嚗匧<E59A97>蝥批<E89DA5>4<EFBFBD>?GB嚗?00餈墧𦻖嚗?```
# 从2核4GB200连接升级到4核8GB400连接
```
---
**<EFBFBD><EFBFBD>2嚗朞<EFBFBD><EFBFBD><EFBFBD>瞍?*
**问题2连接泄漏**
**<EFBFBD><EFBFBD>𠶖**嚗?- 餈墧𦻖<E5A2A7><EFBFBD>蝏剖<E89D8F><E58996>?- <20>喃蝙瘚<E89D99><E7989A><EFBFBD><EFBFBD>嚗諹<E59A97><E8ABB9>交㺭銝滢<E98A9D><E6BBA2>?
**<2A>埝䰻**嚗?```typescript
// <20>?<3F>躰秤嚗𡁏<E59A97><E79488>撱箸鰵摰硺<E691B0>
**症状**
- 连接数持续增长
- 即使流量降低,连接数不下降
**排查**
```typescript
// ❌ 错误:每次创建新实例
function getUser() {
const prisma = new PrismaClient() // 连接泄漏
return prisma.user.findMany()
}
// <EFBFBD>?甇<>嚗帋蝙<E5B88B><EFBFBD><EFBFBD>摰硺<E691B0>
// ✅ 正确:使用全局实例
import { prisma } from '@/config/database'
function getUser() {
@@ -649,35 +737,43 @@ function getUser() {
---
### <EFBFBD><EFBFBD>雿喳<EFBFBD>頝?
**DO <20>?*嚗?1. <20>?雿輻鍂<E8BCBB><EFBFBD> `prisma` 摰硺<E691B0>
2. <20>?<3F>滨蔭 `SIGTERM` 隡㗛<E99AA1><E3979B>喲𡡒
3. <20>?摰𡁏<E691B0><F0A1818F>烐綉餈墧𦻖<E5A2A7>?4. <20>?霈曄蔭餈墧𦻖<E5A2A7><EFBFBD>霅佗<E99C85>80%<25><><EFBFBD><EFBFBD>
5. <20>?雿輻鍂餈墧𦻖瘙𩤃<E79899>Prisma暺䁅恕<E48185>舐鍂嚗?
**DON'T <20>?*嚗?1. <20>?瘥𤩺活霂瑟<E99C82><E7919F>啣遣 `PrismaClient`
2. <20>?銝滚<E98A9D><E6BB9A><EFBFBD><E5899B>亙停<E4BA99><E5819C><EFBFBD><EFBFBD>蝔?3. <20>?敹賜裦餈墧𦻖<E5A2A7><EFBFBD><E59581>?4. <20>?霈曄蔭餈<E894AD><EFBFBD>?`MAX_INSTANCES`
5. <20>?<3F><EFBFBD><E585B6>∩誨<E288A9><E8AAA8><EFBFBD>湔𦻖<E6B994><EFBFBD> `$disconnect()`
### 最佳实践
**DO ✅**
1. ✅ 使用全局 `prisma` 实例
2. ✅ 配置 `SIGTERM` 优雅关闭
3. ✅ 定期监控连接数
4. ✅ 设置连接数告警80%阈值)
5. ✅ 使用连接池Prisma默认启用
**DON'T ❌**
1. ❌ 每次请求新建 `PrismaClient`
2. ❌ 不关闭连接就退出进程
3. ❌ 忽略连接数监控
4. ❌ 设置过大的 `MAX_INSTANCES`
5. ❌ 在业务代码中直接执行 `$disconnect()`
---
## 📚 相关文档
- [平台基础设施规划](./04-平台基础设施规划.md) - 完整的连接池设计
- [鈭穃<EFBFBD><EFBFBD><EFBFBD><EFBFBD>𤏸<EFBFBD><EFBFBD><EFBFBD>(../04-撘<><E69298>𤏸<EFBFBD><F0A48FB8>?08-鈭穃<E988AD><E7A983><EFBFBD><E7AC94>𤏸<EFBFBD><F0A48FB8>?md) - <20>唳旿摨㮖蝙<E3AE96><EFBFBD><E588BB>?- [<5B><EFBFBD><E887AC>滨蔭<E6BBA8><E894AD><EFBFBD>](../07-餈鞟輕<E99E9F><E8BC95>﹝/01-<2D><EFBFBD><E887AC>滨蔭<E6BBA8><E894AD><EFBFBD>.md) - <20><EFBFBD><E887AC><EFBFBD><E3979B>滨蔭
- [Schema<EFBFBD>𠉛氖<EFBFBD><EFBFBD>霈曇恣](../00-蝟餌<E89D9F><E9A48C><EFBFBD>霈曇恣/03-<2D>唳旿摨𤘪沲<F0A498AA><E6B2B2><EFBFBD>?md)
- [云原生开发规范](../04-开发规范/08-云原生开发规范.md) - 数据库使用规范
- [环境配置指南](../07-运维文档/01-环境配置指南.md) - 环境变量配置
- [Schema隔离架构设计](../00-系统总体设计/03-数据库架构说明.md)
---
## 🔄 更新记录
| <EFBFBD><EFBFBD> | <20>湔鰵<E6B994><E9B0B5>捆 | <20>湔鰵鈭?|
| 日期 | 更新内容 | 更新人 |
|------|---------|--------|
| 2025-11-09 | 初始文档创建 | 架构团队 |
| 2025-11-16 | 新增云原生连接池配置章节 | 架构团队 |
---
**<EFBFBD><EFBFBD><EFBFBD><EFBFBD>𧋦嚗?* v1.0
**文档版本:** v1.0
**最后更新:** 2025-11-09
**维护者:** 架构团队