#include "networkaccessmanager.h" #include "appevent.h" #include #include #include #include #include #include #include #include // 基础URL配置 static QString base_url("http://106.55.186.74:8200"); static const QString messageVal = "msg"; static NetworkAccessManager* namInstance = nullptr; void cleanupNetworkAccessManager() { delete namInstance; namInstance = nullptr; } NetworkAccessManager* NetworkAccessManager::instance() { if (!namInstance) { namInstance = new NetworkAccessManager; qAddPostRoutine(cleanupNetworkAccessManager); } return namInstance; } NetworkAccessManager::NetworkAccessManager(QObject* parent) : QNetworkAccessManager(parent) { // #ifdef QT_DEBUG // connect(this, &QNetworkAccessManager::finished, this, [](QNetworkReply* reply) { // const auto data = reply->readAll(); // reply->seek(0); // const auto startTimeValue = reply->property("startTime"); // const QDateTime startTime = startTimeValue.value(); // const QDateTime endTime = QDateTime::currentDateTime(); // qDebug() << QString("[%1] time:[%2]ms --> %3") // .arg(endTime.toString("yyyy-MM-dd hh:mm:ss.zzz")) // .arg((endTime.msecsTo(startTime))) // .arg(QString::fromUtf8(data)); // }); // #endif } QNetworkReply* NetworkAccessManager::createRequest(Operation op, const QNetworkRequest& request, QIODevice* outgoingData) { QNetworkRequest req(request); // 记录请求开始时间 QDateTime startTime = QDateTime::currentDateTime(); // 获取操作类型字符串 QString opStr; switch (op) { case QNetworkAccessManager::GetOperation: opStr = "GET"; break; case QNetworkAccessManager::PostOperation: opStr = "POST"; break; case QNetworkAccessManager::PutOperation: opStr = "PUT"; break; case QNetworkAccessManager::DeleteOperation: opStr = "DELETE"; break; case QNetworkAccessManager::HeadOperation: opStr = "HEAD"; break; default: opStr = "UNKNOWN"; break; } qDebug() << QString("[%1] --> %2 %3") .arg(startTime.toString("yyyy-MM-dd hh:mm:ss.zzz")) .arg(opStr) .arg(request.url().toString()); // 默认的请求处理 QString agentStr = QString::fromLatin1("%1/%2 (QNetworkAccessManager %3; %4; %5; %6 bit)") .arg(QCoreApplication::applicationName(), QCoreApplication::applicationVersion(), QLatin1String(qVersion()), QSysInfo::prettyProductName(), QLocale::system().name()) .arg(QSysInfo::WordSize); req.setRawHeader("User-Agent", agentStr.toLatin1()); const QString token = AppEvent::instance()->jwtToken(); if (!token.isEmpty()) { req.setRawHeader("Authorization", "Bearer " + token.toUtf8()); } req.setRawHeader("Machine-Code", AppEvent::instance()->machineCode().toUtf8()); req.setRawHeader("Accept-Language", AppEvent::instance()->locale().toUtf8()); const QUrl& url = request.url(); const QString urlPath = url.path(); if (!urlPath.contains("/api/auth/refresh-token") && !urlPath.contains("/api/auth/login")) { if (AppEvent::instance()->isRefreshToken()) { emit AppEvent::instance()->refreshTokenNeeded(); } } QNetworkReply* reply = QNetworkAccessManager::createRequest(op, req, outgoingData); // 存储开始时间,用于计算请求耗时 reply->setProperty("startTime", startTime); return reply; } namespace TC { RequestClient::RequestClient(const RequestClientOptions& options) : m_baseUrl(options.baseURL) , m_timeout(options.timeout) , m_networkManager(NetworkAccessManager::instance()) { if (options.enableDefaultInterceptors) { setupDefaultInterceptors(); } } RequestClient::Ptr RequestClient::create(const QString& baseURL, const RequestClientOptions& options) { RequestClientOptions opts = options; opts.baseURL = baseURL; return Ptr(new RequestClient(opts)); } RequestClient::Ptr RequestClient::globalInstance() { static RequestClient::Ptr instance = create(base_url, {"", true}); return instance; } RequestClient::Ptr RequestClient::baseGlobalInstance() { static RequestClient::Ptr instance = create(base_url, {"", false}); return instance; } void RequestClient::setupDefaultInterceptors() { QMutexLocker locker(&m_interceptorMutex); // 添加认证拦截器 m_interceptors.push_back(std::make_shared()); // 添加错误处理拦截器 m_interceptors.push_back(std::make_shared()); } void RequestClient::addInterceptor(std::shared_ptr interceptor) { QMutexLocker locker(&m_interceptorMutex); if (!m_interceptors.contains(interceptor)) { m_interceptors.append(interceptor); } } void RequestClient::removeInterceptor(std::shared_ptr interceptor) { QMutexLocker locker(&m_interceptorMutex); m_interceptors.removeOne(interceptor); } void RequestClient::clearInterceptors() { QMutexLocker locker(&m_interceptorMutex); m_interceptors.clear(); } HttpResponse RequestClient::get(const QString& url, const QVariantMap& params) { QScopedPointer reply(sendGetRequest(url, params)); QEventLoop loop; QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); return parseReplyData(reply.get()); } HttpResponse RequestClient::post(const QString& url, const QJsonDocument& data) { QScopedPointer reply(sendPostRequest(url, data)); QEventLoop loop; QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); return parseReplyData(reply.get()); } HttpResponse RequestClient::put(const QString& url, const QJsonDocument& data) { QScopedPointer reply(sendPutRequest(url, data)); QEventLoop loop; QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); return parseReplyData(reply.get()); } HttpResponse RequestClient::deleteResource(const QString& url) { QScopedPointer reply(sendDeleteRequest(url)); QEventLoop loop; QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); return parseReplyData(reply.get()); } HttpResponse RequestClient::upload(const QString& url, QHttpMultiPart* multiPart) { QScopedPointer reply(sendUploadRequest(url, multiPart)); QEventLoop loop; QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); return parseReplyData(reply.get()); } QFuture RequestClient::getAsync(const QString& url, const QVariantMap& params) { return QtConcurrent::run([this, url, params] { return get(url, params); }); } QFuture RequestClient::postAsync(const QString& url, const QJsonDocument& data) { return QtConcurrent::run([this, url, data] { return post(url, data); }); } QFuture RequestClient::putAsync(const QString& url, const QJsonDocument& data) { return QtConcurrent::run([this, url, data] { return put(url, data); }); } QFuture RequestClient::deleteAsync(const QString& url) { return QtConcurrent::run([this, url] { return deleteResource(url); }); } QFuture RequestClient::uploadAsync(const QString& url, QHttpMultiPart* multiPart) { return QtConcurrent::run([this, url, multiPart] { return upload(url, multiPart); }); } bool RequestClient::download(const QString& url, const QString& saveFilePath) { QScopedPointer reply(sendGetRequest(url, {})); QFile file(saveFilePath); if (!file.open(QIODevice::WriteOnly)) { return false; } QEventLoop loop; QObject::connect(reply.get(), &QNetworkReply::readyRead, [&]() { file.write(reply->readAll()); }); QObject::connect(reply.get(), &QNetworkReply::finished, &loop, &QEventLoop::quit); loop.exec(); file.close(); return reply->error() == QNetworkReply::NoError; } QFuture RequestClient::downloadAsync(const QString& url, const QString& saveFilePath) { return QtConcurrent::run([this, url, saveFilePath] { return download(url, saveFilePath); }); } QNetworkReply* RequestClient::sendGetRequest(const QString& url, const QVariantMap& params) { m_networkManager->setTransferTimeout(m_timeout); QString base = m_baseUrl.isEmpty() ? base_url : m_baseUrl; QUrl fullUrl(base + url); if (!params.isEmpty()) { QUrlQuery query; for (auto it = params.constBegin(); it != params.constEnd(); ++it) { query.addQueryItem(it.key(), it.value().toString()); } fullUrl.setQuery(query); } QNetworkRequest request(fullUrl); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); processRequest(request); emit requestStarted(fullUrl); return m_networkManager->get(request); } QNetworkReply* RequestClient::sendPostRequest(const QString& url, const QJsonDocument& data) { m_networkManager->setTransferTimeout(m_timeout); QString base = m_baseUrl.isEmpty() ? base_url : m_baseUrl; QUrl fullUrl(base + url); QBuffer* buffer = new QBuffer; buffer->setData(data.toJson()); buffer->open(QIODevice::ReadOnly); QNetworkRequest request(fullUrl); request.setHeader(QNetworkRequest::ContentTypeHeader, "application/json"); processRequest(request); emit requestStarted(fullUrl); QNetworkReply* reply = m_networkManager->post(request, buffer); buffer->setParent(reply); return reply; } QNetworkReply* RequestClient::sendPutRequest(const QString& url, const QJsonDocument& data) { m_networkManager->setTransferTimeout(m_timeout); QString base = m_baseUrl.isEmpty() ? base_url : m_baseUrl; QUrl fullUrl(base + url); QBuffer* buffer = new QBuffer; buffer->setData(data.toJson()); buffer->open(QIODevice::ReadOnly); QNetworkRequest request(fullUrl); processRequest(request); emit requestStarted(fullUrl); QNetworkReply* reply = m_networkManager->put(request, buffer); buffer->setParent(reply); return reply; } QNetworkReply* RequestClient::sendDeleteRequest(const QString& url) { m_networkManager->setTransferTimeout(m_timeout); QString base = m_baseUrl.isEmpty() ? base_url : m_baseUrl; QUrl fullUrl(base + url); QNetworkRequest request(fullUrl); processRequest(request); emit requestStarted(fullUrl); return m_networkManager->deleteResource(request); } QNetworkReply* RequestClient::sendUploadRequest(const QString& url, QHttpMultiPart* multiPart) { m_networkManager->setTransferTimeout(m_timeout); QString base = m_baseUrl.isEmpty() ? base_url : m_baseUrl; QUrl fullUrl(base + url); QNetworkRequest request(fullUrl); processRequest(request); emit requestStarted(fullUrl); return m_networkManager->post(request, multiPart); } HttpResponse RequestClient::parseReplyData(QNetworkReply* reply) { HttpResponse response; if (reply->error() != QNetworkReply::NoError) { response.code = reply->error(); response.message = reply->errorString(); emit requestFailed(reply->url(), response.message); return response; } const QByteArray responseData = readReplyData(reply); response.rawData = responseData; QJsonDocument jsonDoc = QJsonDocument::fromJson(responseData); if (jsonDoc.isObject()) { const QJsonObject jsonObj = jsonDoc.object(); if (jsonObj.contains("code")) { response.code = jsonObj["code"].toInt(); } if (jsonObj.contains(messageVal)) { response.message = jsonObj[messageVal].toString(); } if (jsonObj.contains("data")) { response.data = jsonObj["data"]; } } emit requestFinished(reply->url(), response); return response; } void RequestClient::processRequest(QNetworkRequest& request) { QMutexLocker locker(&m_interceptorMutex); for (auto& interceptor : m_interceptors) { interceptor->interceptRequest(request); } } void RequestClient::processResponse(QNetworkReply* reply) { QMutexLocker locker(&m_interceptorMutex); for (auto& interceptor : m_interceptors) { interceptor->interceptResponse(reply); } } QByteArray RequestClient::readReplyData(QNetworkReply* reply) { if (reply->property("cachedData").isValid()) { return reply->property("cachedData").toByteArray(); } QByteArray data = reply->readAll(); cacheReplyData(reply, data); return data; } void RequestClient::cacheReplyData(QNetworkReply* reply, const QByteArray& data) { reply->setProperty("cachedData", data); } // 拦截器实现 void AuthRequestInterceptor::interceptRequest(QNetworkRequest& request) { const QString token = AppEvent::instance()->jwtToken(); if (!token.isEmpty()) { request.setRawHeader("Authorization", "Bearer " + token.toUtf8()); } request.setRawHeader("Machine-Code", AppEvent::instance()->machineCode().toUtf8()); request.setRawHeader("Accept-Language", AppEvent::instance()->locale().toUtf8()); } void AuthRequestInterceptor::interceptResponse(QNetworkReply* reply) { if (reply->error() == QNetworkReply::AuthenticationRequiredError) { // 触发token刷新 emit AppEvent::instance()->refreshTokenNeeded(); if (AppEvent::instance()->isEnableRefreshToken()) { // 刷新token逻辑 auto client = RequestClient::baseGlobalInstance(); QJsonObject emptyData; QJsonDocument jsonDoc(emptyData); HttpResponse response = client->post("/api/auth/refresh-token", jsonDoc); if (response.code == 0) { QString newToken = response.data.toString(); AppEvent::instance()->setJwtToken(newToken); } else { // 刷新失败,需要重新登录 AppEvent::instance()->setJwtToken(""); emit AppEvent::instance()->loginExpired(true); } } else { // 直接重新认证 AppEvent::instance()->setJwtToken(""); emit AppEvent::instance()->loginExpired(true); } } } void ErrorResponseInterceptor::interceptResponse(QNetworkReply* reply) { if (reply->error() != QNetworkReply::NoError) { QByteArray responseData = reply->property("cachedData").toByteArray(); QString errorMessage; if (!responseData.isEmpty()) { QJsonDocument jsonResponse = QJsonDocument::fromJson(responseData); if (jsonResponse.isObject()) { QJsonObject jsonObject = jsonResponse.object(); if (jsonObject.contains("error")) { errorMessage = jsonObject["error"].toString(); } else if (jsonObject.contains(messageVal)) { errorMessage = jsonObject[messageVal].toString(); } } } if (errorMessage.isEmpty()) { errorMessage = reply->errorString(); } emit AppEvent::instance()->errorMessage(errorMessage); } } } // namespace TC