- 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
1555 lines
42 KiB
Markdown
1555 lines
42 KiB
Markdown
# 模块独立部署与单机版方案
|
||
|
||
> **文档版本:** 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
|
||
// 审稿系统独立产品
|
||
- 只支持1-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. 前端代码复用(90%+ 复用)
|
||
|
||
**现有前端代码(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 <div>{/* ... */}</div>;
|
||
}
|
||
```
|
||
|
||
**复用率:90%+ ✅**
|
||
- ✅ 所有React组件
|
||
- ✅ 所有UI库(Ant Design)
|
||
- ✅ 所有状态管理(Zustand)
|
||
- ✅ 所有路由(React Router)
|
||
- ⚠️ 只需修改API调用层(1个文件)
|
||
|
||
---
|
||
|
||
#### 2. 后端代码复用(80%+ 复用)
|
||
|
||
**现有后端代码(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<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)
|
||
|
||
```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
|
||
|
||
总计:500-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架构的设计?
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|
||
|