feat: 支持 Linux 客户端打包与跨平台运行

This commit is contained in:
2026-07-14 08:39:41 +00:00
parent c960d3b04d
commit f703ab0302
27 changed files with 1249 additions and 417 deletions
+2 -6
View File
@@ -27,12 +27,8 @@ target_include_directories(Launcher PRIVATE ${OPENSSL_INC})
# 链接Common,自动带上OpenSSL库
target_link_libraries(Launcher Common Qt5::Core Qt5::Network Qt5::Gui Qt5::Widgets)
# 输出编译产物到 out 文件夹(干净不乱)
set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/out/lib)
set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/out/bin)
set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_SOURCE_DIR}/out/bin)
# 强制VS打开CMake文件夹时默认选中该可执行目标
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Launcher)
# 强制VS打开CMake文件夹时默认选中该可执行目标
set_property(DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY VS_STARTUP_PROJECT Launcher)
# 编译完成自动执行windeployqt,复制Qt dll到exe目录
if(WIN32)
get_target_property(QT_WINDEPLOYQT_EXE Qt5::qmake IMPORTED_LOCATION)
+73 -73
View File
@@ -1,12 +1,12 @@
#include "UpdateLogic.h"
#include "UpdateLogic.h"
#include "ConfigHelper.h"
#include <QDebug>
#include <QJsonArray>
#include <QApplication>
#include <QDir>
#include <QSaveFile>
#include "PolicyHelper.h"
#include "LocalStateHelper.h"
#include <QJsonArray>
#include <QApplication>
#include <QDir>
#include <QSaveFile>
#include "PolicyHelper.h"
#include "LocalStateHelper.h"
UpdateLogic::UpdateLogic(QObject* parent)
: QObject(parent)
@@ -83,8 +83,8 @@ void UpdateLogic::checkUpdate()
});
}
void UpdateLogic::getDownloadUrl(const QString& appId, const QString& channel, const QString& ver, int verId)
{
void UpdateLogic::getDownloadUrl(const QString& appId, const QString& channel, const QString& ver, int verId)
{
QString url = m_serverAddr + "/api/v1/update/download-url";
QJsonObject body;
body["app_id"] = appId;
@@ -99,69 +99,69 @@ void UpdateLogic::getDownloadUrl(const QString& appId, const QString& channel, c
qDebug() << "\n========== File Download Url ==========";
qDebug() << resp["files"].toArray();
});
}
bool UpdateLogic::cacheManifest(const QString& appId, const QString& channel, const QString& version,
int versionId, const QString& cacheDir)
{
m_error.clear();
if (appId.isEmpty() || channel.isEmpty() || version.isEmpty() || versionId <= 0) {
m_error = "manifest identity is incomplete";
return false;
}
QJsonObject response;
int statusCode = 0;
QJsonObject body;
body["app_id"] = appId;
body["channel"] = channel;
body["version"] = version;
body["version_id"] = versionId;
m_http.postRequest(m_serverAddr + "/api/v1/update/manifest", body,
[&](int code, const QJsonObject& resp) {
statusCode = code;
response = resp;
});
if (statusCode != 200) {
m_error = QString("manifest request failed (HTTP %1)").arg(statusCode);
return false;
}
const QJsonObject manifest = response.value("manifest").toObject();
const QString manifestText = response.value("manifest_text").toString();
if (manifest.isEmpty() || manifestText.isEmpty()) {
m_error = "manifest response is incomplete";
return false;
}
if (manifest.value("app_id").toString() != appId
|| manifest.value("channel").toString() != channel
|| manifest.value("version").toString() != version) {
m_error = "manifest identity does not match current version";
return false;
}
QDir dir(cacheDir);
if (!dir.exists() && !dir.mkpath(".")) {
m_error = "cannot create manifest cache directory";
return false;
}
QJsonObject wrapper;
wrapper["manifest"] = manifest;
wrapper["manifest_text"] = manifestText;
QSaveFile file(dir.filePath("manifest_" + version + ".json"));
const QByteArray bytes = QJsonDocument(wrapper).toJson(QJsonDocument::Indented);
if (!file.open(QIODevice::WriteOnly) || file.write(bytes) != bytes.size() || !file.commit()) {
m_error = "cannot save signed manifest cache";
return false;
}
qDebug() << "Current version manifest cached to" << file.fileName();
return true;
}
void UpdateLogic::reportUpdateResult(const QString& deviceId, const QString& fromVer, const QString& toVer, bool success)
{
}
bool UpdateLogic::cacheManifest(const QString& appId, const QString& channel, const QString& version,
int versionId, const QString& cacheDir)
{
m_error.clear();
if (appId.isEmpty() || channel.isEmpty() || version.isEmpty() || versionId <= 0) {
m_error = "manifest identity is incomplete";
return false;
}
QJsonObject response;
int statusCode = 0;
QJsonObject body;
body["app_id"] = appId;
body["channel"] = channel;
body["version"] = version;
body["version_id"] = versionId;
m_http.postRequest(m_serverAddr + "/api/v1/update/manifest", body,
[&](int code, const QJsonObject& resp) {
statusCode = code;
response = resp;
});
if (statusCode != 200) {
m_error = QString("manifest request failed (HTTP %1)").arg(statusCode);
return false;
}
const QJsonObject manifest = response.value("manifest").toObject();
const QString manifestText = response.value("manifest_text").toString();
if (manifest.isEmpty() || manifestText.isEmpty()) {
m_error = "manifest response is incomplete";
return false;
}
if (manifest.value("app_id").toString() != appId
|| manifest.value("channel").toString() != channel
|| manifest.value("version").toString() != version) {
m_error = "manifest identity does not match current version";
return false;
}
QDir dir(cacheDir);
if (!dir.exists() && !dir.mkpath(".")) {
m_error = "cannot create manifest cache directory";
return false;
}
QJsonObject wrapper;
wrapper["manifest"] = manifest;
wrapper["manifest_text"] = manifestText;
QSaveFile file(dir.filePath("manifest_" + version + ".json"));
const QByteArray bytes = QJsonDocument(wrapper).toJson(QJsonDocument::Indented);
if (!file.open(QIODevice::WriteOnly) || file.write(bytes) != bytes.size() || !file.commit()) {
m_error = "cannot save signed manifest cache";
return false;
}
qDebug() << "Current version manifest cached to" << file.fileName();
return true;
}
void UpdateLogic::reportUpdateResult(const QString& deviceId, const QString& fromVer, const QString& toVer, bool success)
{
QString url = m_serverAddr + "/api/v1/update/report";
QJsonObject body;
body["app_id"] = m_appId;
@@ -175,4 +175,4 @@ void UpdateLogic::reportUpdateResult(const QString& deviceId, const QString& fro
qDebug() << "\n========== Update Report Result ==========";
qDebug() << resp;
});
}
}
+5 -5
View File
@@ -162,11 +162,11 @@ int main(int argc, char* argv[])
}
const auto configuredName = [&](const QString& key, const QString& fallback) {
const QString value = config.getValue("Runtime", key).trimmed();
return value.isEmpty() ? fallback : value;
};
const QString mainExecutable = configuredName("main_executable", "MainApp.exe");
const QString updaterExecutable = configuredName("updater_executable", "Updater.exe");
return ConfigHelper::executableNameForCurrentPlatform(
config.getValue("Runtime", key), fallback);
};
const QString mainExecutable = configuredName("main_executable", "MainApp");
const QString updaterExecutable = configuredName("updater_executable", "Updater");
const QString mainAppPath = QDir(appDir).filePath(mainExecutable);
const QString updaterPath = QDir(appDir).filePath(updaterExecutable);
const auto importOfflinePackage = [&]() {