4.0 KiB
4.0 KiB
93 Client:实现安全的在线检查、下载和下次启动更新
- 类型:需求
- 父级大工单:#1
- 所属 MVP / 版本:MVP 后续 / 在线更新
- 状态:已完成,用户验收通过
- 日期:2026-08-10
- Gitea 工单:#93
背景与目标
#92 已建立 Launcher.exe + app/ + data/ 发布结构,但 Client 仍需人工取得并
替换更新包。本任务增加用户主动检查和下载更新的能力,并保证更新不阻塞界面、
不覆盖本地数据,也不强制中断正在执行的采集或采购任务。
最终方案
- 设置页显示当前版本、HTTPS 清单地址、检查按钮和稳定的更新状态。
- 检查和下载使用
QObject + moveToThread,发现新版本后先由用户确认。 - 清单地址只允许不含凭据、查询参数和片段的 HTTPS URL;更新包必须同源,并校验 大小、SHA256、版本和 ZIP 路径。
- 更新包安全暂存到
data/update/app.new,不会直接替换运行中的程序。 - 下次通过
Launcher.exe启动时才替换app,旧版保留为app.old;移动失败或 新版本未通过健康检查时恢复旧版。 - Client 版本升级为
0.2.0。由于0.1.0的 Launcher 不具备更新能力,第一次 升级到0.2.0仍须人工替换完整发布目录,从0.2.0起才支持在线更新。
最终实现与建单方案一致。清单 URL 额外禁止查询参数,避免 token 一类凭据随地址 写入 SQLite 或错误提示。
改了哪些
client/src/update_service.py:检查清单、下载校验、安全解压和待更新状态。client/src/update_ui_event.py:在线更新后台 Worker、确认框、进度和生命周期。client/src/settings_ui.py、settings_ui_event.py:设置页软件更新区域及事件装配。client/build_tools/launcher.py:下次启动替换、运行识别、健康确认和失败回退。client/src/ui_main.py、version.py:启动健康标记和0.2.0版本号。client/test/test_update_service.py、test_update_ui_event.py、test_packaging.py: 更新服务、界面线程和 Launcher 测试。docs/client/01-requirements.md、02-architecture.md、05-ui-specification.md、06-quality-security.md:同步需求、架构、界面和安全基线。
验收结果
| 验收标准 | 结果 |
|---|---|
| 设置页可配置 HTTPS 清单地址,非法地址有明确提示 | 通过 |
| 检查和下载不阻塞主线程,且防止重复启动 | 通过 |
| 新版本由用户确认下载,完成后不强制重启 | 通过 |
| 校验大小、SHA256、版本、HTTPS 和同源 | 通过 |
| 拒绝越界路径、绝对路径、反斜杠路径和符号链接 | 通过 |
更新只写 data/update,本地 data 内容保持不变 |
通过 |
Launcher 保留 app.old,支持移动失败和健康失败回退 |
通过 |
| 已运行的主程序不会被 Launcher 替换 | 通过 |
| 真实下单开关和 Admin 业务接口未改变 | 通过 |
| 自动测试、打包和旧版升级冒烟通过 | 通过 |
测试
- 执行的命令:
- 在
client/执行C:/Python310/python.exe -m pytest test -q --disable-warnings - 在
client/执行.\build_client.bat - 在临时发布目录使用最终
Launcher.exe执行 0.1.0 → 0.2.0 升级冒烟
- 在
- 结果:256 项测试通过;0.2.0 发布产物生成成功;升级后当前
app为 0.2.0,app.old为 0.1.0,data哨兵保留,健康标记生成,待更新状态清除。 - 没验证到的部分:尚无真实 HTTPS 静态发布地址,未执行真实公网下载;尚未在 全新且未安装 Python 的 Windows 机器上人工验证;重复 Launcher 的提示由单元 测试覆盖,未人工点击原生提示框。
遗留问题
- 当前 SHA256 用于发现下载损坏,不能证明发布者身份。清单签名和 Windows 代码 签名不在本工单范围内。
相关提交
6d6829efeat: 实现 Client 在线更新与安全回退 (#93)