Files
AIclinicalresearch/docs/09-架构实施/02-数据库连接配置.md
HaHafeng 66255368b7 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
2026-01-16 13:42:10 +08:00

789 lines
17 KiB
Markdown
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 数据库连接配置
> **文档版本:** v1.0
> **创建日期:** 2025-11-09
> **维护者:** 架构团队
> **最后更新:** 2025-11-09
---
## 📋 文档说明
本文档记录Schema隔离架构实施相关的数据库连接和配置信息包括
- 当前数据库状态
- Schema隔离迁移配置
- 迁移过程的连接方式
- 验证和回滚方案
---
## 🗄️ 当前数据库状态
### 数据库基本信息
**连接信息:**
```
数据库类型PostgreSQL 15+
数据库名称ai_clinical_research
主机地址: localhost
端口: 5432
用户名: postgres
密码: postgres
```
**完整连接字符串:**
```
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ai_clinical_research
```
---
### 当前Schema结构
**现有Schema**
-**public** - 所有13个表都在这里
**表清单13个**
#### 1. 平台基础2个表
```sql
public.users -- 用户表
public.admin_logs -- 管理日志
```
#### 2. AI智能问答5个表
```sql
public.projects -- 项目管理
public.conversations -- 项目对话
public.messages -- 对话消息
public.general_conversations -- 通用对话
public.general_messages -- 通用消息
```
#### 3. 个人知识库2个表
```sql
public.knowledge_bases -- 知识库
public.documents -- 文档
```
#### 4. 批处理系统3个表
```sql
public.batch_tasks -- 批处理任务
public.batch_results -- 批处理结果
public.task_templates -- 任务模板
```
#### 5. 稿件审查1个表
```sql
public.review_tasks -- 审查任务
```
---
### 数据库统计信息
**表数量:** 13个
**索引数量:** 约40个
**外键约束:** 约15个
**当前数据量(估算):**
- users: ~10条
- projects: ~20条
- conversations: ~50条
- messages: ~500条
- knowledge_bases: ~5条
- documents: ~30条
- 其他表:测试数据
**总数据量:** < 10MB测试环境
---
## 🎯 Schema隔离目标架构
### 10个独立Schema
#### 需要详细设计+数据迁移5个
**1. platform_schema平台基础层**
```sql
-- 迁移表:
public.users platform_schema.users
public.admin_logs platform_schema.admin_logs
```
**2. common_schema通用能力层**
```sql
-- 新建表:
common_schema.llm_usage -- LLM使用记录
common_schema.feature_flags -- Feature Flags
```
**3. asl_schemaAI智能文献**
```sql
-- 新建表:
asl_schema.literature_projects -- 文献项目
asl_schema.pico_configs -- PICO配置
asl_schema.literature_items -- 文献条目
-- 更多表见ASL设计文档
```
**4. aia_schemaAI智能问答**
```sql
-- 迁移表:
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个人知识库**
```sql
-- 迁移表:
public.knowledge_bases pkb_schema.knowledge_bases
public.documents pkb_schema.documents
```
#### 只创建空Schema5个
**6. dc_schema数据清洗** - 暂无表结构
**7. rvw_schema审稿系统** - 暂无表结构
**8. admin_schema运营管理** - 暂无表结构
**9. ssa_schema智能统计分析** - 暂无表结构
**10. st_schema统计分析工具** - 暂无表结构
---
## 🔄 迁移过程连接配置
### 迁移前准备
**1. 备份当前数据库**
```bash
# Windows PowerShell
cd D:\MyCursor\AIclinicalresearch\backend
# 使用pg_dump备份
pg_dump -h localhost -U postgres -d ai_clinical_research -F c -f backup_before_schema_migration_$(Get-Date -Format 'yyyyMMdd_HHmmss').dump
# 或者使用SQL格式
pg_dump -h localhost -U postgres -d ai_clinical_research > backup_before_schema_migration_$(Get-Date -Format 'yyyyMMdd_HHmmss').sql
```
**2. 验证备份**
```bash
# 检查备份文件大小
ls -lh backup_*.dump
# 验证备份内容SQL格式
head -n 50 backup_*.sql
```
---
### 迁移过程连接方式
#### 方式1使用Prisma Migrate推荐
**连接配置:**
```env
# backend/.env
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ai_clinical_research
```
**执行迁移:**
```bash
cd backend
# 创建新的迁移
npx prisma migrate dev --name schema_isolation_10_schemas
# 查看迁移状态
npx prisma migrate status
```
---
#### 方式2直接执行SQL脚本
**连接方式:**
```bash
# 使用psql连接
psql -h localhost -U postgres -d ai_clinical_research
# 或使用环境变量
export PGPASSWORD=postgres
psql -h localhost -U postgres -d ai_clinical_research -f migration_script.sql
```
**执行迁移脚本:**
```bash
cd docs/09-架构实施/migration-scripts
# 按顺序执行
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
# ... 更多脚本
```
---
#### 方式3使用可视化工具
**pgAdmin 4**
1. 连接信息:
- 主机localhost
- 端口5432
- 数据库ai_clinical_research
- 用户postgres
- 密码postgres
2. 执行迁移:
- 打开Query Tool
- 加载SQL脚本
- 执行
**DataGrip / DBeaver**
- 同样的连接信息
- 支持事务管理
- 方便回滚
---
## ✅ 迁移验证
### 1. 检查Schema创建
**SQL验证**
```sql
-- 查看所有Schema
SELECT schema_name
FROM information_schema.schemata
WHERE schema_name NOT IN ('pg_catalog', 'information_schema')
ORDER BY schema_name;
-- 预期结果:
-- admin_schema
-- aia_schema
-- asl_schema
-- common_schema
-- dc_schema
-- pkb_schema
-- platform_schema
-- public
-- rvw_schema
-- ssa_schema
-- st_schema
```
---
### 2. 检查表迁移
**验证platform_schema**
```sql
-- 查看表
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'platform_schema';
-- 验证数据
SELECT COUNT(*) FROM platform_schema.users;
-- 预期与原public.users的数量一致
```
**验证aia_schema**
```sql
-- 查看所有表
SELECT table_name
FROM information_schema.tables
WHERE table_schema = 'aia_schema';
-- 验证数据完整性
SELECT COUNT(*) FROM aia_schema.projects;
SELECT COUNT(*) FROM aia_schema.conversations;
SELECT COUNT(*) FROM aia_schema.messages;
```
**验证pkb_schema**
```sql
SELECT COUNT(*) FROM pkb_schema.knowledge_bases;
SELECT COUNT(*) FROM pkb_schema.documents;
```
---
### 3. 验证外键关系
**检查跨Schema外键**
```sql
-- 查看所有外键约束
SELECT
tc.table_schema,
tc.table_name,
kcu.column_name,
ccu.table_schema AS foreign_table_schema,
ccu.table_name AS foreign_table_name,
ccu.column_name AS foreign_column_name
FROM information_schema.table_constraints AS tc
JOIN information_schema.key_column_usage AS kcu
ON tc.constraint_name = kcu.constraint_name
JOIN information_schema.constraint_column_usage AS ccu
ON ccu.constraint_name = tc.constraint_name
WHERE tc.constraint_type = 'FOREIGN KEY'
AND tc.table_schema IN ('platform_schema', 'aia_schema', 'pkb_schema', 'asl_schema', 'common_schema')
ORDER BY tc.table_schema, tc.table_name;
```
---
### 4. 验证索引
**检查索引是否正确迁移:**
```sql
-- 查看某个Schema的所有索引
SELECT
schemaname,
tablename,
indexname,
indexdef
FROM pg_indexes
WHERE schemaname = 'aia_schema'
ORDER BY tablename, indexname;
```
---
### 5. Prisma验证
**更新Prisma配置后验证**
```bash
cd backend
# 生成Prisma Client
npx prisma generate
# 验证Prisma Client可用
npx ts-node -e "import { PrismaClient } from '@prisma/client'; const p = new PrismaClient(); p.user.count().then(console.log)"
```
---
## 🔙 回滚方案
### 场景1迁移过程中发现问题
**立即回滚:**
```bash
# 如果使用Prisma Migrate
npx prisma migrate reset
# 恢复备份
psql -h localhost -U postgres -d ai_clinical_research < backup_before_schema_migration_YYYYMMDD_HHMMSS.sql
```
---
### 场景2迁移完成后发现问题
**方案A从备份恢复推荐**
```bash
# 1. 删除当前数据库
dropdb -h localhost -U postgres ai_clinical_research
# 2. 重新创建
createdb -h localhost -U postgres ai_clinical_research
# 3. 恢复备份
pg_restore -h localhost -U postgres -d ai_clinical_research backup_before_schema_migration_YYYYMMDD_HHMMSS.dump
```
**方案B手动删除新Schema保留public**
```sql
-- 删除新建的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;
DROP SCHEMA IF EXISTS common_schema CASCADE;
DROP SCHEMA IF EXISTS dc_schema CASCADE;
DROP SCHEMA IF EXISTS rvw_schema CASCADE;
DROP SCHEMA IF EXISTS admin_schema CASCADE;
DROP SCHEMA IF EXISTS ssa_schema CASCADE;
DROP SCHEMA IF EXISTS st_schema CASCADE;
-- public schema的表如果被删除了从备份恢复
```
---
## 📊 迁移检查清单
### 迁移前检查
- [ ] ✅ 数据库已备份backup_before_schema_migration_*.dump
- [ ] ✅ 备份文件已验证
- [ ] ✅ Prisma配置已更新datasource.schemas
- [ ] ✅ 迁移脚本已准备001-010.sql
- [ ] ✅ 测试环境已验证迁移步骤
---
### 迁移中检查
- [ ] ✅ 10个Schema创建成功
- [ ] ✅ 表迁移无错误5个有数据的Schema
- [ ] ✅ 数据完整性验证通过
- [ ] ✅ 外键约束正确
- [ ] ✅ 索引正确创建
---
### 迁移后检查
- [ ] ✅ Prisma Client生成成功
- [ ] ✅ 后端服务启动正常
- [ ] ✅ 现有功能测试通过AIA、PKB
- [ ] ✅ API调用正常
- [ ] ✅ 前端功能正常
---
## 🔧 常见问题
### 问题1Schema创建失败
**错误:**
```
ERROR: schema "platform_schema" already exists
```
**解决:**
```sql
-- 检查Schema是否存在
SELECT schema_name FROM information_schema.schemata
WHERE schema_name = 'platform_schema';
-- 如果存在但为空,可删除重建
DROP SCHEMA platform_schema CASCADE;
CREATE SCHEMA platform_schema;
```
---
### 问题2数据迁移失败
**错误:**
```
ERROR: duplicate key value violates unique constraint
```
**解决:**
1. 检查数据是否已部分迁移
2. 清理目标Schema
3. 重新执行迁移
---
### 问题3外键约束错误
**错误:**
```
ERROR: foreign key constraint "fk_user_id" cannot be implemented
```
**解决:**
1. 确保被引用的表已先迁移
2. 检查外键字段类型匹配
3. 考虑先禁用外键,迁移后重建
---
## 🔧 云原生连接池配置2025-11-16 新增)
> **⭐ 重要更新**:为支持阿里云 Serverless 部署,新增连接池配置
> **详细文档**[平台基础设施规划](./04-平台基础设施规划.md)
### 背景:为什么需要连接池?
**问题场景**
```
阿里云 SAE 自动扩容:
- 初始1个实例10个连接
- 高峰100个实例1000个连接
- RDS最大连接数400 ❌ 超限!
结果:数据库连接耗尽,应用崩溃
```
**解决方案**:动态计算每实例连接数
```typescript
= RDS最大连接数 / SAE最大实例数
400 / 20 = 20/
```
---
### Prisma连接池配置
**文件位置**`backend/src/config/database.ts`
**配置代码**
```typescript
import { PrismaClient } from '@prisma/client'
// 动态计算连接数
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(`📊 数据库连接池配置:每实例最多${connectionLimit}个连接`)
// 创建全局Prisma Client
export const prisma = new PrismaClient({
datasources: {
db: {
url: process.env.DATABASE_URL,
},
},
log: process.env.NODE_ENV === 'development'
? ['query', 'error', 'warn']
: ['error'],
errorFormat: 'minimal',
})
// 优雅关闭连接
process.on('SIGTERM', async () => {
console.log('📊 正在关闭数据库连接...')
await prisma.$disconnect()
console.log('✅ 数据库连接已关闭')
process.exit(0)
})
process.on('SIGINT', async () => {
console.log('📊 正在关闭数据库连接...')
await prisma.$disconnect()
console.log('✅ 数据库连接已关闭')
process.exit(0)
})
// 启动时测试连接
prisma.$connect()
.then(() => console.log('✅ 数据库连接成功'))
.catch((err) => {
console.error('❌ 数据库连接失败:', err)
process.exit(1)
})
```
---
### 环境变量配置
**本地开发环境**
```bash
# backend/.env.development
DATABASE_URL=postgresql://postgres:postgres@localhost:5432/ai_clinical_research
# 本地开发无需配置连接池(单实例)
# DB_MAX_CONNECTIONS=N/A
# MAX_INSTANCES=N/A
```
**云端生产环境**
```bash
# SAE控制台 -> 环境变量配置
DATABASE_URL=postgresql://user:password@rm-xxx.aliyuncs.com:5432/prod_db
DB_MAX_CONNECTIONS=400 # 阿里云RDS最大连接数
MAX_INSTANCES=20 # SAE最大实例数
```
**不同RDS规格的连接数**
| RDS规格 | 最大连接数 | 建议SAE实例数 | 每实例连接数 |
|---------|-----------|--------------|-------------|
| 2核4GB | 200 | 10 | 20 |
| 4核8GB | 400 | 20 | 20 |
| 8核16GB | 800 | 40 | 20 |
---
### 监控数据库连接数
**实时查询连接数**
```typescript
// backend/src/common/monitoring/metrics.ts
import { prisma } from '@/config/database'
import { logger } from '@/common/logging'
export class DatabaseMetrics {
// 查询当前连接数
static async getConnectionCount(): Promise<number> {
const result = await prisma.$queryRaw<Array<{ count: bigint }>>`
SELECT count(*) as count
FROM pg_stat_activity
WHERE datname = current_database()
`
return Number(result[0].count)
}
// 监控并告警
static async monitorConnections() {
const currentConnections = await this.getConnectionCount()
const maxConnections = Number(process.env.DB_MAX_CONNECTIONS) || 400
const usagePercent = (currentConnections / maxConnections) * 100
logger.info('数据库连接监控', {
current: currentConnections,
max: maxConnections,
usage: `${usagePercent.toFixed(1)}%`
})
// 告警连接数超过80%
if (usagePercent > 80) {
logger.warn('⚠️ 数据库连接数告警', {
current: currentConnections,
max: maxConnections,
usage: `${usagePercent.toFixed(1)}%`,
action: '建议增加RDS规格或减少SAE实例数'
})
}
return { currentConnections, maxConnections, usagePercent }
}
}
```
**定时监控**(可选):
```typescript
// backend/src/index.ts
import { DatabaseMetrics } from '@/common/monitoring/metrics'
// 每5分钟监控一次
setInterval(async () => {
await DatabaseMetrics.monitorConnections()
}, 5 * 60 * 1000)
```
---
### 故障排查
**问题1连接数耗尽**
**症状**
```
Error: P1001: Can't reach database server
Error: remaining connection slots are reserved
```
**原因**
- SAE实例数过多
- 每实例连接数配置过高
- 存在连接泄漏
**解决方案**
```bash
# 1. 查看当前连接数
SELECT count(*) FROM pg_stat_activity
WHERE datname = 'ai_clinical_research';
# 2. 查看连接来源
SELECT client_addr, count(*)
FROM pg_stat_activity
WHERE datname = 'ai_clinical_research'
GROUP BY client_addr;
# 3. 调整配置
# 方案A减少SAE最大实例数
MAX_INSTANCES=10 # 从20改为10
# 方案B升级RDS规格
# 从2核4GB200连接升级到4核8GB400连接
```
---
**问题2连接泄漏**
**症状**
- 连接数持续增长
- 即使流量降低,连接数不下降
**排查**
```typescript
// ❌ 错误:每次创建新实例
function getUser() {
const prisma = new PrismaClient() // 连接泄漏
return prisma.user.findMany()
}
// ✅ 正确:使用全局实例
import { prisma } from '@/config/database'
function getUser() {
return prisma.user.findMany()
}
```
---
### 最佳实践
**DO ✅**
1. ✅ 使用全局 `prisma` 实例
2. ✅ 配置 `SIGTERM` 优雅关闭
3. ✅ 定期监控连接数
4. ✅ 设置连接数告警80%阈值)
5. ✅ 使用连接池Prisma默认启用
**DON'T ❌**
1. ❌ 每次请求新建 `PrismaClient`
2. ❌ 不关闭连接就退出进程
3. ❌ 忽略连接数监控
4. ❌ 设置过大的 `MAX_INSTANCES`
5. ❌ 在业务代码中直接执行 `$disconnect()`
---
## 📚 相关文档
- [平台基础设施规划](./04-平台基础设施规划.md) - 完整的连接池设计
- [云原生开发规范](../04-开发规范/08-云原生开发规范.md) - 数据库使用规范
- [环境配置指南](../07-运维文档/01-环境配置指南.md) - 环境变量配置
- [Schema隔离架构设计](../00-系统总体设计/03-数据库架构说明.md)
---
## 🔄 更新记录
| 日期 | 更新内容 | 更新人 |
|------|---------|--------|
| 2025-11-09 | 初始文档创建 | 架构团队 |
| 2025-11-16 | 新增云原生连接池配置章节 | 架构团队 |
---
**文档版本:** v1.0
**最后更新:** 2025-11-09
**维护者:** 架构团队