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
1372 lines
41 KiB
Markdown
1372 lines
41 KiB
Markdown
# 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/12(SAE的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_schema(8个表)
|
||
|
||
```
|
||
✅ 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_schema(5个表)
|
||
|
||
```
|
||
✅ projects - 项目管理(2条记录)
|
||
✅ conversations - 项目对话
|
||
✅ messages - 对话消息
|
||
✅ general_conversations - 通用对话
|
||
✅ general_messages - 通用消息
|
||
```
|
||
|
||
#### 3.3 pkb_schema(5个表)
|
||
|
||
```
|
||
✅ knowledge_bases - 知识库(2条记录)
|
||
✅ documents - 文档
|
||
✅ batch_tasks - 批处理任务
|
||
✅ batch_results - 批处理结果(976 KB)
|
||
✅ task_templates - 任务模板
|
||
```
|
||
|
||
#### 3.4 asl_schema(6个表)
|
||
|
||
```
|
||
✅ screening_projects - 文献筛选项目(18条记录)
|
||
✅ literatures - 文献数据(2.9 MB)
|
||
✅ screening_results - 筛选结果(1.2 MB)
|
||
✅ screening_tasks - 筛选任务
|
||
✅ fulltext_screening_tasks - 全文筛选任务
|
||
✅ fulltext_screening_results - 全文筛选结果
|
||
```
|
||
|
||
#### 3.5 dc_schema(6个表)
|
||
|
||
```
|
||
✅ 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 schema(4个表)⚠️
|
||
|
||
```
|
||
⚠️ _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-27(DC 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=10(SAE每实例) |
|
||
| **连接超时** | 默认 | 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>❌ 容易出错 | ⚠️ **不推荐**(费时费力) |
|
||
|
||
### ⭐ 推荐方案A:pg_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.users(Schema迁移后的旧表)
|
||
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编码(中文支持)
|
||
|
||
#### 步骤3:RDS PostgreSQL 15 准备
|
||
|
||
```bash
|
||
# 1. 在阿里云控制台创建RDS PostgreSQL 15实例
|
||
规格:rds.pg.s2.large(2核4GB,入门级)
|
||
存储:100GB SSD(支持自动扩容)
|
||
版本:PostgreSQL 15
|
||
网络:VPC(与SAE在同一VPC)
|
||
白名单:⚠️ 必须配置VPC网段,不能用单机IP!
|
||
- 示例:172.16.0.0/12(SAE的VPC网段)
|
||
- 查看方式:SAE控制台 > 应用详情 > 网络配置 > VPC网段
|
||
- ❌ 错误示例:172.16.1.23(单机IP,SAE实例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
|
||
```
|
||
|
||
#### 步骤8:Prisma 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 Recovery,PITR),可恢复到任意秒
|
||
- ✅ 存储在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/12(SAE的VPC网段)
|
||
获取方式:SAE控制台 > 应用详情 > 网络配置 > VPC网段
|
||
|
||
# ✅ 正确:不开放公网访问(除非临时调试)
|
||
|
||
# ❌ 错误1:白名单配置 0.0.0.0/0(全世界可访问)
|
||
# ❌ 错误2:白名单配置单机IP 172.16.1.23(SAE实例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
|
||
# 前端Nginx:ENV TZ=Asia/Shanghai
|
||
# RDS PostgreSQL:timezone = 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分钟
|
||
- 传输到ECS:1分钟
|
||
- 导入RDS:3分钟
|
||
- 验证: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
|
||
|
||
**核心理念:数据安全第一,充分备份,渐进式迁移,完整验证** ⭐⭐⭐
|
||
|