feat(net): ApiClient.getAsync/postJsonAsync + IApiCall/ApiCall 异步句柄(abort+aborted_ 闸门,AUTOMOC ON)
This commit is contained in:
parent
c90ea83a04
commit
8f94443323
|
|
@ -0,0 +1,31 @@
|
||||||
|
#include "ApiCall.hpp"
|
||||||
|
|
||||||
|
#include <QNetworkReply>
|
||||||
|
#include "ApiResponseParse.hpp"
|
||||||
|
|
||||||
|
namespace geopro::net {
|
||||||
|
|
||||||
|
ApiCall::ApiCall(QNetworkReply* reply, QObject* parent) : IApiCall(parent), reply_(reply) {
|
||||||
|
QObject::connect(reply_, &QNetworkReply::finished, this, &ApiCall::onFinished);
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiCall::onFinished() {
|
||||||
|
if (aborted_) return; // §5.0 入口守卫:迟到信号闸门
|
||||||
|
ApiResponse resp = buildResponse(reply_);
|
||||||
|
if (reply_) reply_->deleteLater();
|
||||||
|
emit finished(resp);
|
||||||
|
deleteLater();
|
||||||
|
}
|
||||||
|
|
||||||
|
void ApiCall::abort() {
|
||||||
|
if (aborted_) return;
|
||||||
|
aborted_ = true; // 先置标志(权威闸门)
|
||||||
|
if (reply_) {
|
||||||
|
QObject::disconnect(reply_, nullptr, this, nullptr); // 尽力而为:断开未派发连接
|
||||||
|
reply_->abort(); // 会再触发一次 finished,已被 disconnect+aborted_ 双挡
|
||||||
|
reply_->deleteLater();
|
||||||
|
}
|
||||||
|
deleteLater(); // 禁止同步 delete
|
||||||
|
}
|
||||||
|
|
||||||
|
} // namespace geopro::net
|
||||||
|
|
@ -0,0 +1,22 @@
|
||||||
|
#pragma once
|
||||||
|
#include <QPointer>
|
||||||
|
#include "IApiCall.hpp"
|
||||||
|
|
||||||
|
class QNetworkReply;
|
||||||
|
|
||||||
|
namespace geopro::net {
|
||||||
|
|
||||||
|
// 包一个 QNetworkReply 的 IApiCall 实现。reply 完成 → buildResponse → emit finished → 自删。
|
||||||
|
// 安全不变量(spec §5.0):abort 后绝不 emit;销毁一律 deleteLater,禁止同步 delete。
|
||||||
|
class ApiCall : public IApiCall {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
explicit ApiCall(QNetworkReply* reply, QObject* parent = nullptr); // 接管 reply
|
||||||
|
void abort() override;
|
||||||
|
private:
|
||||||
|
void onFinished();
|
||||||
|
QPointer<QNetworkReply> reply_;
|
||||||
|
bool aborted_ = false;
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace geopro::net
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
#include "ApiClient.hpp"
|
#include "ApiClient.hpp"
|
||||||
|
|
||||||
|
#include "ApiCall.hpp"
|
||||||
#include "ApiResponseParse.hpp"
|
#include "ApiResponseParse.hpp"
|
||||||
|
|
||||||
#include <QEventLoop>
|
#include <QEventLoop>
|
||||||
|
|
@ -66,4 +67,17 @@ ApiResponse ApiClient::postJson(const QString& path, const QJsonObject& body) {
|
||||||
return resp;
|
return resp;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
IApiCall* ApiClient::getAsync(const QString& path) {
|
||||||
|
QNetworkRequest req = impl_->buildRequest(path);
|
||||||
|
QNetworkReply* reply = impl_->nam.get(req);
|
||||||
|
return new ApiCall(reply);
|
||||||
|
}
|
||||||
|
|
||||||
|
IApiCall* ApiClient::postJsonAsync(const QString& path, const QJsonObject& body) {
|
||||||
|
QNetworkRequest req = impl_->buildRequest(path);
|
||||||
|
const QByteArray payload = QJsonDocument(body).toJson(QJsonDocument::Compact);
|
||||||
|
QNetworkReply* reply = impl_->nam.post(req, payload);
|
||||||
|
return new ApiCall(reply);
|
||||||
|
}
|
||||||
|
|
||||||
} // namespace geopro::net
|
} // namespace geopro::net
|
||||||
|
|
|
||||||
|
|
@ -8,6 +8,8 @@ class QNetworkAccessManager;
|
||||||
|
|
||||||
namespace geopro::net {
|
namespace geopro::net {
|
||||||
|
|
||||||
|
class IApiCall;
|
||||||
|
|
||||||
// 服务端统一响应信封:{code, data, msg}。
|
// 服务端统一响应信封:{code, data, msg}。
|
||||||
// httpStatus 为 HTTP 状态码(如 200);code/data/msg 解析自响应体 JSON。
|
// httpStatus 为 HTTP 状态码(如 200);code/data/msg 解析自响应体 JSON。
|
||||||
// 网络层错误(连接失败、超时、JSON 解析失败)写入 rawError,此时 httpStatus 可能为 0。
|
// 网络层错误(连接失败、超时、JSON 解析失败)写入 rawError,此时 httpStatus 可能为 0。
|
||||||
|
|
@ -38,9 +40,16 @@ public:
|
||||||
ApiResponse get(const QString& path);
|
ApiResponse get(const QString& path);
|
||||||
ApiResponse postJson(const QString& path, const QJsonObject& body);
|
ApiResponse postJson(const QString& path, const QJsonObject& body);
|
||||||
|
|
||||||
|
// 异步 GET / POST(JSON):立即返回自管理句柄,不阻塞。调用方连 IApiCall::finished。
|
||||||
|
IApiCall* getAsync(const QString& path);
|
||||||
|
IApiCall* postJsonAsync(const QString& path, const QJsonObject& body);
|
||||||
|
|
||||||
private:
|
private:
|
||||||
struct Impl;
|
struct Impl;
|
||||||
std::unique_ptr<Impl> impl_;
|
std::unique_ptr<Impl> impl_;
|
||||||
};
|
};
|
||||||
|
|
||||||
} // namespace geopro::net
|
} // namespace geopro::net
|
||||||
|
|
||||||
|
#include <QMetaType>
|
||||||
|
Q_DECLARE_METATYPE(geopro::net::ApiResponse)
|
||||||
|
|
|
||||||
|
|
@ -4,8 +4,10 @@ add_library(geopro_net STATIC
|
||||||
crypto/RsaEncryptor.cpp
|
crypto/RsaEncryptor.cpp
|
||||||
ApiClient.cpp
|
ApiClient.cpp
|
||||||
ApiResponseParse.cpp
|
ApiResponseParse.cpp
|
||||||
|
IApiCall.cpp
|
||||||
|
ApiCall.cpp
|
||||||
AuthService.cpp)
|
AuthService.cpp)
|
||||||
target_include_directories(geopro_net PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
target_include_directories(geopro_net PUBLIC ${CMAKE_CURRENT_SOURCE_DIR})
|
||||||
target_link_libraries(geopro_net PUBLIC OpenSSL::SSL OpenSSL::Crypto Qt6::Core Qt6::Network)
|
target_link_libraries(geopro_net PUBLIC OpenSSL::SSL OpenSSL::Crypto Qt6::Core Qt6::Network)
|
||||||
target_compile_features(geopro_net PUBLIC cxx_std_17)
|
target_compile_features(geopro_net PUBLIC cxx_std_17)
|
||||||
set_target_properties(geopro_net PROPERTIES AUTOMOC OFF AUTOUIC OFF AUTORCC OFF)
|
set_target_properties(geopro_net PROPERTIES AUTOMOC ON AUTOUIC OFF AUTORCC OFF)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,6 @@
|
||||||
|
#include "IApiCall.hpp"
|
||||||
|
|
||||||
|
// IApiCall 是纯虚抽象句柄,无需额外实现。
|
||||||
|
// 此 .cpp 存在是为了让 AUTOMOC 扫描到 Q_OBJECT 宏并生成 moc_IApiCall.cpp。
|
||||||
|
namespace geopro::net {
|
||||||
|
} // namespace geopro::net
|
||||||
|
|
@ -0,0 +1,18 @@
|
||||||
|
#pragma once
|
||||||
|
#include <QObject>
|
||||||
|
#include "ApiClient.hpp" // geopro::net::ApiResponse
|
||||||
|
|
||||||
|
namespace geopro::net {
|
||||||
|
|
||||||
|
// 单个异步请求的抽象句柄(可测试缝)。ApiBatch 仅依赖此抽象,单测可注入假 call。
|
||||||
|
class IApiCall : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
public:
|
||||||
|
using QObject::QObject;
|
||||||
|
~IApiCall() override = default;
|
||||||
|
virtual void abort() = 0; // 置 aborted_、中断、deleteLater;abort 后绝不 emit finished
|
||||||
|
signals:
|
||||||
|
void finished(const geopro::net::ApiResponse& resp); // 成功/错误均经此(错误写 rawError)
|
||||||
|
};
|
||||||
|
|
||||||
|
} // namespace geopro::net
|
||||||
Loading…
Reference in New Issue