Bootstrap

升级测试dd

Seclient.cpp

#include "seclient.h"
#include "ui_seclient.h"
#include <QFileDialog>
#include <QMessageBox>
#include <QDir>
#include <QProcess>
#include <QDebug>
#include <QSettings>
#include "secupgrademodule.h"

Seclient::Seclient(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::Seclient)
{
    ui->setupUi(this);

    this->setWindowTitle("独立客户端");
    this->resize(800,500);
    QFont f("黑体", 10, QFont::Black, false);
    this->setWindowIcon(QIcon("C:/Users/liwencheng/Documents/data/up1.png"));
    // 实例化SecUpgradeModule
    this->secUpgradeModule = new SecUpgradeModule(this);
    this->secUpgradeModule->hide();
    // 去掉窗口边框
//    this->setWindowFlags(Qt::FramelessWindowHint);
    // 设置窗口为始终在顶层
//    this->setWindowFlags(this->windowFlags() | Qt::WindowStaysOnTopHint);
//    this->setStyleSheet("background-color: black;");
    ui->checkUpdateButton->setFont(f);
//    connect(ui->checkUpdateButton,&QPushButton::clicked,this,&MainWindow::on_checkUpdateButton_clicked);
    // 返回信号
    connect(secUpgradeModule,&SecUpgradeModule::return_signal,[this](){
        ui->checkUpdateButton->show();
    });

//    connect(&updateConl, &UpdateConl::updateCompleted, this, &MainWindow::handleUpgradeRecordOnRestart);
}

Seclient::~Seclient()
{
    delete ui;
}

void Seclient::on_checkUpdateButton_clicked()
{
    qDebug() << u8"当前工作路径:" << QDir::currentPath();
//    m_secClient = new MainWindow();
    // 先确保 Update 和 tmp_Update 文件夹存在
    QDir updateDir(DATA_PATH + "/Update");
    if (!updateDir.exists())
    {
        updateDir.mkpath(".");
    }
    QDir tmpUpdateDir(DATA_PATH + "/tmp_Update");
    if (!tmpUpdateDir.exists())
    {
        tmpUpdateDir.mkpath(".");
    }
//    // 调用独立升级系统的检查更新,结合配置文件判断
//    bool hasUpdate = m_updateControl->checkForUpdates();
//    if (hasUpdate)
//    {
//        m_updateControl->checkAndUpdate();
//        qDebug()<<u8"已升级";
//        // 检查是否是重启并处理升级记录
//        m_secClient->handleUpgradeRecordOnRestart();
//    }
//    else
//    {
//        qDebug()<<u8"未升级";
//    }
//    m_secClient->show();


    ui->checkUpdateButton->hide();
    this->secUpgradeModule->show();
//    //单例模式
//    SecUpgradeModule* secUpgradeModule = SecUpgradeModule::getInstance();
//    secUpgradeModule->show();
}
 

#ifndef SECLIENT_H
#define SECLIENT_H

#include <QWidget>
#include "secupgrademodule.h"

namespace Ui {
class Seclient;
}

class Seclient : public QWidget
{
    Q_OBJECT

public:
    explicit Seclient(QWidget *parent = 0);
    ~Seclient();

private slots:
    void on_checkUpdateButton_clicked();

private:
    Ui::Seclient *ui;
    const QString DATA_PATH = "C:/Users/liwencheng/Documents/data";
    SecUpgradeModule *secUpgradeModule;

};

#endif // SECLIENT_H
 

SecUpgradeModule.cpp

#include "secupgrademodule.h"
#include "ui_secupgrademodule.h"
#include <QNetworkRequest>
#include <QMessageBox>


//SecUpgradeModule* SecUpgradeModule::instance = nullptr;


//SecUpgradeModule* SecUpgradeModule::getInstance()
//{
   if (instance == nullptr)
   {
       instance = new SecUpgradeModule();
   }
   return instance;
   // 私有静态属性
   static SecUpgradeModule* instance;
//    //定义局部静态变量
//    static SecUpgradeModule* instance = new SecUpgradeModule();
//    return instance;
//}

SecUpgradeModule::SecUpgradeModule(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::SecUpgradeModule)
{
    ui->setupUi(this);

    m_networkManager = new QNetworkAccessManager(this);
    connect(m_networkManager, &QNetworkAccessManager::finished, this, &SecUpgradeModule::onNetworkReplyFinished);

    // 初始化数据库连接
    initDatabaseConnection();
    // 初始化文件列表
    initializePackageList();
    // 初始化升级控制模块
    m_upcontrol = new UpdateConl();

    // UpdateStatus字段错误
    connect(m_upcontrol, &UpdateConl::UpdateStatusFalse, this, &SecUpgradeModule::handleUpdateStatusFalse);
    // 升级完成信号槽函数
    connect(m_upcontrol, &UpdateConl::updateComplete, this, &SecUpgradeModule::handleUpdateComplete);
}

SecUpgradeModule::~SecUpgradeModule()
{
    delete ui;
    if(!m_upcontrol)
    {
        delete m_upcontrol;
        m_upcontrol = nullptr;
    }
}

void SecUpgradeModule::connectToOTA()
{
    // 建立与OTA服务器的网络连接等逻辑,比如发送初始请求验证等
}


// 初始化文件列表
void SecUpgradeModule::initializePackageList()
{
    // 检查 model 是否已经存在
    if(!model)
    {
        model = new QStandardItemModel(0, 3, this);
        model->setHeaderData(0, Qt::Horizontal, tr("文件名称"));
        model->setHeaderData(1, Qt::Horizontal, tr(" 文件大小 "));
        model->setHeaderData(2, Qt::Horizontal, tr("修改时间"));
        ui->packageList->setModel(model);
    }

    // 加粗标题字体
    QTableView *tableView = qobject_cast<QTableView*>(ui->packageList);
    QFont headerFont = tableView->horizontalHeader()->font();
    headerFont.setBold(true);
    tableView->horizontalHeader()->setFont(headerFont);

    // 初始化本地升级包列表
    QDir tmpDir(DATA_PATH + "/tmp_Update/");
    if (tmpDir.exists())
    {
        upgradePackages = tmpDir.entryList(QDir::Files);
        model->removeRows(0, model->rowCount());  // 清除现有数据
        for (const QString &package : upgradePackages)
        {
            QFileInfo fileInfo(DATA_PATH + "/tmp_Update/" + package);
            QStandardItem *nameItem = new QStandardItem(package);
            QStandardItem *sizeItem = new QStandardItem(QString::number(fileInfo.size() / 1024.0 / 1024.0, 'f', 2) + " M"); // 文件大小以KB为单位
            QStandardItem *timeItem = new QStandardItem(fileInfo.lastModified().toString("yyyy-MM-dd HH:mm:ss"));
            model->appendRow({nameItem, sizeItem, timeItem});
        }
    }

    // 自适应列宽
    ui->packageList->resizeColumnsToContents();

    // 隐藏网格
    ui->packageList->setShowGrid(false);
}

void SecUpgradeModule::downloadUpdatePackage(const QString &packageUrl)
{
    QDir tmpUpdateDir("tmp_Update");
    if (!tmpUpdateDir.exists()) {
        tmpUpdateDir.mkpath(".");  // 创建 tmp_Update 文件夹(如果不存在)
    }
    QString packageFileName = packageUrl.mid(packageUrl.lastIndexOf('/') + 1);
    QString savePath = tmpUpdateDir.absoluteFilePath(packageFileName);
    // 使用QNetworkAccessManager发起下载请求等操作
    QNetworkRequest request(QUrl(packageUrl));
//    m_networkManager->get(request);
}

void SecUpgradeModule::deleteUpdatePackage(const QString &packagePath)
{
    // 实现删除本地文件的逻辑(使用文件操作相关函数)
    QFile::remove(packagePath);
}

void SecUpgradeModule::selectUpgradeOption(int option)
{
    // 根据传入的选项执行对应的升级相关逻辑
}


QList<QMap<QString, QString> > SecUpgradeModule::getLocalUpdatePackages()
{
    QList<QMap<QString, QString>> packageList;
    // 从 tmp_Update 文件夹获取升级包文件名列表
    QDir tmpUpdateDir(DATA_PATH + "/tmp_Update");
    QStringList fileNames = tmpUpdateDir.entryList(QDir::Files);
    for (const QString &fileName : fileNames) {
        QMap<QString, QString> packageInfo;
        packageInfo["package_name"] = fileName;
        // 从数据库查询对应升级包的详细信息,比如下载时间、更新说明等
        QSqlQuery query;
        query.prepare("SELECT download_time, update_description, is_updated FROM updates WHERE package_name = :package_name");
        query.bindValue(":package_name", fileName);
        if (query.exec() && query.next()) {
            packageInfo["download_time"] = query.value(0).toString();
            packageInfo["update_description"] = query.value(1).toString();
            packageInfo["is_updated"] = query.value(2).toString();
        }
        packageList.append(packageInfo);
    }
    return packageList;
}

void SecUpgradeModule::onNetworkReplyFinished()
{
    QNetworkReply *reply = qobject_cast<QNetworkReply *>(sender());
    if (reply) {
        if (reply->error() == QNetworkReply::NoError) {
            // 处理下载成功等情况,比如保存文件到 tmp_Update 文件夹等
        } else {
            // 处理下载出错情况
        }
        reply->deleteLater();
    }
}

void SecUpgradeModule::initDatabaseConnection()
{
    QSqlDatabase db = QSqlDatabase::addDatabase("QSQLITE");
    db.setDatabaseName("updates.db");  // 数据库文件名
    if (!db.open())
    {
        qDebug() << "Error opening database";
    }
}

// 文件更新状态
void SecUpgradeModule::showNotification(const bool success, const QString &upFileName)
{
    if (success)
    {
        QMessageBox::information(this, "提醒", QString("升级成功:%1").arg(upFileName));
    }
    else
    {
        QMessageBox::information(this, "提醒", QString("升级失败:%1").arg(upFileName));
    }
}


//选择上传文件到 tmp_Update
void SecUpgradeModule::on_uploadButton_clicked()
{
    if(!this->m_filelist)
    {
        QMessageBox::warning(this, tr("错误"), tr("该选项仅在Tmp_Update列表下使用!"));
        return;
    }
    // 构建文件过滤器,包含zip、tar.gz和.so文件
    QString filter = tr("All Supported Files (*.zip *.tar.gz *.so);;ZIP Files (*.zip);;TAR.GZ Files (*.tar.gz);;SO Files (*.so)");
//    QString filePath = QFileDialog::getOpenFileName(this, tr("上传升级包"), "C:/Users/liwencheng/Documents", filter);
    // 支持多文件上传
    QStringList filePaths = QFileDialog::getOpenFileNames(this, tr("上传升级包"), "D:/", filter, nullptr, QFileDialog::Options(QFileDialog::ExistingFiles));

    if (!filePaths.isEmpty())
    {
        for (const QString& filePath : filePaths)
        {
            QFileInfo fileInfo(filePath);
            QString newPath =DATA_PATH + "/tmp_Update/" + fileInfo.fileName();
            if (!QFile::copy(filePath, newPath))
            {
                qWarning() << u8"复制失败!" << newPath;
                QMessageBox::warning(this, tr("错误"), tr("文件复制失败,目标路径:") + newPath);
            }
            else
            {
                // 更新列表
                QFileInfo fileInfo(filePath);
                QStandardItem *nameItem = new QStandardItem(fileInfo.fileName());
                QStandardItem *sizeItem = new QStandardItem(QString::number(fileInfo.size()));
                QStandardItem *timeItem = new QStandardItem(fileInfo.lastModified().toString());
                model->appendRow({nameItem, sizeItem, timeItem});
                ui->packageList->resizeColumnsToContents();
                ui->packageList->setShowGrid(false);
                QMessageBox::information(this, tr("成功"), tr("文件上传成功,文件路径:") + newPath);
            }
        }
    }
}

//删除 tmp_Update 的文件,更新列表
void SecUpgradeModule::on_deleteButton_clicked()
{
    QModelIndex index = ui->packageList->currentIndex();
    QString flie_list;
    if (index.isValid())
    {

        // 获取当前行的第一列的索引
        QModelIndex firstColumnIndex = model->index(index.row(), 0);
        // 获取第一列的值(文件名)
        QString packageName = model->itemData(firstColumnIndex).value(Qt::DisplayRole).toString();

//        QString packageName = model->itemData(index).value(Qt::DisplayRole).toString();
        if(this->m_filelist)
        {
            flie_list = "/tmp_Update/";
        }
        else
        {
            flie_list = "/Update/";
        }
        if(QFile::remove(DATA_PATH + flie_list + packageName))
        {
            // 更新列表
            model->removeRow(index.row());
            // 重置当前索引为无效
            ui->packageList->setCurrentIndex(QModelIndex());
            QMessageBox::information(this, tr("成功"), tr("成功删除文件:") + packageName);
        }
        else
        {
            QMessageBox::warning(this, tr("错误"), tr("文件删除失败:") + packageName, tr("请检查!"));
        }
    }
    else
    {
        QMessageBox::warning(this, tr("错误"), tr("请选择要删除的文件!"));
    }
}

//选择 tmp_Update 的文件, 表示要进行升级的包
void SecUpgradeModule::on_selectButton_clicked()
{
    if(!this->m_filelist){
        QMessageBox::warning(this, tr("错误"), tr("该选项仅在tmp_update列表下使用!"));
        return;
    }
    QModelIndex index = ui->packageList->currentIndex();
    if (index.isValid())
    {
        // 获取当前行的第一列的索引
        QModelIndex firstColumnIndex = model->index(index.row(), 0);
        // 获取第一列的值(文件名)
        QString packageName = model->itemData(firstColumnIndex).value(Qt::DisplayRole).toString();
//        QString packageName = model->itemData(index).value(Qt::DisplayRole).toString();
        QFileInfo fileInfo(DATA_PATH + "/tmp_Update/" + packageName);
        if (fileInfo.exists())
        {
            QString newPath =DATA_PATH + "/Update/" + fileInfo.fileName();

            if(QFile::copy(fileInfo.absoluteFilePath(), newPath))
            {
                // 更新配置文件
                UpdateConl::updateUpdateStatus(fileInfo.fileName(), "UpdateWaiting", "success", "updating");
                QMessageBox::information(this, tr("成功"), tr("将要升级文件:") + fileInfo.fileName());
            }
            else
            {
                QMessageBox::warning(this, tr("错误"), tr("文件更新失败,请检查Update目录下是否有同名文件"));
                UpdateConl::updateUpdateStatus(fileInfo.fileName(), "UpdateWaiting", "failed", "chose file failed");
            }
        }
    }
    else
    {
        QMessageBox::warning(this, tr("错误"), tr("请选择要升级的文件!"));
    }
}

// 返回按钮
void SecUpgradeModule::on_returnButton_clicked()
{
//    this->hide();
    this->close();
    emit return_signal();
}

// 立即升级
void SecUpgradeModule::on_nowupButton_clicked()
{
    if(this->m_filelist)
    {
        QMessageBox::warning(this, tr("错误"), tr("该选项仅在Update列表下使用!"));
        return;
    }
    if(model->rowCount() == 0){
        QMessageBox::warning(this, tr("错误"), tr("Update列表下内容为空!"));
        return;
    }
    m_upcontrol->isImmediateUpdateRequired();
    if(!this->m_status)
    {
        return;
    }
    else
    {
        // 是否升级确认框
        const QString title = QString::fromUtf8("更新提示");
        const QString text = QString::fromUtf8("是否立即升级?");
        int result = QMessageBox::information(this,title, text, QMessageBox::Yes | QMessageBox::No);
        if (result == QMessageBox::Yes)
        {
            UpdateConfig configInfo = this->m_upcontrol->getConfigInfo();
            QMessageBox::information(this, tr("提醒"), tr("即将开始升级:") + configInfo.fileName);
            this->upFileName = configInfo.fileName;
            this->m_upcontrol->checkAndUpdate();
            qDebug()<<u8"已升级";
            // 发送升级记录给日志审计模块
            m_upcontrol->handleUpgradeRecordOnRestart();
        }
    }

}


// tmp_update 文件列表
void SecUpgradeModule::on_uploadButton_2_clicked()
{
    this->m_filelist = true;
    initializePackageList();
}

// update 文件列表
void SecUpgradeModule::on_uploadButton_3_clicked()
{
    this->m_filelist = false;
    // 检查 model 是否已经存在
    if(!model)
    {
        model = new QStandardItemModel(0, 3, this);
        model->setHeaderData(0, Qt::Horizontal, tr("文件名称"));
        model->setHeaderData(1, Qt::Horizontal, tr(" 文件大小 "));
        model->setHeaderData(2, Qt::Horizontal, tr("修改时间"));
        ui->packageList->setModel(model);
        // 加粗标题字体
        QTableView *tableView = qobject_cast<QTableView*>(ui->packageList);
        QFont headerFont = tableView->horizontalHeader()->font();
        headerFont.setBold(true);
        tableView->horizontalHeader()->setFont(headerFont);
    }

    // 初始化本地升级包列表
    QDir tmpDir(DATA_PATH + "/Update/");
    if (tmpDir.exists())
    {
        upgradePackages = tmpDir.entryList(QDir::Files);
        model->removeRows(0, model->rowCount());  // 清除现有数据
        for (const QString &package : upgradePackages)
        {
            QFileInfo fileInfo(DATA_PATH + "/tmp_Update/" + package);
            QStandardItem *nameItem = new QStandardItem(package);
            QStandardItem *sizeItem = new QStandardItem(QString::number(fileInfo.size() / 1024.0 / 1024.0, 'f', 2) + " M"); // 文件大小以KB为单位
            QStandardItem *timeItem = new QStandardItem(fileInfo.lastModified().toString("yyyy-MM-dd HH:mm:ss"));
            model->appendRow({nameItem, sizeItem, timeItem});
        }
    }

    // 自适应列宽
    ui->packageList->resizeColumnsToContents();

    // 隐藏网格
    ui->packageList->setShowGrid(false);
}

// 处理 UpdateStatus 字段错误
void SecUpgradeModule::handleUpdateStatusFalse(const QString &status)
{
    this->m_status = false;
    QMessageBox::information(this, "提示", QString("UpdateStatus 字段的值应为 UpdateWaiting 或 UpdateCompleted,当前值为: %1").arg(status));
}

// 升级完成信号槽函数
void SecUpgradeModule::handleUpdateComplete(int exitCode, QProcess::ExitStatus exitStatus)
{
    if (exitStatus == QProcess::NormalExit)
    {
        if (exitCode == 0)
        {
            qDebug() << u8"批处理脚本执行成功";
            qDebug() << "Parent window position: " << this->pos();
            this->m_upcontrol->updateUpdateStatus(upFileName,"UpdateCompleted", "success", QString("exitCode:%1").arg(exitCode));
            QMessageBox::information(nullptr, tr("提醒"), QString("升级成功:%1").arg(upFileName));


        }
        else
        {
            qDebug() << u8"批处理脚本执行失败,退出码: " << exitCode;
            this->m_upcontrol->updateUpdateStatus(upFileName,"UpdateWaiting", "faile", QString("exitCode:%1").arg(exitCode));
            QMessageBox::information(nullptr, tr("提醒"), QString("升级失败:%1").arg(upFileName));
        }
    }
    else if (exitStatus == QProcess::CrashExit)
    {
        qDebug() << u8"批处理脚本执行过程中崩溃";
        this->m_upcontrol->updateUpdateStatus(upFileName,"UpdateWaiting", "faile", "update is CrashExit");
    }
}
 

#ifndef SECUPGRADEMODULE_H
#define SECUPGRADEMODULE_H

#include <QWidget>
#include <QObject>
#include <QNetworkAccessManager>
#include <QNetworkReply>
#include <QSqlDatabase>
#include <QSqlQuery>
#include <QDir>
#include <QFileDialog>
#include <QStandardItemModel>
#include "updateconl.h"

namespace Ui {
class SecUpgradeModule;
}

class SecUpgradeModule : public QWidget
{
    Q_OBJECT

public:
    explicit SecUpgradeModule(QWidget *parent = 0);
    ~SecUpgradeModule();
    //    // 共有静态方法
    //    static SecUpgradeModule* getInstance();

private:
    Ui::SecUpgradeModule *ui;
    //    // 私有构造方法
    //    SecUpgradeModule(QWidget *parent = 0);

    // 连接OTA服务器,初始化通信相关设置
    void connectToOTA();

    // 初始化文件列表
    void initializePackageList();

    // 下载指定的升级包,并保存到 tmp_Update 文件夹
    void downloadUpdatePackage(const QString &packageUrl);

    // 删除本地已下载的升级包(从 tmp_Update 文件夹删除)
    void deleteUpdatePackage(const QString &packagePath);

    // 选择要进行的升级操作(比如立即升级、稍后升级等)
    void selectUpgradeOption(int option);

    // 获取本地升级包列表信息(包含从数据库读取详细信息用于展示)
    QList<QMap<QString, QString>> getLocalUpdatePackages();
    QStandardItemModel *model = nullptr;
    QStringList upgradePackages;
    QNetworkAccessManager *m_networkManager;
    // 初始化数据库连接
    void initDatabaseConnection();
    const QString DATA_PATH = "C:/Users/liwencheng/Documents/data";
    //文件列表
    bool m_filelist = true;
    // status 状态
    bool m_status = true;
    // 升级文件名字
    QString upFileName;
    UpdateConl *m_upcontrol;


signals:
    // 信号用于通知下载进度、通信状态等
    void downloadProgress(int progress);
    void connectionStatusChanged(const QString &status);
    void return_signal();
private slots:
    // 文件更新状态

    void showNotification(const bool success, const QString& upFileName);

    void onNetworkReplyFinished();

    void on_uploadButton_clicked();
    void on_deleteButton_clicked();
    void on_selectButton_clicked();

    void on_returnButton_clicked();
    void on_nowupButton_clicked();
    void on_uploadButton_2_clicked();
    void on_uploadButton_3_clicked();
    void handleUpdateStatusFalse(const QString &status);
    void handleUpdateComplete(int exitCode, QProcess::ExitStatus exitStatus);
};

#endif // SECUPGRADEMODULE_H
 

#include "updateconl.h"
#include "ui_updateconl.h"

const QString UpdateConl::DATA_PATH = "C:/Users/liwencheng/Documents/data";

UpdateConl::UpdateConl(QWidget *parent) :
    QWidget(parent),
    ui(new Ui::UpdateConl)
{
    ui->setupUi(this);

//    m_checkTimer = new QTimer(this);
//    m_reminderTimer = new QTimer(this);
//    updateProcess = new QProcess(this);
   updateProcess = new QProcess(static_cast<QObject*>(this));
//    connect(updateProcess, QOverload<int, QProcess::ExitStatus>::of(&QProcess::finished),
//            this, &UpdateConl::onProcessFinished);

//    connect(m_checkTimer, &QTimer::timeout, this, &UpdateConl::checkUpdateIni);
   connect(m_reminderTimer, &QTimer::timeout, this, &UpdateConl::showUpdateConfirmation);
   connect(m_reminderTimer, &QTimer::timeout, this, &UpdateConl::on_reminderTimer_timeout);

   m_checkTimer->start(10000); // 每20秒检查一次

//    // 有升级可用包发现
//    m_updateConfirmationShown = false;
}

UpdateConl::~UpdateConl()
{
    delete ui;
}


// 更新 Update.ini 中的更新状态
void UpdateConl::updateUpdateStatus(const QString &file,const QString &status, const QString &result, const QString &msg)
{
    QSettings settings(DATA_PATH + "/Update.ini", QSettings::IniFormat);
    settings.setValue("FileName", file);
    settings.setValue("UpdateStatus", status);
    settings.setValue("UpdateResults", result);
    settings.setValue("Msg", msg);
    qDebug()<<u8"当前时间:"<<QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss");
    settings.setValue("UpdateTime", QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss"));
}

//检查 UpdateStatus 字段是否是 UpdateWaiting
bool UpdateConl::isImmediateUpdateRequired()
{
    QSettings settings(DATA_PATH + "/Update.ini", QSettings::IniFormat);
    QString status = settings.value("UpdateStatus", "").toString();
    if (!status.isEmpty() && status!= "UpdateWaiting" && status!= "UpdateCompleted")
    {
//        QMessageBox::information(this, "提示", QString("UpdateStatus 字段的值应为 UpdateWaiting 或 UpdateCompleted,当前值为: %1").arg(status));
        emit UpdateStatusFalse(status);
    }

    return status == "UpdateWaiting";
}

// 执行更新操作,比如下载并安装依赖库、成果物更新等
void UpdateConl::checkAndUpdate()
{
    if (isImmediateUpdateRequired())
    {
        UpdateConfig configInfo = getConfigInfo();
        // 判断 Update 目录下是否包含 configInfo.fileName
        QDir updateDir(DATA_PATH + "/Update/");
        if (updateDir.exists() && updateDir.exists(configInfo.fileName))
        {
            // 解压升级包 校验升级包的完整性 TODO
            // 执行更新脚本操作,依赖库、成果物更新
            QDir scriptDir(DATA_PATH);
            if (scriptDir.exists("update.bat"))
            {
                qDebug() << u8"即将开始升级";
                upFileName = configInfo.fileName;
                updateProcess->start("cmd.exe", QStringList() << "/c" << DATA_PATH + "/update.bat");
//                if (!updateProcess->waitForFinished())
//                {
//                    qDebug() << u8"Update script execution timeout.";
//                }
//                else
//                {
//                    qWarning() << u8"========================waitForFinished 失败" ;
//                }
                // 启动外部更新脚本
                qDebug() << u8"启动外部更新脚本";
//                QProcess::startDetached("cmd.exe", QStringList() << "/c" << DATA_PATH + "/update.bat");
//                QCoreApplication::quit();
//                emit updateCompleted();
            }

        }
        else
        {
            qWarning() << u8"Update 目录下不存在 " << configInfo.fileName;
        }
    }
}

// 获取配置文件信息
UpdateConfig UpdateConl::getConfigInfo()
{
    UpdateConfig config;
    QFile configFile(DATA_PATH + "/Update.ini");
    if (configFile.open(QIODevice::ReadOnly | QIODevice::Text))
    {
        QTextStream in(&configFile);
        QString line;
        while (!in.atEnd())
        {
            line = in.readLine();
            if (line.startsWith("FileName="))
            {
                config.fileName = line.mid(9);
            }
            else if (line.startsWith("UpdateStatus="))
            {
                config.updateStatus = line.mid(13);
            }
        }
        configFile.close();
    }
    qDebug()<<u8"=====================================";
    qDebug()<<u8"文件名:"<<config.fileName<<u8"更新状态:"<<config.updateStatus;
    return config;
}

// 获取 Update.ini 的升级记录信息
void UpdateConl::handleUpgradeRecordOnRestart()
{
    QSettings settings(DATA_PATH + "/Update.ini", QSettings::IniFormat);
    QString status = settings.value("UpdateStatus", "").toString();
    QString result = settings.value("UpdateResults", "").toString();
    QString fileName = settings.value("FileName", "").toString();
    QString updateTime = settings.value("UpdateTime", "").toString();
    QString msg = settings.value("Msg", "").toString();

    QMap<QString, QString> upgradeRecord;
    upgradeRecord["FileName"] = fileName;
    upgradeRecord["UpdateStatus"] = status;
    upgradeRecord["UpdateResults"] = result;
    upgradeRecord["UpdateTime"] = updateTime;
    upgradeRecord["Msg"] = msg;

    sendUpgradeRecordToAudit(upgradeRecord);
}


// 发送升级记录给日志审计模块
void UpdateConl::sendUpgradeRecordToAudit(const QMap<QString, QString> &record)
{
    qDebug() << u8"发送升级记录给日志审计模块:" << record;
}


// 升级完成信号槽函数
void UpdateConl::onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus)
{
    emit updateComplete(exitCode, exitStatus);
//    QCoreApplication::quit();
}


//获取 更新包 的绝对路径
QString UpdateConl::getUpdatePackagePath()
{
    QSettings settings(DATA_PATH + "/Update.ini", QSettings::IniFormat);
    QString fileName = settings.value("FileName", "").toString();
    QDir updateDir(DATA_PATH + "/Update");
    return updateDir.absoluteFilePath(fileName);
}


// 校验升级配置文件的UpdateStatus
void UpdateConl::checkUpdateIni()
{
    if (checkForUpdates() && !m_updateConfirmationShown)
    {
        showUpdateConfirmation();
    }
}

// 是否升级确认框
void UpdateConl::showUpdateConfirmation()
{
    if(!m_updateConfirmationShown)
    {
    //    int result = QMessageBox::question(this, "更新提示", "发现可用更新,是否立即升级?",
    //                                       QMessageBox::Yes | QMessageBox::No);
        const QString title = QString::fromUtf8("更新提示");
        const QString text = QString::fromUtf8("发现可用更新,是否立即升级?");
        int result = QMessageBox::information(this,title, text, QMessageBox::Yes | QMessageBox::No);
    //    QMessageBox msgBox(QMessageBox::Information, title, text, QMessageBox::Yes | QMessageBox::No, this);
    //    msgBox.exec();
        m_updateConfirmationShown = true;
        handleUpdateConfirmation(result);
    }
}

// 处理是否升级
void UpdateConl::handleUpdateConfirmation(int result)
{
    if (result == QMessageBox::Yes)
    {
        checkAndUpdate();
        qDebug()<<u8"已升级";
        // 发送升级记录给日志审计模块
        handleUpgradeRecordOnRestart();
        m_checkTimer->stop();
        m_updateConfirmationShown = false;
    }
    else
    {
        qDebug()<<u8"未升级";
        m_checkTimer->stop();
        // 若不升级1分钟后再次提醒
        m_checkTimer->start(60000);
//        m_reminderTimer->start(60000);
    }
}
 

#ifndef UPDATECONL_H
#define UPDATECONL_H

#include <QWidget>
#include <QProcess>
#include <QTimer>
#include <QSettings>  // 用于读取配置文件
#include <QDebug>
#include <QDir>
#include <QDateTime>
#include <QProcess>
#include <QMessageBox>

struct UpdateConfig
{
    QString fileName;
    QString updateStatus;
};

namespace Ui {
class UpdateConl;
}

class UpdateConl : public QWidget
{
    Q_OBJECT

public:
    explicit UpdateConl(QWidget *parent = 0);
    ~UpdateConl();
    // 检查更新
    bool checkForUpdates();
    // 执行执行更新操作,如下载并安装依赖库、成果物更新等(调用外部的 update.sh 脚本)
    void checkAndUpdate();

    // 获取配置文件信息
    UpdateConfig getConfigInfo();

    // 获取 Update.ini 的升级记录信息
    void handleUpgradeRecordOnRestart();
    // 发送升级记录给日志审计模块
    void sendUpgradeRecordToAudit(const QMap<QString, QString> &record);


    // 更新 Update.ini 中的更新状态
    static void updateUpdateStatus(const QString &file,const QString &status, const QString &result, const QString &msg = "");


    static const QString DATA_PATH;
    // 从配置文件读取是否需要立即更新的设置
    bool isImmediateUpdateRequired();

signals:
    // 信号用于通知更新进度、状态变化等
    void updateProgress(int progress);
    void updateCompleted();
    //发送文件更新信号
    void updateStatus(const bool success, const QString& upFileName);
    void updateStatusChanged(const bool &status);
    void customProcessFinished(const QString& fileName, int exitCode, QProcess::ExitStatus exitStatus);
    void UpdateStatusFalse(const QString &status);
    void updateComplete(int exitCode, QProcess::ExitStatus exitStatus);

public slots:
private slots:
//    // 校验升级配置文件的 UpdateStatus
//    void checkUpdateIni();
//    // 是否升级确认框
//    void showUpdateConfirmation();
//    // 处理是否升级
//    void handleUpdateConfirmation(int result);

private:
    Ui::UpdateConl *ui;

    QProcess *updateProcess;

    void onProcessFinished(int exitCode, QProcess::ExitStatus exitStatus);
    void onCustomProcessFinished(const QString& fileName, int exitCode, QProcess::ExitStatus exitStatus);
    // 升级文件名字
    QString upFileName;
    // 有升级可用包发现
    bool m_updateConfirmationShown;

    // 根据配置文件中的文件名获取完整路径
    QString getUpdatePackagePath();

    QTimer *m_checkTimer;
    QTimer *m_reminderTimer;
};

#endif // UPDATECONL_H
 

;