用户下载的是编译好的、可执行的更新包,而不是源代码。
# Windows平台
# 下载内容: 完整的exe安装包或更新补丁
# 文件大小: 通常20-100MB
# 包含内容:
# ├── youdangdang.exe (主程序)
# ├── resources/ (内置资源)
# ├── chromium/ (内置浏览器)
# └── data/ (应用数据)
# macOS平台
# 下载内容: .dmg磁盘镜像或.app应用程序包
# 文件大小: 通常50-200MB
# 包含内容:
# ├── 游铛铛.app/
# │ ├── Contents/
# │ │ ├── MacOS/ (可执行文件)
# │ │ ├── Resources/ (图标、资源)
# │ │ └── Frameworks/ (依赖库)
# │ └── chromium/ (内置浏览器)
# Linux平台
# 下载内容: .AppImage自包含可执行文件
# 文件大小: 通常40-150MB
# 包含内容:
# └── youdangdang.AppImage (自包含一切的单文件)
graph TB
subgraph "开发环境"
A[代码仓库] --> B[CI/CD流水线]
B --> C[编译打包]
C --> D[生成更新包]
end
subgraph "版本管理服务器"
E[版本数据库] --> F[API服务]
D --> G[上传更新包]
G --> H[CDN存储]
H --> I[分发网络]
end
subgraph "客户端"
J[版本检查] --> F
F --> K[下载更新]
K --> I
end
L[管理后台] --> E
L --> F
-- 版本信息表
CREATE TABLE app_versions (
id INT PRIMARY KEY AUTO_INCREMENT,
version VARCHAR(20) NOT NULL, -- 版本号,如 "1.0.1"
version_code INT NOT NULL, -- 版本代码,如 101
platform ENUM('windows', 'macos', 'linux') NOT NULL,
arch ENUM('x86_64', 'aarch64', 'arm64') NOT NULL,
file_name VARCHAR(255) NOT NULL, -- 文件名
file_path VARCHAR(500) NOT NULL, -- 存储路径
file_size BIGINT NOT NULL, -- 文件大小
file_hash VARCHAR(128) NOT NULL, -- SHA256哈希
signature TEXT, -- 数字签名
release_notes TEXT, -- 发布说明
is_forced BOOLEAN DEFAULT FALSE, -- 是否强制更新
is_active BOOLEAN DEFAULT TRUE, -- 是否激活
download_count INT DEFAULT 0, -- 下载次数
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
UNIQUE KEY unique_version_platform (version, platform, arch)
);
-- 版本发布记录表
CREATE TABLE version_releases (
id INT PRIMARY KEY AUTO_INCREMENT,
version_id INT NOT NULL,
release_type ENUM('major', 'minor', 'patch', 'beta', 'alpha') DEFAULT 'minor',
release_channel ENUM('stable', 'beta', 'dev') DEFAULT 'stable',
min_version VARCHAR(20), -- 最低支持版本
max_version VARCHAR(20), -- 最高支持版本
target_users JSON, -- 目标用户群体
rollout_percentage DECIMAL(5,2) DEFAULT 100.00, -- 灰度发布百分比
is_active BOOLEAN DEFAULT TRUE,
released_at TIMESTAMP NULL,
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
FOREIGN KEY (version_id) REFERENCES app_versions(id)
);
-- 用户版本记录表
CREATE TABLE user_versions (
id INT PRIMARY KEY AUTO_INCREMENT,
user_id INT NOT NULL,
current_version VARCHAR(20),
last_updated TIMESTAMP NULL,
update_status ENUM('up_to_date', 'update_available', 'updating', 'update_failed') DEFAULT 'up_to_date',
platform VARCHAR(50),
device_info JSON, -- 设备信息
created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
updated_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP,
INDEX idx_user_version (user_id, current_version)
);
# /api/admin/versions - 版本管理接口
@app.post("/versions")
async def upload_version(
version: str = Form(...),
platform: str = Form(...),
file: UploadFile = File(...),
release_notes: str = Form(...),
is_forced: bool = Form(False)
):
"""
上传新版本
1. 验证版本号格式
2. 计算文件哈希
3. 生成数字签名
4. 上传到CDN存储
5. 保存到数据库
"""
# 实现版本上传逻辑
pass
@app.put("/versions/{version_id}/release")
async def release_version(
version_id: int,
rollout_percentage: float = 100.0,
target_users: List[str] = None
):
"""
发布版本
支持灰度发布和定向发布
"""
# 实现版本发布逻辑
pass
@app.delete("/versions/{version_id}")
async def rollback_version(version_id: int):
"""
回滚版本
将指定版本标记为非活跃状态
"""
# 实现版本回滚逻辑
pass
# /api/updates - 客户端更新检查接口
@app.get("/updates/{platform}/{current_version}")
async def check_updates(platform: str, current_version: str):
"""
检查更新
返回最新可用版本信息
"""
# 根据平台和当前版本返回更新信息
latest_version = await get_latest_version(platform, current_version)
if not latest_version:
return Response(status_code=204) # 无更新
return {
"version": latest_version.version,
"notes": latest_version.release_notes,
"pub_date": latest_version.created_at.isoformat(),
"platforms": {
f"{platform}-{latest_version.arch}": {
"signature": latest_version.signature,
"url": generate_download_url(latest_version),
"size": latest_version.file_size
}
}
}
@app.get("/updates/download/{version}/{platform}/{filename}")
async def download_update(version: str, platform: str, filename: str):
"""
下载更新包
从CDN重定向或直接提供下载
"""
# 生成下载链接并返回
pass
# 1. 代码提交到主分支
git push origin main
# 2. CI/CD 自动触发构建
# - 拉取最新代码
# - 运行测试
# - 编译不同平台版本
# - 生成更新包
# 3. 构建产物
npm run tauri:build -- --target all
# 输出结果:
# ├── bundles/
# │ ├── windows-x86_64/
# │ │ └── youdangdang_1.0.1_x64-setup.exe
# │ ├── macos-x86_64/
# │ │ └── youdangdang_1.0.1_x64.dmg
# │ └── linux-x86_64/
# │ └── youdangdang_1.0.1_amd64.AppImage
# 管理员通过管理后台上传版本
POST /api/admin/versions
{
"version": "1.0.1",
"platform": "windows",
"file": "youdangdang_1.0.1_x64-setup.exe",
"release_notes": "- 修复扫货功能bug\n- 提升稳定性",
"is_forced": false
}
# 系统自动处理:
# 1. 验证文件完整性
# 2. 计算SHA256哈希
# 3. 生成数字签名
# 4. 上传到CDN存储
# 5. 保存版本信息到数据库
# 设置灰度发布
PUT /api/admin/versions/123/release
{
"rollout_percentage": 10.0, # 先发布给10%的用户
"target_users": ["vip_users"] # 只发布给VIP用户
}
# 观察使用情况后全量发布
PUT /api/admin/versions/123/release
{
"rollout_percentage": 100.0
}
主版本.次版本.补丁版本[-预发布标签]
1.0.0 # 初始版本
1.0.1 # 补丁版本
1.1.0 # 次版本(新增功能)
2.0.0 # 主版本(重大变更)
1.0.0-alpha # 预发布版本
1.0.0-beta # 测试版本
class VersionStrategy:
@staticmethod
def should_force_update(version: str) -> bool:
"""判断是否需要强制更新"""
major, minor, patch = version.split('.')
# 主版本升级强制更新
if int(major) > 1:
return True
# 包含安全修复的版本强制更新
if 'SECURITY' in release_notes:
return True
return False
@staticmethod
def get_rollout_percentage(version: str, days_since_release: int) -> float:
"""根据发布时间动态调整发布比例"""
if days_since_release < 1:
return 1.0 # 第一天1%用户
elif days_since_release < 3:
return 10.0 # 前三天10%用户
elif days_since_release < 7:
return 50.0 # 第一周50%用户
else:
return 100.0 # 之后全量发布
-- 版本分布统计
SELECT
current_version,
COUNT(*) as user_count,
COUNT(*) * 100.0 / SUM(COUNT(*)) OVER() as percentage
FROM user_versions
WHERE last_updated >= DATE_SUB(NOW(), INTERVAL 30 DAY)
GROUP BY current_version
ORDER BY user_count DESC;
-- 更新成功率统计
SELECT
DATE(updated_at) as date,
SUM(CASE WHEN update_status = 'up_to_date' THEN 1 ELSE 0 END) as success_count,
SUM(CASE WHEN update_status = 'update_failed' THEN 1 ELSE 0 END) as failed_count,
ROUND(
SUM(CASE WHEN update_status = 'up_to_date' THEN 1 ELSE 0 END) * 100.0 /
COUNT(*), 2
) as success_rate
FROM user_versions
WHERE updated_at >= DATE_SUB(NOW(), INTERVAL 7 DAY)
GROUP BY DATE(updated_at);
# 1. 生成密钥对
tauri signer generate
# 2. 签名更新包
tauri signer sign -k private_key.pem update.exe
# 3. 客户端验证签名
tauri updater verify -k public_key.pem update.exe
# 版本管理API需要管理员权限
@app.post("/admin/versions")
async def upload_version(
file: UploadFile = File(...),
current_user: User = Depends(get_current_admin_user) # 验证管理员权限
):
# 只有管理员能上传版本
pass
现象: 客户端提示签名验证失败
解决: 重新生成签名,重新上传
现象: 用户无法下载最新版本
解决: 检查CDN缓存策略,强制刷新
现象: 某些用户无法更新
解决: 检查系统要求,添加兼容性检查
总结:公司服务器端通过专门的版本管理系统来管理exe文件,客户端下载的是编译好的可执行包,整个流程包括构建、上传、发布、分发等环节,确保安全、可控、高效的版本管理。