1
0
forked from BRT/arc
arc/services/EventHandler.cpp
trnsmkot b766725b1a Refactor scripts and add enhanced error handling
Revised scripts for payments and history retrieval, introducing improved error management with screenshots for debugging. Added a new LoginAndCheckAccountsScript for account validation, streamlined multi-threading, and updated application logic for clarity and reliability.
2025-05-30 22:16:25 +07:00

153 lines
4.9 KiB
C++

#include "EventHandler.h"
#include <QMap>
#include <QThread>
#include <QTimer>
#include "dao/AccountDAO.h"
#include "dao/DeviceDAO.h"
#include "dao/EventDAO.h"
#include "rshb/CommonScript.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 EventHandler::startThreadForKey(const QString &key) {
const EventInfo event = EventDAO::getFirstWaitEventByDeviceId(key);
if (event.id == -1) {
return;
}
if (!EventDAO::updateEvent(event.id, EventStatus::InProgress, "")) {
qCritical() << "Cant update event: " << event.id;
return;
}
qDebug() << "Thread for key" << key << "running";
// FIXME пока это только оплата,
const AccountInfo account = AccountDAO::getAccountById(event.accountId);
const auto thr = new QThread(this);
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);
const int eventId = event.id;
// При нормальном завершении — перезапустить новый поток
// connect(worker, &PayByPhoneScript::finished, this,
// [this, key, eventId]() {
// if (!EventDAO::updateEvent(eventId, EventStatus::Complete, "")) {
// qCritical() << "Cant update event: " << eventId;
// }
// if (const auto tm = m_timers.take(key)) {
// tm->stop();
// tm->deleteLater();
// }
// m_threads.remove(key);
// });
connect(worker, &PayByPhoneScript::finishedWithResult, this,
[this, key, eventId](const QString &result) {
if (!result.isEmpty()) {
if (!EventDAO::updateEvent(eventId, EventStatus::Error, result)) {
qCritical() << "Cant update event: " << eventId;
}
} else {
if (!EventDAO::updateEvent(eventId, EventStatus::Complete, "")) {
qCritical() << "Cant update event: " << eventId;
}
}
if (const auto tm = m_timers.take(key)) {
tm->stop();
tm->deleteLater();
}
m_threads.remove(key);
});
// Таймер на 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();
}