# 平台基础设施规划(Platform Infrastructure Plan) > **文档版本:** V1.0 > **创建日期:** 2025-11-16 > **适用对象:** 架构师、后端开发、运维 > **文档状态:** 实施规划 > **维护者:** 架构团队 --- ## 📋 文档说明 本文档是壹证循AI科研平台的**平台基础设施规划文档**,定义了: 1. **核心需求**:平台基础设施需要解决的问题 2. **设计方案**:技术架构和实现方案 3. **实施计划**:分阶段的实施路线图 4. **验收标准**:每个模块的验收标准 **核心目标:** - ✅ 支持本地开发和云端部署无缝切换 - ✅ 支持PRD定义的4种部署形态(云端SaaS、私有化、单机版、混合) - ✅ 支持模块化组合售卖(专业版、高级版、旗舰版) - ✅ 提供通用能力,所有业务模块直接复用 --- ## 🎯 需求背景 ### 1. 业务需求(来自PRD) 根据 [09-总体需求文档(PRD).md](../00-系统总体设计/09-总体需求文档(PRD).md),平台必须支持: | 需求ID | 需求描述 | 技术挑战 | |--------|---------|---------| | **NFR-1.1** | 云端SaaS版(多租户、高可用) | Serverless架构、自动扩缩容 | | **NFR-1.2** | 私有化部署(数据不出内网) | 本地存储、本地数据库 | | **NFR-1.3** | 单机版(100%本地化) | 离线运行、本地文件系统 | | **NFR-1.4** | 混合部署(部分本地+部分云端) | 灵活的配置切换 | | **NFR-2.1** | SaaS多版本(专业版、高级版、旗舰版) | Feature Flag、模块化 | | **NFR-2.2** | 模块化售卖(任何模块可独立售卖) | 松耦合架构 | | **NFR-2.3** | AI成本可控(动态切换LLM) | 适配器模式 | ### 2. 技术需求(来自云原生架构) 根据云原生部署架构(阿里云 Serverless + RDS + OSS),平台必须: | 技术需求 | 说明 | 优先级 | |---------|------|--------| | **无状态应用** | 不依赖本地文件系统或内存状态 | P0 | | **存储抽象** | 支持本地存储和OSS无缝切换 | P0 | | **数据库连接池** | 防止Serverless扩容导致连接数超限 | P0 | | **标准化日志** | 输出到stdout,支持集中收集 | P0 | | **异步任务** | 长时间任务必须异步处理(避免超时) | P0 | | **分布式缓存** | 多实例共享缓存 | P1 | | **健康检查** | SAE存活和就绪检查 | P1 | | **监控指标** | 数据库连接数、任务队列等 | P1 | --- ## 🏗️ 设计方案 ### 核心设计原则 > **平台基础设施通过适配器模式(Adapter Pattern)实现多环境支持** ``` ┌─────────────────────────────────────────────────────────┐ │ 业务模块层 │ │ ASL | AIA | PKB | DC | SSA | ST | UAM │ │ 只关注业务逻辑,复用平台能力 │ └─────────────────────────────────────────────────────────┘ ↓ import from '@/common/' ┌─────────────────────────────────────────────────────────┐ │ 平台基础设施层(Adapter Pattern) │ ├─────────────────────────────────────────────────────────┤ │ 存储:LocalAdapter ←→ OSSAdapter │ │ 缓存:MemoryCacheAdapter ←→ RedisCacheAdapter │ │ 任务:MemoryQueueAdapter ←→ DatabaseQueueAdapter │ │ 日志:ConsoleLogger ←→ 阿里云SLS │ │ 数据库:本地PostgreSQL ←→ 阿里云RDS(连接池) │ └─────────────────────────────────────────────────────────┘ ↓ 环境变量切换 ┌─────────────────────────────────────────────────────────┐ │ 部署环境(零代码改动) │ │ 本地开发 | 云端SaaS | 私有化部署 | 单机版 │ └─────────────────────────────────────────────────────────┘ ``` --- ## 📦 平台基础设施模块清单 ### 模块总览 | 模块 | 路径 | 优先级 | 说明 | |------|------|--------|------| | **存储服务** | `common/storage/` | P0 | 文件上传下载(本地/OSS) | | **数据库连接池** | `config/database.ts` | P0 | Prisma连接池配置 | | **日志系统** | `common/logging/` | P0 | 标准化日志输出 | | **环境配置** | `config/env.ts` | P0 | 环境变量管理 | | **异步任务** | `common/jobs/` | P0 | 长时间任务异步处理 | | **缓存服务** | `common/cache/` | P1 | 分布式缓存 | | **健康检查** | `common/health/` | P1 | SAE健康检查端点 | | **监控指标** | `common/monitoring/` | P1 | 关键指标监控 | --- ## 📐 详细设计 ### 1. 存储服务(Storage Service) #### 设计目标 - ✅ 支持本地开发(LocalAdapter) - ✅ 支持云端部署(OSSAdapter) - ✅ 业务代码零改动切换 #### 目录结构 ``` backend/src/common/storage/ ├── StorageAdapter.ts # 接口定义 ├── LocalAdapter.ts # 本地实现 ├── OSSAdapter.ts # OSS实现 ├── StorageFactory.ts # 工厂类 └── index.ts # 统一导出 ``` #### 接口定义 ```typescript // backend/src/common/storage/StorageAdapter.ts export interface StorageAdapter { upload(key: string, buffer: Buffer): Promise download(key: string): Promise delete(key: string): Promise getUrl(key: string): string } ``` #### 环境切换 ```bash # 本地开发 STORAGE_TYPE=local # 生产环境 STORAGE_TYPE=oss OSS_REGION=oss-cn-hangzhou OSS_BUCKET=aiclinical-prod ``` #### 业务模块使用 ```typescript import { storage } from '@/common/storage' // 使用(不关心本地还是OSS) const url = await storage.upload('literature/123.pdf', buffer) ``` --- ### 2. 数据库连接池(Database Connection Pool) #### 设计目标 - ✅ 防止Serverless扩容导致连接数超限 - ✅ 优雅关闭连接 - ✅ 连接数监控 #### 文件位置 ``` backend/src/config/database.ts ``` #### 连接池配置 ```typescript // 计算公式:每实例连接数 = RDS最大连接数 / SAE最大实例数 // 示例:400连接 / 20实例 = 20连接/实例 import { PrismaClient } from '@prisma/client' const connectionLimit = Math.floor( Number(process.env.DB_MAX_CONNECTIONS || 400) / Number(process.env.MAX_INSTANCES || 20) ) export const prisma = new PrismaClient({ log: process.env.NODE_ENV === 'development' ? ['query', 'error'] : ['error'], }) // 优雅关闭 process.on('SIGTERM', async () => { await prisma.$disconnect() process.exit(0) }) ``` #### 环境变量 ```bash DB_MAX_CONNECTIONS=400 # RDS最大连接数 MAX_INSTANCES=20 # SAE最大实例数 DATABASE_URL=postgresql://... ``` --- ### 3. 日志系统(Logging) #### 设计目标 - ✅ 云原生:只输出到stdout(不写本地文件) - ✅ JSON格式(便于阿里云SLS解析) - ✅ 统一的日志格式 #### 目录结构 ``` backend/src/common/logging/ ├── logger.ts # 日志工具 └── index.ts # 导出 ``` #### 实现 ```typescript // backend/src/common/logging/logger.ts import winston from 'winston' export const logger = winston.createLogger({ level: process.env.LOG_LEVEL || 'info', format: winston.format.combine( winston.format.timestamp(), winston.format.errors({ stack: true }), winston.format.json() ), defaultMeta: { service: 'aiclinical-backend', env: process.env.NODE_ENV, instance: process.env.HOSTNAME }, transports: [ new winston.transports.Console({ format: winston.format.json() // ⭐ JSON格式 }) ] }) ``` #### 业务模块使用 ```typescript import { logger } from '@/common/logging' logger.info('Screening started', { projectId, count: 100 }) logger.error('LLM API failed', { error: err.message }) ``` --- ### 4. 环境配置(Environment Config) #### 设计目标 - ✅ 统一的配置管理 - ✅ 本地开发:.env文件 - ✅ 生产环境:SAE环境变量 - ✅ 启动时验证必需配置 #### 文件位置 ``` backend/src/config/env.ts ``` #### 实现 ```typescript // backend/src/config/env.ts import { config } from 'dotenv' // 只在本地开发加载 .env 文件 if (process.env.NODE_ENV !== 'production') { config() } export const env = { // 应用配置 NODE_ENV: process.env.NODE_ENV || 'development', PORT: Number(process.env.PORT) || 3001, // 数据库配置 DATABASE_URL: process.env.DATABASE_URL!, DB_MAX_CONNECTIONS: Number(process.env.DB_MAX_CONNECTIONS) || 400, MAX_INSTANCES: Number(process.env.MAX_INSTANCES) || 20, // 存储配置 STORAGE_TYPE: process.env.STORAGE_TYPE || 'local', OSS_REGION: process.env.OSS_REGION, OSS_BUCKET: process.env.OSS_BUCKET, // 缓存配置 CACHE_TYPE: process.env.CACHE_TYPE || 'memory', REDIS_HOST: process.env.REDIS_HOST, // LLM配置 DEEPSEEK_API_KEY: process.env.DEEPSEEK_API_KEY, QWEN_API_KEY: process.env.QWEN_API_KEY, // 功能开关 ENABLED_MODULES: process.env.ENABLED_MODULES?.split(',') || [], } // 启动时验证 export function validateEnv() { const required = ['DATABASE_URL'] for (const key of required) { if (!process.env[key]) { throw new Error(`Missing required env var: ${key}`) } } } ``` --- ### 5. 异步任务(Async Jobs) #### 设计目标 - ✅ 长时间任务(>10秒)异步处理 - ✅ 避免Serverless超时(30秒) - ✅ 支持进度查询 #### 目录结构 ``` backend/src/common/jobs/ ├── JobQueue.ts # 任务队列接口 ├── MemoryQueue.ts # 本地开发(内存队列) ├── DatabaseQueue.ts # 生产环境(数据库队列) ├── JobProcessor.ts # 任务处理器 └── index.ts ``` #### 接口定义 ```typescript // backend/src/common/jobs/JobQueue.ts export interface Job { id: string type: string data: T status: 'pending' | 'processing' | 'completed' | 'failed' progress: number result?: any error?: string createdAt: Date updatedAt: Date } export interface JobQueue { push(type: string, data: T): Promise> process(type: string, handler: (job: Job) => Promise): void getJob(id: string): Promise updateProgress(id: string, progress: number): Promise } ``` #### 业务模块使用(ASL模块示例) ```typescript import { jobQueue } from '@/common/jobs' // 1. 创建任务(立即返回) app.post('/screening/start', async (req, res) => { const job = await jobQueue.push('asl:screening', { projectId: req.body.projectId, literatureIds: req.body.literatureIds }) res.send({ jobId: job.id }) // ⭐ 立即返回,不等待完成 }) // 2. 查询进度 app.get('/screening/jobs/:id', async (req, res) => { const job = await jobQueue.getJob(req.params.id) res.send({ status: job.status, progress: job.progress, // 0-100 result: job.result }) }) // 3. 处理任务(后台) jobQueue.process('asl:screening', async (job) => { const { projectId, literatureIds } = job.data for (let i = 0; i < literatureIds.length; i++) { await screenLiterature(literatureIds[i]) await jobQueue.updateProgress(job.id, (i + 1) / literatureIds.length * 100) } }) ``` --- ### 6. 缓存服务(Cache Service) #### 设计目标 - ✅ 支持本地开发(MemoryCacheAdapter) - ✅ 支持生产环境(RedisCacheAdapter) - ✅ 多实例共享缓存 #### 目录结构 ``` backend/src/common/cache/ ├── CacheAdapter.ts # 接口定义 ├── MemoryCacheAdapter.ts # 本地实现 ├── RedisCacheAdapter.ts # Redis实现 ├── CacheFactory.ts # 工厂类 └── index.ts ``` #### 接口定义 ```typescript export interface CacheAdapter { get(key: string): Promise set(key: string, value: any, ttl?: number): Promise delete(key: string): Promise clear(): Promise } ``` #### 环境切换 ```bash # 本地开发 CACHE_TYPE=memory # 生产环境 CACHE_TYPE=redis REDIS_HOST=r-***.redis.aliyuncs.com ``` --- ### 7. 健康检查(Health Check) #### 设计目标 - ✅ SAE存活检查(/health/liveness) - ✅ SAE就绪检查(/health/readiness) - ✅ 检查依赖服务(数据库) #### 目录结构 ``` backend/src/common/health/ ├── healthCheck.ts └── index.ts ``` #### 实现 ```typescript import { FastifyInstance } from 'fastify' import { prisma } from '@/config/database' export async function registerHealthRoutes(app: FastifyInstance) { // 存活检查 app.get('/health/liveness', async () => { return { status: 'ok', timestamp: Date.now() } }) // 就绪检查 app.get('/health/readiness', async () => { try { await prisma.$queryRaw`SELECT 1` return { status: 'ready', checks: { database: 'ok' } } } catch (error) { return { status: 'not_ready', checks: { database: 'failed' } } } }) } ``` --- ### 8. 监控指标(Monitoring) #### 设计目标 - ✅ 数据库连接数监控 - ✅ 关键指标告警 #### 目录结构 ``` backend/src/common/monitoring/ ├── metrics.ts └── index.ts ``` #### 实现 ```typescript import { prisma } from '@/config/database' import { logger } from '@/common/logging' import { env } from '@/config/env' export class Metrics { static async recordDBConnectionCount() { const result = await prisma.$queryRaw` SELECT count(*) as count FROM pg_stat_activity WHERE datname = current_database() ` const count = result[0].count logger.info('DB connection count', { count }) // 告警:连接数超过80% if (count > env.DB_MAX_CONNECTIONS * 0.8) { logger.warn('DB connection pool near limit', { current: count, max: env.DB_MAX_CONNECTIONS }) } return count } } ``` --- ## 📅 实施计划 ### 总体时间规划 **预计总耗时:2.5天(20小时)** ``` Day 1: 核心基础设施(P0模块) 8小时 Day 2: 辅助基础设施(P1模块)+ 测试 8小时 Day 3: 文档更新 4小时 ``` --- ### Day 1:核心基础设施(P0模块) #### 上午(4小时) **Task 1.1:存储服务(2小时)** - [ ] 创建 `backend/src/common/storage/` 目录 - [ ] 实现 `StorageAdapter.ts`(接口定义) - [ ] 实现 `LocalAdapter.ts`(本地实现) - [ ] 实现 `OSSAdapter.ts`(OSS实现,预留) - [ ] 实现 `StorageFactory.ts`(工厂类) - [ ] 创建 `index.ts`(统一导出) **验收标准**: - ✅ LocalAdapter 可正常 upload/download - ✅ 通过环境变量 `STORAGE_TYPE` 切换 - ✅ 单元测试通过 **Task 1.2:数据库连接池(2小时)** - [ ] 更新 `backend/src/config/database.ts` - [ ] 添加连接池配置 - [ ] 添加优雅关闭逻辑 - [ ] 添加环境变量验证 **验收标准**: - ✅ Prisma Client 正确配置连接池 - ✅ 启动时验证环境变量 - ✅ SIGTERM信号优雅关闭 --- #### 下午(4小时) **Task 1.3:日志系统(2小时)** - [ ] 创建 `backend/src/common/logging/` 目录 - [ ] 实现 `logger.ts`(winston配置) - [ ] 配置JSON格式输出 - [ ] 创建 `index.ts`(导出) **验收标准**: - ✅ 日志输出到stdout(JSON格式) - ✅ 包含timestamp、service、env等元信息 - ✅ 支持不同日志级别(info/warn/error) **Task 1.4:异步任务(2小时)** - [ ] 创建 `backend/src/common/jobs/` 目录 - [ ] 实现 `JobQueue.ts`(接口定义) - [ ] 实现 `MemoryQueue.ts`(内存队列) - [ ] 实现 `DatabaseQueue.ts`(数据库队列,预留) - [ ] 实现 `JobProcessor.ts`(任务处理器) **验收标准**: - ✅ 可创建任务并立即返回 - ✅ 可查询任务进度 - ✅ 可在后台处理任务 --- ### Day 2:辅助基础设施(P1模块)+ 测试 #### 上午(4小时) **Task 2.1:环境配置(1小时)** - [ ] 更新 `backend/src/config/env.ts` - [ ] 统一环境变量定义 - [ ] 添加启动验证函数 **验收标准**: - ✅ 所有环境变量集中管理 - ✅ 启动时自动验证必需配置 - ✅ 支持本地开发和生产环境 **Task 2.2:缓存服务(2小时)** - [ ] 创建 `backend/src/common/cache/` 目录 - [ ] 实现 `CacheAdapter.ts`(接口定义) - [ ] 实现 `MemoryCacheAdapter.ts`(内存实现) - [ ] 实现 `RedisCacheAdapter.ts`(Redis实现,预留) - [ ] 实现 `CacheFactory.ts`(工厂类) **验收标准**: - ✅ MemoryCacheAdapter 可正常 get/set - ✅ 通过环境变量切换 - ✅ 支持TTL过期 **Task 2.3:健康检查(1小时)** - [ ] 创建 `backend/src/common/health/` 目录 - [ ] 实现 `healthCheck.ts` - [ ] 注册 `/health/liveness` 端点 - [ ] 注册 `/health/readiness` 端点 **验收标准**: - ✅ liveness端点正常返回 - ✅ readiness端点检查数据库连接 - ✅ 错误时返回正确状态 --- #### 下午(4小时) **Task 2.4:监控指标(1小时)** - [ ] 创建 `backend/src/common/monitoring/` 目录 - [ ] 实现 `metrics.ts` - [ ] 实现数据库连接数监控 - [ ] 实现告警逻辑 **验收标准**: - ✅ 可查询数据库连接数 - ✅ 连接数超过80%时告警 - ✅ 日志输出正确 **Task 2.5:集成测试(3小时)** - [ ] 编写存储服务单元测试 - [ ] 编写日志系统单元测试 - [ ] 编写缓存服务单元测试 - [ ] 编写异步任务单元测试 - [ ] 集成测试(所有模块) **验收标准**: - ✅ 所有单元测试通过 - ✅ 集成测试通过 - ✅ 代码覆盖率 > 80% --- ### Day 3:文档更新 #### 上午(4小时) **Task 3.1:更新架构文档(2小时)** - [ ] 更新 `01-系统架构分层设计.md` - 在"平台基础层"章节中详细化各服务 - 添加适配器模式说明 - [ ] 更新 `前后端模块化架构设计-V2.md` - 更新 backend 目录结构 - 体现新增的 common/ 子模块 **Task 3.2:更新实施文档(1小时)** - [ ] 更新 `09-架构实施/03-云原生部署架构指南.md` - 添加平台基础设施章节 - 更新环境变量配置示例 **Task 3.3:简化ASL文档(1小时)** - [ ] 更新 `03-业务模块/ASL-AI智能文献/04-开发计划/02-标题摘要初筛开发计划.md` - 移除"创建存储抽象层"任务 - 添加"前置条件:平台已提供" - [ ] 更新 `03-业务模块/ASL-AI智能文献/04-开发计划/03-任务分解.md` - 移除存储抽象层任务 - 更新验收标准 --- ## 🎯 验收标准 ### 总体验收标准 - [ ] **功能完整性**:所有P0模块实现完成 - [ ] **测试覆盖**:单元测试覆盖率 > 80% - [ ] **文档完整性**:所有架构文档更新完成 - [ ] **多环境支持**:本地开发和云端部署验证通过 - [ ] **业务模块验证**:ASL模块可正常使用平台能力 ### 环境验证矩阵 | 验证项 | 本地开发 | 云端SaaS | 私有化部署 | 单机版 | |-------|---------|---------|-----------|--------| | 存储服务 | ✅ LocalAdapter | ✅ OSSAdapter | ✅ LocalAdapter | ✅ LocalAdapter | | 数据库连接 | ✅ 本地PostgreSQL | ✅ 阿里云RDS | ✅ 内网PostgreSQL | ✅ SQLite | | 缓存服务 | ✅ Memory | ✅ Redis | ✅ 内网Redis | ✅ Memory | | 日志系统 | ✅ Console | ✅ 阿里云SLS | ✅ Console | ✅ Console | | 异步任务 | ✅ MemoryQueue | ✅ DatabaseQueue | ✅ DatabaseQueue | ✅ MemoryQueue | | 健康检查 | ✅ OK | ✅ OK | ✅ OK | N/A | --- ## 🚀 实施后的收益 ### 1. 开发效率提升 | 指标 | 改造前 | 改造后 | 提升 | |------|-------|-------|------| | 业务模块开发时间 | 需要实现存储等基础设施 | 直接使用平台能力 | **节省30%** | | 新模块上手时间 | 需要学习基础设施 | 只需关注业务逻辑 | **节省50%** | | 代码复用率 | 每个模块重复实现 | 所有模块复用 | **提升80%** | ### 2. 部署灵活性 | 部署形态 | 支持情况 | 切换成本 | |---------|---------|---------| | 云端SaaS | ✅ 完全支持 | 修改环境变量 | | 私有化部署 | ✅ 完全支持 | 修改环境变量 | | 单机版 | ✅ 完全支持 | 修改环境变量 | | 混合部署 | ✅ 完全支持 | 按模块配置 | ### 3. 商业模式灵活性 | 商业模式 | 支持情况 | |---------|---------| | 专业版(部分模块) | ✅ Feature Flag控制 | | 高级版(组合模块) | ✅ Feature Flag控制 | | 旗舰版(全部模块) | ✅ Feature Flag控制 | | 单模块售卖 | ✅ Docker镜像分层 | ### 4. 技术债务降低 - ✅ 避免重复代码 - ✅ 统一的架构风格 - ✅ 易于维护和升级 - ✅ 新人快速上手 --- ## 📚 相关文档 - [09-总体需求文档(PRD).md](../00-系统总体设计/09-总体需求文档(PRD).md) - 业务需求来源 - [01-系统架构分层设计.md](../00-系统总体设计/01-系统架构分层设计.md) - 总体架构设计 - [前后端模块化架构设计-V2.md](../00-系统总体设计/前后端模块化架构设计-V2.md) - 代码组织架构 - [03-云原生部署架构指南.md](./03-云原生部署架构指南.md) - 云原生部署详细指南 - [08-云原生开发规范.md](../04-开发规范/08-云原生开发规范.md) - 开发规范 --- ## 📝 变更记录 | 日期 | 版本 | 变更内容 | 变更人 | |------|------|---------|--------| | 2025-11-16 | V1.0 | 初始版本,定义平台基础设施规划 | 架构团队 | --- **文档结束**