feat: 优化客户端跨平台配置和运行态管理

This commit is contained in:
2026-07-16 08:14:51 +00:00
parent fb6b080ad4
commit fcd67e08aa
25 changed files with 994 additions and 416 deletions
+20 -13
View File
@@ -35,9 +35,11 @@ bool UpdateTransaction::copyOverwrite(const QString& source, const QString& dest
return QFile::copy(source, destination);
}
bool UpdateTransaction::writeState(const QString& status, const QString& errorCode, const QString& message)
{
m_state["transaction_id"] = m_transactionId;
bool UpdateTransaction::writeState(const QString& status, const QString& errorCode, const QString& message)
{
// upgrade_state.json 是升级事务的“黑匣子”。
// 如果替换文件时断电或崩溃,Bootstrap/Updater 会根据这里的状态继续提交或回滚。
m_state["transaction_id"] = m_transactionId;
m_state["from_version"] = m_fromVersion;
m_state["to_version"] = m_toVersion;
m_state["status"] = status;
@@ -146,9 +148,11 @@ bool UpdateTransaction::recordVerifiedFiles(const QStringList& changedPaths,
return writeState("verified");
}
bool UpdateTransaction::backupCurrentFiles()
{
if (!writeState("waiting_mainapp_exit")) return false;
bool UpdateTransaction::backupCurrentFiles()
{
// 替换前先备份所有将被修改或删除的文件。
// 后续健康检查失败时,可以用这些备份恢复到升级前版本。
if (!writeState("waiting_mainapp_exit")) return false;
QStringList paths = m_changedPaths;
paths.append(m_obsoletePaths);
for (const QString& path : paths) {
@@ -161,9 +165,11 @@ bool UpdateTransaction::backupCurrentFiles()
return writeState("backed_up");
}
bool UpdateTransaction::installStagedFiles(QString* failedPath)
{
if (!writeState("replacing")) return false;
bool UpdateTransaction::installStagedFiles(QString* failedPath)
{
// staging 目录里只放已经下载并校验过 hash 的新文件。
// 真正覆盖安装目录时如果任意一个文件失败,就进入 rollback_required。
if (!writeState("replacing")) return false;
for (const QString& path : m_changedPaths) {
const QString source = QDir(m_stagingDir).filePath(path);
const QString destination = QDir(m_installDir).filePath(path);
@@ -223,10 +229,11 @@ QString UpdateTransaction::backupDir() const { return m_backupDir; }
QStringList UpdateTransaction::obsoletePaths() const { return m_obsoletePaths; }
QString UpdateTransaction::healthFile() const { return QDir(m_updateDir).filePath("health_" + m_transactionId + ".ok"); }
bool UpdateTransaction::recoverInterrupted(const QString& installDir, const QString& updateDir,
QString* restoredVersion, QString* errorMessage)
{
QString stateFile = QDir(updateDir.isEmpty() ? QDir(installDir).filePath("update") : updateDir)
bool UpdateTransaction::recoverInterrupted(const QString& installDir, const QString& updateDir,
QString* restoredVersion, QString* errorMessage)
{
// 启动时恢复未完成事务:如果上次升级停在替换/验证/回滚中间,优先恢复到可启动状态。
QString stateFile = QDir(updateDir.isEmpty() ? QDir(installDir).filePath("update") : updateDir)
.filePath("upgrade_state.json");
const QString legacyStateFile = QDir(installDir).filePath("update/upgrade_state.json");
if (!QFile::exists(stateFile) && QFile::exists(legacyStateFile))
+15 -9
View File
@@ -31,9 +31,11 @@ UpdaterLogic::UpdaterLogic(QObject* parent)
m_serverAddr = ConfigHelper::instance().getValue("Server", "api_base_url");
}
void UpdaterLogic::getManifest(const QString& appId, const QString& channel, const QString& targetVer, int versionId)
{
QString url = m_serverAddr + "/api/v1/update/manifest";
void UpdaterLogic::getManifest(const QString& appId, const QString& channel, const QString& targetVer, int versionId)
{
// Manifest 由服务端按版本动态生成,描述目标版本包含哪些文件以及每个文件的 SHA256。
// Updater 先拿到 Manifest,再请求下载 URL,最后按 Manifest 校验本地文件。
QString url = m_serverAddr + "/api/v1/update/manifest";
QJsonObject body;
body["app_id"] = appId;
body["channel"] = channel;
@@ -145,9 +147,11 @@ bool UpdaterLogic::verifySignature(const QByteArray& payload, const QString& sig
#endif
}
bool UpdaterLogic::verifyManifestSignature(const QString& publicKeyPath) const
{
if (m_manifest.isEmpty() || m_manifestText.isEmpty())
bool UpdaterLogic::verifyManifestSignature(const QString& publicKeyPath) const
{
// 验签用的是客户端随 SDK 分发的公钥。
// 只要服务端私钥没有泄漏,客户端就能发现被篡改的 Manifest。
if (m_manifest.isEmpty() || m_manifestText.isEmpty())
{
qDebug() << "No manifest available to verify";
return false;
@@ -167,9 +171,11 @@ bool UpdaterLogic::verifyManifestSignature(const QString& publicKeyPath) const
return verifySignature(m_manifestText.toUtf8(), signature, path);
}
bool UpdaterLogic::saveManifestCache(const QString& cacheDir) const
{
if (m_manifest.isEmpty())
bool UpdaterLogic::saveManifestCache(const QString& cacheDir) const
{
// 启动后的完整性检查依赖本地 Manifest 缓存。
// 升级成功后缓存签名清单,下一次离线启动也能校验当前版本文件。
if (m_manifest.isEmpty())
return false;
QDir dir(cacheDir);
+1 -1
View File
@@ -1,4 +1,4 @@
#include <QApplication>
#include <QApplication>
#include <QCoreApplication>
#include <QDebug>
#include <QDir>