📋 项目概述
在 Electron 应用中实现应用商店功能,支持一键下载、安装和运行各种工具(Web应用、桌面应用),实现真正的"开箱即用"体验。
🎯 系统架构图

ini
graph TB
A[Electron 主应用] --> B[应用商店模块]
B --> C[工具管理器]
C --> D[下载引擎]
C --> E[安装引擎]
C --> F[运行引擎]

D --> D1[HTTP 下载]
D --> D2[BT 下载]
D --> D3[分块下载]

E --> E1[Web应用安装]
E --> E2[桌面应用安装]
E --> E3[Docker应用安装]
F --> F1[WebView 运行]
F --> F2[子进程运行]
F --> F3[Docker 运行]
G[工具仓库] --> D
H[本地工具库] --> E
📁 目录结构
bash
electron-app/
├── src/
│ ├── main/
│ │ ├── appStore/ # 应用商店核心
│ │ │ ├── ToolManager.js # 工具管理器
│ │ │ ├── DownloadEngine.js # 下载引擎
│ │ │ ├── InstallEngine.js # 安装引擎
│ │ │ └── RunEngine.js # 运行引擎
│ │ ├── utils/
│ │ │ ├── fileUtils.js # 文件操作工具
│ │ │ ├── networkUtils.js # 网络工具
│ │ │ └── processUtils.js # 进程工具
│ │ └── config/
│ │ ├── toolRegistry.js # 工具注册表
│ │ └── paths.js # 路径配置
│ ├── renderer/
│ │ └── components/
│ │ ├── AppStore.vue # 应用商店界面
│ │ ├── ToolCard.vue # 工具卡片
│ │ └── ProgressModal.vue # 进度弹窗
│ └── shared/
│ └── constants.js # 共享常量
├── tools/ # 本地工具存储
│ ├── web/ # Web应用目录
│ ├── desktop/ # 桌面应用目录
│ └── docker/ # Docker应用目录
└── config/
└── tool-manifest.json # 工具清单配置
🛠️ 核心模块设计
1. 工具管理器 (ToolManager)
javascript
// src/main/appStore/ToolManager.js
class ToolManager {
constructor() {
this.downloadEngine = new DownloadEngine()
this.installEngine = new InstallEngine()
this.runEngine = new RunEngine()
this.installedTools = new Map()
}
// 安装工具
async installTool(toolId, options = {}) {
const toolConfig = await this.getToolConfig(toolId)
// 下载工具包
const downloadPath = await this.downloadEngine.download(
toolConfig.downloadUrl,
toolId,
options
)
// 安装工具
const installPath = await this.installEngine.install(
downloadPath,
toolConfig.type,
toolConfig.installConfig
)
// 注册工具
await this.registerTool(toolId, installPath, toolConfig)
return installPath
}
// 运行工具
async runTool(toolId, runOptions = {}) {
const toolInfo = this.installedTools.get(toolId)
if (!toolInfo) throw new Error(`工具未安装: ${toolId}`)
return await this.runEngine.run(toolInfo, runOptions)
}
// 卸载工具
async uninstallTool(toolId) {
const toolInfo = this.installedTools.get(toolId)
if (!toolInfo) return
await this.runEngine.stop(toolInfo)
await this.installEngine.uninstall(toolInfo.installPath)
this.installedTools.delete(toolId)
}
}
2. 下载引擎 (DownloadEngine)
javascript
// src/main/appStore/DownloadEngine.js
class DownloadEngine {
constructor() {
this.downloads = new Map()
}
async download(url, toolId, options = {}) {
const downloadDir = this.getDownloadDir(toolId)
const fileName = this.getFileNameFromUrl(url)
const filePath = path.join(downloadDir, fileName)
// 支持断点续传
return await this.downloadWithResume(url, filePath, {
onProgress: options.onProgress,
onComplete: options.onComplete
})
}
// 分块下载实现
async downloadWithResume(url, filePath, callbacks = {}) {
return new Promise((resolve, reject) => {
const fileStream = fs.createWriteStream(filePath)
const request = https.get(url, (response) => {
const totalSize = parseInt(response.headers['content-length'], 10)
let downloadedSize = 0
response.on('data', (chunk) => {
downloadedSize += chunk.length
const progress = (downloadedSize / totalSize) * 100
callbacks.onProgress?.(progress)
})
response.pipe(fileStream)
fileStream.on('finish', () => {
fileStream.close()
callbacks.onComplete?.(filePath)
resolve(filePath)
})
})
request.on('error', reject)
})
}
}
3. 安装引擎 (InstallEngine)
javascript
// src/main/appStore/InstallEngine.js
class InstallEngine {
// 根据工具类型进行安装
async install(filePath, toolType, config = {}) {
switch (toolType) {
case 'web':
return await this.installWebApp(filePath, config)
case 'desktop':
return await this.installDesktopApp(filePath, config)
case 'docker':
return await this.installDockerApp(filePath, config)
default:
throw new Error(`不支持的工具体型: ${toolType}`)
}
}
// 安装 Web 应用
async installWebApp(filePath, config) {
const installDir = this.getWebAppInstallDir(config.id)
// 解压文件
if (filePath.endsWith('.zip')) {
await this.extractZip(filePath, installDir)
} else if (filePath.endsWith('.tar.gz')) {
await this.extractTarGz(filePath, installDir)
}
// 检查依赖并自动安装
await this.installDependencies(installDir, config.dependencies)
return installDir
}
// 安装桌面应用
async installDesktopApp(filePath, config) {
const installDir = this.getDesktopAppInstallDir(config.id)
if (process.platform === 'win32') {
// Windows: 静默安装 MSI/EXE
return await this.silentInstallWindows(filePath, installDir, config)
} else if (process.platform === 'darwin') {
// macOS: 安装 DMG/APP
return await this.installMacApp(filePath, installDir, config)
} else {
// Linux: 安装 DEB/RPM/AppImage
return await this.installLinuxApp(filePath, installDir, config)
}
}
}
4. 运行引擎 (RunEngine)
typescript
// src/main/appStore/RunEngine.js
class RunEngine {
async run(toolInfo, options = {}) {
const { type, installPath, config } = toolInfo
switch (type) {
case 'web':
return await this.runWebApp(installPath, config, options)
case 'desktop':
return await this.runDesktopApp(installPath, config, options)
case 'docker':
return await this.runDockerApp(installPath, config, options)
}
}
// 运行 Web 应用
async runWebApp(installPath, config, options) {
// 启动本地服务器
const serverProcess = await this.startLocalServer(installPath, config)
// 等待服务就绪
await this.waitForServerReady(config.port, 30000)
// 在 Electron 窗口中打开
const window = new BrowserWindow({
width: options.width || 1200,
height: options.height || 800,
webPreferences: {
webSecurity: false,
allowRunningInsecureContent: true
}
})
window.loadURL(`http://localhost:${config.port}`)
return {
type: 'web',
window,
process: serverProcess,
url: `http://localhost:${config.port}`
}
}
// 启动本地服务器
async startLocalServer(installPath, config) {
const packageJsonPath = path.join(installPath, 'package.json')
if (fs.existsSync(packageJsonPath)) {
// Node.js 项目
return this.startNodeServer(installPath, config)
}
// 其他类型服务器(Python、Go等)
return this.startGenericServer(installPath, config)
}
}
📊 工具清单配置
json
// config/tool-manifest.json
{
"tools": {
"presenton": {
"id": "presenton",
"name": "Presenton AI",
"description": "AI 演示文稿生成器",
"version": "1.0.0",
"type": "web",
"category": "productivity",
"icon": "https://example.com/presenton-icon.png",
"download": {
"url": "https://github.com/presenton/presenton/releases/latest/download/presenton-web.zip",
"checksum": "sha256:abc123...",
"size": 15678900
},
"install": {
"dependencies": ["nodejs"],
"commands": {
"windows": "npm install && npm run build",
"unix": "npm install && npm run build"
}
},
"run": {
"command": "npm start",
"port": 5000,
"healthCheck": "/health",
"timeout": 30000
},
"requirements": {
"minMemory": "512MB",
"minStorage": "200MB",
"dependencies": ["docker"]
}
},
"vscode": {
"id": "vscode",
"name": "Visual Studio Code",
"type": "desktop",
"download": {
"windows": "https://code.visualstudio.com/sha/download?build=stable&os=win32-x64",
"darwin": "https://code.visualstudio.com/sha/download?build=stable&os=darwin",
"linux": "https://code.visualstudio.com/sha/download?build=stable&os=linux-x64"
}
}
}
}
🔧 环境隔离方案
1. 依赖管理
javascript
// 自动环境管理
class EnvironmentManager {
async ensureRuntime(toolId, runtimeConfig) {
const runtimeDir = this.getRuntimeDir(toolId)
// 检查是否已安装
if (!await this.checkRuntimeInstalled(runtimeDir, runtimeConfig)) {
await this.installRuntime(runtimeDir, runtimeConfig)
}
return runtimeDir
}
// 安装 Node.js 运行时
async installNodeRuntime(runtimeDir, version) {
const nodeUrl = this.getNodeDownloadUrl(version, process.platform)
const nodeArchive = await this.downloadEngine.download(nodeUrl)
await this.extractArchive(nodeArchive, runtimeDir)
await this.setupEnvironment(runtimeDir)
}
}
2. 容器化运行(可选)
javascript
// 使用 Docker 进行环境隔离
class DockerRunner {
async runToolInContainer(toolInfo, options) {
const imageName = `tool-${toolInfo.id}`
// 构建 Docker 镜像
if (!await this.imageExists(imageName)) {
await this.buildDockerImage(toolInfo.installPath, imageName)
}
// 运行容器
const containerId = await this.runContainer(imageName, {
ports: { [toolInfo.config.port]: options.hostPort },
volumes: this.getVolumeMounts(toolInfo),
environment: options.environment
})
return { containerId, imageName }
}
}
🎨 用户界面设计
1. 应用商店界面组件
ini
<!-- src/renderer/components/AppStore.vue -->
<template>
<div class="app-store">
<div class="toolbar">
<input v-model="searchQuery" placeholder="搜索工具..." />
<div class="filters">
<button
v-for="category in categories"
:key="category"
:class="{ active: activeCategory === category }"
@click="setCategory(category)"
>
{{ category }}
</button>
</div>
</div>
<div class="tool-grid">
<ToolCard
v-for="tool in filteredTools"
:key="tool.id"
:tool="tool"
:installation-status="getInstallationStatus(tool.id)"
@install="installTool(tool)"
@run="runTool(tool)"
@uninstall="uninstallTool(tool)"
/>
</div>
<ProgressModal
v-if="currentOperation"
:operation="currentOperation"
:progress="operationProgress"
@cancel="cancelCurrentOperation"
/>
</div>
</template>
🚀 部署和更新策略
1. 自动更新机制
csharp
// 工具自动更新
class AutoUpdater {
async checkForUpdates() {
const updates = []
for (const [toolId, toolInfo] of this.toolManager.installedTools) {
const latestVersion = await this.getLatestVersion(toolId)
if (this.isNewerVersion(latestVersion, toolInfo.version)) {
updates.push({
toolId,
currentVersion: toolInfo.version,
latestVersion,
changelog: await this.getChangelog(toolId)
})
}