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 "ApiCall.hpp"
|
||||
#include "ApiResponseParse.hpp"
|
||||
|
||||
#include <QEventLoop>
|
||||
|
|
@ -66,4 +67,17 @@ ApiResponse ApiClient::postJson(const QString& path, const QJsonObject& body) {
|
|||
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
|
||||
|
|
|
|||
|
|
@ -8,6 +8,8 @@ class QNetworkAccessManager;
|
|||
|
||||
namespace geopro::net {
|
||||
|
||||
class IApiCall;
|
||||
|
||||
// 服务端统一响应信封:{code, data, msg}。
|
||||
// httpStatus 为 HTTP 状态码(如 200);code/data/msg 解析自响应体 JSON。
|
||||
// 网络层错误(连接失败、超时、JSON 解析失败)写入 rawError,此时 httpStatus 可能为 0。
|
||||
|
|
@ -38,9 +40,16 @@ public:
|
|||
ApiResponse get(const QString& path);
|
||||
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:
|
||||
struct Impl;
|
||||
std::unique_ptr<Impl> impl_;
|
||||
};
|
||||
|
||||
} // namespace geopro::net
|
||||
|
||||
#include <QMetaType>
|
||||
Q_DECLARE_METATYPE(geopro::net::ApiResponse)
|
||||
|
|
|
|||
|
|
@ -4,8 +4,10 @@ add_library(geopro_net STATIC
|
|||
crypto/RsaEncryptor.cpp
|
||||
ApiClient.cpp
|
||||
ApiResponseParse.cpp
|
||||
IApiCall.cpp
|
||||
ApiCall.cpp
|
||||
AuthService.cpp)
|
||||
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_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