Refactor and enhance device and event handling logic
Refactored `DeviceScreener`, `NetworkService`, and event-related logic for improved maintainability and flexibility. Introduced `EventHandler` for thread management and made changes to include configurable URLs via `config.ini`. Modified database schema to support device-level event linking.
This commit is contained in:
parent
89ea2ac53a
commit
5cb32ec4f1
@ -6,6 +6,8 @@ set(CMAKE_AUTOMOC ON)
|
|||||||
set(CMAKE_AUTORCC ON)
|
set(CMAKE_AUTORCC ON)
|
||||||
set(CMAKE_AUTOUIC ON)
|
set(CMAKE_AUTOUIC ON)
|
||||||
|
|
||||||
|
add_compile_definitions(QT_MESSAGELOGCONTEXT)
|
||||||
|
|
||||||
if (WIN32)
|
if (WIN32)
|
||||||
# Укажи путь к Qt6
|
# Укажи путь к Qt6
|
||||||
set(CMAKE_PREFIX_PATH "C:/Qt/6.9.0/msvc2022_arm64")
|
set(CMAKE_PREFIX_PATH "C:/Qt/6.9.0/msvc2022_arm64")
|
||||||
@ -75,6 +77,7 @@ if (WIN32)
|
|||||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/database/app_data.db" DESTINATION "${CMAKE_BINARY_DIR}")
|
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/database/app_data.db" DESTINATION "${CMAKE_BINARY_DIR}")
|
||||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/images" DESTINATION "${CMAKE_BINARY_DIR}")
|
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/images" DESTINATION "${CMAKE_BINARY_DIR}")
|
||||||
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/rus.traineddata" DESTINATION "${CMAKE_BINARY_DIR}")
|
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/rus.traineddata" DESTINATION "${CMAKE_BINARY_DIR}")
|
||||||
|
file(COPY "${CMAKE_CURRENT_SOURCE_DIR}/config.ini" DESTINATION "${CMAKE_BINARY_DIR}")
|
||||||
|
|
||||||
elseif (APPLE)
|
elseif (APPLE)
|
||||||
target_link_libraries(ARCDesktopProject
|
target_link_libraries(ARCDesktopProject
|
||||||
|
|||||||
14
config.ini
14
config.ini
@ -1,6 +1,16 @@
|
|||||||
|
[common]
|
||||||
|
varsion=0.0.1
|
||||||
|
|
||||||
[apps]
|
[apps]
|
||||||
list=rshb
|
list=rshb
|
||||||
|
|
||||||
[rshb]
|
[rshb]
|
||||||
name=Рос Сельхоз Банк
|
name=Росcельхозбанк
|
||||||
android_package_name=ru.rshb.dbo
|
android_package_name=ru.rshb.dbo
|
||||||
|
|
||||||
|
[net]
|
||||||
|
appInfoUpdate="http://localhost:9999/api/v1/app/update"
|
||||||
|
accountUpdate=http://localhost:9999/api/v1/account/update
|
||||||
|
paymentsGet=http://localhost:9999/api/v1/payments
|
||||||
|
transactionUpdate=http://localhost:9999/api/v1/transaction/update
|
||||||
|
transactionInsert=http://localhost:9999/api/v1/transaction/insert
|
||||||
Binary file not shown.
@ -45,6 +45,28 @@ QList<DeviceInfo> DeviceDAO::getAllDevices(const bool online) {
|
|||||||
return devices;
|
return devices;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
QList<QString> DeviceDAO::getOnlineDeviceCodes() {
|
||||||
|
QList<QString> codes;
|
||||||
|
QSqlQuery query(DatabaseManager::instance().database());
|
||||||
|
QString sql = R"(
|
||||||
|
SELECT id
|
||||||
|
FROM devices
|
||||||
|
WHERE image IS NOT NULL AND status = 'device'
|
||||||
|
)";
|
||||||
|
query.prepare(sql);
|
||||||
|
|
||||||
|
if (!query.exec()) {
|
||||||
|
qWarning() << "Ошибка при получении списка устройств:" << query.lastError().text();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
while (query.next()) {
|
||||||
|
codes.append(query.value("id").toString());
|
||||||
|
}
|
||||||
|
|
||||||
|
return codes;
|
||||||
|
}
|
||||||
|
|
||||||
DeviceInfo DeviceDAO::getDeviceById(const QString &deviceId) {
|
DeviceInfo DeviceDAO::getDeviceById(const QString &deviceId) {
|
||||||
QSqlQuery query(DatabaseManager::instance().database());
|
QSqlQuery query(DatabaseManager::instance().database());
|
||||||
|
|
||||||
|
|||||||
@ -13,4 +13,6 @@ public:
|
|||||||
[[nodiscard]] static bool updateImage(const QString &deviceId, const QByteArray &imageData);
|
[[nodiscard]] static bool updateImage(const QString &deviceId, const QByteArray &imageData);
|
||||||
|
|
||||||
[[nodiscard]] static QList<DeviceInfo> getAllDevices(bool online);
|
[[nodiscard]] static QList<DeviceInfo> getAllDevices(bool online);
|
||||||
|
|
||||||
|
[[nodiscard]] static QList<QString> getOnlineDeviceCodes();
|
||||||
};
|
};
|
||||||
|
|||||||
@ -12,6 +12,7 @@ EventInfo parseEvent(const QSqlQuery &query) {
|
|||||||
event.status = eventStatusFromString(query.value("status").toString());
|
event.status = eventStatusFromString(query.value("status").toString());
|
||||||
event.externalId = query.value("external_id").toInt();
|
event.externalId = query.value("external_id").toInt();
|
||||||
event.accountId = query.value("account_id").toInt();
|
event.accountId = query.value("account_id").toInt();
|
||||||
|
event.deviceId = query.value("device_id").toString();
|
||||||
event.amount = query.value("amount").toDouble();
|
event.amount = query.value("amount").toDouble();
|
||||||
event.phone = query.value("phone").toString();
|
event.phone = query.value("phone").toString();
|
||||||
event.bankName = query.value("bank_name").toString();
|
event.bankName = query.value("bank_name").toString();
|
||||||
@ -26,22 +27,23 @@ int EventDAO::insertEvent(const EventInfo &event) {
|
|||||||
|
|
||||||
query.prepare(R"(
|
query.prepare(R"(
|
||||||
INSERT INTO events (
|
INSERT INTO events (
|
||||||
status, external_id, account_id, amount, phone, bank_name, timestamp
|
status, external_id, account_id, amount, phone, bank_name, timestamp, device_id
|
||||||
) VALUES (
|
) VALUES (
|
||||||
:status, :external_id, :account_id, :amount, :phone, :bank_name, :timestamp
|
:status, :external_id, :account_id, :amount, :phone, :bank_name, :timestamp, :device_id
|
||||||
)
|
)
|
||||||
)");
|
)");
|
||||||
|
|
||||||
query.bindValue(":status", eventStatusToString(event.status));
|
query.bindValue(":status", eventStatusToString(event.status));
|
||||||
query.bindValue(":external_id", event.externalId);
|
query.bindValue(":external_id", event.externalId);
|
||||||
query.bindValue(":account_id", event.accountId);
|
query.bindValue(":account_id", event.accountId);
|
||||||
|
query.bindValue(":device_id", event.deviceId);
|
||||||
query.bindValue(":amount", event.amount);
|
query.bindValue(":amount", event.amount);
|
||||||
query.bindValue(":phone", event.phone);
|
query.bindValue(":phone", event.phone);
|
||||||
query.bindValue(":bank_name", event.bankName);
|
query.bindValue(":bank_name", event.bankName);
|
||||||
query.bindValue(":timestamp", DateUtils::toUtcString(DateUtils::getUtcNow()));
|
query.bindValue(":timestamp", DateUtils::toUtcString(DateUtils::getUtcNow()));
|
||||||
|
|
||||||
if (!query.exec()) {
|
if (!query.exec()) {
|
||||||
qWarning() << "Ошибка при вставке event:" << query.lastError().text();
|
qCritical() << "Ошибка при вставке event:" << query.lastError().text();
|
||||||
return -1;
|
return -1;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -78,7 +80,7 @@ bool EventDAO::updateEvent(
|
|||||||
}
|
}
|
||||||
|
|
||||||
if (!query.exec()) {
|
if (!query.exec()) {
|
||||||
qWarning() << "Ошибка при обновлении event:" << query.lastError().text();
|
qCritical() << "Ошибка при обновлении event:" << query.lastError().text();
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
return true;
|
return true;
|
||||||
@ -89,14 +91,14 @@ QList<EventInfo> EventDAO::getAllEvents(const EventStatus &status) {
|
|||||||
|
|
||||||
QSqlQuery query(DatabaseManager::instance().database());
|
QSqlQuery query(DatabaseManager::instance().database());
|
||||||
query.prepare(R"(
|
query.prepare(R"(
|
||||||
SELECT id, account_id, external_id, amount, phone, bank_name, status, update_time, complete_time, timestamp
|
SELECT id, account_id, device_id, external_id, amount, phone, bank_name, status, update_time, complete_time, timestamp
|
||||||
FROM events
|
FROM events
|
||||||
WHERE status = :status
|
WHERE status = :status
|
||||||
)");
|
)");
|
||||||
query.bindValue(":status", eventStatusToString(status));
|
query.bindValue(":status", eventStatusToString(status));
|
||||||
|
|
||||||
if (!query.exec()) {
|
if (!query.exec()) {
|
||||||
qWarning() << "Ошибка при получении списка устройств:" << query.lastError().text();
|
qWarning() << "Ошибка при получении getAllEvents:" << query.lastError().text();
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -106,3 +108,26 @@ QList<EventInfo> EventDAO::getAllEvents(const EventStatus &status) {
|
|||||||
|
|
||||||
return events;
|
return events;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
EventInfo EventDAO::getFirstWaitEventByDeviceId(const QString &key) {
|
||||||
|
QSqlQuery query(DatabaseManager::instance().database());
|
||||||
|
query.prepare(R"(
|
||||||
|
SELECT id, account_id, device_id, external_id, amount, phone, bank_name, status, update_time, complete_time, timestamp
|
||||||
|
FROM events
|
||||||
|
WHERE status = :status AND device_id = :device_id
|
||||||
|
ORDER BY timestamp ASC
|
||||||
|
LIMIT 1
|
||||||
|
)");
|
||||||
|
query.bindValue(":status", eventStatusToString(EventStatus::Wait));
|
||||||
|
query.bindValue(":device_id", key);
|
||||||
|
|
||||||
|
if (!query.exec()) {
|
||||||
|
qCritical() << "Ошибка при получении getFirstWaitEventByDeviceId:" << query.lastError().text();
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|
||||||
|
while (query.next()) {
|
||||||
|
return parseEvent(query);
|
||||||
|
}
|
||||||
|
return {};
|
||||||
|
}
|
||||||
|
|||||||
@ -12,4 +12,6 @@ public:
|
|||||||
);
|
);
|
||||||
|
|
||||||
[[nodiscard]] static QList<EventInfo> getAllEvents(const EventStatus &status);
|
[[nodiscard]] static QList<EventInfo> getAllEvents(const EventStatus &status);
|
||||||
|
|
||||||
|
[[nodiscard]] static EventInfo getFirstWaitEventByDeviceId(const QString &key);
|
||||||
};
|
};
|
||||||
|
|||||||
35
main.cpp
35
main.cpp
@ -16,6 +16,7 @@
|
|||||||
#include <chrono>
|
#include <chrono>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
|
|
||||||
|
#include "EventHandler.h"
|
||||||
#include "net/NetworkService.h"
|
#include "net/NetworkService.h"
|
||||||
#include "rshb/GetLastDaysHistoryScript.h"
|
#include "rshb/GetLastDaysHistoryScript.h"
|
||||||
#include "rshb/HomeScreenScript.h"
|
#include "rshb/HomeScreenScript.h"
|
||||||
@ -145,7 +146,7 @@ static QFile logFile;
|
|||||||
static QMutex logMutex;
|
static QMutex logMutex;
|
||||||
|
|
||||||
void messageHandler(
|
void messageHandler(
|
||||||
QtMsgType type,
|
const QtMsgType type,
|
||||||
const QMessageLogContext &context,
|
const QMessageLogContext &context,
|
||||||
const QString &msg
|
const QString &msg
|
||||||
) {
|
) {
|
||||||
@ -166,8 +167,9 @@ void messageHandler(
|
|||||||
break;
|
break;
|
||||||
}
|
}
|
||||||
|
|
||||||
const QString line = QString("%1 [%2] %3\n").arg(timestamp, level, msg);
|
|
||||||
|
|
||||||
|
const QString line = QString("%1 [%2] [%3:%4] %5\n")
|
||||||
|
.arg(timestamp, level, context.function, QString::number(context.line), msg);
|
||||||
// Записываем в файл (потокобезопасно)
|
// Записываем в файл (потокобезопасно)
|
||||||
{
|
{
|
||||||
QMutexLocker locker(&logMutex);
|
QMutexLocker locker(&logMutex);
|
||||||
@ -178,7 +180,7 @@ void messageHandler(
|
|||||||
}
|
}
|
||||||
|
|
||||||
// По желанию — дублировать в консоль
|
// По желанию — дублировать в консоль
|
||||||
QByteArray localMsg = line.toLocal8Bit();
|
const QByteArray localMsg = line.toLocal8Bit();
|
||||||
fprintf(stderr, "%s", localMsg.constData());
|
fprintf(stderr, "%s", localMsg.constData());
|
||||||
|
|
||||||
if (type == QtFatalMsg) {
|
if (type == QtFatalMsg) {
|
||||||
@ -196,21 +198,28 @@ int main(int argc, char *argv[]) {
|
|||||||
// Устанавливаем свой обработчик
|
// Устанавливаем свой обработчик
|
||||||
qInstallMessageHandler(messageHandler);
|
qInstallMessageHandler(messageHandler);
|
||||||
|
|
||||||
|
|
||||||
auto *thread = new QThread;
|
auto *thread = new QThread;
|
||||||
auto *worker = new NetworkService;
|
auto *worker = new EventHandler;
|
||||||
|
|
||||||
// FIXME доставить десктоп ид из настроеек
|
|
||||||
worker->setDeviceData("DT88bokcwQ", "");
|
|
||||||
worker->moveToThread(thread);
|
worker->moveToThread(thread);
|
||||||
QObject::connect(thread, &QThread::started, worker, &NetworkService::getPayments);
|
QObject::connect(thread, &QThread::started, worker, &EventHandler::start);
|
||||||
QObject::connect(worker, &NetworkService::finished, thread, &QThread::quit);
|
QObject::connect(worker, &EventHandler::finished, thread, &QThread::quit);
|
||||||
QObject::connect(worker, &NetworkService::finished, worker, &QObject::deleteLater);
|
QObject::connect(worker, &EventHandler::finished, worker, &QObject::deleteLater);
|
||||||
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
|
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
|
||||||
thread->start();
|
thread->start();
|
||||||
|
|
||||||
// setupScriptAutoPaymentAndThread(app);
|
auto *paymentThread = new QThread;
|
||||||
// setupWorkerAndThread(app);
|
auto *networkService = new NetworkService;
|
||||||
|
|
||||||
|
// FIXME доставить десктоп ид из настроеек
|
||||||
|
networkService->setDeviceData("DT88bokcwQ", "");
|
||||||
|
networkService->moveToThread(paymentThread);
|
||||||
|
QObject::connect(paymentThread, &QThread::started, networkService, &NetworkService::start);
|
||||||
|
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();
|
||||||
|
|
||||||
|
setupWorkerAndThread(app);
|
||||||
|
|
||||||
MainWindow window;
|
MainWindow window;
|
||||||
window.show();
|
window.show();
|
||||||
|
|||||||
@ -74,6 +74,7 @@ CREATE TABLE IF NOT EXISTS events
|
|||||||
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
id INTEGER PRIMARY KEY AUTOINCREMENT,
|
||||||
account_id INTEGER,
|
account_id INTEGER,
|
||||||
external_id INTEGER,
|
external_id INTEGER,
|
||||||
|
device_id TEXT,
|
||||||
|
|
||||||
amount REAL,
|
amount REAL,
|
||||||
phone TEXT,
|
phone TEXT,
|
||||||
@ -85,5 +86,6 @@ CREATE TABLE IF NOT EXISTS events
|
|||||||
complete_time DATETIME,
|
complete_time DATETIME,
|
||||||
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
|
||||||
FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE,
|
FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE,
|
||||||
|
FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE CASCADE,
|
||||||
UNIQUE (external_id)
|
UNIQUE (external_id)
|
||||||
);
|
);
|
||||||
@ -31,6 +31,7 @@ struct EventInfo {
|
|||||||
int id = -1;
|
int id = -1;
|
||||||
int externalId = -1;
|
int externalId = -1;
|
||||||
int accountId = -1;
|
int accountId = -1;
|
||||||
|
QString deviceId;
|
||||||
|
|
||||||
double amount = 0;
|
double amount = 0;
|
||||||
QString phone;
|
QString phone;
|
||||||
|
|||||||
@ -1,6 +1,6 @@
|
|||||||
#include "DeviceScreener.h"
|
#include "DeviceScreener.h"
|
||||||
|
|
||||||
#include <QRegularExpression>
|
#include <QSettings>
|
||||||
#include <QProcess>
|
#include <QProcess>
|
||||||
#include <QFile>
|
#include <QFile>
|
||||||
#include <QBuffer>
|
#include <QBuffer>
|
||||||
@ -16,11 +16,13 @@
|
|||||||
#include <db/DeviceInfo.h>
|
#include <db/DeviceInfo.h>
|
||||||
|
|
||||||
DeviceScreener::DeviceScreener(QObject *parent) : QObject(parent) {
|
DeviceScreener::DeviceScreener(QObject *parent) : QObject(parent) {
|
||||||
|
const QSettings settings("config.ini", QSettings::IniFormat);
|
||||||
|
m_urlAppUpdate = settings.value("net/appInfoUpdate").toString();
|
||||||
}
|
}
|
||||||
|
|
||||||
DeviceScreener::~DeviceScreener() = default;
|
DeviceScreener::~DeviceScreener() = default;
|
||||||
|
|
||||||
void postDevicesData(const QList<DeviceInfo> &devices) {
|
void DeviceScreener::postDevicesData(const QList<DeviceInfo> &devices) {
|
||||||
auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||||
|
|
||||||
QHttpPart jsonPart;
|
QHttpPart jsonPart;
|
||||||
@ -60,7 +62,7 @@ void postDevicesData(const QList<DeviceInfo> &devices) {
|
|||||||
multiPart->append(uniquePart);
|
multiPart->append(uniquePart);
|
||||||
|
|
||||||
QNetworkAccessManager manager; // создаём внутри потока
|
QNetworkAccessManager manager; // создаём внутри потока
|
||||||
QUrl url("http://localhost:9999/api/v1/app/update");
|
QUrl url(m_urlAppUpdate);
|
||||||
QNetworkRequest request(url);
|
QNetworkRequest request(url);
|
||||||
|
|
||||||
// делаем запрос
|
// делаем запрос
|
||||||
@ -82,10 +84,12 @@ void postDevicesData(const QList<DeviceInfo> &devices) {
|
|||||||
QJsonDocument doc = QJsonDocument::fromJson(raw, &err);
|
QJsonDocument doc = QJsonDocument::fromJson(raw, &err);
|
||||||
if (err.error == QJsonParseError::NoError && doc.isObject()) {
|
if (err.error == QJsonParseError::NoError && doc.isObject()) {
|
||||||
QJsonObject root = doc.object();
|
QJsonObject root = doc.object();
|
||||||
qDebug() << "Успешный ответ: " << root.value("success").toBool();
|
if (!root.value("success").toBool()) {
|
||||||
|
qWarning() << root;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Ошибка:" << reply->errorString();
|
qWarning() << "Response error:" << reply->errorString() << m_urlAppUpdate;
|
||||||
}
|
}
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
}
|
}
|
||||||
@ -116,12 +120,14 @@ void DeviceScreener::start() {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
postDevicesData(deviceInfos);
|
if (!deviceInfos.empty()) {
|
||||||
|
postDevicesData(deviceInfos);
|
||||||
|
}
|
||||||
|
|
||||||
if (!DeviceDAO::markDevicesOfflineExcept(onlineIds)) {
|
if (!DeviceDAO::markDevicesOfflineExcept(onlineIds)) {
|
||||||
qDebug() << "Cant mark devices offline";
|
qDebug() << "Cant mark devices offline";
|
||||||
}
|
}
|
||||||
QThread::sleep(3);
|
QThread::msleep(1000);
|
||||||
}
|
}
|
||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|||||||
@ -21,10 +21,13 @@ signals:
|
|||||||
|
|
||||||
private:
|
private:
|
||||||
bool m_running = true;
|
bool m_running = true;
|
||||||
|
QString m_urlAppUpdate;
|
||||||
|
|
||||||
static QList<QPair<QString, QString> > readAdbDevices();
|
static QList<QPair<QString, QString> > readAdbDevices();
|
||||||
|
|
||||||
static QByteArray takeScreenshot(const QString &deviceId);
|
static QByteArray takeScreenshot(const QString &deviceId);
|
||||||
|
|
||||||
static DeviceInfo readDeviceInfo(const QString &deviceId);
|
static DeviceInfo readDeviceInfo(const QString &deviceId);
|
||||||
|
|
||||||
|
void postDevicesData(const QList<DeviceInfo> &devices);
|
||||||
};
|
};
|
||||||
|
|||||||
130
services/EventHandler.cpp
Normal file
130
services/EventHandler.cpp
Normal file
@ -0,0 +1,130 @@
|
|||||||
|
#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);
|
||||||
|
|
||||||
|
// При нормальном завершении — перезапустить новый поток
|
||||||
|
connect(worker, &PayByPhoneScript::finished, this,
|
||||||
|
[this, key]() {
|
||||||
|
// qDebug() << "Thread finished for key" << key << "- restarting.";
|
||||||
|
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();
|
||||||
|
}
|
||||||
36
services/EventHandler.h
Normal file
36
services/EventHandler.h
Normal file
@ -0,0 +1,36 @@
|
|||||||
|
#pragma once
|
||||||
|
#include <QMap>
|
||||||
|
#include <QObject>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
|
class EventHandler final : public QObject {
|
||||||
|
Q_OBJECT
|
||||||
|
|
||||||
|
public:
|
||||||
|
explicit EventHandler(QObject *parent = nullptr);
|
||||||
|
|
||||||
|
~EventHandler() override;
|
||||||
|
|
||||||
|
public slots:
|
||||||
|
void start();
|
||||||
|
|
||||||
|
void stop();
|
||||||
|
|
||||||
|
signals:
|
||||||
|
void finished();
|
||||||
|
|
||||||
|
private slots:
|
||||||
|
void checkAndRestart(); // вызывается по таймеру
|
||||||
|
|
||||||
|
private:
|
||||||
|
bool m_running = true;
|
||||||
|
QMap<QString, QThread *> m_threads;
|
||||||
|
QMap<QString, QTimer *> m_timers;
|
||||||
|
QTimer *m_checkTimer = nullptr;
|
||||||
|
|
||||||
|
QStringList m_devices;
|
||||||
|
|
||||||
|
void clearAll();
|
||||||
|
|
||||||
|
void startThreadForKey(const QString &key);
|
||||||
|
};
|
||||||
@ -1,11 +1,13 @@
|
|||||||
#include "NetworkService.h"
|
#include "NetworkService.h"
|
||||||
|
|
||||||
#include <QEventLoop>
|
|
||||||
#include <QUrlQuery>
|
#include <QUrlQuery>
|
||||||
#include <QHttpPart>
|
#include <QHttpPart>
|
||||||
#include <QNetworkReply>
|
#include <QNetworkReply>
|
||||||
#include <QJsonObject>
|
#include <QJsonObject>
|
||||||
#include <QJsonArray>
|
#include <QJsonArray>
|
||||||
|
#include <QThread>
|
||||||
|
#include <QSettings>
|
||||||
|
#include <QTimer>
|
||||||
|
|
||||||
#include "dao/AccountDAO.h"
|
#include "dao/AccountDAO.h"
|
||||||
#include "dao/EventDAO.h"
|
#include "dao/EventDAO.h"
|
||||||
@ -13,6 +15,12 @@
|
|||||||
#include "db/EventInfo.h"
|
#include "db/EventInfo.h"
|
||||||
|
|
||||||
NetworkService::NetworkService(QObject *parent) : QObject(parent) {
|
NetworkService::NetworkService(QObject *parent) : QObject(parent) {
|
||||||
|
const QSettings settings("config.ini", QSettings::IniFormat);
|
||||||
|
m_urlAccountUpdate = settings.value("net/accountUpdate").toString();
|
||||||
|
m_urlPaymentsGet = settings.value("net/paymentsGet").toString();
|
||||||
|
m_urlTransactionUpdate = settings.value("net/transactionUpdate").toString();
|
||||||
|
m_urlTransactionInsert = settings.value("net/transactionInsert").toString();
|
||||||
|
m_manager = new QNetworkAccessManager(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
NetworkService::~NetworkService() = default;
|
NetworkService::~NetworkService() = default;
|
||||||
@ -40,11 +48,10 @@ void addFormMetadata(QHttpMultiPart *multiPart, const QByteArray &fieldValue) {
|
|||||||
}
|
}
|
||||||
|
|
||||||
QJsonValue NetworkService::post(QHttpMultiPart *multiPart, const QString &path) {
|
QJsonValue NetworkService::post(QHttpMultiPart *multiPart, const QString &path) {
|
||||||
QNetworkAccessManager manager; // создаём внутри потока
|
|
||||||
const QUrl url(path);
|
const QUrl url(path);
|
||||||
const QNetworkRequest request(url);
|
const QNetworkRequest request(url);
|
||||||
|
|
||||||
QNetworkReply *reply = manager.post(request, multiPart);
|
QNetworkReply *reply = m_manager->post(request, multiPart);
|
||||||
multiPart->setParent(reply);
|
multiPart->setParent(reply);
|
||||||
|
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
@ -64,20 +71,17 @@ QJsonValue NetworkService::post(QHttpMultiPart *multiPart, const QString &path)
|
|||||||
if (root.value("success").toBool()) {
|
if (root.value("success").toBool()) {
|
||||||
result = root.value("data");
|
result = root.value("data");
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Ошибка:" << root;
|
qCritical() << "Ошибка:" << root;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
qWarning() << "Ошибка:" << reply->errorString();
|
qCritical() << "Ошибка:" << reply->errorString() << path;
|
||||||
}
|
}
|
||||||
reply->deleteLater();
|
reply->deleteLater();
|
||||||
return result;
|
return result;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
QJsonArray NetworkService::get(const QString &path, const QMap<QString, QString> ¶ms) {
|
QJsonArray NetworkService::get(const QString &path, const QMap<QString, QString> ¶ms) {
|
||||||
QNetworkAccessManager manager; // создаём внутри потока
|
|
||||||
|
|
||||||
QUrl url(path);
|
QUrl url(path);
|
||||||
QUrlQuery query;
|
QUrlQuery query;
|
||||||
for (auto it = params.constBegin(); it != params.constEnd(); ++it) {
|
for (auto it = params.constBegin(); it != params.constEnd(); ++it) {
|
||||||
@ -86,7 +90,8 @@ QJsonArray NetworkService::get(const QString &path, const QMap<QString, QString>
|
|||||||
url.setQuery(query);
|
url.setQuery(query);
|
||||||
|
|
||||||
const QNetworkRequest request(url);
|
const QNetworkRequest request(url);
|
||||||
QNetworkReply *reply = manager.get(request);
|
QNetworkReply *reply = m_manager->get(request);
|
||||||
|
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> guard(reply);
|
||||||
|
|
||||||
QEventLoop loop;
|
QEventLoop loop;
|
||||||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||||||
@ -121,9 +126,9 @@ void NetworkService::postAccountsData() {
|
|||||||
addFormMetadata(multiPart, m_json);
|
addFormMetadata(multiPart, m_json);
|
||||||
addFormField(multiPart, "desktopUniqueName", m_desktopId);
|
addFormField(multiPart, "desktopUniqueName", m_desktopId);
|
||||||
addFormField(multiPart, "androidUniqueName", m_deviceId);
|
addFormField(multiPart, "androidUniqueName", m_deviceId);
|
||||||
const bool isSuccess = post(multiPart, "http://localhost:9999/api/v1/account/update").toBool();
|
const bool isSuccess = post(multiPart, m_urlAccountUpdate).toBool();
|
||||||
if (!isSuccess) {
|
if (!isSuccess) {
|
||||||
qDebug() << "Cant post accounts data";
|
qCritical() << "Cant post accounts data";
|
||||||
}
|
}
|
||||||
|
|
||||||
emit finished();
|
emit finished();
|
||||||
@ -132,7 +137,7 @@ void NetworkService::postAccountsData() {
|
|||||||
void NetworkService::getPayments() {
|
void NetworkService::getPayments() {
|
||||||
QMap<QString, QString> params;
|
QMap<QString, QString> params;
|
||||||
params["desktopUniqueName"] = m_desktopId;
|
params["desktopUniqueName"] = m_desktopId;
|
||||||
const QJsonArray data = get("http://localhost:9999/api/v1/payments", params);
|
const QJsonArray data = get(m_urlPaymentsGet, params);
|
||||||
|
|
||||||
for (QJsonValue item: data) {
|
for (QJsonValue item: data) {
|
||||||
QJsonObject obj = item.toObject();
|
QJsonObject obj = item.toObject();
|
||||||
@ -149,20 +154,16 @@ void NetworkService::getPayments() {
|
|||||||
AccountInfo account = AccountDAO::findAppAccount(deviceId, appName, lastNumbers);
|
AccountInfo account = AccountDAO::findAppAccount(deviceId, appName, lastNumbers);
|
||||||
|
|
||||||
eventInfo.accountId = account.id;
|
eventInfo.accountId = account.id;
|
||||||
|
eventInfo.deviceId = account.deviceId;
|
||||||
|
|
||||||
|
if (int id = EventDAO::insertEvent(eventInfo); id == -1) {
|
||||||
int id = EventDAO::insertEvent(eventInfo);
|
qCritical() << "Cant insert event: " << eventInfo.externalId;
|
||||||
if (id == -1) {
|
|
||||||
qDebug() << "Cant insert event";
|
|
||||||
} else {
|
|
||||||
if (!EventDAO::updateEvent(id, EventStatus::Wait)) {
|
|
||||||
qDebug() << "Cant update event";
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (!m_running) {
|
||||||
emit finished();
|
emit finished();
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ -174,9 +175,9 @@ void NetworkService::updateTransactionData() {
|
|||||||
addFormField(multiPart, "androidUniqueName", m_deviceId);
|
addFormField(multiPart, "androidUniqueName", m_deviceId);
|
||||||
addFormField(multiPart, "lastNumbers", m_extraData["lastNumbers"].toString());
|
addFormField(multiPart, "lastNumbers", m_extraData["lastNumbers"].toString());
|
||||||
|
|
||||||
const bool isSuccess = post(multiPart, "http://localhost:9999/api/v1/transaction/update").toBool();
|
const bool isSuccess = post(multiPart, m_urlTransactionUpdate).toBool();
|
||||||
if (!isSuccess) {
|
if (!isSuccess) {
|
||||||
qDebug() << "Cant update transaction";
|
qCritical() << "Cant update transaction: updateTransactionData";
|
||||||
}
|
}
|
||||||
|
|
||||||
emit finished();
|
emit finished();
|
||||||
@ -190,16 +191,23 @@ void NetworkService::insertTransactionData() {
|
|||||||
addFormField(multiPart, "androidUniqueName", m_deviceId);
|
addFormField(multiPart, "androidUniqueName", m_deviceId);
|
||||||
addFormField(multiPart, "lastNumbers", m_extraData["lastNumbers"].toString());
|
addFormField(multiPart, "lastNumbers", m_extraData["lastNumbers"].toString());
|
||||||
|
|
||||||
const int id = post(multiPart, "http://localhost:9999/api/v1/transaction/insert").toInt();
|
const int id = post(multiPart, m_urlTransactionInsert).toInt();
|
||||||
if (id > 0) {
|
if (id > 0) {
|
||||||
const int transactionId = m_extraData["transactionId"].toInt();
|
const int transactionId = m_extraData["transactionId"].toInt();
|
||||||
if (!TransactionDAO::updateExternalTransactionId(transactionId, id)) {
|
if (!TransactionDAO::updateExternalTransactionId(transactionId, id)) {
|
||||||
qDebug() << "Cant update transaction id";
|
qCritical() << "Cant update transaction id: " << id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
emit finished();
|
emit finished();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
void NetworkService::start() {
|
||||||
|
QTimer *poller = new QTimer(this);
|
||||||
|
poller->setInterval(1000);
|
||||||
|
connect(poller, &QTimer::timeout, this, &NetworkService::getPayments);
|
||||||
|
poller->start();
|
||||||
|
}
|
||||||
|
|
||||||
void NetworkService::stop() {
|
void NetworkService::stop() {
|
||||||
m_running = false;
|
m_running = false;
|
||||||
}
|
}
|
||||||
|
|||||||
@ -1,5 +1,6 @@
|
|||||||
#pragma once
|
#pragma once
|
||||||
#include <qhttpmultipart.h>
|
#include <qhttpmultipart.h>
|
||||||
|
#include <QNetworkAccessManager>
|
||||||
#include <QObject>
|
#include <QObject>
|
||||||
#include <QMap>
|
#include <QMap>
|
||||||
|
|
||||||
@ -29,6 +30,7 @@ public slots:
|
|||||||
|
|
||||||
void updateTransactionData();
|
void updateTransactionData();
|
||||||
|
|
||||||
|
void start();
|
||||||
void stop();
|
void stop();
|
||||||
|
|
||||||
signals:
|
signals:
|
||||||
@ -41,6 +43,13 @@ private:
|
|||||||
QMap<QString, QVariant> m_extraData = {};
|
QMap<QString, QVariant> m_extraData = {};
|
||||||
bool m_running = true;
|
bool m_running = true;
|
||||||
|
|
||||||
|
QString m_urlAccountUpdate;
|
||||||
|
QString m_urlPaymentsGet;
|
||||||
|
QString m_urlTransactionUpdate;
|
||||||
|
QString m_urlTransactionInsert;
|
||||||
|
|
||||||
|
QNetworkAccessManager *m_manager;
|
||||||
|
|
||||||
|
|
||||||
QJsonValue post(QHttpMultiPart *multiPart, const QString &path);
|
QJsonValue post(QHttpMultiPart *multiPart, const QString &path);
|
||||||
|
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user