#include "EventHandler.h" #include #include #include #include #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 "ozon/LoginAndCheckAccountsScript.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 Rshb::GetLastDaysHistoryScript(account); // worker->moveToThread(thr); // // connect(thr, &QThread::started, worker, &Rshb::GetLastDaysHistoryScript::start); // connect(worker, &Rshb::GetLastDaysHistoryScript::finished, thr, &QThread::quit); // connect(worker, &Rshb::GetLastDaysHistoryScript::finished, worker, &QObject::deleteLater); // connect(thr, &QThread::finished, thr, &QObject::deleteLater); // // connect(worker, &Rshb::GetLastDaysHistoryScript::finishedWithResult, this, // [this, key, event](const QString &result) { // updateEventThread(key, event, result); // } // ); } if (event.type == EventType::FetchProfile) { if ("ozon" == account.appCode) { auto *worker = new Ozon::LoginAndCheckAccountsScript(event.deviceId, account.appCode); worker->moveToThread(thr); connect(thr, &QThread::started, worker, &Ozon::LoginAndCheckAccountsScript::start); connect(worker, &Ozon::LoginAndCheckAccountsScript::finished, thr, &QThread::quit); connect(worker, &Ozon::LoginAndCheckAccountsScript::finished, worker, &QObject::deleteLater); connect(thr, &QThread::finished, thr, &QObject::deleteLater); connect(worker, &Ozon::LoginAndCheckAccountsScript::finishedWithResult, this, [this, key, event](const QString &result) { updateEventThread(key, event, result); } ); } } if (event.type == EventType::Phone) { // auto *worker = new Rshb::PayByPhoneScript(account, event.phone, event.bankName, event.amount); // worker->moveToThread(thr); // // connect(thr, &QThread::started, worker, &Rshb::PayByPhoneScript::start); // connect(worker, &Rshb::PayByPhoneScript::finished, thr, &QThread::quit); // connect(worker, &Rshb::PayByPhoneScript::finished, worker, &QObject::deleteLater); // connect(thr, &QThread::finished, thr, &QObject::deleteLater); // // connect(worker, &Rshb::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(); }