Files
AIclinicalresearch/docs/09-架构实施/04-平台基础设施规划.md
HaHafeng 31d555f7bb docs: Update architecture docs with platform infrastructure details
- Add platform infrastructure chapter to frontend-backend architecture design
- Update system architecture document with 6 new infrastructure modules
- Update AI onboarding guide with infrastructure overview
- Link to backend/src/common/README.md for detailed usage guide

Key Updates:
- Storage service (LocalAdapter + OSSAdapter)
- Logging system (Winston + JSON format)
- Cache service (Memory + Redis)
- Async job queue (Memory + Database)
- Health check endpoints
- Monitoring metrics
- Database connection pool
- Environment config management

All modules support zero-code switching between local and cloud environments.

Related: #Platform-Infrastructure
2025-11-17 08:36:10 +08:00

22 KiB
Raw Blame History

平台基础设施规划Platform Infrastructure Plan

文档版本: V1.0
创建日期: 2025-11-16
适用对象: 架构师、后端开发、运维
文档状态: 实施规划
维护者: 架构团队


📋 文档说明

本文档是壹证循AI科研平台的平台基础设施规划文档,定义了:

  1. 核心需求:平台基础设施需要解决的问题
  2. 设计方案:技术架构和实现方案
  3. 实施计划:分阶段的实施路线图
  4. 验收标准:每个模块的验收标准

核心目标:

  • 支持本地开发和云端部署无缝切换
  • 支持PRD定义的4种部署形态云端SaaS、私有化、单机版、混合
  • 支持模块化组合售卖(专业版、高级版、旗舰版)
  • 提供通用能力,所有业务模块直接复用

🎯 需求背景

1. 业务需求来自PRD

根据 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               # 统一导出

接口定义

// backend/src/common/storage/StorageAdapter.ts
export interface StorageAdapter {
  upload(key: string, buffer: Buffer): Promise<string>
  download(key: string): Promise<Buffer>
  delete(key: string): Promise<void>
  getUrl(key: string): string
}

环境切换

# 本地开发
STORAGE_TYPE=local

# 生产环境
STORAGE_TYPE=oss
OSS_REGION=oss-cn-hangzhou
OSS_BUCKET=aiclinical-prod

业务模块使用

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

连接池配置

// 计算公式:每实例连接数 = 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)
})

环境变量

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               # 导出

实现

// 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格式
    })
  ]
})

业务模块使用

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

实现

// 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

接口定义

// backend/src/common/jobs/JobQueue.ts
export interface Job<T = any> {
  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<T>(type: string, data: T): Promise<Job<T>>
  process<T>(type: string, handler: (job: Job<T>) => Promise<void>): void
  getJob(id: string): Promise<Job | null>
  updateProgress(id: string, progress: number): Promise<void>
}

业务模块使用ASL模块示例

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

接口定义

export interface CacheAdapter {
  get<T>(key: string): Promise<T | null>
  set(key: string, value: any, ttl?: number): Promise<void>
  delete(key: string): Promise<void>
  clear(): Promise<void>
}

环境切换

# 本地开发
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

实现

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

实现

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.tsOSS实现预留
  • 实现 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.tswinston配置
  • 配置JSON格式输出
  • 创建 index.ts(导出)

验收标准

  • 日志输出到stdoutJSON格式
  • 包含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.tsRedis实现预留
  • 实现 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. 技术债务降低

  • 避免重复代码
  • 统一的架构风格
  • 易于维护和升级
  • 新人快速上手

📚 相关文档


📝 变更记录

日期 版本 变更内容 变更人
2025-11-16 V1.0 初始版本,定义平台基础设施规划 架构团队

文档结束