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
1068 lines
23 KiB
Markdown
1068 lines
23 KiB
Markdown
# Schema隔离方案与成本分析
|
||
|
||
> **文档版本:** v1.0
|
||
> **创建日期:** 2025-11-06
|
||
> **最后更新:** 2025-11-06
|
||
> **文档状态:** 架构分析
|
||
> **作者:** 技术架构师
|
||
|
||
---
|
||
|
||
## 📋 核心问题
|
||
|
||
1. 什么是真正的Schema隔离?
|
||
2. 从逻辑隔离到物理隔离的改造成本有多高?
|
||
3. 现在做Schema隔离的成本高吗?
|
||
4. 当前业务体量下,需要现在做吗?
|
||
5. 最佳实施时机是什么时候?
|
||
|
||
---
|
||
|
||
## 🎯 两种隔离方式对比
|
||
|
||
### 逻辑隔离(当前方案)
|
||
|
||
**定义:**
|
||
- 所有表都在同一个Schema(`public`)中
|
||
- 通过**表名前缀**来区分不同模块
|
||
- 代码层面按模块组织
|
||
|
||
**示例:**
|
||
```sql
|
||
-- 所有表都在public schema
|
||
public.users -- 用户表
|
||
public.aia_projects -- AI问答模块的项目表
|
||
public.aia_conversations -- AI问答模块的对话表
|
||
public.asl_projects -- AI文献模块的项目表
|
||
public.asl_literature_items -- AI文献模块的文献表
|
||
public.pkb_knowledge_bases -- 知识库模块的知识库表
|
||
public.dc_projects -- 数据清洗模块的项目表
|
||
public.review_tasks -- 稿件审查模块的任务表
|
||
```
|
||
|
||
**Prisma Schema示例:**
|
||
```prisma
|
||
// 逻辑隔离:使用@@map指定表名
|
||
model AiaProject {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
name String
|
||
// ...
|
||
|
||
@@map("aia_projects") // 表名前缀标识模块
|
||
}
|
||
|
||
model AslProject {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
name String
|
||
// ...
|
||
|
||
@@map("asl_projects") // 表名前缀标识模块
|
||
}
|
||
```
|
||
|
||
**查询方式:**
|
||
```typescript
|
||
// 业务代码无感知
|
||
const project = await prisma.aiaProject.findUnique({
|
||
where: { id: projectId }
|
||
});
|
||
```
|
||
|
||
---
|
||
|
||
### 物理隔离(真正的Schema隔离)
|
||
|
||
**定义:**
|
||
- 为每个模块创建**独立的PostgreSQL Schema**
|
||
- 表分散在不同的Schema中
|
||
- 数据库层面真正隔离
|
||
|
||
**示例:**
|
||
```sql
|
||
-- 创建独立Schema
|
||
CREATE SCHEMA platform_schema; -- 平台层
|
||
CREATE SCHEMA aia_schema; -- AI问答模块
|
||
CREATE SCHEMA asl_schema; -- AI文献模块
|
||
CREATE SCHEMA pkb_schema; -- 知识库模块
|
||
CREATE SCHEMA dc_schema; -- 数据清洗模块
|
||
CREATE SCHEMA review_schema; -- 稿件审查模块
|
||
CREATE SCHEMA admin_schema; -- 运营管理端
|
||
|
||
-- 表分布在不同Schema
|
||
platform_schema.users -- 用户表
|
||
aia_schema.projects -- AI问答的项目表
|
||
aia_schema.conversations -- AI问答的对话表
|
||
asl_schema.projects -- AI文献的项目表
|
||
asl_schema.literature_items -- AI文献的文献表
|
||
pkb_schema.knowledge_bases -- 知识库表
|
||
dc_schema.projects -- 数据清洗的项目表
|
||
review_schema.tasks -- 稿件审查的任务表
|
||
```
|
||
|
||
**Prisma Schema示例:**
|
||
```prisma
|
||
// 物理隔离:指定schema
|
||
model AiaProject {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
name String
|
||
// ...
|
||
|
||
@@map("projects") // 表名不需要前缀
|
||
@@schema("aia_schema") // 指定Schema ⭐
|
||
}
|
||
|
||
model AslProject {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
name String
|
||
// ...
|
||
|
||
@@map("projects") // 表名不需要前缀
|
||
@@schema("asl_schema") // 指定Schema ⭐
|
||
}
|
||
```
|
||
|
||
**查询方式:**
|
||
```typescript
|
||
// 业务代码无感知(Prisma会自动处理)
|
||
const project = await prisma.aiaProject.findUnique({
|
||
where: { id: projectId }
|
||
});
|
||
|
||
// 实际执行的SQL:
|
||
// SELECT * FROM aia_schema.projects WHERE id = $1
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 两种方案对比
|
||
|
||
| 维度 | 逻辑隔离(当前) | 物理隔离(目标) |
|
||
|------|----------------|----------------|
|
||
| **复杂度** | ⭐ 简单 | ⭐⭐⭐ 中等 |
|
||
| **实施难度** | ⭐ 低 | ⭐⭐⭐ 中等 |
|
||
| **维护成本** | ⭐⭐ 低 | ⭐⭐⭐ 中等 |
|
||
| **隔离性** | ⭐⭐ 中等(代码层面) | ⭐⭐⭐⭐⭐ 高(数据库层面) |
|
||
| **权限控制** | ⭐⭐ 中等 | ⭐⭐⭐⭐⭐ 高(数据库级别) |
|
||
| **微服务拆分** | ⭐⭐⭐ 需要改造 | ⭐⭐⭐⭐⭐ 易于拆分 |
|
||
| **模块独立部署** | ⭐⭐ 困难 | ⭐⭐⭐⭐⭐ 容易 |
|
||
| **跨模块查询** | ⭐⭐⭐⭐⭐ 容易(同一Schema) | ⭐⭐⭐ 需要跨Schema查询 |
|
||
| **备份恢复** | ⭐⭐⭐ 整体备份 | ⭐⭐⭐⭐⭐ 可按Schema备份 |
|
||
| **当前适用性** | ✅ 适合当前阶段 | ⚠️ 未来阶段 |
|
||
|
||
---
|
||
|
||
## 💰 改造成本分析
|
||
|
||
### 从逻辑隔离到物理隔离需要改什么?
|
||
|
||
#### 1. 数据库层面改造
|
||
|
||
**步骤1:创建Schema**
|
||
```sql
|
||
-- 创建新Schema(非常简单)
|
||
CREATE SCHEMA platform_schema;
|
||
CREATE SCHEMA aia_schema;
|
||
CREATE SCHEMA asl_schema;
|
||
CREATE SCHEMA pkb_schema;
|
||
CREATE SCHEMA dc_schema;
|
||
CREATE SCHEMA review_schema;
|
||
CREATE SCHEMA admin_schema;
|
||
```
|
||
|
||
**工作量:** 10分钟
|
||
|
||
---
|
||
|
||
**步骤2:创建新表结构**
|
||
```sql
|
||
-- 在新Schema中创建表
|
||
CREATE TABLE aia_schema.projects (
|
||
id UUID PRIMARY KEY DEFAULT gen_random_uuid(),
|
||
user_id UUID NOT NULL,
|
||
name VARCHAR(255) NOT NULL,
|
||
-- ... 其他字段
|
||
|
||
-- 外键可能需要跨Schema
|
||
CONSTRAINT fk_user FOREIGN KEY (user_id)
|
||
REFERENCES platform_schema.users(id)
|
||
);
|
||
|
||
-- 类似的,为所有表创建新的表结构
|
||
```
|
||
|
||
**工作量:**
|
||
- 自动生成(Prisma Migrate):1-2小时
|
||
- 手动创建:1天
|
||
|
||
---
|
||
|
||
**步骤3:数据迁移**
|
||
```sql
|
||
-- 迁移数据(从public到新Schema)
|
||
INSERT INTO aia_schema.projects
|
||
SELECT * FROM public.aia_projects;
|
||
|
||
INSERT INTO aia_schema.conversations
|
||
SELECT * FROM public.aia_conversations;
|
||
|
||
-- ... 为所有表迁移数据
|
||
```
|
||
|
||
**工作量:**
|
||
- 数据量小(<100万行):1-2小时
|
||
- 数据量大(>100万行):半天到1天
|
||
|
||
**风险:**
|
||
- ⚠️ 需要停机(或做在线迁移)
|
||
- ⚠️ 需要验证数据完整性
|
||
- ⚠️ 外键约束可能有问题
|
||
|
||
---
|
||
|
||
**步骤4:清理旧表**
|
||
```sql
|
||
-- 删除旧表(确认无误后)
|
||
DROP TABLE public.aia_projects;
|
||
DROP TABLE public.aia_conversations;
|
||
-- ...
|
||
```
|
||
|
||
**工作量:** 1小时
|
||
|
||
**总计数据库改造工作量:**
|
||
- 自动化:半天到1天
|
||
- 手动:2-3天
|
||
|
||
---
|
||
|
||
#### 2. 代码层面改造
|
||
|
||
**步骤1:修改Prisma Schema**
|
||
|
||
**逻辑隔离(当前):**
|
||
```prisma
|
||
model AiaProject {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
name String
|
||
|
||
@@map("aia_projects") // 需要前缀
|
||
}
|
||
```
|
||
|
||
**物理隔离(修改后):**
|
||
```prisma
|
||
model AiaProject {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
name String
|
||
|
||
@@map("projects") // 不需要前缀
|
||
@@schema("aia_schema") // 新增:指定Schema ⭐
|
||
}
|
||
```
|
||
|
||
**工作量:**
|
||
- 修改所有Model(约50-100个Model)
|
||
- 时间:2-4小时
|
||
|
||
---
|
||
|
||
**步骤2:处理外键和关联**
|
||
|
||
**问题:跨Schema的外键**
|
||
```prisma
|
||
// User在platform_schema
|
||
model User {
|
||
id String @id @default(uuid())
|
||
email String
|
||
|
||
@@map("users")
|
||
@@schema("platform_schema")
|
||
}
|
||
|
||
// Project在aia_schema
|
||
model AiaProject {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
|
||
// 跨Schema关联 ⚠️
|
||
user User @relation(fields: [userId], references: [id])
|
||
|
||
@@map("projects")
|
||
@@schema("aia_schema")
|
||
}
|
||
```
|
||
|
||
**Prisma处理:**
|
||
- ✅ Prisma会自动处理跨Schema的关联
|
||
- ✅ 生成的SQL会包含正确的Schema前缀
|
||
- ⚠️ 但需要测试验证
|
||
|
||
**工作量:**
|
||
- 测试所有跨Schema关联:半天到1天
|
||
|
||
---
|
||
|
||
**步骤3:业务代码改造**
|
||
|
||
**好消息:业务代码几乎不需要改!** ✅
|
||
|
||
```typescript
|
||
// 业务代码完全不变
|
||
const project = await prisma.aiaProject.findUnique({
|
||
where: { id: projectId },
|
||
include: {
|
||
user: true // 跨Schema关联,Prisma自动处理
|
||
}
|
||
});
|
||
```
|
||
|
||
**原因:**
|
||
- ✅ Prisma ORM抽象了底层Schema
|
||
- ✅ 业务代码只依赖Prisma Model,不直接写SQL
|
||
|
||
**例外情况:**
|
||
- ⚠️ 如果有原始SQL查询(`prisma.$queryRaw`),需要修改
|
||
- ⚠️ 如果有数据库视图(View),需要修改
|
||
- ⚠️ 如果有数据库函数(Function),需要修改
|
||
|
||
**工作量:**
|
||
- 业务代码:0改造(如果没有原始SQL)
|
||
- 原始SQL:需要逐个修改(估计10-20处)
|
||
- 时间:半天到1天
|
||
|
||
---
|
||
|
||
**步骤4:运行Prisma Migrate**
|
||
|
||
```bash
|
||
# 生成新的迁移文件
|
||
npx prisma migrate dev --name schema-isolation
|
||
|
||
# 或手动迁移(生产环境)
|
||
npx prisma migrate deploy
|
||
```
|
||
|
||
**工作量:**
|
||
- 开发环境:10分钟
|
||
- 生产环境:需要详细计划(半天准备)
|
||
|
||
---
|
||
|
||
#### 3. 测试验证
|
||
|
||
**需要测试的内容:**
|
||
1. ✅ 所有API接口是否正常
|
||
2. ✅ 跨Schema关联是否正常(用户-项目、项目-对话等)
|
||
3. ✅ 外键约束是否正常
|
||
4. ✅ 数据完整性检查
|
||
5. ✅ 性能测试(跨Schema查询性能)
|
||
6. ✅ 备份恢复测试
|
||
|
||
**工作量:**
|
||
- 单元测试:自动化,1-2小时
|
||
- 集成测试:半天
|
||
- 端到端测试:1天
|
||
- 总计:1-2天
|
||
|
||
---
|
||
|
||
### 总改造成本
|
||
|
||
**开发环境:**
|
||
| 任务 | 工作量 |
|
||
|------|-------|
|
||
| 数据库Schema创建 | 10分钟 |
|
||
| Prisma Schema修改 | 2-4小时 |
|
||
| 数据迁移脚本 | 1-2小时 |
|
||
| 原始SQL修改 | 半天 |
|
||
| 测试验证 | 1-2天 |
|
||
| **总计** | **2-3天** |
|
||
|
||
**生产环境:**
|
||
| 任务 | 工作量 |
|
||
|------|-------|
|
||
| 迁移方案设计 | 半天 |
|
||
| 数据备份 | 1小时 |
|
||
| 数据迁移 | 半天到1天 |
|
||
| 验证测试 | 1天 |
|
||
| 应急回滚方案 | 半天 |
|
||
| **总计** | **3-4天** |
|
||
|
||
**风险评估:**
|
||
- ⚠️ **风险等级:中等**
|
||
- ⚠️ **主要风险:数据迁移失败、外键约束问题、停机时间**
|
||
- ✅ **缓解措施:详细测试、回滚方案、灰度发布**
|
||
|
||
---
|
||
|
||
## 🤔 现在做 vs 以后做
|
||
|
||
### 现在做的优势 ✅
|
||
|
||
1. **数据量小,迁移快**
|
||
```
|
||
当前数据量(估算):
|
||
- 用户:< 100
|
||
- 项目:< 500
|
||
- 对话:< 5,000
|
||
- 文档:< 1,000
|
||
|
||
迁移时间:< 1小时
|
||
风险:低
|
||
```
|
||
|
||
2. **业务复杂度低,测试简单**
|
||
```
|
||
当前模块:
|
||
- ✅ AIA(AI问答)- 已完成
|
||
- ✅ PKB(知识库)- 已完成
|
||
- ⏳ ASL(AI文献)- 未开发
|
||
- ⏳ DC、SSA、ST、RVW - 未开发
|
||
|
||
需要测试的模块少
|
||
```
|
||
|
||
3. **为未来打下坚实基础**
|
||
```
|
||
优势:
|
||
- ✅ 新模块(ASL、DC等)直接用物理隔离
|
||
- ✅ 避免未来大规模改造
|
||
- ✅ 支持模块独立部署
|
||
- ✅ 支持微服务拆分
|
||
```
|
||
|
||
4. **团队学习成本低**
|
||
```
|
||
当前团队小:
|
||
- 开发人员少,沟通成本低
|
||
- 代码改动影响范围小
|
||
- 易于推广新规范
|
||
```
|
||
|
||
---
|
||
|
||
### 现在做的劣势 ❌
|
||
|
||
1. **需要投入时间**
|
||
```
|
||
开发时间:2-3天(开发环境)
|
||
生产时间:3-4天(生产环境)
|
||
总计:1周
|
||
|
||
延迟:ASL模块开发延迟1周
|
||
```
|
||
|
||
2. **有一定风险**
|
||
```
|
||
风险:
|
||
- 数据迁移失败
|
||
- 外键约束问题
|
||
- 需要停机
|
||
|
||
缓解:详细测试、回滚方案
|
||
```
|
||
|
||
3. **当前没有紧迫需求**
|
||
```
|
||
事实:
|
||
- 没有微服务拆分需求
|
||
- 没有模块独立部署需求
|
||
- 没有私有化部署需求
|
||
|
||
结论:暂时不需要物理隔离
|
||
```
|
||
|
||
---
|
||
|
||
### 以后做的优势 ✅
|
||
|
||
1. **延迟投入**
|
||
```
|
||
当前:专注业务开发(ASL、DC等)
|
||
未来:有需求时再改造
|
||
|
||
优势:快速推进业务
|
||
```
|
||
|
||
2. **需求明确时再做**
|
||
```
|
||
触发条件:
|
||
- 需要微服务拆分
|
||
- 需要模块独立部署
|
||
- 需要私有化部署
|
||
- 数据量大,需要性能优化
|
||
|
||
此时改造目标明确
|
||
```
|
||
|
||
---
|
||
|
||
### 以后做的劣势 ❌
|
||
|
||
1. **数据量大,迁移慢**
|
||
```
|
||
未来数据量(估算):
|
||
- 用户:10,000+
|
||
- 项目:100,000+
|
||
- 对话:1,000,000+
|
||
- 文档:500,000+
|
||
|
||
迁移时间:数小时到数天
|
||
风险:高
|
||
```
|
||
|
||
2. **业务复杂,测试困难**
|
||
```
|
||
未来模块:
|
||
- ✅ 7个模块全部上线
|
||
- ✅ 复杂的跨模块关联
|
||
- ✅ 大量用户在使用
|
||
|
||
测试难度:高
|
||
回滚成本:高
|
||
```
|
||
|
||
3. **需要停机或在线迁移**
|
||
```
|
||
停机迁移:
|
||
- 影响用户体验
|
||
- 可能丢失订单
|
||
|
||
在线迁移:
|
||
- 技术复杂度高
|
||
- 需要双写、数据同步
|
||
```
|
||
|
||
4. **改造成本成倍增长**
|
||
```
|
||
未来改造成本(估算):
|
||
- 开发时间:1-2周
|
||
- 测试时间:1-2周
|
||
- 生产迁移:1周
|
||
- 总计:3-5周
|
||
|
||
现在的3-5倍!
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 建议与决策
|
||
|
||
### 方案A:现在做物理隔离 ⭐⭐⭐⭐⭐ **强烈推荐**
|
||
|
||
**适用场景:**
|
||
- ✅ 有1周时间投入
|
||
- ✅ 重视长期架构健康
|
||
- ✅ 未来有模块独立部署需求
|
||
- ✅ 未来有私有化部署需求
|
||
|
||
**理由:**
|
||
1. **成本最低**:数据量小,迁移快(< 1小时)
|
||
2. **风险最低**:业务简单,测试容易
|
||
3. **收益最大**:为未来7个模块打下坚实基础
|
||
4. **避免技术债**:避免未来大规模改造
|
||
|
||
**实施计划:**
|
||
```
|
||
Week 1:Schema隔离改造(2-3天)
|
||
Day 1-2:开发环境改造
|
||
- 创建Schema
|
||
- 修改Prisma Schema
|
||
- 数据迁移脚本
|
||
- 测试验证
|
||
|
||
Day 3:生产环境迁移
|
||
- 备份数据
|
||
- 执行迁移
|
||
- 验证测试
|
||
- 监控
|
||
|
||
Week 2:继续ASL模块开发
|
||
```
|
||
|
||
**投入产出比:** ⭐⭐⭐⭐⭐ 极高
|
||
|
||
---
|
||
|
||
### 方案B:暂不做物理隔离,继续逻辑隔离 ⭐⭐⭐ **可接受**
|
||
|
||
**适用场景:**
|
||
- ✅ 时间紧迫,必须尽快上线ASL模块
|
||
- ✅ 近期(6个月内)没有微服务拆分需求
|
||
- ✅ 近期没有私有化部署需求
|
||
|
||
**理由:**
|
||
1. **快速推进业务**:专注ASL模块开发
|
||
2. **逻辑隔离足够用**:当前阶段可以满足需求
|
||
3. **延迟改造**:未来有需求时再做
|
||
|
||
**但需要遵守纪律:** ⚠️ 非常重要
|
||
```
|
||
严格使用表名前缀:
|
||
- aia_*
|
||
- asl_*
|
||
- pkb_*
|
||
- dc_*
|
||
- review_*
|
||
- admin_*
|
||
|
||
为未来改造打基础
|
||
```
|
||
|
||
**触发改造的条件:**
|
||
1. 需要微服务拆分
|
||
2. 需要模块独立部署
|
||
3. 需要私有化部署
|
||
4. 数据量超过100万行
|
||
|
||
**投入产出比:** ⭐⭐⭐ 中等
|
||
|
||
---
|
||
|
||
### 方案C:混合方案(折中) ⭐⭐⭐⭐ **推荐**
|
||
|
||
**方案描述:**
|
||
1. **新模块使用物理隔离**
|
||
- ASL、DC、SSA、ST、RVW等新模块直接用物理隔离
|
||
- 从一开始就创建独立Schema
|
||
|
||
2. **老模块暂时保持逻辑隔离**
|
||
- AIA、PKB等已完成模块暂时不动
|
||
- 等未来有需求时再迁移
|
||
|
||
**实施计划:**
|
||
```
|
||
立即:
|
||
1. 创建新Schema
|
||
CREATE SCHEMA asl_schema;
|
||
CREATE SCHEMA dc_schema;
|
||
CREATE SCHEMA admin_schema;
|
||
...
|
||
|
||
2. ASL模块使用物理隔离
|
||
model AslProject {
|
||
@@schema("asl_schema")
|
||
}
|
||
|
||
未来(6-12个月后):
|
||
3. 迁移老模块
|
||
- 迁移AIA到aia_schema
|
||
- 迁移PKB到pkb_schema
|
||
```
|
||
|
||
**优势:**
|
||
- ✅ 立即投入小(只需创建Schema,10分钟)
|
||
- ✅ 新模块享受物理隔离的好处
|
||
- ✅ 老模块延迟改造,风险低
|
||
|
||
**投入产出比:** ⭐⭐⭐⭐ 高
|
||
|
||
---
|
||
|
||
## 📊 决策矩阵
|
||
|
||
| 方案 | 立即投入 | 未来成本 | 风险 | 灵活性 | 推荐度 |
|
||
|------|---------|---------|------|-------|-------|
|
||
| **方案A:现在做物理隔离** | ⭐⭐⭐ 中(1周) | ⭐⭐⭐⭐⭐ 低 | ⭐⭐⭐⭐ 低 | ⭐⭐⭐⭐⭐ 高 | ⭐⭐⭐⭐⭐ |
|
||
| **方案B:继续逻辑隔离** | ⭐⭐⭐⭐⭐ 零 | ⭐⭐ 高 | ⭐⭐⭐ 中 | ⭐⭐ 低 | ⭐⭐⭐ |
|
||
| **方案C:混合方案** | ⭐⭐⭐⭐⭐ 低(10分钟) | ⭐⭐⭐⭐ 中 | ⭐⭐⭐⭐ 低 | ⭐⭐⭐⭐ 高 | ⭐⭐⭐⭐ |
|
||
|
||
---
|
||
|
||
## 🎯 最终建议
|
||
|
||
### 我的推荐:方案A(现在做物理隔离)⭐⭐⭐⭐⭐
|
||
|
||
**理由:**
|
||
|
||
**1. 成本最低的时间窗口**
|
||
```
|
||
当前:
|
||
- 数据量小(< 1万行)
|
||
- 迁移时间:< 1小时
|
||
- 测试时间:1-2天
|
||
- 总成本:1周
|
||
|
||
6个月后:
|
||
- 数据量大(100万行+)
|
||
- 迁移时间:数小时到数天
|
||
- 测试时间:1-2周
|
||
- 总成本:3-5周
|
||
|
||
成本差异:3-5倍!
|
||
```
|
||
|
||
**2. 最佳学习机会**
|
||
```
|
||
当前:
|
||
- 团队小,沟通成本低
|
||
- 模块少,改造范围小
|
||
- 易于建立规范
|
||
|
||
未来:
|
||
- 团队大,沟通成本高
|
||
- 模块多,改造范围大
|
||
- 难以统一规范
|
||
```
|
||
|
||
**3. 避免技术债累积**
|
||
```
|
||
技术债的特点:
|
||
- 时间越久,利息越高
|
||
- 改造成本成倍增长
|
||
- 影响业务创新
|
||
|
||
现在改造:
|
||
- 一次性还清
|
||
- 轻装上阵
|
||
```
|
||
|
||
**4. 为未来打下坚实基础**
|
||
```
|
||
未来需求(6-12个月):
|
||
- 审稿系统独立部署
|
||
- AI文献模块独立销售
|
||
- 私有化部署
|
||
- 微服务拆分
|
||
|
||
物理隔离是基础设施
|
||
```
|
||
|
||
---
|
||
|
||
### 如果必须选择方案B或C
|
||
|
||
**推荐:方案C(混合方案)**
|
||
|
||
**最低要求:**
|
||
1. ✅ 立即创建所有Schema(10分钟)
|
||
2. ✅ 新模块(ASL、DC等)使用物理隔离
|
||
3. ✅ 老模块(AIA、PKB)延迟迁移
|
||
|
||
**触发老模块迁移的条件:**
|
||
- 数据量超过50万行
|
||
- 需要模块独立部署
|
||
- 需要私有化部署
|
||
|
||
---
|
||
|
||
## 📋 实施清单(方案A)
|
||
|
||
### 准备阶段(1天)
|
||
|
||
- [ ] 详细阅读Prisma Schema文档
|
||
- [ ] 设计Schema结构(platform_schema, aia_schema, asl_schema等)
|
||
- [ ] 编写数据迁移脚本
|
||
- [ ] 准备测试用例
|
||
- [ ] 准备回滚方案
|
||
|
||
### 开发环境改造(1-2天)
|
||
|
||
- [ ] 创建所有Schema
|
||
- [ ] 修改Prisma Schema(所有Model)
|
||
- [ ] 运行Prisma Migrate
|
||
- [ ] 迁移开发环境数据
|
||
- [ ] 运行单元测试
|
||
- [ ] 运行集成测试
|
||
- [ ] 手动测试所有功能
|
||
|
||
### 生产环境迁移(1天)
|
||
|
||
- [ ] 备份数据库
|
||
- [ ] 创建Schema
|
||
- [ ] 执行数据迁移
|
||
- [ ] 验证数据完整性
|
||
- [ ] 启动应用
|
||
- [ ] 监控错误日志
|
||
- [ ] 性能测试
|
||
|
||
### 验收标准
|
||
|
||
- [ ] 所有API接口正常
|
||
- [ ] 跨Schema关联正常
|
||
- [ ] 外键约束正常
|
||
- [ ] 数据完整性正常
|
||
- [ ] 性能无明显下降
|
||
- [ ] 无错误日志
|
||
|
||
---
|
||
|
||
## 💡 技术细节:如何实现物理隔离
|
||
|
||
### Step 1: 创建Schema
|
||
|
||
```sql
|
||
-- 创建所有Schema
|
||
CREATE SCHEMA platform_schema;
|
||
CREATE SCHEMA aia_schema;
|
||
CREATE SCHEMA asl_schema;
|
||
CREATE SCHEMA pkb_schema;
|
||
CREATE SCHEMA dc_schema;
|
||
CREATE SCHEMA ssa_schema;
|
||
CREATE SCHEMA st_schema;
|
||
CREATE SCHEMA review_schema;
|
||
CREATE SCHEMA admin_schema;
|
||
```
|
||
|
||
---
|
||
|
||
### Step 2: 修改Prisma Schema
|
||
|
||
**修改database配置:**
|
||
```prisma
|
||
generator client {
|
||
provider = "prisma-client-js"
|
||
previewFeatures = ["multiSchema"] // 启用多Schema支持 ⭐
|
||
}
|
||
|
||
datasource db {
|
||
provider = "postgresql"
|
||
url = env("DATABASE_URL")
|
||
schemas = [
|
||
"platform_schema",
|
||
"aia_schema",
|
||
"asl_schema",
|
||
"pkb_schema",
|
||
"dc_schema",
|
||
"ssa_schema",
|
||
"st_schema",
|
||
"review_schema",
|
||
"admin_schema"
|
||
] // 声明所有Schema ⭐
|
||
}
|
||
```
|
||
|
||
**修改Model:**
|
||
```prisma
|
||
// 平台层
|
||
model User {
|
||
id String @id @default(uuid())
|
||
email String @unique
|
||
password String
|
||
// ...
|
||
|
||
@@map("users")
|
||
@@schema("platform_schema") // 指定Schema ⭐
|
||
}
|
||
|
||
// AI问答模块
|
||
model AiaProject {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
name String
|
||
|
||
user User @relation(fields: [userId], references: [id])
|
||
|
||
@@map("projects") // 不需要前缀
|
||
@@schema("aia_schema") // 指定Schema ⭐
|
||
}
|
||
|
||
// AI文献模块
|
||
model AslProject {
|
||
id String @id @default(uuid())
|
||
userId String @map("user_id")
|
||
name String
|
||
|
||
user User @relation(fields: [userId], references: [id])
|
||
|
||
@@map("projects")
|
||
@@schema("asl_schema") // 指定Schema ⭐
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
### Step 3: 生成迁移
|
||
|
||
```bash
|
||
# 生成迁移文件
|
||
npx prisma migrate dev --name schema-isolation --create-only
|
||
|
||
# 查看生成的SQL
|
||
# migrations/20251106_schema-isolation/migration.sql
|
||
```
|
||
|
||
**生成的SQL示例:**
|
||
```sql
|
||
-- CreateSchema
|
||
CREATE SCHEMA IF NOT EXISTS "platform_schema";
|
||
CREATE SCHEMA IF NOT EXISTS "aia_schema";
|
||
-- ...
|
||
|
||
-- CreateTable
|
||
CREATE TABLE "platform_schema"."users" (
|
||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||
"email" VARCHAR(255) NOT NULL,
|
||
-- ...
|
||
PRIMARY KEY ("id")
|
||
);
|
||
|
||
CREATE TABLE "aia_schema"."projects" (
|
||
"id" UUID NOT NULL DEFAULT gen_random_uuid(),
|
||
"user_id" UUID NOT NULL,
|
||
"name" VARCHAR(255) NOT NULL,
|
||
-- ...
|
||
|
||
CONSTRAINT "fk_user" FOREIGN KEY ("user_id")
|
||
REFERENCES "platform_schema"."users"("id") ON DELETE CASCADE,
|
||
|
||
PRIMARY KEY ("id")
|
||
);
|
||
```
|
||
|
||
---
|
||
|
||
### Step 4: 数据迁移
|
||
|
||
**编写迁移脚本:**
|
||
```sql
|
||
-- migrate-data.sql
|
||
|
||
-- 迁移用户表
|
||
INSERT INTO platform_schema.users
|
||
SELECT * FROM public.users;
|
||
|
||
-- 迁移AIA模块
|
||
INSERT INTO aia_schema.projects
|
||
SELECT * FROM public.aia_projects;
|
||
|
||
INSERT INTO aia_schema.conversations
|
||
SELECT * FROM public.aia_conversations;
|
||
|
||
INSERT INTO aia_schema.messages
|
||
SELECT * FROM public.aia_messages;
|
||
|
||
-- 迁移PKB模块
|
||
INSERT INTO pkb_schema.knowledge_bases
|
||
SELECT * FROM public.knowledge_bases;
|
||
|
||
INSERT INTO pkb_schema.documents
|
||
SELECT * FROM public.documents;
|
||
|
||
-- ... 其他表
|
||
```
|
||
|
||
**执行迁移:**
|
||
```bash
|
||
# 方式1:使用psql
|
||
psql -U postgres -d ai_clinical_research -f migrate-data.sql
|
||
|
||
# 方式2:使用Prisma
|
||
npx prisma db execute --file migrate-data.sql
|
||
```
|
||
|
||
---
|
||
|
||
### Step 5: 验证
|
||
|
||
```typescript
|
||
// test-schema-isolation.ts
|
||
|
||
async function testSchemaIsolation() {
|
||
// 测试跨Schema关联
|
||
const project = await prisma.aiaProject.findUnique({
|
||
where: { id: 'test-id' },
|
||
include: {
|
||
user: true // 跨Schema关联(platform_schema.users)
|
||
}
|
||
});
|
||
|
||
console.log('跨Schema关联正常:', project);
|
||
|
||
// 测试所有模块
|
||
const stats = {
|
||
users: await prisma.user.count(),
|
||
aiaProjects: await prisma.aiaProject.count(),
|
||
aslProjects: await prisma.aslProject.count(),
|
||
knowledgeBases: await prisma.knowledgeBase.count(),
|
||
};
|
||
|
||
console.log('数据统计:', stats);
|
||
}
|
||
```
|
||
|
||
---
|
||
|
||
## 📊 成本收益总结
|
||
|
||
### 投入成本
|
||
|
||
| 项目 | 时间 | 风险 |
|
||
|------|------|------|
|
||
| 学习和准备 | 1天 | 低 |
|
||
| 开发环境改造 | 1-2天 | 低 |
|
||
| 测试验证 | 1-2天 | 中 |
|
||
| 生产环境迁移 | 1天 | 中 |
|
||
| **总计** | **5-6天** | **中等** |
|
||
|
||
### 预期收益
|
||
|
||
| 收益 | 价值 |
|
||
|------|------|
|
||
| 模块独立部署 | ⭐⭐⭐⭐⭐ |
|
||
| 微服务拆分 | ⭐⭐⭐⭐⭐ |
|
||
| 数据库级别权限控制 | ⭐⭐⭐⭐ |
|
||
| 按Schema备份恢复 | ⭐⭐⭐⭐ |
|
||
| 避免未来大规模改造 | ⭐⭐⭐⭐⭐ |
|
||
| **总价值** | **极高** |
|
||
|
||
---
|
||
|
||
## 🎯 最终建议总结
|
||
|
||
### 我的建议:立即做物理隔离(方案A)
|
||
|
||
**核心理由:**
|
||
1. ✅ **现在是成本最低的时间窗口**(数据量小,改造快)
|
||
2. ✅ **避免技术债累积**(未来改造成本成倍增长)
|
||
3. ✅ **为7个模块打下坚实基础**(模块独立部署、微服务拆分)
|
||
4. ✅ **支持未来商业模式**(模块化销售、私有化部署)
|
||
|
||
**投入:** 1周
|
||
**收益:** 避免未来3-5倍的改造成本
|
||
**投入产出比:** 极高
|
||
|
||
---
|
||
|
||
**如果您决定采纳方案A,我可以立即帮您:**
|
||
1. 设计详细的Schema结构
|
||
2. 编写Prisma Schema修改方案
|
||
3. 编写数据迁移脚本
|
||
4. 制定详细的实施计划
|
||
5. 准备测试用例和验收标准
|
||
|
||
**您觉得呢?** 😊
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|