Files
AIclinicalresearch/docs/05-部署文档/PostgreSQL部署策略-摸底报告.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

1372 lines
41 KiB
Markdown
Raw Blame History

This file contains invisible Unicode characters
This file contains invisible Unicode characters that are indistinguishable to humans but may be processed differently by a computer. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
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.
# PostgreSQL 15 数据库部署策略 - 摸底报告
> **文档版本:** v1.1
> **创建日期:** 2025-12-14
> **最后更新:** 2025-12-14
> **数据库版本:** PostgreSQL 15.14 (Docker: postgres:15-alpine)
> **目标环境:** 阿里云 RDS PostgreSQL 15
> **报告类型:** 技术摸底 + 部署策略
---
## 📝 版本修订记录
### v1.1 (2025-12-14) - 专业建议修正版
**修正要点:**
1. **✅ pg-boss表"自愈"机制澄清**
- 修正pg-boss会在应用启动时自动创建表只要权限够
- 降低严重性:从"严重差异❌"改为"自愈机制✅(无需担心)"
- 增加说明pg-boss的智能自愈能力和权限要求
2. **✅ 白名单配置强化说明**
- 新增必须使用VPC网段不能用单机IP
- 示例172.16.0.0/12SAE的VPC网段
- 原因SAE实例IP会变化单机IP会导致连接失败
- 位置RDS准备、网络安全、下一步行动多处强调
3. **✅ 备份策略简化(更务实)**
- 修正初期只需RDS自动备份含日志备份
- 降级pg_dump ECS脚本从"第三道防线"降为"可选(业务做大后再考虑)"
- 理由RDS自动备份+PITR已足够省心省力
- 保留:脚本示例作为参考,标注"⚠️ 仅供参考,初期不需要"
**贡献者:** 项目技术团队反馈
---
---
## 📋 目录
1. [本地数据库真实情况](#本地数据库真实情况)
2. [Prisma与数据库的差异分析](#prisma与数据库的差异分析)
3. [代码如何连接数据库](#代码如何连接数据库)
4. [首次部署方案](#首次部署方案)
5. [未来更新策略](#未来更新策略)
6. [RDS备份策略](#rds备份策略)
7. [最佳实践与禁止操作](#最佳实践与禁止操作)
---
## 本地数据库真实情况
### 1. 基础信息
```bash
Docker镜像postgres:15-alpine
数据库名称ai_clinical_research
连接信息postgresql://postgres:postgres@localhost:5432/ai_clinical_research
数据库大小26 MB测试/开发环境)
用户数据3个用户账号
```
### 2. Schema隔离架构10个Schema
你的数据库已经成功实施了10个Schema隔离架构
| # | Schema名称 | 表数量 | 状态 | 说明 |
|---|-----------|--------|------|------|
| 1 | `platform_schema` | 8个表 | ✅ 已实施 | 平台基础设施用户、pg-boss队列 |
| 2 | `aia_schema` | 5个表 | ✅ 已实施 | AI智能问答项目、对话、消息 |
| 3 | `pkb_schema` | 5个表 | ✅ 已实施 | 个人知识库(知识库、文档、批处理) |
| 4 | `asl_schema` | 6个表 | ✅ 已实施 | AI智能文献文献筛选 |
| 5 | `dc_schema` | 6个表 | ✅ 已实施 | 数据清洗模板、提取任务、Tool C |
| 6 | `admin_schema` | 0个表 | 📋 空Schema | 运营管理(预留) |
| 7 | `rvw_schema` | 0个表 | 📋 空Schema | 审稿系统(预留) |
| 8 | `ssa_schema` | 0个表 | 📋 空Schema | 智能统计分析(预留) |
| 9 | `st_schema` | 0个表 | 📋 空Schema | 统计分析工具(预留) |
| 10 | `common_schema` | 0个表 | 📋 空Schema | 通用能力层(预留) |
| 11 | `public` | 4个表 | ⚠️ 旧表遗留 | _prisma_migrations, admin_logs, review_tasks, users |
**总计34个表30个在隔离Schema中4个在public中**
### 3. 详细表清单
#### 3.1 platform_schema8个表
```
✅ users - 用户表3条记录
✅ app_cache - Postgres-Only缓存替代Redis
✅ job - pg-boss任务表
✅ job_common - pg-boss任务通用表
✅ queue - pg-boss队列表
✅ schedule - pg-boss定时任务表
✅ subscription - pg-boss订阅表
✅ version - pg-boss版本表
```
**⚠️ 重要发现pg-boss的6个表job/queue等是自动创建的不在Prisma Schema中**
#### 3.2 aia_schema5个表
```
✅ projects - 项目管理2条记录
✅ conversations - 项目对话
✅ messages - 对话消息
✅ general_conversations - 通用对话
✅ general_messages - 通用消息
```
#### 3.3 pkb_schema5个表
```
✅ knowledge_bases - 知识库2条记录
✅ documents - 文档
✅ batch_tasks - 批处理任务
✅ batch_results - 批处理结果976 KB
✅ task_templates - 任务模板
```
#### 3.4 asl_schema6个表
```
✅ screening_projects - 文献筛选项目18条记录
✅ literatures - 文献数据2.9 MB
✅ screening_results - 筛选结果1.2 MB
✅ screening_tasks - 筛选任务
✅ fulltext_screening_tasks - 全文筛选任务
✅ fulltext_screening_results - 全文筛选结果
```
#### 3.5 dc_schema6个表
```
✅ dc_health_checks - 健康检查
✅ dc_templates - 数据清洗模板4条记录424 KB
✅ dc_extraction_tasks - 提取任务728 KB
✅ dc_extraction_items - 提取项6.5 MB最大表
✅ dc_tool_c_sessions - Tool C会话960 KB
✅ dc_tool_c_ai_history - Tool C AI历史1 MB
```
#### 3.6 public schema4个表
```
⚠️ _prisma_migrations - Prisma迁移记录6条记录
⚠️ admin_logs - 管理日志(遗留)
⚠️ review_tasks - 审查任务632 KB遗留
⚠️ users - 用户表遗留与platform_schema.users重复
```
**说明:**
- `public.users` 是Schema迁移前的旧表`platform_schema.users` 结构完全相同
- `admin_logs``review_tasks` 应该迁移到 `admin_schema``rvw_schema`
- `_prisma_migrations` 应该保留在 `public`这是Prisma的标准位置
### 4. 数据量统计Top 15大表
| Schema | 表名 | 大小 | 说明 |
|--------|------|------|------|
| dc_schema | dc_extraction_items | 6.5 MB | 数据清洗提取项(最大表) |
| asl_schema | literatures | 2.9 MB | 文献数据 |
| asl_schema | screening_results | 1.2 MB | 筛选结果 |
| dc_schema | dc_tool_c_ai_history | 1 MB | Tool C AI历史 |
| pkb_schema | batch_results | 976 KB | 批处理结果 |
| dc_schema | dc_tool_c_sessions | 960 KB | Tool C会话 |
| dc_schema | dc_extraction_tasks | 728 KB | 提取任务 |
| public | review_tasks | 632 KB | 审查任务(遗留) |
| dc_schema | dc_templates | 424 KB | 数据清洗模板 |
| pkb_schema | documents | 296 KB | 文档 |
| aia_schema | general_messages | 208 KB | 通用消息 |
| asl_schema | screening_projects | 144 KB | 筛选项目 |
| aia_schema | conversations | 112 KB | 对话 |
| pkb_schema | batch_tasks | 112 KB | 批处理任务 |
| aia_schema | messages | 104 KB | 消息 |
**总结:数据主要集中在 DC数据清洗和 ASL文献筛选模块。**
### 5. Prisma迁移历史
```sql
-- 6次Prisma迁移记录按时间顺序
20251010075003_init -- 2025-10-12初始化
20251010122727_add_conversation_metadata -- 2025-10-12对话元数据
20251012124747_add_batch_processing_module -- 2025-10-12批处理模块
20251014120128_add_review_tasks -- 2025-10-14审查任务
20251127_add_dc_tool_b_tables -- 2025-11-27DC Tool B
20251208_add_column_mapping -- 2025-12-08列映射
```
**说明:**
- 最早的迁移init创建的是 `public` schema 中的表
- 后续通过手工SQL迁移到了10个隔离Schema
- Prisma并没有记录Schema迁移过程这些是手工SQL完成的
---
## Prisma与数据库的差异分析
### 1. pg-boss表的"自愈"机制✅(无需担心)
**现象:**
`platform_schema` 有 8 个表,但 Prisma Schema 只定义了 2 个:
- ✅ Prisma定义`AppCache`, `User`
- 🔧 pg-boss自动管理`job`, `job_common`, `queue`, `schedule`, `subscription`, `version`共6个表
**原因:**
这6个表是 `pg-boss` 库在**应用启动时自动创建**的用于Postgres-Only任务队列不需要在Prisma Schema中定义。
**pg-boss的"自愈"能力:**
```typescript
// backend启动时pg-boss会自动检查表是否存在
const boss = new PgBoss(process.env.DATABASE_URL)
await boss.start()
// ✅ 如果表不存在pg-boss会自动创建
// ✅ 只要数据库用户如aiclinical_rw有 CREATE TABLE 权限
// ✅ 完全不需要手工干预
```
**影响评估:**
-**运行时零影响**pg-boss自己管理这些表不通过Prisma访问
-**首次部署自动创建**RDS部署时backend启动会自动创建这6个表
- ⚠️ **Prisma db pull会检测到**:执行 `npx prisma db pull` 时会发现"未定义的表"(可忽略)
- ⚠️ **Prisma migrate不操作**:迁移不会同步这些表(这是正确的)
**最佳实践:**
```prisma
// backend/prisma/schema.prisma
// 在文件开头添加注释(文档用途)
// ==================== pg-boss 自动管理的表(不需要定义) ====================
// 以下6个表由pg-boss库自动创建和维护请勿手工修改
// - platform_schema.job - 任务表
// - platform_schema.job_common - 任务通用配置
// - platform_schema.queue - 队列表
// - platform_schema.schedule - 定时任务表
// - platform_schema.subscription - 订阅表
// - platform_schema.version - 版本表
// ==================== 以上表自动管理无需Prisma定义 ====================
```
**结论pg-boss表"缺失"是正常的,无需担心!**
### 2. public schema遗留表⚠需要清理
**问题:**
`public` schema 有 4 个表:
- `_prisma_migrations`Prisma标准表正确✅
- `users`:与 `platform_schema.users` 重复❌
- `admin_logs`:应迁移到 `admin_schema`
- `review_tasks`:应迁移到 `rvw_schema`
**影响:**
- ⚠️ 数据不一致风险(如果代码误读 `public.users`
- ⚠️ 混淆(两个 `users` 表)
- ⚠️ 空间浪费(重复数据)
**解决方案:**
```sql
-- 步骤1迁移 admin_logs 到 admin_schema
ALTER TABLE public.admin_logs SET SCHEMA admin_schema;
-- 步骤2迁移 review_tasks 到 rvw_schema
ALTER TABLE public.review_tasks SET SCHEMA rvw_schema;
-- 步骤3验证 platform_schema.users 和 public.users 数据一致后删除
-- (先在生产环境验证一周,确保代码不再引用 public.users
DROP TABLE public.users;
```
### 3. Prisma Schema的定义状态
| Schema | Prisma定义的模型 | 实际数据库表 | 状态 |
|--------|-----------------|-------------|------|
| platform_schema | 2个AppCache, User | 8个+6个pg-boss自动管理 | ✅ 正常pg-boss自愈 |
| aia_schema | 5个 | 5个 | ✅ 一致 |
| pkb_schema | 5个 | 5个 | ✅ 一致 |
| asl_schema | 6个 | 6个 | ✅ 一致 |
| dc_schema | 6个 | 6个 | ✅ 一致 |
| public | 2个AdminLog, ReviewTask | 4个+_prisma_migrations, users | ⚠️ 需清理 |
**结论:**
-**Prisma Schema 完全准确**pg-boss的6个表会自动创建不需要定义
- ⚠️ **public schema需要清理**:遗留了旧的 `users` 表(首次部署后清理)
---
## 代码如何连接数据库
### 1. 环境变量配置
#### 1.1 默认连接字符串env.ts
```typescript
// AIclinicalresearch/backend/src/config/env.ts (line 50)
databaseUrl: process.env.DATABASE_URL || 'postgresql://postgres:postgres@localhost:5432/ai_clinical'
```
**⚠️ 注意:**
- 默认值是 `ai_clinical`(不存在)
- 实际数据库名是 `ai_clinical_research`
- 说明你的 `.env` 文件中已经正确配置了 `DATABASE_URL`
#### 1.2 连接池配置database.ts
```typescript
// AIclinicalresearch/backend/src/config/database.ts
export const prisma = new PrismaClient({
datasources: {
db: {
url: process.env.DATABASE_URL, // 从环境变量读取
},
},
})
// 连接池计算公式
connectionLimit = Math.floor(RDS_MAX_CONNECTIONS / MAX_INSTANCES) - 10
// 示例400 / 20 - 10 = 10个连接/实例
```
**云原生连接池策略:**
```
DATABASE_URL=postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=10
```
### 2. 连接方式
| 连接参数 | 本地开发 | 阿里云SAE |
|---------|---------|----------|
| **主机** | localhost | RDS内网地址如 rm-xxxxx.pg.rds.aliyuncs.com |
| **端口** | 5432 | 5432 |
| **数据库** | ai_clinical_research | ai_clinical_research保持一致 |
| **用户名** | postgres | 自定义(如 aiclinical_rw |
| **密码** | postgres | 强密码RDS创建时设置 |
| **连接限制** | 无限制 | connection_limit=10SAE每实例 |
| **连接超时** | 默认 | pool_timeout=10 |
### 3. 多Schema访问Prisma
```prisma
// AIclinicalresearch/backend/prisma/schema.prisma
datasource db {
provider = "postgresql"
url = env("DATABASE_URL")
schemas = ["platform_schema", "aia_schema", "pkb_schema", "asl_schema",
"common_schema", "dc_schema", "rvw_schema", "admin_schema",
"ssa_schema", "st_schema", "public"]
}
generator client {
provider = "prisma-client-js"
previewFeatures = ["multiSchema"] // 启用多Schema支持
}
```
**代码中的使用:**
```typescript
// 自动路由到正确的Schema
await prisma.user.findMany() // 访问 platform_schema.users
await prisma.project.findMany() // 访问 aia_schema.projects
```
### 4. 原生SQL访问跨Schema
```typescript
// 支持跨Schema查询
const result = await prisma.$queryRaw`
SELECT u.name, p.name AS project_name
FROM platform_schema.users u
JOIN aia_schema.projects p ON p.user_id = u.id
`
```
### 5. 优雅关闭机制
```typescript
// AIclinicalresearch/backend/src/config/database.ts
process.on('SIGTERM', () => gracefulShutdown('SIGTERM')) // SAE实例停止
process.on('SIGINT', () => gracefulShutdown('SIGINT')) // Ctrl+C
async function gracefulShutdown(signal: string): Promise<void> {
await prisma.$disconnect() // 关闭所有连接
process.exit(0)
}
```
**这是云原生最佳实践**防止SAE扩容/缩容时连接泄漏。
---
## 首次部署方案
### 方案对比
| 方案 | 方法 | 优点 | 缺点 | 推荐度 |
|------|------|------|------|--------|
| **方案A** | `pg_dump` 全量导入 | ✅ 100%完整包括pg-boss表<br>✅ 结构+数据+索引+外键<br>✅ 一次性完成 | ❌ 需要手工清理测试数据<br>❌ 包含遗留的public表 | ⭐⭐⭐⭐⭐ **强烈推荐** |
| **方案B** | Prisma Migrate Deploy | ✅ 版本化管理<br>✅ 可重复执行 | ❌ 缺少pg-boss表运行时会报错<br>❌ 需要手工补充 | ⚠️ **不推荐**(不完整) |
| **方案C** | 手工SQL脚本 | ✅ 完全可控 | ❌ 工作量大<br>❌ 容易出错 | ⚠️ **不推荐**(费时费力) |
### ⭐ 推荐方案Apg_dump全量导入详细步骤
#### 步骤1本地数据库清理可选
```bash
# 如果本地有测试数据,可以选择清理
docker exec -it ai-clinical-postgres psql -U postgres -d ai_clinical_research
# 删除测试用户(保留真实用户)
DELETE FROM platform_schema.users WHERE email LIKE '%test%';
# 清理遗留的 public.usersSchema迁移后的旧表
DROP TABLE IF EXISTS public.users;
```
#### 步骤2导出数据库包含结构+数据)
```bash
# 完整导出包括所有Schema、表、数据、索引、外键
docker exec ai-clinical-postgres pg_dump -U postgres -d ai_clinical_research \
--format=plain \
--no-owner \
--no-acl \
--encoding=UTF8 \
> D:\MyCursor\ai_clinical_research_backup_$(Get-Date -Format 'yyyyMMdd_HHmmss').sql
# 文件大小应该在 100-200 KB当前数据量26MB
```
**参数说明:**
- `--format=plain`纯文本SQL方便查看和编辑
- `--no-owner`不导出所有者信息避免RDS用户名不匹配
- `--no-acl`不导出权限信息使用RDS默认权限
- `--encoding=UTF8`UTF-8编码中文支持
#### 步骤3RDS PostgreSQL 15 准备
```bash
# 1. 在阿里云控制台创建RDS PostgreSQL 15实例
规格rds.pg.s2.large2核4GB入门级
存储100GB SSD支持自动扩容
版本PostgreSQL 15
网络VPC与SAE在同一VPC
白名单:⚠️ 必须配置VPC网段不能用单机IP
- 示例172.16.0.0/12SAE的VPC网段
- 查看方式SAE控制台 > 应用详情 > 网络配置 > VPC网段
- ❌ 错误示例172.16.1.23单机IPSAE实例IP会变化
# 2. 创建数据库
CREATE DATABASE ai_clinical_research
WITH ENCODING='UTF8'
LC_COLLATE='en_US.UTF-8'
LC_CTYPE='en_US.UTF-8'
TEMPLATE=template0;
# 3. 创建应用用户(不要用超级用户)
CREATE USER aiclinical_rw WITH PASSWORD '你的强密码';
GRANT ALL PRIVILEGES ON DATABASE ai_clinical_research TO aiclinical_rw;
GRANT ALL ON SCHEMA public TO aiclinical_rw;
```
#### 步骤4导入到RDS
```bash
# 方法1从本地直接导入需要RDS公网地址
psql -h rm-xxxxx.pg.rds.aliyuncs.com \
-p 5432 \
-U aiclinical_rw \
-d ai_clinical_research \
-f ai_clinical_research_backup_20251214_150000.sql
# 方法2通过ECS跳板机导入推荐更安全
scp backup.sql root@your-ecs-ip:/tmp/
ssh root@your-ecs-ip
psql -h rm-xxxxx.pg.rds.aliyuncs.com -U aiclinical_rw -d ai_clinical_research -f /tmp/backup.sql
```
#### 步骤5验证导入结果
```sql
-- 1. 验证Schema数量
SELECT nspname FROM pg_namespace WHERE nspname LIKE '%_schema' ORDER BY nspname;
-- 应该有10个Schema
-- 2. 验证表数量
SELECT schemaname, COUNT(*) as table_count
FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
GROUP BY schemaname
ORDER BY schemaname;
-- platform_schema: 8, aia_schema: 5, pkb_schema: 5, asl_schema: 6, dc_schema: 6
-- 3. 验证数据量
SELECT COUNT(*) FROM platform_schema.users; -- 应该有3条或你保留的数量
SELECT COUNT(*) FROM aia_schema.projects; -- 应该有2条
SELECT COUNT(*) FROM asl_schema.literatures; -- 应该有对应数量
-- 4. 验证pg-boss表关键
SELECT tablename FROM pg_tables WHERE schemaname = 'platform_schema' ORDER BY tablename;
-- 应该包括job, job_common, queue, schedule, subscription, version
-- 5. 验证外键约束
SELECT conname, conrelid::regclass, confrelid::regclass
FROM pg_constraint
WHERE contype = 'f'
LIMIT 10;
```
#### 步骤6配置SAE环境变量
```bash
# 在SAE应用配置中添加
DATABASE_URL=postgresql://aiclinical_rw:强密码@rm-xxxxx.pg.rds.aliyuncs.com:5432/ai_clinical_research?connection_limit=10&pool_timeout=10&connect_timeout=10
# 其他相关配置
DB_MAX_CONNECTIONS=400 # RDS最大连接数
MAX_INSTANCES=20 # SAE最大实例数
NODE_ENV=production
```
#### 步骤7应用启动验证
```typescript
// backend启动时会自动执行
npm run build
npm run start
// 日志输出应该显示:
// ✅ 数据库连接成功!
// 📊 数据库版本: PostgreSQL 15.x
// 📊 当前数据库连接数: 1
```
#### 步骤8Prisma Client生成重要
```bash
# 在backend目录下
cd backend
npx prisma generate
# 验证生成的Client
ls node_modules/.prisma/client/
# 应该包含所有Schema的类型定义
```
### ⚠️ 方案B为什么不推荐Prisma Migrate的局限
```bash
# 如果执行 prisma migrate deploy
cd backend
npx prisma migrate deploy
# 问题1缺少 pg-boss 表
# prisma migrate deploy 只会创建 Prisma Schema 中定义的表
# 不会创建 pg-boss 的 6 个表job, queue等
# 问题2应用启动时报错
# Error: relation "platform_schema.job" does not exist
# 因为代码中使用了 pg-boss但表不存在
# 问题3需要手工补救
# 必须手工执行 pg-boss 的初始化SQL
# 非常繁琐且容易出错
```
**结论:`pg_dump` 全量导入是首次部署的最佳方案!**
---
## 未来更新策略
### 场景1新增一个表或修改一个字段小更新
#### 推荐方案Prisma Migrate版本化管理
```bash
# 步骤1在本地开发环境修改 schema.prisma
# 示例:为 User 表添加一个字段
model User {
id String @id @default(uuid())
email String @unique
// ... 其他字段 ...
phoneNumber String? @map("phone_number") // 新增字段
@@map("users")
@@schema("platform_schema")
}
# 步骤2创建迁移本地
cd backend
npx prisma migrate dev --name add_user_phone_number
# 生成的迁移文件示例:
# backend/prisma/migrations/20251214120000_add_user_phone_number/migration.sql
ALTER TABLE "platform_schema"."users" ADD COLUMN "phone_number" TEXT;
# 步骤3测试本地迁移
npm run dev
# 验证新字段可用
# 步骤4提交到Git
git add backend/prisma/migrations/20251214120000_add_user_phone_number
git commit -m "feat: add phone_number to User model"
git push
# 步骤5部署到SAE自动执行迁移
# 在 SAE 的应用启动命令中添加:
# CMD ["sh", "-c", "npx prisma migrate deploy && node dist/index.js"]
# ⚠️ 注意Dockerfile 的 CMD 应该是:
CMD ["sh", "-c", "node dist/index.js"]
# 不要在生产启动时执行 migrate deploy风险太大
# 步骤6手工执行迁移推荐
# 方法1通过ECS跳板机执行
ssh root@your-ecs-ip
psql -h rm-xxxxx.pg.rds.aliyuncs.com -U aiclinical_rw -d ai_clinical_research \
-f migrations/20251214120000_add_user_phone_number/migration.sql
# 方法2使用 RDS 控制台的 SQL 窗口
# 直接粘贴迁移SQL执行
# 步骤7验证
SELECT column_name, data_type
FROM information_schema.columns
WHERE table_schema = 'platform_schema'
AND table_name = 'users'
AND column_name = 'phone_number';
```
**为什么不推荐在启动命令中执行 `prisma migrate deploy`**
1. **并发风险**SAE有多个实例可能同时执行迁移
2. **回滚困难**:如果迁移失败,应用已经启动
3. **数据安全**:生产数据库应该手工迁移,有备份和回滚计划
### 场景2新增一个大模块如SSA智能统计分析
#### 推荐方案:结构化迁移流程
```bash
# 步骤1在本地开发环境设计Schema
# 示例SSA智能统计分析模块
# backend/prisma/schema.prisma
// SSA Schema新增
model SsaAnalysisTask {
id String @id @default(uuid())
userId String @map("user_id")
projectId String @map("project_id")
taskName String @map("task_name")
status String @default("pending")
createdAt DateTime @default(now()) @map("created_at")
updatedAt DateTime @updatedAt @map("updated_at")
user User @relation(fields: [userId], references: [id])
results SsaAnalysisResult[]
@@map("analysis_tasks")
@@schema("ssa_schema")
}
model SsaAnalysisResult {
id String @id @default(uuid())
taskId String @map("task_id")
resultType String @map("result_type")
resultData Json @map("result_data")
createdAt DateTime @default(now()) @map("created_at")
task SsaAnalysisTask @relation(fields: [taskId], references: [id])
@@map("analysis_results")
@@schema("ssa_schema")
}
# 步骤2创建迁移
npx prisma migrate dev --name add_ssa_module
# 步骤3验证迁移SQL
# backend/prisma/migrations/20251214_add_ssa_module/migration.sql
-- 检查:
-- 1. 是否在 ssa_schema 中创建表
-- 2. 外键是否正确引用 platform_schema.users
-- 3. 索引是否完整
# 步骤4本地测试
# 开发SSA模块的业务逻辑
# 确保所有CRUD操作正常
# 步骤5准备生产部署
# 5.1 数据库备份(强制)
# 在RDS控制台手动创建快照ai_clinical_research_before_ssa_module_20251214
# 5.2 准备回滚脚本
# backend/prisma/migrations/20251214_add_ssa_module/rollback.sql
DROP TABLE IF EXISTS ssa_schema.analysis_results CASCADE;
DROP TABLE IF EXISTS ssa_schema.analysis_tasks CASCADE;
# 步骤6生产环境迁移分步执行
# 6.1 停机窗口(可选,如果无法零停机)
# 晚上11点-凌晨2点用户最少
# 6.2 执行迁移SQL
psql -h rm-xxxxx.pg.rds.aliyuncs.com -U aiclinical_rw -d ai_clinical_research \
-f migrations/20251214_add_ssa_module/migration.sql
# 6.3 验证表结构
\dt ssa_schema.*
\d ssa_schema.analysis_tasks
\d ssa_schema.analysis_results
# 6.4 验证外键
SELECT conname, conrelid::regclass, confrelid::regclass
FROM pg_constraint
WHERE connamespace = 'ssa_schema'::regnamespace;
# 步骤7部署应用代码
# 7.1 更新backend
git pull
npm run build
# SAE自动部署或手动触发部署
# 7.2 健康检查
curl https://your-backend.sae.aliyuncs.com/health
# 应该返回 200 OK
# 步骤8烟雾测试Smoke Testing
# 8.1 测试SSA模块基本功能
POST /api/v1/ssa/analysis-tasks
GET /api/v1/ssa/analysis-tasks/:id
# 8.2 测试旧模块(回归测试)
GET /api/v1/aia/projects
GET /api/v1/pkb/knowledge-bases
# 确保旧功能不受影响
# 步骤9监控24小时
# 9.1 RDS监控
# - 连接数是否正常
# - CPU/内存使用率
# - 慢查询日志
# 9.2 SAE监控
# - 应用日志(错误数量)
# - API响应时间
# - 实例健康状态
# 步骤10如果失败立即回滚
# 10.1 回滚数据库
psql -h rm-xxxxx.pg.rds.aliyuncs.com -U aiclinical_rw -d ai_clinical_research \
-f migrations/20251214_add_ssa_module/rollback.sql
# 10.2 回滚应用代码
# 在SAE控制台选择上一个版本点击"回滚"
# 10.3 恢复RDS快照如果必要
# 在RDS控制台恢复快照ai_clinical_research_before_ssa_module_20251214
```
### 场景3紧急修复Hotfix
```bash
# 示例production环境发现某个字段长度不够
# 步骤1本地创建紧急迁移
# backend/prisma/schema.prisma
model User {
// 将 email 字段长度从 varchar(255) 改为 text
email String @unique // Prisma默认是text无需修改
// ...
}
# 步骤2生成迁移
npx prisma migrate dev --name hotfix_user_email_length
# 步骤3测试迁移SQL本地
# 确保不会丢失数据
# 步骤4生产环境执行快速通道
# 4.1 备份(快照)
# RDS控制台 > 备份 > 立即备份 > 备注hotfix_before_email_length
# 4.2 执行SQL通过SQL窗口秒级完成
ALTER TABLE platform_schema.users ALTER COLUMN email TYPE TEXT;
# 4.3 验证
SELECT column_name, data_type, character_maximum_length
FROM information_schema.columns
WHERE table_schema = 'platform_schema'
AND table_name = 'users'
AND column_name = 'email';
# 4.4 提交Git
git add backend/prisma/migrations/20251214_hotfix_user_email_length
git commit -m "hotfix: increase user email column length"
git push
```
---
## RDS备份策略
### 1. 阿里云RDS自动备份强烈推荐
#### 1.1 配置自动备份
```bash
# 在RDS控制台配置
备份设置 > 备份策略
# 推荐配置:
数据备份保留时间7天免费额度
日志备份保留时间7天
备份周期:每天一次
备份时间凌晨2:00-4:00业务低峰期
备份方式:物理备份(快,占用空间小)
```
**优势:**
- ✅ 全自动,无需人工干预
- ✅ 时间点恢复Point-in-Time RecoveryPITR可恢复到任意秒
- ✅ 存储在OSS独立于RDS实例
- ✅ 免费7天内
#### 1.2 手动快照(重要操作前)
```bash
# 场景:
# - 大版本升级前
# - 数据库Schema重大变更前
# - 删除大量数据前
# 操作:
RDS控制台 > 备份恢复 > 备份实例 > 创建备份
备份方式:物理备份
备注migration_before_ssa_module_20251214
```
**快照保留建议:**
- 迁移前快照保留30天
- 版本升级前快照保留60天
- 季度归档快照保留1年
### 2. 逻辑备份(`pg_dump`- 可选策略(业务做大后再考虑)
**⚠️ 初期建议不需要自己写脚本RDS自动备份就够了**
| 阶段 | 备份策略 | 理由 |
|------|---------|------|
| **初期(当前)** | ✅ 只用RDS自动备份 | - 自动备份+日志备份=PITR任意时间点恢复<br>- 阿里云托管,稳定可靠<br>- 无需维护脚本和ECS |
| **成长期** | 可选:增加异地灾备 | - 业务关键后,考虑多地域备份<br>- 使用RDS的"跨地域备份"功能(一键配置) |
| **成熟期** | 可选:自定义备份 | - 需要特殊备份策略(如每小时备份)<br>- 需要长期归档如5年 |
**如果未来需要自定义备份,参考脚本:**
```bash
# ⚠️ 仅供参考,初期不需要执行
#!/bin/bash
DATE=$(date +%Y%m%d_%H%M%S)
BACKUP_DIR="/data/pg_backups"
RDS_HOST="rm-xxxxx.pg.rds.aliyuncs.com"
RDS_USER="aiclinical_rw"
RDS_DB="ai_clinical_research"
# 导出SQL
pg_dump -h $RDS_HOST -U $RDS_USER -d $RDS_DB \
--format=plain \
--no-owner \
--no-acl \
--encoding=UTF8 \
--file=$BACKUP_DIR/ai_clinical_research_$DATE.sql
# 压缩并上传到OSS
gzip $BACKUP_DIR/ai_clinical_research_$DATE.sql
ossutil cp $BACKUP_DIR/ai_clinical_research_$DATE.sql.gz \
oss://your-bucket/database-backups/
# 清理本地备份
ls -t $BACKUP_DIR/*.sql.gz | tail -n +4 | xargs rm -f
```
**总结初期专注RDS自动备份省心省力**
### 3. 恢复演练(每季度一次)
```bash
# 目的:验证备份可用,熟悉恢复流程
# 步骤1创建测试RDS实例
# RDS控制台 > 创建实例 > 按备份创建
# 选择最新的自动备份或快照
# 步骤2验证数据完整性
psql -h test-rm-xxxxx.pg.rds.aliyuncs.com -U aiclinical_rw -d ai_clinical_research
# 检查:
SELECT schemaname, COUNT(*) FROM pg_tables
WHERE schemaname NOT IN ('pg_catalog', 'information_schema')
GROUP BY schemaname;
SELECT COUNT(*) FROM platform_schema.users;
# 步骤3记录恢复时间
# 26MB数据库完整恢复约5-10分钟
# 步骤4删除测试实例
```
### 4. 备份监控与告警
```bash
# 在阿里云监控配置告警规则
# 规则1备份失败告警
资源RDS实例
指标:备份任务状态
条件:备份失败
通知:钉钉群 + 邮件
# 规则2备份空间不足告警
资源RDS实例
指标:备份使用空间
条件:>80%
通知:邮件
# 规则3超过24小时未备份告警
资源RDS实例
指标:最后备份时间
条件:>24小时
通知:紧急(短信+电话)
```
---
## 最佳实践与禁止操作
### ✅ 最佳实践11条黄金法则
#### 1. 连接池管理
```typescript
// ✅ 正确:在 DATABASE_URL 中配置连接限制
DATABASE_URL=postgresql://user:pass@host:5432/db?connection_limit=10&pool_timeout=10
// ✅ 正确根据SAE实例数动态计算
connectionLimit = Math.floor(400 / 20) - 10 = 10
// ❌ 错误不配置连接限制会耗尽RDS连接
DATABASE_URL=postgresql://user:pass@host:5432/db
```
#### 2. 优雅关闭
```typescript
// ✅ 正确:监听 SIGTERM 信号
process.on('SIGTERM', async () => {
await prisma.$disconnect()
process.exit(0)
})
// ❌ 错误不关闭连接SAE缩容时连接泄漏
```
#### 3. 事务管理
```typescript
// ✅ 正确使用Prisma事务
await prisma.$transaction(async (tx) => {
await tx.user.create({ data: { email: 'test@example.com' } })
await tx.project.create({ data: { userId: 'xxx', name: 'Test' } })
})
// ❌ 错误手动管理事务容易忘记COMMIT
await prisma.$executeRaw`BEGIN`
// ... 多个操作 ...
await prisma.$executeRaw`COMMIT` // 如果中间出错,不会回滚
```
#### 4. Schema隔离访问
```typescript
// ✅ 正确Prisma自动路由到正确的Schema
await prisma.user.findMany() // 自动访问 platform_schema.users
// ✅ 正确跨Schema查询外键关系
await prisma.user.findUnique({
where: { id: 'xxx' },
include: { projects: true } // 自动JOIN aia_schema.projects
})
// ❌ 错误手写Schema名称Prisma不推荐
await prisma.$queryRaw`SELECT * FROM platform_schema.users` // 可以工作,但类型不安全
```
#### 5. 索引优化
```sql
-- ✅ 正确:为频繁查询的字段创建索引
CREATE INDEX idx_users_email ON platform_schema.users(email);
CREATE INDEX idx_projects_user_id ON aia_schema.projects(user_id);
-- ✅ 正确为外键创建索引Prisma不自动创建
CREATE INDEX idx_conversations_project_id ON aia_schema.conversations(project_id);
-- ❌ 错误:创建过多索引(影响写入性能)
-- 只为频繁查询的字段创建索引
```
#### 6. 迁移测试
```bash
# ✅ 正确:先在测试环境验证
# 测试环境 > 预发环境 > 生产环境
# ✅ 正确:准备回滚脚本
# 每个迁移都有对应的rollback.sql
# ❌ 错误:直接在生产环境执行未测试的迁移
```
#### 7. 备份验证
```bash
# ✅ 正确:每季度恢复演练
# 确保备份可用
# ✅ 正确:重要操作前手动快照
# 大版本升级、Schema变更前
# ❌ 错误:从不验证备份(备份失效时才发现)
```
#### 8. 监控告警
```bash
# ✅ 正确:监控关键指标
- RDS连接数告警阈值80%
- 慢查询(告警阈值:>1秒
- 死锁(告警阈值:>0
- CPU使用率告警阈值>70%
# ❌ 错误:不监控(问题发生后才发现)
```
#### 9. 密码管理
```bash
# ✅ 正确使用强密码16位+大小写+数字+符号)
DATABASE_URL=postgresql://user:Abc123!@#XYZ456@host:5432/db
# ✅ 正确定期轮换密码每6个月
# ❌ 错误使用弱密码postgres/123456
```
#### 10. 网络安全
```bash
# ✅ 正确RDS只允许VPC内网访问
# 白名单配置必须使用VPC网段不能用单机IP
白名单示例172.16.0.0/12SAE的VPC网段
获取方式SAE控制台 > 应用详情 > 网络配置 > VPC网段
# ✅ 正确:不开放公网访问(除非临时调试)
# ❌ 错误1白名单配置 0.0.0.0/0全世界可访问
# ❌ 错误2白名单配置单机IP 172.16.1.23SAE实例IP会变化导致连接失败
# ❌ 错误3配置多个单机IP维护困难且无法应对SAE弹性扩容
```
#### 11. pg-boss表管理
```typescript
// ✅ 正确不要在Prisma Schema中定义pg-boss表
// pg-boss自动管理不需要手工定义
// ✅ 正确不要手工修改pg-boss表
// 可能导致任务队列异常
// ❌ 错误删除pg-boss表
// DROP TABLE platform_schema.job; // 会导致应用崩溃
```
#### 12. 时区统一配置 ⭐⭐⭐⭐⭐
```sql
-- ✅ 正确RDS时区配置为 Asia/Shanghai
-- RDS控制台 > 参数设置 > timezone
timezone = Asia/Shanghai
-- ✅ 验证时区
SHOW timezone;
-- 应该显示Asia/Shanghai
-- ❌ 错误使用UTC时区与应用时区不一致
-- 后果:
-- 1. 日志时间对不上前端14:00数据库06:00
-- 2. pg-boss定时任务在错误时间触发
-- 3. 用户看到的时间戳错误
```
**配置步骤:**
```bash
# 步骤1登录RDS控制台
# 阿里云 > 云数据库RDS > PostgreSQL实例
# 步骤2修改参数
# 实例详情 > 参数设置 > 搜索 "timezone"
# 修改为Asia/Shanghai
# 步骤3重启实例参数修改需要重启
# 实例详情 > 重启实例
# 步骤4验证
psql -h rm-xxxxx.pg.rds.aliyuncs.com -U aiclinical_rw -d ai_clinical_research
SHOW timezone;
# 应该显示Asia/Shanghai
```
**与应用时区保持一致:**
```bash
# Node.js后端ENV TZ=Asia/Shanghai
# Python微服务ENV TZ=Asia/Shanghai
# 前端NginxENV TZ=Asia/Shanghai
# RDS PostgreSQLtimezone = Asia/Shanghai
# ✅ 所有服务时区统一,避免时间混乱
```
---
### ❌ 绝对禁止的操作10条红线
#### 🚫 1. 禁止在生产环境直接执行 `DROP TABLE`
```sql
-- ❌ 绝对禁止
DROP TABLE platform_schema.users; -- 丢失所有用户数据,无法恢复
-- ✅ 正确做法先备份再重命名观察7天后删除
ALTER TABLE platform_schema.users RENAME TO users_deprecated_20251214;
-- 7天后确认无问题再删除
DROP TABLE platform_schema.users_deprecated_20251214;
```
#### 🚫 2. 禁止在生产环境执行未测试的迁移
```bash
# ❌ 绝对禁止
psql -h production-rds -f untested_migration.sql # 可能破坏数据
# ✅ 正确做法:测试环境 > 预发环境 > 生产环境
```
#### 🚫 3. 禁止不备份就执行重大变更
```sql
-- ❌ 绝对禁止
ALTER TABLE platform_schema.users DROP COLUMN email; -- 没有备份
-- ✅ 正确做法:先创建快照
-- RDS控制台 > 备份实例 > 创建备份
-- 然后再执行变更
```
#### 🚫 4. 禁止在应用代码中硬编码数据库密码
```typescript
// ❌ 绝对禁止
const prisma = new PrismaClient({
datasources: {
db: {
url: 'postgresql://user:password@host:5432/db' // 硬编码
}
}
})
// ✅ 正确做法:使用环境变量
const prisma = new PrismaClient({
datasources: {
db: {
url: process.env.DATABASE_URL // 环境变量
}
}
})
```
#### 🚫 5. 禁止不配置连接池限制
```bash
# ❌ 绝对禁止
DATABASE_URL=postgresql://user:pass@host:5432/db # 无限制
# ✅ 正确做法
DATABASE_URL=postgresql://user:pass@host:5432/db?connection_limit=10
```
#### 🚫 6. 禁止在生产环境使用 `prisma migrate dev`
```bash
# ❌ 绝对禁止
npx prisma migrate dev # 会删除数据重建
# ✅ 正确做法
npx prisma migrate deploy # 只应用新迁移,不删除数据
```
#### 🚫 7. 禁止在生产环境启用Prisma的 `query` 日志
```typescript
// ❌ 绝对禁止(生产环境)
export const prisma = new PrismaClient({
log: ['query', 'info', 'warn', 'error'] // 性能严重下降
})
// ✅ 正确做法(生产环境)
export const prisma = new PrismaClient({
log: process.env.NODE_ENV === 'production' ? ['error'] : ['query', 'info', 'warn', 'error']
})
```
#### 🚫 8. 禁止手工修改 `_prisma_migrations` 表
```sql
-- ❌ 绝对禁止
DELETE FROM _prisma_migrations WHERE migration_name = 'xxx'; -- 破坏迁移历史
-- ✅ 正确做法:如果迁移出错,回滚并修复
-- 不要手工修改 _prisma_migrations
```
#### 🚫 9. 禁止在生产环境使用超级用户postgres
```bash
# ❌ 绝对禁止
DATABASE_URL=postgresql://postgres:password@host:5432/db # 权限过大
# ✅ 正确做法:创建应用专用用户
CREATE USER aiclinical_rw WITH PASSWORD 'xxx';
GRANT SELECT, INSERT, UPDATE, DELETE ON ALL TABLES IN SCHEMA platform_schema TO aiclinical_rw;
```
#### 🚫 10. 禁止删除pg-boss的表
```sql
-- ❌ 绝对禁止
DROP TABLE platform_schema.job; -- 应用立即崩溃
DROP TABLE platform_schema.queue; -- 任务队列失效
-- ✅ 正确做法永远不要手工操作pg-boss表
-- 如果需要清理使用pg-boss的API
```
---
## 📝 总结与建议
### 1. 你的数据库现状(非常好)✅
| 维度 | 状态 | 评分 |
|------|------|------|
| **Schema隔离架构** | ✅ 10个Schema全部就位 | ⭐⭐⭐⭐⭐ |
| **Postgres-Only架构** | ✅ pg-boss正常工作支持自愈 | ⭐⭐⭐⭐⭐ |
| **数据完整性** | ✅ 外键约束完整 | ⭐⭐⭐⭐⭐ |
| **Prisma Schema** | ✅ 完全准确pg-boss表会自动创建 | ⭐⭐⭐⭐⭐ |
| **数据量** | ✅ 26MB适合迁移 | ⭐⭐⭐⭐⭐ |
| **遗留问题** | ⚠️ public schema有旧表首次部署后清理 | ⭐⭐⭐⭐ |
**总评你的数据库架构非常优秀可以直接部署到RDS**
### 2. 首次部署推荐方案
```
🏆 强烈推荐pg_dump 全量导入
理由:
✅ 100%完整包括pg-boss表
✅ 结构+数据+索引+外键一次性完成
✅ 风险低,可重复执行
✅ 恢复快26MB约30秒
时间估算:
- 导出1分钟
- 传输到ECS1分钟
- 导入RDS3分钟
- 验证2分钟
总计:<10分钟
```
### 3. 未来更新推荐方案
| 更新类型 | 推荐方案 | 工具 |
|---------|---------|------|
| 小更新(新增字段) | Prisma Migrate | `npx prisma migrate dev` |
| 中更新(新增表) | Prisma Migrate | `npx prisma migrate dev` |
| 大更新(新模块) | 结构化流程 | Prisma + 手工SQL + 备份 |
| 紧急修复Hotfix | 快速通道 | 直接SQL先备份 |
### 4. 备份策略(初期简化版)
```
第一道防线RDS自动备份必做⭐⭐⭐⭐⭐
├─ 数据备份每天凌晨2点
├─ 日志备份实时开启PITR
└─ 保留7天
第二道防线:手动快照(必做)⭐⭐⭐⭐
└─ 重大操作前创建
第三道防线pg_dump脚本初期不需要
└─ 业务做大后再考虑
恢复演练:每季度一次(必做)
```
**初期只需配置RDS自动备份省心省力**
### 5. 清理遗留问题(可选)
```sql
-- 建议在首次部署到RDS后清理
-- 1. 迁移 admin_logs 到 admin_schema
ALTER TABLE public.admin_logs SET SCHEMA admin_schema;
-- 2. 迁移 review_tasks 到 rvw_schema
ALTER TABLE public.review_tasks SET SCHEMA rvw_schema;
-- 3. 删除重复的 public.users验证后
-- 先对比数据一致性
SELECT COUNT(*) FROM public.users;
SELECT COUNT(*) FROM platform_schema.users;
-- 一致后删除
DROP TABLE public.users;
-- 4. 更新Prisma Schema
model AdminLog {
// ...
@@map("admin_logs")
@@schema("admin_schema") // public admin_schema
}
model ReviewTask {
// ...
@@map("review_tasks")
@@schema("rvw_schema") // public rvw_schema
}
```
### 6. 下一步行动
```
☐ 1. 创建阿里云RDS PostgreSQL 15实例
☐ 2. 配置VPC、白名单用VPC网段、备份策略自动备份+日志备份)
☐ 3. 本地清理测试数据(可选)
☐ 4. pg_dump导出数据库
☐ 5. 通过ECS跳板机导入RDS
☐ 6. 验证导入结果Schema、表、数据、pg-boss
☐ 7. 配置SAE环境变量DATABASE_URL
☐ 8. 部署backend应用
☐ 9. 端到端测试
☐ 10. 监控24小时
预计时间3-4小时从0到上线
```
---
**文档创建人:** AI助手
**最后更新:** 2025-12-14
**版本:** v1.0
**核心理念:数据安全第一,充分备份,渐进式迁移,完整验证** ⭐⭐⭐