Files
AIclinicalresearch/docs/00-系统总体设计/06-模块独立部署与单机版方案.md
HaHafeng e52020409c docs: complete documentation system (250+ files)
- System architecture and design documentation
- Business module docs (ASL/AIA/PKB/RVW/DC/SSA/ST)
- ASL module complete design (quality assurance, tech selection)
- Platform layer and common capabilities docs
- Development standards and API specifications
- Deployment and operations guides
- Project management and milestone tracking
- Architecture implementation reports
- Documentation templates and guides
2025-11-16 15:43:55 +08:00

42 KiB
Raw Blame History

模块独立部署与单机版方案

文档版本: v1.0
创建日期: 2025-11-06
最后更新: 2025-11-06
文档状态: 架构设计
作者: 技术架构师


📋 核心问题

  1. 如何实现每个模块独立部署?

    • 模块依赖平台层和通用能力层,如何独立?
    • 如何变成独立产品销售?
    • 如何实现本地化部署?
  2. 如何开发单机版?

    • 如何利用Electron框架
    • 如何复用现有代码?
    • 架构如何设计?

🎯 Part 1模块独立部署方案

核心理念:独立部署 ≠ 完全孤立

关键洞察:

独立部署的真正含义:
✅ 可以单独打包
✅ 可以单独运行
✅ 可以单独销售
✅ 可以单独升级

但不是:
❌ 完全不依赖其他代码
❌ 完全不共享基础设施

类比:

就像一个独立的手机App
- 它依赖操作系统Android/iOS
- 它依赖系统API摄像头、定位等
- 但它可以独立下载、安装、运行、卸载

模块独立部署也是一样:
- 它依赖平台层(用户认证、存储等)
- 它依赖通用能力层LLM网关、文档处理等
- 但它可以独立打包、部署、销售

📦 方案一:完整打包(推荐用于独立产品)

适用场景

  • 模块作为独立产品销售(如审稿系统)
  • 客户要求完全本地化部署
  • 不依赖其他模块或平台

核心思路

将依赖的平台层和通用能力层一起打包

审稿系统独立产品包:
┌─────────────────────────────────────────┐
│    RVW审稿系统独立产品                    │
│                                          │
│  ┌────────────────────────────────────┐ │
│  │  业务模块层                          │ │
│  │  RVW稿件审查                     │ │
│  └────────────────────────────────────┘ │
│                                          │
│  ┌────────────────────────────────────┐ │
│  │  通用能力层(必需部分)              │ │
│  │  - LLM网关                          │ │
│  │  - 文档处理引擎                      │ │
│  └────────────────────────────────────┘ │
│                                          │
│  ┌────────────────────────────────────┐ │
│  │  平台基础层(必需部分)              │ │
│  │  - 用户认证(简化版)                │ │
│  │  - 存储服务                          │ │
│  │  - 监控日志                          │ │
│  └────────────────────────────────────┘ │
│                                          │
│  ┌────────────────────────────────────┐ │
│  │  独立数据库                          │ │
│  │  PostgreSQLDocker                │ │
│  └────────────────────────────────────┘ │
└─────────────────────────────────────────┘

技术实现

Step 1: 代码组织Monorepo

AIclinicalresearch/
  ├── packages/                    # Monorepo包管理
  │   │
  │   ├── platform-core/           # 平台核心(可复用)
  │   │   ├── auth/                # 用户认证
  │   │   ├── storage/             # 存储服务
  │   │   ├── monitoring/          # 监控日志
  │   │   └── index.ts
  │   │
  │   ├── capabilities-core/       # 通用能力核心(可复用)
  │   │   ├── llm-gateway/         # LLM网关
  │   │   ├── document-processor/  # 文档处理
  │   │   ├── rag-engine/          # RAG引擎
  │   │   └── index.ts
  │   │
  │   ├── module-aia/              # AI问答模块
  │   │   ├── backend/
  │   │   ├── frontend/
  │   │   └── package.json
  │   │
  │   ├── module-asl/              # AI文献模块
  │   │   ├── backend/
  │   │   ├── frontend/
  │   │   └── package.json
  │   │
  │   ├── module-review/           # 审稿系统模块
  │   │   ├── backend/
  │   │   ├── frontend/
  │   │   └── package.json
  │   │
  │   └── ...
  │
  ├── products/                    # 独立产品打包
  │   │
  │   ├── review-system/           # 审稿系统独立产品 ⭐
  │   │   ├── docker-compose.yml
  │   │   ├── deploy.sh
  │   │   ├── README.md
  │   │   └── package.json         # 依赖关系
  │   │
  │   ├── literature-system/       # AI文献独立产品 ⭐
  │   │   └── ...
  │   │
  │   └── full-platform/           # 完整平台
  │       └── ...
  │
  └── ...

Step 2: 依赖管理package.json

审稿系统独立产品的依赖:

// products/review-system/package.json
{
  "name": "@yizhengxun/review-system",
  "version": "1.0.0",
  "private": true,
  "dependencies": {
    // 平台核心(必需)
    "@yizhengxun/platform-core": "workspace:*",
    
    // 通用能力(必需)
    "@yizhengxun/capabilities-core": "workspace:*",
    
    // 业务模块
    "@yizhengxun/module-review": "workspace:*"
  },
  "scripts": {
    "build": "node build.js",
    "deploy": "sh deploy.sh"
  }
}

平台核心的选择性导出:

// packages/platform-core/index.ts

// 完整导出(用于完整平台)
export * from './auth';
export * from './storage';
export * from './monitoring';
export * from './notification';

// 精简导出(用于独立产品)
export { 
  // 只导出必需的认证功能
  authMiddleware, 
  jwtService 
} from './auth';

export { 
  // 只导出必需的存储功能
  uploadFile, 
  downloadFile 
} from './storage';

export { 
  // 只导出必需的监控功能
  logError, 
  logAudit 
} from './monitoring';

Step 3: 打包脚本

审稿系统独立打包:

// products/review-system/build.js

const { build } = require('esbuild');
const { dependencies } = require('./package.json');

async function buildReviewSystem() {
  console.log('构建审稿系统独立产品...');
  
  // 1. 构建后端
  await build({
    entryPoints: ['../../packages/module-review/backend/src/index.ts'],
    bundle: true,
    platform: 'node',
    target: 'node18',
    outfile: 'dist/backend/index.js',
    external: ['pg', 'fastify', 'prisma'],  // 不打包这些大库
    
    // 自动包含依赖
    plugins: [
      // 自动解析workspace依赖
      resolveWorkspaceDependencies()
    ]
  });
  
  // 2. 构建前端
  await build({
    entryPoints: ['../../packages/module-review/frontend/src/main.tsx'],
    bundle: true,
    platform: 'browser',
    outfile: 'dist/frontend/index.js',
    loader: { '.tsx': 'tsx' }
  });
  
  // 3. 复制必要文件
  copyFiles([
    'docker-compose.yml',
    'README.md',
    'deploy.sh'
  ]);
  
  console.log('构建完成!');
}

buildReviewSystem();

Step 4: Docker部署配置

审稿系统独立部署:

# products/review-system/docker-compose.yml

version: '3.8'

services:
  # PostgreSQL数据库独立
  postgres:
    image: postgres:15-alpine
    container_name: review-system-postgres
    environment:
      POSTGRES_DB: review_system
      POSTGRES_USER: review
      POSTGRES_PASSWORD: ${DB_PASSWORD}
    volumes:
      - postgres_data:/var/lib/postgresql/data
    networks:
      - review-network

  # 后端服务(包含平台层和通用能力层)
  backend:
    build:
      context: ./dist/backend
    container_name: review-system-backend
    environment:
      DATABASE_URL: postgresql://review:${DB_PASSWORD}@postgres:5432/review_system
      LLM_API_KEY: ${LLM_API_KEY}
    depends_on:
      - postgres
    networks:
      - review-network

  # 前端服务
  frontend:
    build:
      context: ./dist/frontend
    container_name: review-system-frontend
    ports:
      - "80:80"
    depends_on:
      - backend
    networks:
      - review-network

volumes:
  postgres_data:

networks:
  review-network:
    driver: bridge

Step 5: 一键部署脚本

#!/bin/bash
# products/review-system/deploy.sh

echo "======================================"
echo "  审稿系统独立部署脚本"
echo "======================================"

# 1. 检查Docker
if ! command -v docker &> /dev/null; then
    echo "错误未安装Docker"
    exit 1
fi

# 2. 设置环境变量
read -p "请输入数据库密码: " DB_PASSWORD
read -p "请输入LLM API密钥: " LLM_API_KEY

export DB_PASSWORD
export LLM_API_KEY

# 3. 启动服务
docker-compose up -d

# 4. 等待服务启动
echo "等待服务启动..."
sleep 10

# 5. 初始化数据库
docker exec review-system-backend npx prisma migrate deploy

# 6. 创建默认管理员
docker exec review-system-backend node scripts/create-admin.js

echo "======================================"
echo "  部署完成!"
echo "  访问地址http://localhost"
echo "  管理员账号admin@review.com"
echo "  密码admin123请及时修改"
echo "======================================"

关键技术点

1. 平台层的精简和适配

完整平台的用户认证(复杂):

// 完整平台
- 多租户支持
- RBAC权限控制
- SSO单点登录
- 第三方登录(微信、企业微信等)
- 复杂的权限树

审稿系统的用户认证(简化):

// 审稿系统独立产品
- 简化的用户认证(邮箱+密码)
- 简单的角色(管理员、编辑、审稿人)
- 基于JWT的Token认证
- SSO、无多租户

实现方式:

// packages/platform-core/auth/simple-auth.ts
// 为独立产品提供简化版认证

export class SimpleAuthService {
  // 只保留核心功能
  async login(email: string, password: string) { }
  async register(email: string, password: string) { }
  async verifyToken(token: string) { }
  async logout(userId: string) { }
  
  // 移除复杂功能
  // ❌ async loginWithWeChat()
  // ❌ async setupSSO()
  // ❌ async multiTenantCheck()
}

2. 通用能力层的精简和适配

完整平台的LLM网关复杂

// 完整平台
- 支持10+种模型
- Feature Flag控制
- 配额管理
- 成本分析
- A/B测试

审稿系统的LLM网关简化

// 审稿系统独立产品
- 只支持1-2种模型(DeepSeek + Claude
- 简单的配额检查
- Feature Flag
- 无成本分析

实现方式:

// packages/capabilities-core/llm-gateway/simple-llm.ts

export class SimpleLLMGateway {
  // 只保留核心功能
  async chat(params: ChatParams) { }
  async checkQuota(userId: string) { }
  
  // 移除复杂功能
  // ❌ async getCostStats()
  // ❌ async selectModelByVersion()
  // ❌ async runABTest()
}

3. 数据库的隔离

完整平台的数据库(复杂):

-- 平台层Schema
platform_schema.users
platform_schema.tenants
platform_schema.feature_flags
-- ...

-- 所有模块Schema
aia_schema.*
asl_schema.*
review_schema.*
-- ...

审稿系统独立产品的数据库(简化):

-- 只包含必需的表
CREATE DATABASE review_system;

-- 简化的用户表
CREATE TABLE users (
  id UUID PRIMARY KEY,
  email VARCHAR(255) UNIQUE NOT NULL,
  password VARCHAR(255) NOT NULL,
  role VARCHAR(50) NOT NULL,  -- admin/editor/reviewer
  created_at TIMESTAMP DEFAULT NOW()
);

-- 审稿系统的业务表
CREATE TABLE review_tasks (...);
CREATE TABLE review_journals (...);
CREATE TABLE review_reviewers (...);
-- ...

独立产品的优势

优势 说明
独立销售 可以单独定价、单独销售
独立部署 客户可以本地化部署,数据完全隔离
独立升级 不影响其他模块
轻量化 只包含必需功能,包体积小
定制化 可以针对特定客户定制

📦 方案二:共享服务(推荐用于平台内模块)

适用场景

  • 同一客户购买多个模块
  • 云端SaaS部署
  • 模块之间需要共享用户和数据

核心思路

平台层和通用能力层作为共享服务

完整平台架构(微服务版):

┌─────────────────────────────────────────────────────────┐
│                    API网关Kong/Traefik                │
│              路由:/aia/* /asl/* /review/*               │
└─────────────────────────────────────────────────────────┘
         │              │              │
         ↓              ↓              ↓
┌─────────────┐ ┌─────────────┐ ┌─────────────┐
│ AIA模块服务  │ │ ASL模块服务  │ │ RVW模块服务  │
│ (独立部署)   │ │ (独立部署)   │ │ (独立部署)   │
└─────────────┘ └─────────────┘ └─────────────┘
         │              │              │
         └──────────────┴──────────────┘
                      ↓
         ┌───────────────────────────┐
         │    共享服务(平台层)        │
         │  - 用户认证服务              │
         │  - 存储服务                 │
         │  - 监控服务                 │
         └───────────────────────────┘
                      ↓
         ┌───────────────────────────┐
         │  共享服务(通用能力层)      │
         │  - LLM网关服务              │
         │  - 文档处理服务              │
         │  - RAG引擎服务              │
         └───────────────────────────┘
                      ↓
         ┌───────────────────────────┐
         │    共享数据库               │
         │  - PostgreSQL (多Schema)   │
         └───────────────────────────┘

技术实现

Step 1: 服务拆分

平台基础服务(独立部署):

# services/platform/docker-compose.yml

services:
  # 用户认证服务
  auth-service:
    image: yizhengxun/auth-service:latest
    ports:
      - "3001:3000"
    environment:
      DATABASE_URL: ${DATABASE_URL}
      JWT_SECRET: ${JWT_SECRET}

  # 存储服务
  storage-service:
    image: yizhengxun/storage-service:latest
    ports:
      - "3002:3000"
    environment:
      OSS_ENDPOINT: ${OSS_ENDPOINT}

  # 监控服务
  monitoring-service:
    image: yizhengxun/monitoring-service:latest
    ports:
      - "3003:3000"

通用能力服务(独立部署):

# services/capabilities/docker-compose.yml

services:
  # LLM网关服务
  llm-gateway:
    image: yizhengxun/llm-gateway:latest
    ports:
      - "3010:3000"
    environment:
      DEEPSEEK_API_KEY: ${DEEPSEEK_API_KEY}
      QWEN_API_KEY: ${QWEN_API_KEY}

  # 文档处理服务
  document-processor:
    image: yizhengxun/document-processor:latest
    ports:
      - "3011:3000"

  # RAG引擎服务
  rag-engine:
    image: yizhengxun/rag-engine:latest
    ports:
      - "3012:3000"

业务模块服务(独立部署):

# services/modules/review-system/docker-compose.yml

services:
  review-system:
    image: yizhengxun/review-system:latest
    ports:
      - "4001:3000"
    environment:
      # 依赖共享服务
      AUTH_SERVICE_URL: http://auth-service:3000
      STORAGE_SERVICE_URL: http://storage-service:3000
      LLM_GATEWAY_URL: http://llm-gateway:3000
      DOCUMENT_PROCESSOR_URL: http://document-processor:3000
      
      # 自己的数据库Schema
      DATABASE_URL: postgresql://user:pass@postgres:5432/platform?schema=review_schema

Step 2: API网关配置

Kong API网关配置

# api-gateway/kong.yml

_format_version: "3.0"

services:
  # 用户认证服务
  - name: auth-service
    url: http://auth-service:3000
    routes:
      - name: auth-routes
        paths:
          - /api/auth

  # 审稿系统模块
  - name: review-system
    url: http://review-system:3000
    routes:
      - name: review-routes
        paths:
          - /api/review
    plugins:
      - name: jwt
        config:
          secret_is_base64: false
          key_claim_name: kid

  # AI文献模块
  - name: literature-system
    url: http://literature-system:3000
    routes:
      - name: literature-routes
        paths:
          - /api/literature
    plugins:
      - name: jwt

Step 3: 服务间通信

业务模块调用共享服务:

// packages/module-review/backend/src/services/review.service.ts

import { AuthClient } from '@yizhengxun/platform-core/clients';
import { LLMClient } from '@yizhengxun/capabilities-core/clients';

export class ReviewService {
  private authClient: AuthClient;
  private llmClient: LLMClient;
  
  constructor() {
    // 连接到共享服务
    this.authClient = new AuthClient({
      baseURL: process.env.AUTH_SERVICE_URL
    });
    
    this.llmClient = new LLMClient({
      baseURL: process.env.LLM_GATEWAY_URL
    });
  }
  
  async createReviewTask(userId: string, file: File) {
    // 1. 验证用户(调用共享认证服务)
    const user = await this.authClient.verifyUser(userId);
    
    // 2. 上传文件(调用共享存储服务)
    const fileUrl = await this.storageClient.upload(file);
    
    // 3. AI分析调用共享LLM网关
    const analysis = await this.llmClient.chat({
      messages: [{ role: 'user', content: `分析这篇稿件: ${fileUrl}` }]
    });
    
    // 4. 保存到自己的数据库
    const task = await prisma.reviewTask.create({
      data: {
        userId,
        fileUrl,
        analysis: analysis.content
      }
    });
    
    return task;
  }
}

模块独立部署的步骤

1. 部署共享服务(一次性):

# 部署平台基础服务
cd services/platform
docker-compose up -d

# 部署通用能力服务
cd services/capabilities
docker-compose up -d

# 部署API网关
cd services/api-gateway
docker-compose up -d

2. 部署业务模块(按需):

# 只部署审稿系统模块
cd services/modules/review-system
docker-compose up -d

# 客户A购买了审稿系统只需部署这一个模块
# 其他模块不需要部署

3. 新客户购买其他模块:

# 客户B购买了AI文献模块再部署这个模块
cd services/modules/literature-system
docker-compose up -d

# 共享服务已经部署,不需要重复部署
# 只需增加新的业务模块

🖥️ Part 2Electron单机版方案

为什么需要单机版?

核心需求:

  1. 数据隐私:医生个人数据不能上传云端
  2. 离线使用:无需网络连接
  3. 便携性:可以在任何电脑上安装使用
  4. 独立运行:不依赖外部服务器

目标用户:

  • 个人医生
  • 数据敏感的研究者
  • 无网络环境的场景

Electron架构与现有代码的对应关系

现有架构(云端版)

┌─────────────────────────────────────────┐
│            浏览器 (Chrome)               │
│  ┌───────────────────────────────────┐  │
│  │   前端 (React + Vite)              │  │
│  │   http://localhost:5173            │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘
              ↓ HTTP请求
┌─────────────────────────────────────────┐
│     后端 (Node.js + Fastify)            │
│     http://localhost:3001               │
│  ┌───────────────────────────────────┐  │
│  │  API路由、业务逻辑、Prisma ORM     │  │
│  └───────────────────────────────────┘  │
└─────────────────────────────────────────┘
              ↓
┌─────────────────────────────────────────┐
│  PostgreSQL (Docker)                    │
│  localhost:5432                         │
└─────────────────────────────────────────┘

Electron架构单机版

┌───────────────────────────────────────────────────────────┐
│                  Electron 应用                              │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐  │
│  │            渲染进程 (Chromium)                        │  │
│  │  ┌───────────────────────────────────────────────┐  │  │
│  │  │   前端 (React)  ⭐ 复用现有代码                 │  │  │
│  │  │   file://app/index.html                       │  │  │
│  │  └───────────────────────────────────────────────┘  │  │
│  └─────────────────────────────────────────────────────┘  │
│                      ↓ IPC通信                            │
│  ┌─────────────────────────────────────────────────────┐  │
│  │            主进程 (Node.js)                          │  │
│  │  ┌───────────────────────────────────────────────┐  │  │
│  │  │  API逻辑、业务逻辑 ⭐ 复用现有代码              │  │  │
│  │  │  Prisma ORM、本地文件系统                      │  │  │
│  │  └───────────────────────────────────────────────┘  │  │
│  └─────────────────────────────────────────────────────┘  │
│                      ↓                                     │
│  ┌─────────────────────────────────────────────────────┐  │
│  │     SQLite (嵌入式数据库)                            │  │
│  │     ~/Documents/YizhengxunData/app.db               │  │
│  └─────────────────────────────────────────────────────┘  │
│                                                             │
│  ┌─────────────────────────────────────────────────────┐  │
│  │     子进程 (Python/R)                                │  │
│  │     文档提取、数据分析                                │  │
│  └─────────────────────────────────────────────────────┘  │
└───────────────────────────────────────────────────────────┘

核心技术方案

1. 前端代码复用90%+ 复用)

现有前端代码Web版

// frontend/src/api/client.ts
const API_BASE_URL = 'http://localhost:3001';

export const apiClient = {
  async get(url: string) {
    return fetch(`${API_BASE_URL}${url}`);
  },
  async post(url: string, data: any) {
    return fetch(`${API_BASE_URL}${url}`, {
      method: 'POST',
      body: JSON.stringify(data)
    });
  }
};

Electron版前端修改API调用方式

// electron-frontend/src/api/client.ts

// 使用Electron的IPC通信而不是HTTP
const { ipcRenderer } = window.require('electron');

export const apiClient = {
  async get(url: string) {
    // 通过IPC发送到主进程
    return ipcRenderer.invoke('api-request', {
      method: 'GET',
      url
    });
  },
  async post(url: string, data: any) {
    return ipcRenderer.invoke('api-request', {
      method: 'POST',
      url,
      data
    });
  }
};

React组件完全不需要改

// 业务组件完全不变 ✅
function UserList() {
  const [users, setUsers] = useState([]);
  
  useEffect(() => {
    // API调用方式不变底层自动适配
    apiClient.get('/api/users').then(setUsers);
  }, []);
  
  return <div>{/* ... */}</div>;
}

复用率90%+

  • 所有React组件
  • 所有UI库Ant Design
  • 所有状态管理Zustand
  • 所有路由React Router
  • ⚠️ 只需修改API调用层1个文件

2. 后端代码复用80%+ 复用)

现有后端代码Web版

// backend/src/routes/users.ts
import { FastifyInstance } from 'fastify';

export async function userRoutes(fastify: FastifyInstance) {
  // GET /api/users
  fastify.get('/api/users', async (request, reply) => {
    const users = await prisma.user.findMany();
    return users;
  });
  
  // POST /api/users
  fastify.post('/api/users', async (request, reply) => {
    const user = await prisma.user.create({
      data: request.body
    });
    return user;
  });
}

Electron版后端主进程

// electron-backend/src/ipc-handlers.ts
import { ipcMain } from 'electron';
import { prisma } from './prisma-client';

// 复用业务逻辑 ⭐
import { UserService } from '../../../backend/src/services/user.service';

export function setupIpcHandlers() {
  const userService = new UserService(prisma);
  
  // 将HTTP路由转换为IPC Handler
  ipcMain.handle('api-request', async (event, { method, url, data }) => {
    // 路由分发简化版的Fastify
    if (url === '/api/users' && method === 'GET') {
      return userService.findAll();
    }
    
    if (url === '/api/users' && method === 'POST') {
      return userService.create(data);
    }
    
    // ... 其他路由
  });
}

核心业务逻辑完全复用:

// backend/src/services/user.service.ts
// 这个文件在Web版和Electron版完全共享 ✅

export class UserService {
  constructor(private prisma: PrismaClient) {}
  
  async findAll() {
    return this.prisma.user.findMany();
  }
  
  async create(data: CreateUserDto) {
    // 业务逻辑
    const hashedPassword = await bcrypt.hash(data.password, 10);
    return this.prisma.user.create({
      data: {
        ...data,
        password: hashedPassword
      }
    });
  }
}

复用率80%+

  • 所有Service层业务逻辑
  • 所有Prisma Model和查询
  • 所有工具函数
  • ⚠️ 需要适配HTTP路由 → IPC Handler
  • ⚠️ 需要替换PostgreSQL → SQLite

3. 数据库适配PostgreSQL → SQLite

Prisma Schema修改最小改动

// electron-backend/prisma/schema.prisma

datasource db {
  // Web版PostgreSQL
  // provider = "postgresql"
  // url      = env("DATABASE_URL")
  
  // Electron版SQLite ⭐
  provider = "sqlite"
  url      = "file:./app.db"
}

// Model定义完全不变 ✅
model User {
  id        String   @id @default(uuid())
  email     String   @unique
  password  String
  name      String?
  
  createdAt DateTime @default(now())
  updatedAt DateTime @updatedAt
  
  @@map("users")
}

注意事项:

// SQLite不支持某些PostgreSQL特性

// ❌ PostgreSQL支持SQLite不支持
- JSON列类型(需要用TEXT存储
- 复杂的全文搜索
- 某些聚合函数

// ✅ 解决方案:
// 1. 用TEXT存储JSON手动序列化
model Config {
  id    String @id
  data  String  // JSON.stringify(data)
}

// 2. 简化查询避免复杂SQL
// 3. 在应用层实现某些功能

完整项目结构

AIclinicalresearch/
  ├── packages/                    # 共享代码Monorepo
  │   ├── frontend-core/           # 前端核心Web + Electron复用
  │   ├── backend-core/            # 后端核心Web + Electron复用
  │   └── shared-types/            # 共享类型定义
  │
  ├── apps/
  │   ├── web/                     # Web版当前
  │   │   ├── frontend/
  │   │   └── backend/
  │   │
  │   └── electron/                # Electron版 ⭐
  │       ├── main/                # 主进程Node.js后端
  │       │   ├── src/
  │       │   │   ├── main.ts      # Electron入口
  │       │   │   ├── ipc-handlers.ts  # IPC处理器
  │       │   │   ├── services/    # 复用backend-core
  │       │   │   └── prisma/
  │       │   │       └── schema.prisma  # SQLite版本
  │       │   └── package.json
  │       │
  │       ├── renderer/            # 渲染进程React前端
  │       │   ├── src/
  │       │   │   ├── main.tsx
  │       │   │   ├── api/         # IPC客户端
  │       │   │   ├── pages/       # 复用frontend-core
  │       │   │   └── components/  # 复用frontend-core
  │       │   └── package.json
  │       │
  │       ├── resources/           # 打包资源
  │       │   ├── python/          # Python运行时
  │       │   ├── r/               # R运行时如需要
  │       │   └── assets/
  │       │
  │       └── electron-builder.yml # 打包配置
  │
  └── ...

关键实现细节

1. Electron主进程main.ts

// apps/electron/main/src/main.ts

import { app, BrowserWindow, ipcMain } from 'electron';
import path from 'path';
import { setupIpcHandlers } from './ipc-handlers';
import { initDatabase } from './database';
import { startPythonService } from './python-service';

let mainWindow: BrowserWindow | null = null;

async function createWindow() {
  // 1. 创建浏览器窗口
  mainWindow = new BrowserWindow({
    width: 1200,
    height: 800,
    webPreferences: {
      nodeIntegration: false,
      contextIsolation: true,
      preload: path.join(__dirname, 'preload.js')  // 安全的IPC桥接
    }
  });
  
  // 2. 加载前端(生产环境)
  if (app.isPackaged) {
    mainWindow.loadFile(path.join(__dirname, '../renderer/index.html'));
  } else {
    // 开发环境连接到Vite开发服务器
    mainWindow.loadURL('http://localhost:5173');
  }
  
  // 3. 初始化数据库
  await initDatabase();
  
  // 4. 启动Python文档处理服务
  await startPythonService();
  
  // 5. 设置IPC处理器
  setupIpcHandlers();
}

// Electron生命周期
app.whenReady().then(createWindow);

app.on('window-all-closed', () => {
  if (process.platform !== 'darwin') {
    app.quit();
  }
});

app.on('activate', () => {
  if (BrowserWindow.getAllWindows().length === 0) {
    createWindow();
  }
});

2. IPC处理器ipc-handlers.ts

// apps/electron/main/src/ipc-handlers.ts

import { ipcMain } from 'electron';
import { prisma } from './prisma-client';

// 复用业务逻辑 ⭐
import { UserService } from '@yizhengxun/backend-core/services';
import { ProjectService } from '@yizhengxun/backend-core/services';
import { ConversationService } from '@yizhengxun/backend-core/services';

export function setupIpcHandlers() {
  // 初始化服务
  const userService = new UserService(prisma);
  const projectService = new ProjectService(prisma);
  const conversationService = new ConversationService(prisma);
  
  // 用户相关API
  ipcMain.handle('api:users:list', async () => {
    return userService.findAll();
  });
  
  ipcMain.handle('api:users:create', async (event, data) => {
    return userService.create(data);
  });
  
  // 项目相关API
  ipcMain.handle('api:projects:list', async (event, userId) => {
    return projectService.findByUser(userId);
  });
  
  ipcMain.handle('api:projects:create', async (event, data) => {
    return projectService.create(data);
  });
  
  // 对话相关API
  ipcMain.handle('api:conversations:create', async (event, data) => {
    return conversationService.create(data);
  });
  
  ipcMain.handle('api:conversations:chat', async (event, data) => {
    // 调用LLM本地或云端API
    return conversationService.chat(data);
  });
  
  // 文件操作
  ipcMain.handle('api:files:upload', async (event, file) => {
    // 保存到本地文件系统
    const filePath = await saveFile(file);
    return { url: filePath };
  });
}

3. 前端IPC客户端api-client.ts

// apps/electron/renderer/src/api/client.ts

const { ipcRenderer } = window.require('electron');

export const apiClient = {
  // 用户API
  users: {
    async list() {
      return ipcRenderer.invoke('api:users:list');
    },
    async create(data: any) {
      return ipcRenderer.invoke('api:users:create', data);
    }
  },
  
  // 项目API
  projects: {
    async list(userId: string) {
      return ipcRenderer.invoke('api:projects:list', userId);
    },
    async create(data: any) {
      return ipcRenderer.invoke('api:projects:create', data);
    }
  },
  
  // 对话API
  conversations: {
    async create(data: any) {
      return ipcRenderer.invoke('api:conversations:create', data);
    },
    async chat(data: any) {
      return ipcRenderer.invoke('api:conversations:chat', data);
    }
  },
  
  // 文件上传
  files: {
    async upload(file: File) {
      // 将File转换为Buffer
      const buffer = await file.arrayBuffer();
      return ipcRenderer.invoke('api:files:upload', {
        name: file.name,
        data: Buffer.from(buffer)
      });
    }
  }
};

4. Python子进程管理

// apps/electron/main/src/python-service.ts

import { spawn, ChildProcess } from 'child_process';
import path from 'path';
import { app } from 'electron';

let pythonProcess: ChildProcess | null = null;

export async function startPythonService(): Promise<void> {
  return new Promise((resolve, reject) => {
    // Python运行时路径打包时包含
    const pythonPath = app.isPackaged
      ? path.join(process.resourcesPath, 'python', 'python.exe')
      : 'python';  // 开发环境使用系统Python
    
    // Python脚本路径
    const scriptPath = app.isPackaged
      ? path.join(process.resourcesPath, 'python', 'extraction_service', 'main.py')
      : path.join(__dirname, '../../../../extraction_service/main.py');
    
    // 启动Python进程
    pythonProcess = spawn(pythonPath, [
      '-m', 'uvicorn',
      'main:app',
      '--host', '127.0.0.1',
      '--port', '8000'
    ], {
      cwd: path.dirname(scriptPath),
      stdio: 'pipe'
    });
    
    // 监听输出
    pythonProcess.stdout?.on('data', (data) => {
      console.log(`Python: ${data}`);
      if (data.toString().includes('Application startup complete')) {
        resolve();
      }
    });
    
    pythonProcess.stderr?.on('data', (data) => {
      console.error(`Python Error: ${data}`);
    });
    
    pythonProcess.on('error', reject);
  });
}

export function stopPythonService(): void {
  if (pythonProcess) {
    pythonProcess.kill();
    pythonProcess = null;
  }
}

// 应用退出时关闭Python服务
app.on('will-quit', stopPythonService);

5. 打包配置electron-builder.yml

# apps/electron/electron-builder.yml

appId: com.yizhengxun.aiclinical
productName: 壹证循AI科研助手

# 打包目录
directories:
  output: dist
  buildResources: resources

# Windows配置
win:
  target:
    - nsis
    - portable
  icon: resources/icon.ico

# NSIS安装程序配置
nsis:
  oneClick: false
  allowToChangeInstallationDirectory: true
  createDesktopShortcut: always
  createStartMenuShortcut: true

# Mac配置
mac:
  target:
    - dmg
    - zip
  icon: resources/icon.icns
  category: public.app-category.productivity

# 打包包含的文件
files:
  - "main/**/*"
  - "renderer/**/*"
  - "!**/*.ts"
  - "!**/*.map"

# 额外资源Python运行时等
extraResources:
  - from: resources/python
    to: python
  - from: ../../../../extraction_service
    to: python/extraction_service

# 打包后大小
asar: true
asarUnpack:
  - "**/*.node"
  - "resources/**/*"

单机版的关键技术挑战

挑战1打包体积500MB+

包含内容:

安装包组成:
- Electron框架~100MB
- Node.js运行时包含在Electron中
- 前端代码(编译后):~10MB
- 后端代码(编译后):~5MB
- SQLite数据库~5MB空库
- Python运行时~150MB
- Python依赖~100MB
- R运行时可选~200MB
- R依赖可选~100MB

总计500-700MB

优化方案:

  1. 使用ASAR压缩Electron默认
  2. 不包含R运行时仅云端版需要
  3. Python依赖精简只包含必需的包
  4. 增量更新(只下载变化的部分)

挑战2跨平台兼容性

需要分别打包:

  • Windows x64
  • Windows ARM64Surface等设备
  • macOS x64
  • macOS ARM64Apple Silicon
  • Linux x64

测试工作量:

  • 每个平台都需要独立测试
  • 安装、升级、卸载流程
  • 权限问题、文件路径问题

挑战3自动更新

electron-updater配置

// apps/electron/main/src/updater.ts

import { autoUpdater } from 'electron-updater';
import { dialog } from 'electron';

export function setupAutoUpdater() {
  // 配置更新服务器
  autoUpdater.setFeedURL({
    provider: 'generic',
    url: 'https://releases.yizhengxun.com'
  });
  
  // 检查更新
  autoUpdater.checkForUpdatesAndNotify();
  
  // 发现新版本
  autoUpdater.on('update-available', () => {
    dialog.showMessageBox({
      type: 'info',
      title: '发现新版本',
      message: '检测到新版本,是否立即下载?',
      buttons: ['是', '否']
    }).then((result) => {
      if (result.response === 0) {
        autoUpdater.downloadUpdate();
      }
    });
  });
  
  // 下载完成
  autoUpdater.on('update-downloaded', () => {
    dialog.showMessageBox({
      type: 'info',
      title: '更新已就绪',
      message: '新版本已下载,是否立即安装?',
      buttons: ['立即安装', '稍后']
    }).then((result) => {
      if (result.response === 0) {
        autoUpdater.quitAndInstall();
      }
    });
  });
}

📊 总结对比

三种部署方式对比

维度 云端SaaS 独立产品包 Electron单机版
部署方式 云端服务器 Docker容器 本地安装
数据存储 云端PostgreSQL 本地PostgreSQL 本地SQLite
网络需求 必须联网 可离线LLM除外 完全离线
更新方式 无感知更新 Docker镜像更新 应用内更新
安装难度 无需安装 Docker部署 一键安装
代码复用 100% 80%(精简版) 90%
适用客户 个人、小机构 医院、大机构 个人医生
商业模式 订阅制 一次性License 一次性购买
维护成本

🎯 最终建议

分阶段实施

阶段一(当前-6个月

  • 专注云端SaaS版
  • 完善7个业务模块
  • 建立Monorepo架构为未来打基础

阶段二6-12个月

  • 开发独立产品包审稿系统、AI文献
  • 支持Docker本地化部署
  • 验证商业模式

阶段三12-18个月

  • 开发Electron单机版如有需求
  • 支持完全离线使用
  • 扩展个人用户市场

您想先深入哪个方案?

  1. 模块独立部署的详细实施?
  2. Electron单机版的开发计划
  3. Monorepo架构的设计