feat(迁移功能): 添加包迁移功能支持

实现从aptss到apm的包迁移功能
- 添加迁移包集合存储用户确认的迁移项
- 在数据模型中添加迁移相关字段
- 修改合并逻辑以识别迁移场景
- 添加迁移确认对话框
- 处理迁移安装时的特殊逻辑
This commit is contained in:
2026-04-05 10:49:03 +08:00
parent f395d654ea
commit 1becfbc9be
4 changed files with 182 additions and 23 deletions

View File

@@ -9,6 +9,7 @@
#include <QMouseEvent>
#include <QFile>
#include <QEventLoop>
#include <QMessageBox>
AppDelegate::AppDelegate(QObject *parent)
: QStyledItemDelegate(parent), m_downloadManager(new DownloadManager(this)), m_installProcess(nullptr) {
@@ -379,7 +380,24 @@ bool AppDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
if (m_downloads.contains(packageName) && !m_downloads[packageName].isDownloading) {
return false;
}
// 检查是否为迁移项
bool isMigration = index.data(Qt::UserRole + 10).toBool();
if (isMigration) {
QString migrationTarget = index.data(Qt::UserRole + 12).toString();
QMessageBox::StandardButton reply = QMessageBox::question(
nullptr,
"确认迁移",
QString("此更新将把应用替换为 %1 版本,是否继续?").arg(migrationTarget.toUpper()),
QMessageBox::Yes | QMessageBox::No
);
if (reply != QMessageBox::Yes) {
return true; // 用户取消,不执行更新
}
// 用户确认,标记为迁移安装
m_migrationPackages.insert(packageName);
}
// 检查/tmp目录下是否已经存在deb包
QDir tempDir(QDir::tempPath());
QStringList debs = tempDir.entryList(QStringList() << QString("%1_*.deb").arg(packageName), QDir::Files);
@@ -392,7 +410,7 @@ bool AppDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
debPath = tempDir.absoluteFilePath(debs.first());
}
}
// 触发下载流程无论是否存在deb包都尝试续传
QString downloadUrl = index.data(Qt::UserRole + 7).toString();
QString outputPath = QString("%1/%2.metalink").arg(QDir::tempPath(), packageName);
@@ -410,17 +428,77 @@ bool AppDelegate::editorEvent(QEvent *event, QAbstractItemModel *model,
void AppDelegate::startDownloadForAll() {
if (!m_model) return;
// 收集所有迁移项,准备批量提示
QStringList migrationPackages;
for (int row = 0; row < m_model->rowCount(); ++row) {
QModelIndex index = m_model->index(row, 0);
// 检查应用是否被忽略
bool isIgnored = index.data(Qt::UserRole + 8).toBool();
if (isIgnored) {
continue;
}
// 检查是否为迁移项
bool isMigration = index.data(Qt::UserRole + 10).toBool();
if (isMigration) {
QString packageName = index.data(Qt::UserRole + 1).toString();
QString migrationTarget = index.data(Qt::UserRole + 12).toString();
migrationPackages.append(QString("%1 → %2").arg(packageName, migrationTarget.toUpper()));
}
}
// 如果有迁移项,弹出批量确认对话框
if (!migrationPackages.isEmpty()) {
QString message = "以下应用更新后将替换为新的版本来源:\n\n";
message += migrationPackages.join("\n");
message += "\n\n是否继续更新这些应用?";
QMessageBox::StandardButton reply = QMessageBox::question(
nullptr,
"确认迁移",
message,
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel
);
if (reply == QMessageBox::Cancel) {
return; // 完全取消更新全部
}
// 如果用户点击 Yes将所有迁移项加入迁移集合
if (reply == QMessageBox::Yes) {
for (int row = 0; row < m_model->rowCount(); ++row) {
QModelIndex index = m_model->index(row, 0);
bool isMigration = index.data(Qt::UserRole + 10).toBool();
if (isMigration) {
QString packageName = index.data(Qt::UserRole + 1).toString();
m_migrationPackages.insert(packageName);
}
}
}
// 如果用户点击 No跳过迁移项继续更新其他应用
}
for (int row = 0; row < m_model->rowCount(); ++row) {
QModelIndex index = m_model->index(row, 0);
// 检查应用是否被忽略
bool isIgnored = index.data(Qt::UserRole + 8).toBool();
if (isIgnored) {
qDebug() << "跳过被忽略的应用:" << index.data(Qt::UserRole + 1).toString();
continue;
}
QString packageName = index.data(Qt::UserRole + 1).toString();
// 检查是否为迁移项且用户未确认
bool isMigration = index.data(Qt::UserRole + 10).toBool();
if (isMigration && !m_migrationPackages.contains(packageName)) {
qDebug() << "跳过未确认的迁移项:" << packageName;
continue;
}
if (m_downloads.contains(packageName) && (m_downloads[packageName].isDownloading || m_downloads[packageName].isInstalled))
continue;
QString downloadUrl = index.data(Qt::UserRole + 7).toString();
@@ -485,15 +563,18 @@ void AppDelegate::startNextInstall() {
}
}
// 如果是APM包先检查APM中是否存在对应的包再卸载APTSS版本
if (source == "apm") {
// 检查是否为迁移场景
bool isMigration = m_migrationPackages.contains(packageName);
// 如果是APM包或者是迁移场景先检查APM中是否存在对应的包再卸载APTSS版本
if (source == "apm" || isMigration) {
// 检查APM中是否存在对应的包
QProcess checkProcess;
QStringList checkArgs;
checkArgs << "list" << packageName;
checkProcess.start("apm", checkArgs);
checkProcess.waitForFinished(30000); // 30秒超时
QString checkOutput = checkProcess.readAllStandardOutput();
if (checkOutput.contains(packageName)) {
// APM中存在对应的包卸载APTSS版本
@@ -849,19 +930,75 @@ void AppDelegate::updateSpinner() {
// 新增:更新选中应用的方法
void AppDelegate::startDownloadForSelected() {
if (!m_model) return;
// 首先检查选中的应用中是否有迁移项
QStringList selectedMigrationPackages;
for (int row = 0; row < m_model->rowCount(); ++row) {
QModelIndex index = m_model->index(row, 0);
QString packageName = index.data(Qt::UserRole + 1).toString();
if (m_selectedPackages.contains(packageName)) {
bool isMigration = index.data(Qt::UserRole + 10).toBool();
if (isMigration && !m_migrationPackages.contains(packageName)) {
QString migrationTarget = index.data(Qt::UserRole + 12).toString();
selectedMigrationPackages.append(QString("%1 → %2").arg(packageName, migrationTarget.toUpper()));
}
}
}
// 如果有未确认的迁移项,弹出确认对话框
if (!selectedMigrationPackages.isEmpty()) {
QString message = "以下选中的应用更新后将替换为新的版本来源:\n\n";
message += selectedMigrationPackages.join("\n");
message += "\n\n是否继续更新这些应用?";
QMessageBox::StandardButton reply = QMessageBox::question(
nullptr,
"确认迁移",
message,
QMessageBox::Yes | QMessageBox::No | QMessageBox::Cancel
);
if (reply == QMessageBox::Cancel) {
return; // 完全取消更新
}
// 如果用户点击 Yes将选中的迁移项加入迁移集合
if (reply == QMessageBox::Yes) {
for (int row = 0; row < m_model->rowCount(); ++row) {
QModelIndex index = m_model->index(row, 0);
QString packageName = index.data(Qt::UserRole + 1).toString();
if (m_selectedPackages.contains(packageName)) {
bool isMigration = index.data(Qt::UserRole + 10).toBool();
if (isMigration) {
m_migrationPackages.insert(packageName);
}
}
}
}
// 如果用户点击 No继续更新其他应用跳过未确认的迁移项
}
for (int row = 0; row < m_model->rowCount(); ++row) {
QModelIndex index = m_model->index(row, 0);
QString packageName = index.data(Qt::UserRole + 1).toString();
// 检查应用是否被忽略
bool isIgnored = index.data(Qt::UserRole + 8).toBool();
if (isIgnored) {
qDebug() << "跳过被忽略的应用:" << packageName;
continue;
}
// 只下载选中的应用
if (m_selectedPackages.contains(packageName)) {
// 检查是否为迁移项且用户未确认
bool isMigration = index.data(Qt::UserRole + 10).toBool();
if (isMigration && !m_migrationPackages.contains(packageName)) {
qDebug() << "跳过未确认的迁移项:" << packageName;
continue;
}
if (m_downloads.contains(packageName) && (m_downloads[packageName].isDownloading || m_downloads[packageName].isInstalled))
continue;
QString downloadUrl = index.data(Qt::UserRole + 7).toString();

View File

@@ -58,6 +58,9 @@ private:
// 复选框相关成员变量
QSet<QString> m_selectedPackages;
// 迁移包集合(用户确认要迁移的包)
QSet<QString> m_migrationPackages;
QQueue<QString> m_installQueue;
bool m_isInstalling = false;
QProcess *m_installProcess = nullptr;

View File

@@ -36,6 +36,12 @@ QVariant AppListModel::data(const QModelIndex &index, int role) const
return map.value("ignored");
case Qt::UserRole + 9: // 包来源
return map.value("source");
case Qt::UserRole + 10: // 是否为迁移项
return map.value("is_migration");
case Qt::UserRole + 11: // 迁移源
return map.value("migration_source");
case Qt::UserRole + 12: // 迁移目标
return map.value("migration_target");
default:
return QVariant();
}
@@ -58,6 +64,9 @@ void AppListModel::setUpdateData(const QJsonArray &updateInfo)
map["download_url"] = obj["download_url"].toString(); // 确保设置下载 URL
map["ignored"] = obj["ignored"].toBool(); // 设置忽略状态
map["source"] = obj["source"].toString(); // 设置包来源
map["is_migration"] = obj["is_migration"].toBool(); // 设置是否为迁移项
map["migration_source"] = obj["migration_source"].toString(); // 设置迁移源
map["migration_target"] = obj["migration_target"].toString(); // 设置迁移目标
m_data.append(map); // 添加到 QList<QVariantMap>
qDebug() << "设置到模型的包名:" << map["package"].toString() << "忽略状态:" << map["ignored"].toBool() << "来源:" << map["source"].toString();

View File

@@ -603,7 +603,7 @@ QJsonArray aptssUpdater::mergeUpdateInfo()
{
QJsonArray aptssInfo = getUpdateInfoAsJson();
QJsonArray apmInfo = getApmUpdateInfoAsJson();
// 创建包名到更新信息的映射
QHash<QString, QJsonObject> aptssMap;
for (const QJsonValue &value : aptssInfo) {
@@ -612,7 +612,7 @@ QJsonArray aptssUpdater::mergeUpdateInfo()
obj["source"] = "aptss";
aptssMap[packageName] = obj;
}
QHash<QString, QJsonObject> apmMap;
for (const QJsonValue &value : apmInfo) {
QJsonObject obj = value.toObject();
@@ -620,44 +620,54 @@ QJsonArray aptssUpdater::mergeUpdateInfo()
obj["source"] = "apm";
apmMap[packageName] = obj;
}
QJsonArray mergedArray;
// 处理只在aptss中存在的包
for (const QString &packageName : aptssMap.keys()) {
if (!apmMap.contains(packageName)) {
mergedArray.append(aptssMap[packageName]);
}
}
// 处理只在apm中存在的包
for (const QString &packageName : apmMap.keys()) {
if (!aptssMap.contains(packageName)) {
mergedArray.append(apmMap[packageName]);
}
}
// 处理在两者中都存在的包
for (const QString &packageName : aptssMap.keys()) {
if (apmMap.contains(packageName)) {
QJsonObject aptssObj = aptssMap[packageName];
QJsonObject apmObj = apmMap[packageName];
// 比较版本
QString aptssVersion = aptssObj["new_version"].toString();
QString apmVersion = apmObj["new_version"].toString();
// 这里简化处理,实际应该使用版本比较函数
// 检查是否为迁移场景APM版本更新且当前包是通过aptss安装的
// 通过检查aptss中是否有当前版本号来判断是否通过aptss安装
if (apmVersion > aptssVersion) {
// APM版本更高使用APM版本
mergedArray.append(apmObj);
// 迁移场景Spark -> APM
QJsonObject migrationObj = apmObj;
migrationObj["is_migration"] = true;
migrationObj["migration_source"] = "aptss";
migrationObj["migration_target"] = "apm";
migrationObj["aptss_version"] = aptssVersion;
mergedArray.append(migrationObj);
// 同时保留aptss的更新项如果aptss也有更新
mergedArray.append(aptssObj);
} else {
// APTSS版本更高或相同不展示该包
qDebug() << "APTSS版本更高不展示APM包:" << packageName;
// 非迁移场景:同时展示两个来源的更新
mergedArray.append(aptssObj);
mergedArray.append(apmObj);
}
}
}
qDebug()<<"合并后的更新信息:"<<mergedArray;
return mergedArray;
}