Files
AIclinicalresearch/docs/09-架构实施/后端架构增量演进方案.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

463 lines
14 KiB
Markdown
Raw Permalink Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# 后端架构增量演进方案
> **版本:** V1.0
> **创建日期:** 2025-11-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 步:配置 TSConfig2分钟
添加路径别名配置。
**验证:** 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
**维护者:** 开发团队