feat(DataManager/DataLoader/HttpRequsestManager):添加数据加载/管理模块
This commit is contained in:
parent
41ec213b5a
commit
5ba65992c4
|
|
@ -12,6 +12,8 @@ set(CMAKE_CXX_STANDARD_REQUIRED ON)
|
|||
find_package(QT NAMES Qt6 Qt5 REQUIRED COMPONENTS Widgets)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Widgets)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS PrintSupport)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Network)
|
||||
find_package(Qt${QT_VERSION_MAJOR} REQUIRED COMPONENTS Concurrent)
|
||||
|
||||
set(ADS_VERSION 4.3.1)
|
||||
add_subdirectory(QtADS)
|
||||
|
|
@ -37,11 +39,14 @@ set(H_HEADER_FILES
|
|||
include/dashboardFrame.h
|
||||
include/dashboardNamingDialog.h
|
||||
include/dataPanel.h
|
||||
include/dataLoader.h
|
||||
include/dataManager.h
|
||||
include/panelSelectionDialog.h
|
||||
include/panelConfigurationWidget.h
|
||||
include/dateTimeWidget.h
|
||||
include/customCalendarWidget.h
|
||||
include/dateTimeSelectionPanel.h
|
||||
include/httpRequestManager.h
|
||||
)
|
||||
|
||||
set(CPP_SOURCE_FILES
|
||||
|
|
@ -63,11 +68,14 @@ set(CPP_SOURCE_FILES
|
|||
source/dashboardFrame.cpp
|
||||
source/dashboardNamingDialog.cpp
|
||||
source/dataPanel.cpp
|
||||
source/dataLoader.cpp
|
||||
source/dataManager.cpp
|
||||
source/panelSelectionDialog.cpp
|
||||
source/panelConfigurationWidget.cpp
|
||||
source/dateTimeWidget.cpp
|
||||
source/customCalendarWidget.cpp
|
||||
source/dateTimeSelectionPanel.cpp
|
||||
source/httpRequestManager.cpp
|
||||
)
|
||||
|
||||
set(UI_FILES
|
||||
|
|
@ -153,6 +161,8 @@ endif()
|
|||
|
||||
target_include_directories(PowerMaster PRIVATE "${CMAKE_CURRENT_SOURCE_DIR}/include")
|
||||
target_link_libraries(PowerMaster PRIVATE Qt${QT_VERSION_MAJOR}::PrintSupport)
|
||||
target_link_libraries(PowerMaster PRIVATE Qt${QT_VERSION_MAJOR}::Network)
|
||||
target_link_libraries(PowerMaster PRIVATE Qt${QT_VERSION_MAJOR}::Concurrent)
|
||||
target_link_libraries(PowerMaster PRIVATE qt${QT_VERSION_MAJOR}advanceddocking)
|
||||
target_link_libraries(PowerMaster PUBLIC Qt${QT_VERSION_MAJOR}::Core
|
||||
Qt${QT_VERSION_MAJOR}::Gui
|
||||
|
|
|
|||
|
|
@ -0,0 +1,58 @@
|
|||
#ifndef DATALOADER_H
|
||||
#define DATALOADER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QRunnable>
|
||||
#include <QThreadPool>
|
||||
#include <QHash>
|
||||
#include <QMutex>
|
||||
#include <QVariant>
|
||||
|
||||
class DataLoader : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit DataLoader(QObject* parent = nullptr);
|
||||
~DataLoader();
|
||||
|
||||
void requestData(const QString& dataKey);
|
||||
void cancelRequest(const QString& dataKey);
|
||||
void setMaxThreads(int count);
|
||||
|
||||
signals:
|
||||
void dataLoaded(const QString& dataKey, const QVariant& data);
|
||||
void errorOccurred(const QString& dataKey, const QString& error);
|
||||
|
||||
public slots:
|
||||
void handleShutdown();
|
||||
|
||||
private:
|
||||
class LoadTask : public QRunnable
|
||||
{
|
||||
public:
|
||||
LoadTask(DataLoader* loader, const QString& key)
|
||||
:m_loader(loader), m_key(key)
|
||||
{
|
||||
setAutoDelete(false);//禁用自动析构,由DataLoader控制管理
|
||||
}
|
||||
|
||||
void run() override
|
||||
{
|
||||
QVariant data = fetchRealData(m_key);
|
||||
emit m_loader->dataLoaded(m_key, data);
|
||||
}
|
||||
|
||||
private:
|
||||
QVariant fetchRealData(const QString& dataKey);
|
||||
|
||||
DataLoader* m_loader;
|
||||
QString m_key;
|
||||
};
|
||||
|
||||
QThreadPool m_threadPool;
|
||||
QHash<QString, LoadTask*> m_runningTasks;
|
||||
QMutex m_taskMutex;
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,75 @@
|
|||
#ifndef DATAMANAGER_H
|
||||
#define DATAMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QHash>
|
||||
#include <QQueue>
|
||||
#include <QVariant>
|
||||
#include <QTimer>
|
||||
#include <QDateTime>
|
||||
#include <QMutexLocker>
|
||||
#include <QReadWriteLock>
|
||||
|
||||
class HttpRequestManager;
|
||||
class DataManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
Q_DISABLE_COPY(DataManager) //禁止拷贝,等价于:DataManager(const DataManager&) = delete; DataManager& operator=(const DataManager&) = delete;
|
||||
|
||||
public:
|
||||
struct CacheEntry
|
||||
{
|
||||
QVariant data;
|
||||
QDateTime timestamp;
|
||||
int accessCount = 0;
|
||||
bool isUpdating = false;
|
||||
//QSet<QObject*> pendingRequests;
|
||||
};
|
||||
|
||||
static DataManager* instance();
|
||||
|
||||
void registerDataSource(const QString& dataKey, std::function<QVariant()> fetcher);
|
||||
void registerHttpDataSource(const QString& dataKey, const QUrl& url, const QByteArray& method = "GET",
|
||||
const QByteArray& body = QByteArray(), const QMap<QByteArray, QByteArray>& headers = {});
|
||||
QVariant getCacheData(const QString& dataKey);
|
||||
bool hasData(const QString& dataKey);
|
||||
void requestData(const QString& dataKey, QObject* requester);
|
||||
void setCacheTimeout(int seconds);
|
||||
HttpRequestManager* httpManager();
|
||||
|
||||
signals:
|
||||
void dataUpdated(const QString& dataKey, const QVariant& data);
|
||||
void cacheInvalidated(const QString& dataKey);
|
||||
|
||||
public slots:
|
||||
void invalidateCache(const QString& dataKey = QString());
|
||||
void processRequestQueue();
|
||||
|
||||
void handleHttpDataReceived(const QString& dataKey, const QVariant& data);
|
||||
void handleHttpRequsetFailed(const QString& dataKey, const QVariant& data);
|
||||
|
||||
private:
|
||||
struct RequestInfo
|
||||
{
|
||||
QString dataKey;
|
||||
QObject* requester;
|
||||
};
|
||||
|
||||
explicit DataManager();
|
||||
~DataManager();
|
||||
|
||||
void triggerDataUpdate(const QString& dataKey);
|
||||
|
||||
QHash<QString, CacheEntry> m_cache;
|
||||
QHash<QString, std::function<QVariant()>> m_dataSources;
|
||||
QSet<QString> m_pendingUpdates;
|
||||
QQueue<RequestInfo> m_requestQueue;
|
||||
QTimer m_requestTimer;
|
||||
QReadWriteLock m_cacheLock;
|
||||
QMutex m_requestMutex;
|
||||
HttpRequestManager* m_httpManager;
|
||||
int m_cacheTimeout = 300; //默认5分钟
|
||||
int m_minRefreshInterval = 100; //最小刷新间隔(ms)
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,71 @@
|
|||
#ifndef HTTPREQUESTMANAGER_H
|
||||
#define HTTPREQUESTMANAGER_H
|
||||
|
||||
#include <QObject>
|
||||
#include <QNetworkAccessManager>
|
||||
#include <QNetworkReply>
|
||||
#include <QTimer>
|
||||
#include <QMap>
|
||||
#include <QUrl>
|
||||
#include <QJsonDocument>
|
||||
|
||||
class HttpRequestManager : public QObject
|
||||
{
|
||||
Q_OBJECT
|
||||
|
||||
public:
|
||||
explicit HttpRequestManager(QObject* parent = nullptr);
|
||||
~HttpRequestManager();
|
||||
|
||||
void registerEndpoint(const QString& dataKey, const QUrl& url, const QByteArray& method = "GET",
|
||||
const QByteArray& body = QByteArray(), const QMap<QByteArray, QByteArray>& headers = {});
|
||||
|
||||
void requestData(const QString& dataKey);
|
||||
void cancleRequest(const QString& dataKey);
|
||||
|
||||
void setDefaultTimeout(int ms);
|
||||
void setRetryPolicy(int maxRetries, int retryInterval);
|
||||
|
||||
bool hasVaildEndpoint(const QString& dataKey);
|
||||
|
||||
signals:
|
||||
void dataReceived(const QString& dataKey, const QVariant& data);
|
||||
void requestFailed(const QString& dataKey, const QString& error);
|
||||
//void requestProgress(const QString& dataKey, int progress);
|
||||
|
||||
private slots:
|
||||
void onReplyFinished();
|
||||
void onReplyError(QNetworkReply::NetworkError code);
|
||||
|
||||
private:
|
||||
struct EndpointConfig
|
||||
{
|
||||
QString dataKey;
|
||||
QUrl url;
|
||||
QByteArray method; //"GET","POST",使用QByteArray而非QString是因为网络编程中的参数都是QByteArray,直接使用可以避免QString引起的转码开销
|
||||
QByteArray body; //JSON格式的请求体内容,如"{\"key\":\"value\"}"
|
||||
QMap<QByteArray, QByteArray> headers; //http请求头,如{{"Content-Type", "application/json"}, {"Authorization", "Bearer token123"}}
|
||||
QNetworkReply* activeReply = nullptr;
|
||||
QTimer* timeoutTimer = nullptr;
|
||||
int retryCount = 0; //当前重试次数
|
||||
int maxRetries = 0; //最大重试次数
|
||||
int retryInterval = 1000;
|
||||
};
|
||||
|
||||
QNetworkAccessManager m_networkManager;
|
||||
QMap<QString, EndpointConfig> m_endpoints;
|
||||
int m_defaultTimeout = 5000;
|
||||
int m_maxRetries = 2;
|
||||
int m_retryIntrval = 1000;
|
||||
|
||||
void sendRequest(EndpointConfig& config);
|
||||
void handleResponse(const QString& dataKey, QNetworkReply* reply);
|
||||
void handleRequestTimeout(const QString& dataKey);
|
||||
void scheduleRetry(const QString& dataKey);
|
||||
|
||||
void parseJsonResponse(const QString& dataKey, const QByteArray& data);
|
||||
void parseTextResponse(const QString& dataKey, const QByteArray& data);
|
||||
void parseBinaryResponse(const QString& dataKey, const QByteArray& data);
|
||||
};
|
||||
|
||||
#endif
|
||||
|
|
@ -0,0 +1,32 @@
|
|||
#include "dataLoader.h"
|
||||
|
||||
QVariant DataLoader::LoadTask::fetchRealData(const QString& key)
|
||||
{
|
||||
//后续替换成实际接口
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
DataLoader::DataLoader(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
m_threadPool.setMaxThreadCount(5);
|
||||
}
|
||||
|
||||
DataLoader::~DataLoader()
|
||||
{
|
||||
handleShutdown();
|
||||
}
|
||||
|
||||
void DataLoader::requestData(const QString& dataKey)
|
||||
{
|
||||
QMutexLocker lockder(&m_taskMutex);
|
||||
if(m_runningTasks.contains(dataKey))
|
||||
return;
|
||||
|
||||
LoadTask* task = new LoadTask(this, dataKey);
|
||||
m_runningTasks.insert(dataKey, task);
|
||||
m_threadPool.start(task);
|
||||
}
|
||||
|
||||
void DataLoader::handleShutdown()
|
||||
{}
|
||||
|
|
@ -0,0 +1,236 @@
|
|||
#include "dataManager.h"
|
||||
#include "httpRequestManager.h"
|
||||
#include <QtConcurrent>
|
||||
|
||||
DataManager* DataManager::instance()
|
||||
{
|
||||
static DataManager instance;
|
||||
return &instance;
|
||||
}
|
||||
|
||||
DataManager::DataManager()
|
||||
:m_httpManager(new HttpRequestManager(this))
|
||||
{
|
||||
connect(m_httpManager, &HttpRequestManager::dataReceived, this, &DataManager::handleHttpDataReceived);
|
||||
connect(m_httpManager, &HttpRequestManager::requestFailed, this, &DataManager::handleHttpRequsetFailed);
|
||||
|
||||
m_requestTimer.setInterval(100);
|
||||
connect(&m_requestTimer, &QTimer::timeout, this, &DataManager::processRequestQueue);
|
||||
m_requestTimer.start();
|
||||
}
|
||||
DataManager::~DataManager()
|
||||
{}
|
||||
|
||||
void DataManager::triggerDataUpdate(const QString& dataKey)
|
||||
{
|
||||
bool isHttpSource = false;
|
||||
{
|
||||
QReadLocker locker(&m_cacheLock);
|
||||
isHttpSource = m_dataSources.contains(dataKey) && m_httpManager->hasVaildEndpoint(dataKey);
|
||||
}
|
||||
|
||||
if(isHttpSource)
|
||||
m_httpManager->requestData(dataKey);
|
||||
else
|
||||
{
|
||||
QThreadPool::globalInstance()->start([weakThis = QPointer<DataManager>(this), dataKey]() {
|
||||
if(weakThis.isNull())
|
||||
return;
|
||||
|
||||
QVariant newData;
|
||||
bool success = false;
|
||||
//获取数据源
|
||||
std::function<QVariant()> fetcher;
|
||||
{
|
||||
QReadLocker locker(&weakThis->m_cacheLock);
|
||||
if(!weakThis->m_dataSources.contains(dataKey))
|
||||
{
|
||||
qWarning() << "Data source not found for: " << dataKey;
|
||||
weakThis->m_pendingUpdates.remove(dataKey);
|
||||
return;
|
||||
}
|
||||
fetcher = weakThis->m_dataSources[dataKey];
|
||||
}
|
||||
|
||||
try {
|
||||
newData = fetcher();
|
||||
success = true;
|
||||
} catch (const std::exception& e) {
|
||||
qWarning() << "Data fetch error for" << dataKey << ": " << e.what();
|
||||
}
|
||||
|
||||
QWriteLocker locker(&weakThis->m_cacheLock);
|
||||
if(weakThis->m_cache.contains(dataKey))
|
||||
{
|
||||
CacheEntry& entry = weakThis->m_cache[dataKey];
|
||||
if(success)
|
||||
{
|
||||
entry.data = newData;
|
||||
entry.timestamp = QDateTime::currentDateTime();
|
||||
emit weakThis->dataUpdated(dataKey, newData);
|
||||
}
|
||||
entry.isUpdating = false;
|
||||
}
|
||||
|
||||
weakThis->m_pendingUpdates.remove(dataKey);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
HttpRequestManager* DataManager::httpManager()
|
||||
{
|
||||
return m_httpManager;
|
||||
}
|
||||
|
||||
void DataManager::registerDataSource(const QString& dataKey, std::function<QVariant()> fetcher)
|
||||
{
|
||||
if(dataKey.isEmpty() || !fetcher)
|
||||
{
|
||||
qWarning() << "Invalid data source registration";
|
||||
return;
|
||||
}
|
||||
|
||||
QWriteLocker locker(&m_cacheLock);
|
||||
m_dataSources[dataKey] = fetcher;
|
||||
|
||||
//初始化缓存条目
|
||||
if(!m_cache.contains(dataKey))
|
||||
m_cache[dataKey] = CacheEntry();
|
||||
}
|
||||
|
||||
void DataManager::registerHttpDataSource(const QString& dataKey, const QUrl& url, const QByteArray& method,
|
||||
const QByteArray& body, const QMap<QByteArray, QByteArray>& headers)
|
||||
{
|
||||
m_httpManager->registerEndpoint(dataKey, url, method, body, headers);
|
||||
//调用registerDataSource初始化缓存
|
||||
registerDataSource(dataKey, [](){return QVariant();}); //http的数据通过httpManager的信号进行接收,所以生成函数给空值即可
|
||||
}
|
||||
|
||||
QVariant DataManager::getCacheData(const QString& dataKey)
|
||||
{
|
||||
if(dataKey.isEmpty())
|
||||
return QVariant();
|
||||
|
||||
QReadLocker locker(&m_cacheLock);
|
||||
if(!m_cache.contains(dataKey))
|
||||
{
|
||||
qWarning() << "Unknown data key:" << dataKey;
|
||||
return QVariant();
|
||||
}
|
||||
|
||||
return m_cache[dataKey].data;
|
||||
}
|
||||
|
||||
bool DataManager::hasData(const QString& dataKey)
|
||||
{
|
||||
QReadLocker locker(&m_cacheLock);
|
||||
return m_cache.contains(dataKey) && !m_cache[dataKey].data.isNull();
|
||||
}
|
||||
|
||||
void DataManager::requestData(const QString& dataKey, QObject* requester)
|
||||
{
|
||||
QMutexLocker lock(&m_requestMutex);
|
||||
m_requestQueue.enqueue({dataKey, requester});
|
||||
}
|
||||
|
||||
void DataManager::invalidateCache(const QString& dataKey)
|
||||
{
|
||||
QWriteLocker locker(&m_cacheLock);
|
||||
if(dataKey.isEmpty())
|
||||
{
|
||||
m_cache.clear();
|
||||
emit cacheInvalidated("");
|
||||
}
|
||||
else
|
||||
{
|
||||
m_cache.remove(dataKey);
|
||||
emit cacheInvalidated(dataKey);
|
||||
}
|
||||
}
|
||||
|
||||
void DataManager::setCacheTimeout(int seconds)
|
||||
{
|
||||
m_cacheTimeout = qMax(60, seconds); //最小1分钟
|
||||
}
|
||||
|
||||
void DataManager::processRequestQueue()
|
||||
{
|
||||
//收集到本地,减少互斥锁的持有时间,后续都是对本地数据进行操作,无需加锁
|
||||
QList<RequestInfo> requests;
|
||||
{
|
||||
QMutexLocker lock(&m_requestMutex);
|
||||
while(!m_requestQueue.isEmpty())
|
||||
requests.append(m_requestQueue.dequeue());
|
||||
}
|
||||
|
||||
//按数据类型进行分组,减少更新频率
|
||||
QMap<QString, QSet<QObject*>> groupedRequests;
|
||||
for(const auto& req : requests)
|
||||
groupedRequests[req.dataKey].insert(req.requester);
|
||||
|
||||
QSet<QString> needsUpdate;
|
||||
QDateTime curTime = QDateTime::currentDateTime();
|
||||
|
||||
//减少写锁范围
|
||||
{
|
||||
QWriteLocker locker(&m_cacheLock);
|
||||
for(auto it = groupedRequests.begin(); it != groupedRequests.end(); ++it)
|
||||
{
|
||||
const QString& dataKey = it.key();
|
||||
const QSet<QObject*>& requesters = it.value();
|
||||
|
||||
if(!m_cache.contains(dataKey))
|
||||
{
|
||||
qWarning() << "Skipping request for unknown data key:" << dataKey;
|
||||
continue;
|
||||
}
|
||||
|
||||
CacheEntry& entry = m_cache[dataKey];
|
||||
//entry.pendingRequests.unite(it.value());
|
||||
|
||||
bool needsRefresh = false;
|
||||
if(entry.timestamp.isNull() || entry.timestamp.msecsTo(curTime) > m_minRefreshInterval)
|
||||
needsRefresh = true;
|
||||
if(needsRefresh)
|
||||
{
|
||||
entry.isUpdating = true;
|
||||
m_pendingUpdates.insert(dataKey);
|
||||
needsUpdate.insert(dataKey);
|
||||
}
|
||||
}
|
||||
} //写锁在这里自动释放
|
||||
|
||||
//触发更新
|
||||
for(const QString& dataKey : needsUpdate)
|
||||
triggerDataUpdate(dataKey);
|
||||
}
|
||||
|
||||
void DataManager::handleHttpDataReceived(const QString& dataKey, const QVariant& data)
|
||||
{
|
||||
QWriteLocker locker(&m_cacheLock);
|
||||
|
||||
if(m_cache.contains(dataKey))
|
||||
{
|
||||
CacheEntry& entry = m_cache[dataKey];
|
||||
entry.data = data;
|
||||
entry.timestamp = QDateTime::currentDateTime();
|
||||
entry.isUpdating = false;
|
||||
|
||||
emit dataUpdated(dataKey, data);
|
||||
}
|
||||
|
||||
m_pendingUpdates.remove(dataKey);
|
||||
}
|
||||
|
||||
void DataManager::handleHttpRequsetFailed(const QString& dataKey, const QVariant& data)
|
||||
{
|
||||
QWriteLocker locker(&m_cacheLock);
|
||||
|
||||
if(m_cache.contains(dataKey))
|
||||
{
|
||||
CacheEntry& entry = m_cache[dataKey];
|
||||
entry.isUpdating = false;
|
||||
}
|
||||
|
||||
m_pendingUpdates.remove(dataKey);
|
||||
}
|
||||
|
|
@ -0,0 +1,318 @@
|
|||
#include "httpRequestManager.h"
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
#include <QDebug>
|
||||
|
||||
HttpRequestManager::HttpRequestManager(QObject* parent)
|
||||
: QObject(parent)
|
||||
{
|
||||
}
|
||||
HttpRequestManager::~HttpRequestManager()
|
||||
{
|
||||
for(auto& config : m_endpoints)
|
||||
{
|
||||
if(config.activeReply)
|
||||
{
|
||||
config.activeReply->abort();
|
||||
config.activeReply->deleteLater();
|
||||
config.activeReply = nullptr;
|
||||
}
|
||||
if(config.timeoutTimer)
|
||||
{
|
||||
config.timeoutTimer->stop();
|
||||
config.timeoutTimer->deleteLater();
|
||||
config.timeoutTimer = nullptr;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
void HttpRequestManager::sendRequest(EndpointConfig& config)
|
||||
{
|
||||
QNetworkRequest request(config.url);
|
||||
|
||||
for(auto it = config.headers.constBegin(); it != config.headers.constEnd(); ++it)
|
||||
request.setRawHeader(it.key(), it.value());
|
||||
|
||||
QNetworkReply* reply = nullptr;
|
||||
if(config.method == "GET")
|
||||
reply = m_networkManager.get(request);
|
||||
else if(config.method == "POST")
|
||||
reply = m_networkManager.post(request, config.body);
|
||||
else if(config.method == "PUT")
|
||||
reply = m_networkManager.put(request, config.body);
|
||||
else if(config.method == "DELETE")
|
||||
reply = m_networkManager.deleteResource(request);
|
||||
else
|
||||
{
|
||||
qWarning() << "Unsupported HTTP method:" << config.method;
|
||||
emit requestFailed(config.dataKey, "Unsupported HTTP method");
|
||||
return;
|
||||
}
|
||||
|
||||
connect(reply, &QNetworkReply::finished, this, &HttpRequestManager::onReplyFinished);
|
||||
connect(reply, &QNetworkReply::errorOccurred, this, &HttpRequestManager::onReplyError);
|
||||
|
||||
config.timeoutTimer = new QTimer(this);
|
||||
config.timeoutTimer->setSingleShot(true);
|
||||
config.timeoutTimer->setInterval(m_defaultTimeout);
|
||||
connect(config.timeoutTimer, &QTimer::timeout, this, [this, &config](){
|
||||
handleRequestTimeout(config.dataKey);
|
||||
});
|
||||
}
|
||||
|
||||
void HttpRequestManager::handleResponse(const QString& dataKey, QNetworkReply* reply)
|
||||
{
|
||||
if(!m_endpoints.contains(dataKey))
|
||||
{
|
||||
qWarning() << "Recived response for unregistered dataKey:" << dataKey;
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
EndpointConfig& config = m_endpoints[dataKey];
|
||||
//关闭超时重连定时器
|
||||
if(config.timeoutTimer)
|
||||
{
|
||||
config.timeoutTimer->stop();
|
||||
config.timeoutTimer->deleteLater();
|
||||
config.timeoutTimer = nullptr;
|
||||
}
|
||||
|
||||
if(reply->error() != QNetworkReply::NoError)
|
||||
return; //具体错误信息在onReplyError中进行处理
|
||||
|
||||
QByteArray responseData = reply->readAll();
|
||||
QString contentType = reply->header(QNetworkRequest::ContentTypeHeader).toString();
|
||||
if(contentType.contains("application/json"))
|
||||
parseJsonResponse(dataKey, responseData);
|
||||
else if(contentType.contains("text/"))
|
||||
parseTextResponse(dataKey, responseData);
|
||||
else
|
||||
parseBinaryResponse(dataKey, responseData);
|
||||
|
||||
//数据重置
|
||||
reply->deleteLater();
|
||||
config.retryCount = 0;
|
||||
config.activeReply = nullptr;
|
||||
}
|
||||
|
||||
void HttpRequestManager::handleRequestTimeout(const QString& dataKey)
|
||||
{
|
||||
if(!m_endpoints.contains(dataKey))
|
||||
return;
|
||||
|
||||
EndpointConfig& config = m_endpoints[dataKey];
|
||||
qWarning() << "Request timeout for dataKey: " << dataKey;
|
||||
if(config.activeReply)
|
||||
config.activeReply->abort();
|
||||
//重试
|
||||
if(config.retryCount < config.maxRetries)
|
||||
scheduleRetry(config.dataKey);
|
||||
else
|
||||
{
|
||||
emit requestFailed(config.dataKey, "Request timed out");
|
||||
config.retryCount = 0;
|
||||
}
|
||||
}
|
||||
|
||||
void HttpRequestManager::scheduleRetry(const QString& dataKey)
|
||||
{
|
||||
if(!m_endpoints.contains(dataKey))
|
||||
return;
|
||||
|
||||
EndpointConfig& config = m_endpoints[dataKey];
|
||||
config.retryCount++;
|
||||
|
||||
QTimer::singleShot(config.retryInterval, this, [this, dataKey](){
|
||||
requestData(dataKey);
|
||||
});
|
||||
}
|
||||
|
||||
void HttpRequestManager::parseJsonResponse(const QString& dataKey, const QByteArray& data)
|
||||
{
|
||||
QJsonParseError parseError;
|
||||
QJsonDocument document = QJsonDocument::fromJson(data, &parseError);
|
||||
if(document.isNull())
|
||||
{
|
||||
emit requestFailed(dataKey, "get JSonDocument failed");
|
||||
return;
|
||||
}
|
||||
if(parseError.error != QJsonParseError::NoError)
|
||||
{
|
||||
emit requestFailed(dataKey, "JSON parse error: " + parseError.errorString());
|
||||
return;
|
||||
}
|
||||
|
||||
if(document.isObject())
|
||||
emit dataReceived(dataKey, document.object().toVariantMap());
|
||||
else if(document.isArray())
|
||||
emit dataReceived(dataKey, document.array().toVariantList());
|
||||
else
|
||||
emit dataReceived(dataKey, QVariant());
|
||||
}
|
||||
|
||||
void HttpRequestManager::parseTextResponse(const QString& dataKey, const QByteArray& data)
|
||||
{
|
||||
emit dataReceived(dataKey, QString::fromUtf8(data));
|
||||
}
|
||||
|
||||
void HttpRequestManager::parseBinaryResponse(const QString& dataKey, const QByteArray& data)
|
||||
{
|
||||
emit dataReceived(dataKey, data);
|
||||
}
|
||||
|
||||
void HttpRequestManager::registerEndpoint(const QString& dataKey, const QUrl& url, const QByteArray& method,
|
||||
const QByteArray& body, const QMap<QByteArray, QByteArray>& headers)
|
||||
{
|
||||
EndpointConfig config;
|
||||
config.dataKey = dataKey;
|
||||
config.url = url;
|
||||
config.method = method.toUpper();
|
||||
config.body = body;
|
||||
config.headers = headers;
|
||||
config.maxRetries = m_maxRetries;
|
||||
config.retryInterval = m_retryIntrval;
|
||||
|
||||
m_endpoints[dataKey] = config;
|
||||
}
|
||||
|
||||
void HttpRequestManager::requestData(const QString& dataKey)
|
||||
{
|
||||
if(!m_endpoints.contains(dataKey))
|
||||
{
|
||||
qWarning() << "This dataKey is not present in endpoints: " << dataKey;
|
||||
emit requestFailed(dataKey, "dataKey is not present in endpoints");
|
||||
return;
|
||||
}
|
||||
|
||||
EndpointConfig& config = m_endpoints[dataKey];
|
||||
if(config.activeReply)
|
||||
{
|
||||
config.activeReply->abort();
|
||||
config.activeReply->deleteLater();
|
||||
config.activeReply = nullptr;
|
||||
}
|
||||
if(config.timeoutTimer)
|
||||
{
|
||||
config.timeoutTimer->stop();
|
||||
config.timeoutTimer->deleteLater();
|
||||
config.timeoutTimer = nullptr;
|
||||
}
|
||||
|
||||
sendRequest(config);
|
||||
}
|
||||
|
||||
void HttpRequestManager::cancleRequest(const QString& dataKey)
|
||||
{
|
||||
if(!m_endpoints.contains(dataKey))
|
||||
return;
|
||||
|
||||
EndpointConfig& config = m_endpoints[dataKey];
|
||||
if(config.activeReply)
|
||||
{
|
||||
config.activeReply->abort();
|
||||
config.activeReply->deleteLater();
|
||||
config.activeReply = nullptr;
|
||||
}
|
||||
if(config.timeoutTimer)
|
||||
{
|
||||
config.timeoutTimer->stop();
|
||||
config.timeoutTimer->deleteLater();
|
||||
config.timeoutTimer = nullptr;
|
||||
}
|
||||
config.retryCount = 0;
|
||||
}
|
||||
|
||||
void HttpRequestManager::setDefaultTimeout(int ms)
|
||||
{
|
||||
m_defaultTimeout = qMax(1000, ms);
|
||||
}
|
||||
|
||||
void HttpRequestManager::setRetryPolicy(int maxRetries, int retryInterval)
|
||||
{
|
||||
m_maxRetries = qMax(0, maxRetries);
|
||||
m_retryIntrval = qMax(500, retryInterval);
|
||||
//更新所以已注册端点的充实策略
|
||||
for(auto& config : m_endpoints)
|
||||
{
|
||||
config.maxRetries = m_maxRetries;
|
||||
config.retryInterval = m_retryIntrval;
|
||||
}
|
||||
}
|
||||
|
||||
bool HttpRequestManager::hasVaildEndpoint(const QString& dataKey)
|
||||
{
|
||||
return m_endpoints.contains(dataKey) && m_endpoints[dataKey].url.isEmpty();
|
||||
}
|
||||
|
||||
void HttpRequestManager::onReplyFinished()
|
||||
{
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
if(!reply)
|
||||
return;
|
||||
|
||||
QString dataKey;
|
||||
for(auto& config : m_endpoints)
|
||||
{
|
||||
if(config.activeReply == reply)
|
||||
{
|
||||
dataKey = config.dataKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(dataKey.isEmpty())
|
||||
{
|
||||
reply->deleteLater();
|
||||
return;
|
||||
}
|
||||
|
||||
handleResponse(dataKey, reply);
|
||||
}
|
||||
|
||||
void HttpRequestManager::onReplyError(QNetworkReply::NetworkError code)
|
||||
{
|
||||
Q_UNUSED(code);
|
||||
|
||||
QNetworkReply* reply = qobject_cast<QNetworkReply*>(sender());
|
||||
if(!reply)
|
||||
return;
|
||||
|
||||
QString dataKey;
|
||||
for(auto& config : m_endpoints)
|
||||
{
|
||||
if(config.activeReply == reply)
|
||||
{
|
||||
dataKey = config.dataKey;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(dataKey.isEmpty())
|
||||
return;
|
||||
|
||||
EndpointConfig& config = m_endpoints[dataKey];
|
||||
//关闭超时重连定时器
|
||||
if(config.timeoutTimer)
|
||||
{
|
||||
config.timeoutTimer->stop();
|
||||
config.timeoutTimer->deleteLater();
|
||||
config.timeoutTimer = nullptr;
|
||||
}
|
||||
|
||||
//打印错误
|
||||
QString error = reply->errorString();
|
||||
qWarning() << "HTTP request failed for " << dataKey << ":" << error;
|
||||
|
||||
//重试
|
||||
if(config.retryCount < config.maxRetries)
|
||||
scheduleRetry(dataKey);
|
||||
else
|
||||
{
|
||||
emit requestFailed(dataKey, error);
|
||||
config.retryCount = 0;
|
||||
}
|
||||
|
||||
reply->deleteLater();
|
||||
config.activeReply = nullptr;
|
||||
}
|
||||
Loading…
Reference in New Issue