1
0
forked from BRT/arc
arc/services/EventHandler.cpp
2025-07-27 21:01:13 +05:00

225 lines
7.7 KiB
C++

#include "EventHandler.h"
#include <QJsonObject>
#include <QMap>
#include <QThread>
#include <QTimer>
#include "dao/AccountDAO.h"
#include "dao/ApplicationDAO.h"
#include "dao/DeviceDAO.h"
#include "dao/EventDAO.h"
#include "db/ApplicationInfo.h"
#include "net/NetworkService.h"
#include "birbank/GetLastDaysHistoryBirbankScript.h"
#include "rshb/CommonScript.h"
#include "rshb/GetLastDaysHistoryScript.h"
#include "rshb/LoginAndCheckAccountsScript.h"
#include "rshb/PayByPhoneScript.h"
EventHandler::EventHandler(QObject *parent) : QObject(parent) {
m_checkTimer = new QTimer(this);
m_checkTimer->setInterval(1000);
connect(m_checkTimer, &QTimer::timeout, this, &EventHandler::checkAndRestart);
}
EventHandler::~EventHandler() = default;
void EventHandler::start() {
m_devices = DeviceDAO::getOnlineDeviceCodes();
clearAll();
// Запустим сразу потоки для всех ключей
for (auto &key: m_devices) {
startThreadForKey(key);
}
m_checkTimer->start();
}
void EventHandler::checkAndRestart() {
if (!m_running) {
return;
}
m_devices = DeviceDAO::getOnlineDeviceCodes();
for (const auto &key: m_devices) {
const QThread *thr = m_threads.value(key, nullptr);
if (!thr || !thr->isRunning()) {
startThreadForKey(key);
}
}
}
void sendEventStatus(const QString &deviceId, const int externalId, const int eventId, const QString &status,
const QString &error) {
QJsonObject obj;
obj["id"] = externalId;
obj["externalId"] = eventId;
obj["status"] = status;
obj["error"] = error;
auto *paymentThread = new QThread;
auto *networkService = new NetworkService;
networkService->moveToThread(paymentThread);
networkService->setDeviceId(deviceId);
networkService->setPayload(QJsonDocument(obj).toJson());
QObject::connect(paymentThread, &QThread::started, networkService, &NetworkService::postEventStatus);
QObject::connect(networkService, &NetworkService::finished, paymentThread, &QThread::quit);
QObject::connect(networkService, &NetworkService::finished, networkService, &QObject::deleteLater);
QObject::connect(paymentThread, &QThread::finished, paymentThread, &QThread::deleteLater);
paymentThread->start();
}
void EventHandler::updateEventThread(const QString &key, const EventInfo &event, const QString &result) {
if (!result.isEmpty()) {
if (!EventDAO::updateEvent(event.id, EventStatus::Error, result)) {
qCritical() << "Cant update event: " << event.id;
} else {
sendEventStatus(
event.deviceId,
event.externalId,
event.id,
eventStatusToString(EventStatus::Error),
result
);
}
} else {
if (!EventDAO::updateEvent(event.id, EventStatus::Complete, "")) {
qCritical() << "Cant update event: " << event.id;
} else {
sendEventStatus(
event.deviceId,
event.externalId,
event.id,
eventStatusToString(EventStatus::Complete),
""
);
}
}
if (const auto tm = m_timers.take(key)) {
tm->stop();
tm->deleteLater();
}
m_threads.remove(key);
}
void EventHandler::startThreadForKey(const QString &key) {
const EventInfo event = EventDAO::getFirstWaitEventByDeviceId(key);
if (event.id == -1) {
return;
}
const AccountInfo account = AccountDAO::getAccountById(event.accountId);
const ApplicationInfo app = ApplicationDAO::getApplication(event.deviceId, account.appCode);
if (app.status != "active") {
return;
}
if (!EventDAO::updateEvent(event.id, EventStatus::InProgress, "")) {
qCritical() << "Cant update event: " << event.id;
return;
}
const auto thr = new QThread(this);
// qDebug() << "Start thread for key" << eventTypeToString(event.type);
if (event.type == EventType::History) {
if ("birbank" == account.appCode) {
auto *worker = new GetLastDaysHistoryBirbankScript(account);
worker->moveToThread(thr);
connect(thr, &QThread::started, worker, &GetLastDaysHistoryBirbankScript::start);
connect(worker, &GetLastDaysHistoryBirbankScript::finished, thr, &QThread::quit);
connect(worker, &GetLastDaysHistoryBirbankScript::finished, worker, &QObject::deleteLater);
connect(thr, &QThread::finished, thr, &QObject::deleteLater);
connect(worker, &GetLastDaysHistoryBirbankScript::finishedWithResult, this,
[this, key, event](const QString &result) {
updateEventThread(key, event, result);
}
);
}
// auto *worker = new GetLastDaysHistoryScript(account);
// worker->moveToThread(thr);
//
// connect(thr, &QThread::started, worker, &GetLastDaysHistoryScript::start);
// connect(worker, &GetLastDaysHistoryScript::finished, thr, &QThread::quit);
// connect(worker, &GetLastDaysHistoryScript::finished, worker, &QObject::deleteLater);
// connect(thr, &QThread::finished, thr, &QObject::deleteLater);
//
// connect(worker, &GetLastDaysHistoryScript::finishedWithResult, this,
// [this, key, event](const QString &result) {
// updateEventThread(key, event, result);
// }
// );
}
if (event.type == EventType::Phone) {
// auto *worker = new PayByPhoneScript(account, event.phone, event.bankName, event.amount);
// worker->moveToThread(thr);
//
// connect(thr, &QThread::started, worker, &PayByPhoneScript::start);
// connect(worker, &PayByPhoneScript::finished, thr, &QThread::quit);
// connect(worker, &PayByPhoneScript::finished, worker, &QObject::deleteLater);
// connect(thr, &QThread::finished, thr, &QObject::deleteLater);
//
// connect(worker, &PayByPhoneScript::finishedWithResult, this,
// [this, key, event](const QString &result) {
// updateEventThread(key, event, result);
// }
// );
}
// Таймер на 5 минут — если не успел finish(), убить и перезапустить
const auto timer = new QTimer(this);
timer->setSingleShot(true);
connect(timer, &QTimer::timeout, this, [this, key]() {
qWarning() << "Timeout for key" << key << "- killing stuck thread.";
if (QThread *thread = m_threads.value(key); thread && thread->isRunning()) {
thread->requestInterruption(); // мягкое прерывание
if (!thread->wait(500)) {
qWarning() << "Hard terminate for key" << key;
thread->terminate();
thread->wait();
}
}
if (const auto tm = m_timers.take(key)) {
tm->deleteLater();
}
m_threads.remove(key);
});
// Сохраняем и запускаем
m_threads[key] = thr;
m_timers[key] = timer;
timer->start(5 * 60 * 1000);
thr->start();
}
void EventHandler::clearAll() {
// Останавливаем и удаляем старые
for (QThread *thread: m_threads) {
if (thread->isRunning()) {
thread->requestInterruption();
thread->terminate();
thread->wait();
}
thread->deleteLater();
}
m_threads.clear();
for (QTimer *timer: m_timers) {
timer->stop();
timer->deleteLater();
}
m_timers.clear();
}
void EventHandler::stop() {
m_running = false;
m_checkTimer->stop();
clearAll();
}