#include "EventHandler.h" #include #include #include #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(); }