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
463 lines
14 KiB
Markdown
463 lines
14 KiB
Markdown
# 后端架构增量演进方案
|
||
|
||
> **版本:** V1.0
|
||
> **创建日期:** 2025-11-14
|
||
> **策略:** 新旧并存,增量演进
|
||
> **原则:** 零风险改造,新模块新架构
|
||
|
||
---
|
||
|
||
## 🎯 核心策略
|
||
|
||
### "绞杀者模式"(Strangler Fig Pattern)
|
||
|
||
**不改造旧代码,新功能新架构**
|
||
|
||
```
|
||
┌─────────────────────────────────────────────────────┐
|
||
│ AI临床研究平台 - Backend │
|
||
├─────────────────────────────────────────────────────┤
|
||
│ │
|
||
│ ┌──────────────┐ ┌──────────────────┐ │
|
||
│ │ 现有模块 │ │ 新模块 │ │
|
||
│ │ (旧架构) │ │ (新架构) │ │
|
||
│ ├──────────────┤ ├──────────────────┤ │
|
||
│ │ • AIA │ │ • ASL (新) │ │
|
||
│ │ • PKB │ │ • 未来模块... │ │
|
||
│ │ • RVW │ │ │ │
|
||
│ └──────────────┘ └──────────────────┘ │
|
||
│ ↓ ↓ │
|
||
│ 平铺结构 platform/common/modules │
|
||
│ 保持不变 标准化三层架构 │
|
||
└─────────────────────────────────────────────────────┘
|
||
```
|
||
|
||
---
|
||
|
||
## 📁 新的目录结构(并存版)
|
||
|
||
```
|
||
backend/
|
||
├── src/
|
||
│ ├── index.ts # 主入口(注册所有模块)
|
||
│ │
|
||
│ ├── config/ # 【共用】配置层
|
||
│ │ ├── env.ts
|
||
│ │ └── database.ts
|
||
│ │
|
||
│ ├── legacy/ # 🔸 现有代码(旧架构)
|
||
│ │ ├── routes/
|
||
│ │ │ ├── agents.ts # AIA 路由
|
||
│ │ │ ├── conversations.ts
|
||
│ │ │ ├── chatRoutes.ts
|
||
│ │ │ ├── projects.ts
|
||
│ │ │ ├── knowledgeBases.ts # PKB 路由
|
||
│ │ │ ├── batchRoutes.ts
|
||
│ │ │ └── reviewRoutes.ts # RVW 路由
|
||
│ │ │
|
||
│ │ ├── controllers/
|
||
│ │ │ ├── agentController.ts
|
||
│ │ │ ├── conversationController.ts
|
||
│ │ │ ├── chatController.ts
|
||
│ │ │ ├── knowledgeBaseController.ts
|
||
│ │ │ └── reviewController.ts
|
||
│ │ │
|
||
│ │ ├── services/
|
||
│ │ │ ├── agentService.ts
|
||
│ │ │ ├── conversationService.ts
|
||
│ │ │ ├── knowledgeBaseService.ts
|
||
│ │ │ └── reviewService.ts
|
||
│ │ │
|
||
│ │ └── templates/
|
||
│ │ └── clinicalResearch.ts
|
||
│ │
|
||
│ ├── common/ # 【共用】通用能力层
|
||
│ │ ├── llm/ # LLM 适配器
|
||
│ │ │ └── adapters/
|
||
│ │ │ ├── DeepSeekAdapter.ts
|
||
│ │ │ ├── QwenAdapter.ts
|
||
│ │ │ ├── LLMFactory.ts
|
||
│ │ │ └── types.ts
|
||
│ │ │
|
||
│ │ ├── rag/ # RAG 能力
|
||
│ │ │ ├── DifyClient.ts
|
||
│ │ │ └── types.ts
|
||
│ │ │
|
||
│ │ ├── document/ # 文档处理
|
||
│ │ │ ├── ExtractionClient.ts
|
||
│ │ │ └── TokenService.ts
|
||
│ │ │
|
||
│ │ ├── middleware/ # 中间件
|
||
│ │ │ └── validateProject.ts
|
||
│ │ │
|
||
│ │ └── utils/ # 工具函数
|
||
│ │ └── jsonParser.ts
|
||
│ │
|
||
│ └── modules/ # 🌟 新架构模块(标准化)
|
||
│ │
|
||
│ ├── asl/ # ⭐ AI智能文献(新模块)
|
||
│ │ ├── index.ts # 模块导出
|
||
│ │ ├── routes/
|
||
│ │ │ ├── index.ts # 路由统一导出
|
||
│ │ │ ├── projects.ts # 项目管理
|
||
│ │ │ ├── screening.ts # 文献筛选
|
||
│ │ │ ├── extraction.ts # 数据提取
|
||
│ │ │ └── analysis.ts # 综合分析
|
||
│ │ │
|
||
│ │ ├── controllers/
|
||
│ │ │ ├── projectController.ts
|
||
│ │ │ ├── screeningController.ts
|
||
│ │ │ ├── extractionController.ts
|
||
│ │ │ └── analysisController.ts
|
||
│ │ │
|
||
│ │ ├── services/
|
||
│ │ │ ├── projectService.ts
|
||
│ │ │ ├── screeningService.ts
|
||
│ │ │ ├── extractionService.ts
|
||
│ │ │ └── analysisService.ts
|
||
│ │ │
|
||
│ │ └── types/ # 模块类型定义
|
||
│ │ └── index.ts
|
||
│ │
|
||
│ └── [未来模块]/ # 未来的新模块都按此结构
|
||
│
|
||
├── package.json
|
||
├── tsconfig.json # TypeScript 配置
|
||
└── .env
|
||
```
|
||
|
||
---
|
||
|
||
## 🔄 主入口文件(index.ts)
|
||
|
||
```typescript
|
||
/**
|
||
* AI临床研究平台 - 统一入口
|
||
*
|
||
* 架构演进策略:新旧并存
|
||
* - Legacy 模块:保持现有结构
|
||
* - New 模块:采用标准化三层架构
|
||
*/
|
||
|
||
import Fastify from 'fastify';
|
||
import cors from '@fastify/cors';
|
||
import multipart from '@fastify/multipart';
|
||
import { config, validateEnv } from './config/env.js';
|
||
import { testDatabaseConnection, prisma } from './config/database.js';
|
||
|
||
// ============================================
|
||
// 【旧架构】Legacy 模块 - 保持不变
|
||
// ============================================
|
||
import { projectRoutes } from './legacy/routes/projects.js';
|
||
import { agentRoutes } from './legacy/routes/agents.js';
|
||
import { conversationRoutes } from './legacy/routes/conversations.js';
|
||
import { chatRoutes } from './legacy/routes/chatRoutes.js';
|
||
import knowledgeBaseRoutes from './legacy/routes/knowledgeBases.js';
|
||
import batchRoutes from './legacy/routes/batchRoutes.js';
|
||
import reviewRoutes from './legacy/routes/reviewRoutes.js';
|
||
|
||
// ============================================
|
||
// 【新架构】标准化模块
|
||
// ============================================
|
||
import { aslRoutes } from './modules/asl/routes/index.js'; // ASL 模块(新)
|
||
|
||
const fastify = Fastify({ logger: true });
|
||
|
||
// 注册插件
|
||
await fastify.register(cors, { origin: true });
|
||
await fastify.register(multipart, { limits: { fileSize: 10 * 1024 * 1024 } });
|
||
|
||
// 健康检查
|
||
fastify.get('/health', async () => ({
|
||
status: 'ok',
|
||
architecture: 'hybrid', // 混合架构
|
||
modules: {
|
||
legacy: ['aia', 'pkb', 'rvw'],
|
||
modern: ['asl'],
|
||
},
|
||
}));
|
||
|
||
// ============================================
|
||
// 注册 Legacy 模块路由(旧架构)
|
||
// ============================================
|
||
console.log('\n📦 加载 Legacy 模块...');
|
||
await fastify.register(projectRoutes, { prefix: '/api/v1/aia' });
|
||
await fastify.register(agentRoutes, { prefix: '/api/v1/aia' });
|
||
await fastify.register(conversationRoutes, { prefix: '/api/v1/aia' });
|
||
await fastify.register(chatRoutes, { prefix: '/api/v1/aia' });
|
||
await fastify.register(knowledgeBaseRoutes, { prefix: '/api/v1/pkb' });
|
||
await fastify.register(batchRoutes, { prefix: '/api/v1/pkb' });
|
||
await fastify.register(reviewRoutes, { prefix: '/api/v1/rvw' });
|
||
console.log('✅ Legacy 模块加载完成(AIA, PKB, RVW)');
|
||
|
||
// ============================================
|
||
// 注册新架构模块路由
|
||
// ============================================
|
||
console.log('\n🌟 加载新架构模块...');
|
||
await fastify.register(aslRoutes, { prefix: '/api/v1/asl' });
|
||
console.log('✅ 新架构模块加载完成(ASL)');
|
||
|
||
// 启动服务器
|
||
async function start() {
|
||
try {
|
||
validateEnv();
|
||
await testDatabaseConnection();
|
||
await fastify.listen({ port: config.port, host: config.host });
|
||
|
||
console.log('\n' + '='.repeat(60));
|
||
console.log('🚀 AI临床研究平台启动成功!');
|
||
console.log('='.repeat(60));
|
||
console.log(`📍 服务地址: http://${config.host}:${config.port}`);
|
||
console.log('\n📦 模块架构:');
|
||
console.log(' 🔸 Legacy 架构: AIA, PKB, RVW (稳定运行)');
|
||
console.log(' 🌟 新架构: ASL (标准化模块)');
|
||
console.log('='.repeat(60) + '\n');
|
||
|
||
} catch (error) {
|
||
console.error('❌ 启动失败:', error);
|
||
await prisma.$disconnect();
|
||
process.exit(1);
|
||
}
|
||
}
|
||
|
||
start();
|
||
```
|
||
|
||
---
|
||
|
||
## 🌟 新模块开发指南(ASL 为例)
|
||
|
||
### 1. 创建模块骨架
|
||
|
||
```bash
|
||
# 一次性创建所有目录
|
||
mkdir -p backend/src/modules/asl/{routes,controllers,services,types}
|
||
```
|
||
|
||
### 2. 创建模块导出(index.ts)
|
||
|
||
```typescript
|
||
// backend/src/modules/asl/index.ts
|
||
/**
|
||
* ASL 模块统一导出
|
||
*/
|
||
export { aslRoutes } from './routes/index.js';
|
||
export * from './types/index.js';
|
||
```
|
||
|
||
### 3. 创建路由统一导出
|
||
|
||
```typescript
|
||
// backend/src/modules/asl/routes/index.ts
|
||
import { FastifyInstance } from 'fastify';
|
||
import { projectRoutes } from './projects.js';
|
||
import { screeningRoutes } from './screening.js';
|
||
import { extractionRoutes } from './extraction.js';
|
||
import { analysisRoutes } from './analysis.js';
|
||
|
||
/**
|
||
* ASL 模块路由注册
|
||
*/
|
||
export async function aslRoutes(fastify: FastifyInstance) {
|
||
// 注册各子模块路由
|
||
await fastify.register(projectRoutes); // /projects
|
||
await fastify.register(screeningRoutes); // /screening
|
||
await fastify.register(extractionRoutes); // /extraction
|
||
await fastify.register(analysisRoutes); // /analysis
|
||
|
||
console.log('✅ ASL 模块路由注册完成');
|
||
}
|
||
```
|
||
|
||
### 4. 标准化的 Controller 模式
|
||
|
||
```typescript
|
||
// backend/src/modules/asl/controllers/projectController.ts
|
||
import { FastifyRequest, FastifyReply } from 'fastify';
|
||
import * as projectService from '../services/projectService.js';
|
||
|
||
export class ProjectController {
|
||
/**
|
||
* 获取项目列表
|
||
*/
|
||
async getProjects(request: FastifyRequest, reply: FastifyReply) {
|
||
try {
|
||
const projects = await projectService.getProjects();
|
||
return reply.send({ success: true, data: projects });
|
||
} catch (error) {
|
||
console.error('获取项目列表失败:', error);
|
||
return reply.status(500).send({
|
||
success: false,
|
||
message: error instanceof Error ? error.message : '服务器错误'
|
||
});
|
||
}
|
||
}
|
||
|
||
// ... 其他方法
|
||
}
|
||
|
||
export const projectController = new ProjectController();
|
||
```
|
||
|
||
---
|
||
|
||
## 🔧 TSConfig 路径别名配置
|
||
|
||
```json
|
||
{
|
||
"compilerOptions": {
|
||
"baseUrl": ".",
|
||
"paths": {
|
||
"@config/*": ["src/config/*"],
|
||
"@common/*": ["src/common/*"],
|
||
"@legacy/*": ["src/legacy/*"],
|
||
"@modules/*": ["src/modules/*"]
|
||
}
|
||
}
|
||
}
|
||
```
|
||
|
||
**使用示例:**
|
||
|
||
```typescript
|
||
// 新模块中导入通用能力
|
||
import { LLMFactory } from '@common/llm/adapters/LLMFactory.js';
|
||
import { DifyClient } from '@common/rag/DifyClient.js';
|
||
import { config } from '@config/env.js';
|
||
|
||
// Legacy 模块保持原有导入不变
|
||
import { agentService } from '../services/agentService.js';
|
||
```
|
||
|
||
---
|
||
|
||
## 📋 实施步骤(零风险)
|
||
|
||
### 第 1 步:文件重组(5分钟)⭐
|
||
|
||
```bash
|
||
# 1. 创建 legacy 目录
|
||
mkdir backend/src/legacy
|
||
|
||
# 2. 移动现有代码到 legacy(保持相对路径不变)
|
||
mv backend/src/routes backend/src/legacy/
|
||
mv backend/src/controllers backend/src/legacy/
|
||
mv backend/src/services backend/src/legacy/
|
||
mv backend/src/templates backend/src/legacy/
|
||
|
||
# 3. 创建新架构目录
|
||
mkdir -p backend/src/modules/asl/{routes,controllers,services,types}
|
||
```
|
||
|
||
**验证:** 无需修改任何代码,只是移动目录位置
|
||
|
||
---
|
||
|
||
### 第 2 步:更新主入口(5分钟)
|
||
|
||
只修改 `src/index.ts` 的导入路径:
|
||
|
||
```typescript
|
||
// 旧:from './routes/agents.js'
|
||
// 新:from './legacy/routes/agents.js'
|
||
```
|
||
|
||
**验证:** `npm run dev` 服务器正常启动
|
||
|
||
---
|
||
|
||
### 第 3 步:配置 TSConfig(2分钟)
|
||
|
||
添加路径别名配置。
|
||
|
||
**验证:** IDE 能识别 `@legacy/*`, `@modules/*` 等路径
|
||
|
||
---
|
||
|
||
### 第 4 步:开发 ASL 模块(新功能)
|
||
|
||
按新架构标准开发,完全不影响现有模块。
|
||
|
||
---
|
||
|
||
## ✅ 方案优势
|
||
|
||
### 1. **零风险改造**
|
||
- ✅ 现有代码只移动目录,不修改内容
|
||
- ✅ 只改一个文件的导入路径
|
||
- ✅ 可随时回滚
|
||
|
||
### 2. **清晰的架构边界**
|
||
```
|
||
legacy/ ← 旧代码,明确标识
|
||
modules/ ← 新代码,标准化
|
||
```
|
||
|
||
### 3. **新模块新架构**
|
||
- ✅ ASL 直接按标准实施
|
||
- ✅ 成为未来模块的范本
|
||
- ✅ 不受旧代码约束
|
||
|
||
### 4. **平滑演进路径**
|
||
```
|
||
现在:7个旧模块 + 0个新模块
|
||
未来:7个旧模块 + 1个新模块(ASL)
|
||
更远:7个旧模块 + N个新模块
|
||
最终:按需逐步迁移旧模块(可选)
|
||
```
|
||
|
||
---
|
||
|
||
## 🎯 演进时间轴
|
||
|
||
| 阶段 | 时间 | 工作内容 | 风险 |
|
||
|------|------|---------|------|
|
||
| **今天** | 15分钟 | 重组目录+更新入口 | 极低 |
|
||
| **Week 3** | - | 开发 ASL 模块(新架构) | 无(新功能) |
|
||
| **Week 4+** | - | 继续开发新模块 | 无 |
|
||
| **未来** | 按需 | 逐步迁移旧模块(可选) | 可控 |
|
||
|
||
---
|
||
|
||
## 📝 开发规范
|
||
|
||
### Legacy 模块规范
|
||
- ⚠️ **不主动修改**,除非修复bug
|
||
- ⚠️ 保持现有结构不变
|
||
- ⚠️ 可以引用 `common/` 层的能力
|
||
|
||
### 新模块规范
|
||
- ✅ 必须按标准三层架构
|
||
- ✅ 必须使用路径别名
|
||
- ✅ 必须独立可部署
|
||
- ✅ 必须有完整的类型定义
|
||
|
||
---
|
||
|
||
## 🚀 下一步行动
|
||
|
||
1. **今天(15分钟)**:执行第1-3步,完成目录重组
|
||
2. **明天**:开始 ASL 模块开发(新架构)
|
||
3. **Week 3**:ASL 模块上线
|
||
4. **未来**:新模块持续按标准开发
|
||
|
||
---
|
||
|
||
**最后更新:** 2025-11-14
|
||
**维护者:** 开发团队
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|