1
0
forked from BRT/arc
arc/services/net/NetworkService.h

177 lines
6.6 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#pragma once
#include <qhttpmultipart.h>
#include <QJsonArray>
#include <QNetworkAccessManager>
#include <QObject>
#include <QMap>
#include <QMutex>
#include <QSharedPointer>
#include <atomic>
class QWidget;
class NetworkService final : public QObject {
Q_OBJECT
public:
explicit NetworkService(QObject *parent = nullptr);
~NetworkService() override;
void setPayload(const QByteArray &json) { m_json = json; }
void setDeviceId(const QString &deviceId) {
m_deviceId = deviceId;
}
void addExtra(const QString &key, const QVariant &value);
// Подвешивает QMessageBox с текстом ошибки от backend (HTTP 409 — "материал
// заблокирован" и подобные) к ближайшему окну. Вызывать после `new NetworkService`
// в UI-коде, до запуска треда.
static void connectErrorDialog(QWidget *parent, NetworkService *net);
public slots:
void postAccountsData();
void postBankProfile();
void patchBankProfile();
void toggleBankProfile();
void postMaterial();
void patchMaterial();
void toggleMaterial();
void postDeviceScreenshot();
void getPayments();
void postAppStatus();
void postEventStatus();
void insertTransactionData();
void updateTransactionData();
void loginAndSetup();
void syncCurrentProfile();
// Включает периодическую синхронизацию статусов (60s). Должно быть вызвано
// ОДИН раз для долгоживущего инстанса после firstPassDone от DeviceScreener.
void startPeriodicSync();
// Обрабатывает переход устройства в офлайн (дебаунс-сигнал от DeviceScreener):
// выключает bank_profile локально и на сервере.
void onDeviceWentOffline(const QString &deviceId);
// Обрабатывает переход устройства в онлайн: пушит текущее локальное
// состояние профиля и материалов на сервер (локаль не меняем).
void onDeviceWentOnline(const QString &deviceId);
void createDevice();
void updateDevice();
void getTasks(const QStringList &materialIds = {});
void postTaskResult();
void postMonitoringLog(const QString &type, const QString &event,
const QString &message, const QByteArray &image);
void postMonitoringDump(const QString &text, const QByteArray &archive,
const QByteArray &image,
const QString &archiveFilename = QStringLiteral("task_dump.zip"));
void start();
void stop();
void disableAllProfilesOnServer();
void deleteDeviceFromServer();
signals:
void finished();
void finishedWithResult(int value);
void tokenRefreshFailed();
void tasksReceived(QJsonArray tasks);
// Эмитится при HTTP-ошибке PATCH-запроса (например, 409 'материал заблокирован').
// UI подписывается и показывает QMessageBox.
void apiPatchError(int httpStatus, QString message);
// healthy=false при kSyncFailureThreshold подряд неудачных тиков; healthy=true
// при первом успехе после такой серии. consecutiveFailures даёт текущий счёт.
void syncHealthChanged(bool healthy, int consecutiveFailures);
private:
QByteArray m_json;
QString m_deviceId;
QString m_token;
QString m_accessToken;
QString m_apiBase;
QMap<QString, QVariant> m_extraData = {};
bool m_running = true;
bool m_tokenErrorShown = false;
QString m_urlAccountUpdate;
QString m_urlPaymentsGet;
QString m_urlTransactionUpdate;
QString m_urlAppStatusUpdate;
QString m_urlTransactionInsert;
QString m_urlEventStatusUpdate;
QString m_pathAuthLogin;
QString m_pathAuthRefresh;
QString m_pathDesktopCreate;
QString m_pathDevice;
QString m_pathDeviceScreenshot;
QString m_pathBankProfile;
QString m_pathMaterial;
QString m_pathGetTasks;
QString m_pathTaskResult;
QString m_pathMonitoringLog;
QString m_pathMonitoringDump;
QString m_monitoringBase;
QString m_monitoringToken;
QString m_pathCurrentProfile;
QString m_pathProfileGetInfo;
bool m_fetchingTasks = false;
// Периодическая синхронизация
bool m_periodicSyncEnabled = false; // включается только после firstPassDone
std::atomic<bool> m_syncInProgress{false}; // защита от наложения тиков
int m_consecutiveSyncFailures = 0; // счётчик подряд неуспешных тиков
bool m_unhealthyEmitted = false; // чтобы не спамить syncHealthChanged
static constexpr int kSyncFailureThreshold = 3;
static constexpr int kPeriodicSyncMs = 60 * 1000;
// Реестр мьютексов на пару (deviceId, bankProfileId) — общий для всех
// инстансов NetworkService. Используется для сериализации PATCH-операций.
static QMutex s_keyRegistryMutex;
static QMap<QString, QSharedPointer<QMutex>> s_keyMutexes;
QNetworkAccessManager *m_manager;
QJsonValue post(QHttpMultiPart *multiPart, const QString &path);
QJsonArray get(const QString &path, const QMap<QString, QString> &params);
QJsonValue getJson(const QString &path, bool withAuth = false);
QJsonValue postJson(const QString &path, const QJsonObject &body, bool withAuth = false);
QJsonValue patchJson(const QString &path, const QJsonObject &body, bool withAuth = false);
QJsonValue deleteJson(const QString &path, bool withAuth = false);
bool refreshToken();
bool ensureValidToken();
void scheduleNextRefresh();
void scheduleNextStatusSync();
bool runPeriodicSyncTick(); // делает один тик, возвращает true при успехе
void syncDevicesFromServer(const QString &desktopId);
void syncBankProfilesFromServer();
void syncBanksIfEmpty();
public:
// Возвращает (лениво создавая) shared mutex для пары (deviceId, bankProfileId).
// Используется через QMutexLocker для сериализации PATCH-ов по одному профилю.
static QSharedPointer<QMutex> keyMutex(const QString &deviceId, const QString &bankProfileId);
};