# 模块独立部署与单机版方案 > **文档版本?* v1.0 > **创建日期?* 2025-11-06 > **最后更新:** 2025-11-06 > **文档状态:** 架构设计 > **作者:** 技术架构师 --- ## 📋 核心问题 1. **如何实现每个模块独立部署?* - 模块依赖平台层和通用能力层,如何独立? - 如何变成独立产品销售? - 如何实现本地化部署? 2. **如何开发单机版?* - 如何利用Electron框架? - 如何复用现有代码? - 架构如何设计? --- ## 🎯 Part 1:模块独立部署方? ### 核心理念:独立部??完全孤立 **关键洞察?* ``` 独立部署的真正含义: ?可以单独打包 ?可以单独运行 ?可以单独销? ?可以单独升级 但不是: ?完全不依赖其他代? ?完全不共享基础设施 ``` **类比?* ``` 就像一个独立的手机App? - 它依赖操作系统(Android/iOS? - 它依赖系统API(摄像头、定位等? - 但它可以独立下载、安装、运行、卸? 模块独立部署也是一样: - 它依赖平台层(用户认证、存储等? - 它依赖通用能力层(LLM网关、文档处理等? - 但它可以独立打包、部署、销? ``` --- ## 📦 方案一:完整打包(推荐用于独立产品? ### 适用场景 - ?模块作为独立产品销售(如审稿系统) - ?客户要求完全本地化部? - ?不依赖其他模块或平台 ### 核心思路 **将依赖的平台层和通用能力层一起打?* ``` 审稿系统独立产品包: ┌─────────────────────────────────────────? ? RVW审稿系统独立产品 ? ? ? ? ┌────────────────────────────────────?? ? ? 业务模块? ?? ? ? RVW(稿件审查) ?? ? └────────────────────────────────────?? ? ? ? ┌────────────────────────────────────?? ? ? 通用能力层(必需部分? ?? ? ? - LLM网关 ?? ? ? - 文档处理引擎 ?? ? └────────────────────────────────────?? ? ? ? ┌────────────────────────────────────?? ? ? 平台基础层(必需部分? ?? ? ? - 用户认证(简化版? ?? ? ? - 存储服务 ?? ? ? - 监控日志 ?? ? └────────────────────────────────────?? ? ? ? ┌────────────────────────────────────?? ? ? 独立数据? ?? ? ? PostgreSQL(Docker? ?? ? └────────────────────────────────────?? └─────────────────────────────────────────? ``` --- ### 技术实? #### 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? **审稿系统独立产品的依赖:** ```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" } } ``` **平台核心的选择性导出:** ```typescript // 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: 打包脚本 **审稿系统独立打包?* ```javascript // 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部署配置 **审稿系统独立部署?* ```yaml # 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: 一键部署脚? ```bash #!/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. 平台层的精简和适配 **完整平台的用户认证(复杂):** ```typescript // 完整平台 - 多租户支? - RBAC权限控制 - SSO单点登录 - 第三方登录(微信、企业微信等? - 复杂的权限树 ``` **审稿系统的用户认证(简化)?* ```typescript // 审稿系统独立产品 - 简化的用户认证(邮?密码? - 简单的角色(管理员、编辑、审稿人? - 基于JWT的Token认证 - 无SSO、无多租? ``` **实现方式?* ```typescript // 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网关(复杂)?* ```typescript // 完整平台 - 支持10+种模? - Feature Flag控制 - 配额管理 - 成本分析 - A/B测试 ``` **审稿系统的LLM网关(简化)?* ```typescript // 审稿系统独立产品 - 只支?-2种模型(DeepSeek + Claude? - 简单的配额检? - 无Feature Flag - 无成本分? ``` **实现方式?* ```typescript // 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. 数据库的隔离 **完整平台的数据库(复杂)?* ```sql -- 平台层Schema platform_schema.users platform_schema.tenants platform_schema.feature_flags -- ... -- 所有模块Schema aia_schema.* asl_schema.* review_schema.* -- ... ``` **审稿系统独立产品的数据库(简化)?* ```sql -- 只包含必需的表 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: 服务拆分 **平台基础服务(独立部署)?* ```yaml # 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" ``` **通用能力服务(独立部署)?* ```yaml # 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" ``` **业务模块服务(独立部署)?* ```yaml # 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网关配置?* ```yaml # 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: 服务间通信 **业务模块调用共享服务?* ```typescript // 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. 部署共享服务(一次性)?* ```bash # 部署平台基础服务 cd services/platform docker-compose up -d # 部署通用能力服务 cd services/capabilities docker-compose up -d # 部署API网关 cd services/api-gateway docker-compose up -d ``` **2. 部署业务模块(按需):** ```bash # 只部署审稿系统模? cd services/modules/review-system docker-compose up -d # 客户A购买了审稿系统,只需部署这一个模? # 其他模块不需要部? ``` **3. 新客户购买其他模块:** ```bash # 客户B购买了AI文献模块,再部署这个模块 cd services/modules/literature-system docker-compose up -d # 共享服务已经部署,不需要重复部? # 只需增加新的业务模块 ``` --- ## 🖥?Part 2:Electron单机版方? ### 为什么需要单机版? **核心需求:** 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. 前端代码复用?0%+ 复用? **现有前端代码(Web版)?* ```typescript // 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调用方式):** ```typescript // 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组件完全不需要改?* ```typescript // 业务组件完全不变 ? function UserList() { const [users, setUsers] = useState([]); useEffect(() => { // API调用方式不变,底层自动适配 apiClient.get('/api/users').then(setUsers); }, []); return
{/* ... */}
; } ``` **复用率:90%+ ?* - ?所有React组件 - ?所有UI库(Ant Design? - ?所有状态管理(Zustand? - ?所有路由(React Router? - ⚠️ 只需修改API调用层(1个文件) --- #### 2. 后端代码复用?0%+ 复用? **现有后端代码(Web版)?* ```typescript // 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版后端(主进程)?* ```typescript // 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); } // ... 其他路由 }); } ``` **核心业务逻辑完全复用?* ```typescript // 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修改(最小改动)?* ```prisma // 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") } ``` **注意事项?* ```typescript // 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? ```typescript // 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? ```typescript // 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? ```typescript // 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子进程管? ```typescript // 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 { 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? ```yaml # 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 总计?00-700MB ``` **优化方案?* 1. ?使用ASAR压缩(Electron默认? 2. ?不包含R运行时(仅云端版需要) 3. ?Python依赖精简(只包含必需的包? 4. ?增量更新(只下载变化的部分) --- #### 挑战2:跨平台兼容? **需要分别打包:** - Windows x64 - Windows ARM64(Surface等设备) - macOS x64 - macOS ARM64(Apple Silicon? - Linux x64 **测试工作量:** - 每个平台都需要独立测? - 安装、升级、卸载流? - 权限问题、文件路径问? --- #### 挑战3:自动更? **electron-updater配置?* ```typescript // 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架构的设计?