diff --git a/android/rshb/CommonScript.cpp b/android/rshb/CommonScript.cpp index ecda021..5800325 100644 --- a/android/rshb/CommonScript.cpp +++ b/android/rshb/CommonScript.cpp @@ -100,6 +100,7 @@ bool CommonScript::runAppAndGoToHomeScreen( continue; } + // Проверяем, что это экран ввода пин-кода if (!findAndInputPincode) { if (const QList pincode = xmlScreenParser.tryToFindPinCode(pinCode, "Введите ПИН"); @@ -212,6 +213,7 @@ void CommonScript::postAccountsData(const QList > &acco obj["lastNumbers"] = account.lastNumbers; obj["amount"] = account.amount; obj["description"] = account.description; + obj["status"] = accountStatusToString(account.status); list.append(obj); } diff --git a/android/rshb/GetLastDaysHistoryScript.cpp b/android/rshb/GetLastDaysHistoryScript.cpp index 12a42c4..1580baf 100644 --- a/android/rshb/GetLastDaysHistoryScript.cpp +++ b/android/rshb/GetLastDaysHistoryScript.cpp @@ -8,6 +8,7 @@ #include "DatabaseManager.h" #include "adb/AdbUtils.h" #include "dao/AccountDAO.h" +#include "dao/ApplicationDAO.h" #include "dao/DeviceDAO.h" #include "dao/TransactionDAO.h" #include "db/DeviceInfo.h" @@ -83,59 +84,6 @@ QString formatPrice(const double value) { return QString("%1 %2").arg(sign, formatted); } -// bool GetLastDaysHistoryScript::openAndSaveDitailInfo(TransactionInfo &transaction, int width, int height) { -// QString text = QString("%1 %2").arg(transaction.shortDescription, formatPrice(transaction.amount)); -// -// Node trButton = swipeToButton(m_account.deviceId, text, m_counter, width, height); -// if (!trButton.isEmpty()) { -// AdbUtils::makeTap(m_account.deviceId, trButton.x(), trButton.y()); -// QThread::msleep(500); -// -// xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); -// -// Node moreDetailBtn = xmlScreenParser.findButtonNode("Детали операции", false); -// if (!moreDetailBtn.isEmpty()) { -// AdbUtils::makeTap(m_account.deviceId, moreDetailBtn.x(), moreDetailBtn.y()); -// QThread::msleep(200); -// xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); -// TransactionInfo info = xmlScreenParser.parseTransactionInfoHistory(width, height); -// -// if (transaction.id == -1) { -// info.accountId = m_account.id; -// info.shortDescription = transaction.shortDescription; -// info.amount = transaction.amount; -// info.type = info.phone.isEmpty() ? TransactionType::Card : TransactionType::Phone; -// info.updateTime = DateUtils::getUtcNow(); -// if (info.status == TransactionStatus::Complete) { -// info.completeTime = DateUtils::getUtcNow(); -// } -// if (TransactionDAO::insertTransaction(info) == -1) { -// qWarning() << "--- insertTransaction failed"; -// } -// } else { -// transaction.status = info.status; -// transaction.description = info.description; -// if (transaction.trExternalId.isEmpty()) { -// transaction.trExternalId = info.trExternalId; -// } -// transaction.updateTime = DateUtils::getUtcNow(); -// if (transaction.status == TransactionStatus::Complete && !transaction.completeTime.isValid()) { -// transaction.completeTime = DateUtils::getUtcNow(); -// } -// if (!TransactionDAO::updateTransaction(transaction)) { -// qWarning() << "--- updateTransaction failed"; -// } -// } -// } -// -// AdbUtils::goBack(m_account.deviceId); -// QThread::msleep(200); -// } else { -// qWarning() << "--- trButton not found.........."; -// } -// return false; -// } - bool GetLastDaysHistoryScript::saveAndPostNewTransactionData( const AccountInfo &account, const TransactionInfo &transaction @@ -178,14 +126,32 @@ bool GetLastDaysHistoryScript::updateAndPostTransactionData( } void GetLastDaysHistoryScript::doStart() { - // берем все транзакции из БД - QList localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48); const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); + const ApplicationInfo app = ApplicationDAO::getApplication(m_account.deviceId, m_account.appCode); + + if (!xmlScreenParser.isHomeScreen()) { + if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { + m_error = "Не смогли дойти до домашней страницы"; + qCritical() << m_error; + emit finishedWithResult(m_error); + return; + } + } xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); + Node closeBannerOrDialogNode = xmlScreenParser.tryToFindBannerOrDialog(device.screenWidth, device.screenHeight); + if (closeBannerOrDialogNode.x() != -1 && closeBannerOrDialogNode.y() != -1) { + AdbUtils::makeTap(device.id, closeBannerOrDialogNode.x(), closeBannerOrDialogNode.y()); + QThread::msleep(500); + } + + xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); + if (xmlScreenParser.isHomeScreen()) { if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) { + QList localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48); + QList > accounts = xmlScreenParser.parseAccountsInfo(); for (auto &[account, node]: accounts) { account.deviceId = m_account.deviceId; @@ -204,10 +170,17 @@ void GetLastDaysHistoryScript::doStart() { AdbUtils::makeTap(m_account.deviceId, actionBtn.x(), actionBtn.y()); QThread::msleep(200); } else { - qWarning() << "--- actionBtn not found"; + m_error = "All action btn not found"; + qCritical() << m_error; + emit finishedWithResult(m_error); + return; } - Node reportButton = swipeToButton(m_account.deviceId, "Выписка", m_counter, device.screenWidth, - device.screenHeight); + Node reportButton = swipeToButton( + m_account.deviceId, + "Выписка", + m_counter, device.screenWidth, + device.screenHeight + ); if (!reportButton.isEmpty()) { AdbUtils::makeTap(m_account.deviceId, reportButton.x(), reportButton.y()); @@ -236,7 +209,10 @@ void GetLastDaysHistoryScript::doStart() { xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); } } else { - qWarning() << "--- reportTitle not found"; + m_error = "All action - report title not found"; + qCritical() << m_error; + emit finishedWithResult(m_error); + return; } // все поддвержденные @@ -362,7 +338,6 @@ void GetLastDaysHistoryScript::doStart() { } } if (!updated) { - qDebug() << "--- insert: " << transaction.description; if (!saveAndPostNewTransactionData(m_account, transaction)) { qDebug() << "--- saveAndPostNewTransactionData failed"; } @@ -375,6 +350,9 @@ void GetLastDaysHistoryScript::doStart() { } } } else { - qDebug() << "--- home screen not found"; + m_error = "GetLastDaysHistoryScript - home screen not found"; + qWarning() << m_error; + emit finishedWithResult(m_error); } + emit finishedWithResult(m_error); } diff --git a/android/rshb/GetLastDaysHistoryScript.h b/android/rshb/GetLastDaysHistoryScript.h index 9538e64..f298c66 100644 --- a/android/rshb/GetLastDaysHistoryScript.h +++ b/android/rshb/GetLastDaysHistoryScript.h @@ -17,6 +17,7 @@ protected: private: const AccountInfo m_account; + QString m_error; bool saveAndPostNewTransactionData(const AccountInfo &account, const TransactionInfo &transaction); diff --git a/android/rshb/LoginAndCheckAccountsScript.cpp b/android/rshb/LoginAndCheckAccountsScript.cpp index d9d9cac..dfc5b61 100644 --- a/android/rshb/LoginAndCheckAccountsScript.cpp +++ b/android/rshb/LoginAndCheckAccountsScript.cpp @@ -21,20 +21,23 @@ void LoginAndCheckAccountsScript::doStart() { const ApplicationInfo app = ApplicationDAO::getApplication(m_deviceId, m_appCode); if (device.id.isEmpty() || app.id == -1) { - qCritical() << "Device or app not found: " << m_deviceId << " " << m_appCode; - emit finished(); + m_error = "Device or app not found: " + m_deviceId + " " + m_appCode; + qCritical() << m_error; + emit finishedWithResult(m_error); return; } xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); + if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { - qWarning() << "Cant open the home screen: " << m_deviceId << " " << m_appCode; + m_error = "Cant open the home screen: " + m_deviceId + " " + m_appCode; + qWarning() << m_error; if (!ApplicationDAO::updatePinCodeStatus(app.id, "no", "Не прошла проверка [дата]")) { qWarning() << "Cant update the pincode: " << m_deviceId << " " << m_appCode; } AdbUtils::tryToKillApplication(m_deviceId, app.package); - emit finished(); + emit finishedWithResult(m_error); return; } else { if (!ApplicationDAO::updatePinCodeStatus(app.id, "yes", "")) { @@ -46,7 +49,8 @@ void LoginAndCheckAccountsScript::doStart() { account.deviceId = m_deviceId; account.appCode = m_appCode; if (!AccountDAO::upsertAccount(account)) { - qDebug() << "Not updated: " << account.lastNumbers; + m_error = "Not updated: " + account.lastNumbers; + qCritical() << m_error; } } @@ -57,5 +61,5 @@ void LoginAndCheckAccountsScript::doStart() { } AdbUtils::tryToKillApplication(m_deviceId, app.package); - emit finished(); + emit finishedWithResult(m_error); } diff --git a/android/rshb/LoginAndCheckAccountsScript.h b/android/rshb/LoginAndCheckAccountsScript.h index 283e811..0a3f676 100644 --- a/android/rshb/LoginAndCheckAccountsScript.h +++ b/android/rshb/LoginAndCheckAccountsScript.h @@ -9,7 +9,7 @@ public: explicit LoginAndCheckAccountsScript( QString deviceId, - QString aooCode, + QString appCode, QObject *parent = nullptr ); @@ -19,4 +19,5 @@ protected: private: const QString m_deviceId; const QString m_appCode; + QString m_error; }; \ No newline at end of file diff --git a/android/rshb/PayByPhoneScript.cpp b/android/rshb/PayByPhoneScript.cpp index e1a209a..e3f4e49 100644 --- a/android/rshb/PayByPhoneScript.cpp +++ b/android/rshb/PayByPhoneScript.cpp @@ -296,6 +296,7 @@ void PayByPhoneScript::doStart() { for (auto &[account, node]: accounts) { account.deviceId = m_account.deviceId; account.appCode = m_account.appCode; + account.status = account.amount > 0 ? AccountStatus::Active : AccountStatus::Blocked; if (!AccountDAO::upsertAccount(account)) { qDebug() << "Not updated: " << account.lastNumbers; } diff --git a/android/rshb/ScreenXmlParser.cpp b/android/rshb/ScreenXmlParser.cpp index 0360c91..68566ad 100644 --- a/android/rshb/ScreenXmlParser.cpp +++ b/android/rshb/ScreenXmlParser.cpp @@ -48,7 +48,8 @@ Node ScreenXmlParser::tryToFindBannerOrDialog(const int width, const int height) return node; } // Если нашли пустую кнпоку - if (node.className == "android.widget.Button" && node.clickable && node.text.isEmpty()) { + if (node.className == "android.widget.Button" && node.clickable + && node.text.isEmpty() && node.contentDesc.isEmpty()) { // if (node.x() > width / 2 && node.y() < width / 2) { return node; @@ -82,6 +83,21 @@ Node ScreenXmlParser::tryToFindSecurityBanner() { return {}; } +Node ScreenXmlParser::tryToFindSelectBottomSheet() { + Node closeBtn; + for (const Node &node: m_nodes) { + if ((node.text.trimmed() == "Отмена" || node.text.trimmed() == "Отложить") && node.className == + "android.widget.Button") { + closeBtn = node; + } + } + // Не нужно проверять все, если хотя бы одна координата есть + if (!closeBtn.isEmpty()) { + return closeBtn; + } + return {}; +} + Node ScreenXmlParser::findTextNode(const QString &text) { for (const Node &node: m_nodes) { if (node.className == "android.widget.TextView" && node.text.trimmed() == text) { @@ -220,6 +236,10 @@ void ScreenXmlParser::parseAndSaveXml(const QString &xml) { node.text = reader.attributes().value("text").toString(); } + if (reader.attributes().hasAttribute("content-desc")) { + node.contentDesc = reader.attributes().value("content-desc").toString(); + } + if (reader.attributes().hasAttribute("class")) { node.className = reader.attributes().value("class").toString(); } @@ -379,7 +399,7 @@ void traverseNodes(const QDomNode &node) { case 5: qDebug() << "Название:" << findSubInfo(infoElement, "1"); qDebug() << "-------------------------------------"; - // parseTransfer(findSubInfo(infoElement)); + // parseTransfer(findSubInfo(infoElement)); break; case 7: qDebug() << "Комиссия:" << findSubInfo(infoElement, "1"); diff --git a/android/rshb/ScreenXmlParser.h b/android/rshb/ScreenXmlParser.h index de942f2..11e1fe4 100644 --- a/android/rshb/ScreenXmlParser.h +++ b/android/rshb/ScreenXmlParser.h @@ -18,6 +18,8 @@ public: Node tryToFindSecurityBanner(); + Node tryToFindSelectBottomSheet(); + Node findTextNode(const QString &text); Node findTextNode(const QString &text, int x1, int y1, int x2, int y2); diff --git a/config.ini b/config.ini index 1c3d1b0..6a662e1 100644 --- a/config.ini +++ b/config.ini @@ -18,8 +18,10 @@ android_package_name=ar.com.santander.rio.mbanking [net] -appInfoUpdate="http://localhost:9999/api/v1/app/update" +appInfoUpdate=http://localhost:9999/api/v1/device/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 \ No newline at end of file +transactionInsert=http://localhost:9999/api/v1/transaction/insert +appStatusUpdate=http://localhost:9999/api/v1/application/update +eventStatusUpdate=http://localhost:9999/api/v1/event/update \ No newline at end of file diff --git a/database/app_data.db b/database/app_data.db index b314354..642bcc2 100644 Binary files a/database/app_data.db and b/database/app_data.db differ diff --git a/database/dao/ApplicationDAO.h b/database/dao/ApplicationDAO.h index 73076c6..a511606 100644 --- a/database/dao/ApplicationDAO.h +++ b/database/dao/ApplicationDAO.h @@ -21,7 +21,7 @@ public: const QString &comment ); - [[nodiscard]] static ApplicationInfo getApplication(const QString &deviceId, const QString &appName); + [[nodiscard]] static ApplicationInfo getApplication(const QString &deviceId, const QString &appCode); [[nodiscard]] static QList getApplicationsByDeviceId(const QString &deviceId); diff --git a/database/dao/EventDAO.cpp b/database/dao/EventDAO.cpp index dfd5bed..7484362 100644 --- a/database/dao/EventDAO.cpp +++ b/database/dao/EventDAO.cpp @@ -10,6 +10,7 @@ EventInfo parseEvent(const QSqlQuery &query) { EventInfo event; event.id = query.value("id").toInt(); event.status = eventStatusFromString(query.value("status").toString()); + event.type = eventTypeFromString(query.value("type").toString()); event.externalId = query.value("external_id").toInt(); event.accountId = query.value("account_id").toInt(); event.deviceId = query.value("device_id").toString(); @@ -27,13 +28,14 @@ int EventDAO::insertEvent(const EventInfo &event) { query.prepare(R"( INSERT INTO events ( - status, external_id, account_id, amount, phone, bank_name, timestamp, device_id + status, external_id, account_id, amount, phone, bank_name, timestamp, device_id, type ) VALUES ( - :status, :external_id, :account_id, :amount, :phone, :bank_name, :timestamp, :device_id + :status, :external_id, :account_id, :amount, :phone, :bank_name, :timestamp, :device_id, :type ) )"); query.bindValue(":status", eventStatusToString(event.status)); + query.bindValue(":type", eventTypeToString(event.type)); query.bindValue(":external_id", event.externalId); query.bindValue(":account_id", event.accountId); query.bindValue(":device_id", event.deviceId); @@ -104,7 +106,7 @@ QList EventDAO::getAllEvents(const EventStatus &status) { 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 + SELECT id, account_id, device_id, external_id, amount, phone, bank_name, status, update_time, complete_time, timestamp, type FROM events WHERE status = :status )"); @@ -125,7 +127,7 @@ QList EventDAO::getAllEvents(const EventStatus &status) { 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 + SELECT id, account_id, device_id, external_id, amount, phone, bank_name, status, update_time, complete_time, timestamp, type FROM events WHERE status = :status AND device_id = :device_id ORDER BY timestamp ASC diff --git a/main.cpp b/main.cpp index 834a0cb..9c4ae61 100644 --- a/main.cpp +++ b/main.cpp @@ -144,7 +144,7 @@ int main(int argc, char *argv[]) { // 79641815146 - Мама Альфа // 79199196105 - Папа рсхб - // startNetworkService(app); + startNetworkService(app); startDeviceScreener(app); startEventHandler(app); diff --git a/migrations.sql b/migrations.sql index 7d98723..aa884ae 100644 --- a/migrations.sql +++ b/migrations.sql @@ -83,6 +83,7 @@ CREATE TABLE IF NOT EXISTS events comment TEXT, status TEXT, + type TEXT, update_time DATETIME, complete_time DATETIME, diff --git a/models/android/xml/Node.h b/models/android/xml/Node.h index cbc7b64..7ec7aff 100644 --- a/models/android/xml/Node.h +++ b/models/android/xml/Node.h @@ -36,6 +36,7 @@ public: } QString text; + QString contentDesc; QString resourceId; QString className; bool clickable = false; diff --git a/models/db/EventInfo.h b/models/db/EventInfo.h index e202c90..1117ff8 100644 --- a/models/db/EventInfo.h +++ b/models/db/EventInfo.h @@ -11,9 +11,17 @@ enum class EventStatus { Unknown }; +enum class EventType { + Phone, + Card, + Balance, + History, + Unknown +}; -inline QString eventStatusToString(const EventStatus type) { - switch (type) { + +inline QString eventStatusToString(const EventStatus status) { + switch (status) { case EventStatus::Wait: return "wait"; case EventStatus::Error: return "error"; case EventStatus::Complete: return "complete"; @@ -30,6 +38,24 @@ inline EventStatus eventStatusFromString(const QString &str) { return EventStatus::Unknown; } +inline QString eventTypeToString(const EventType type) { + switch (type) { + case EventType::Phone: return "phone"; + case EventType::Card: return "card"; + case EventType::Balance: return "balance"; + case EventType::History: return "history"; + default: return "unknown"; + } +} + +inline EventType eventTypeFromString(const QString &str) { + if (str == "phone") return EventType::Phone; + if (str == "card") return EventType::Card; + if (str == "balance") return EventType::Balance; + if (str == "history") return EventType::History; + return EventType::Unknown; +} + struct EventInfo { int id = -1; int externalId = -1; @@ -42,6 +68,7 @@ struct EventInfo { QString comment; EventStatus status = EventStatus::Unknown; + EventType type = EventType::Unknown; QDateTime updateTime; QDateTime completeTime; diff --git a/services/DeviceScreener.cpp b/services/DeviceScreener.cpp index 7647727..1b12848 100644 --- a/services/DeviceScreener.cpp +++ b/services/DeviceScreener.cpp @@ -126,7 +126,7 @@ void DeviceScreener::start() { } if (!deviceInfos.empty()) { - // postDevicesData(deviceInfos); FIXME + postDevicesData(deviceInfos); } if (!DeviceDAO::markDevicesOfflineExcept(onlineIds)) { diff --git a/services/EventHandler.cpp b/services/EventHandler.cpp index 31e7b7b..a3f5e56 100644 --- a/services/EventHandler.cpp +++ b/services/EventHandler.cpp @@ -1,13 +1,19 @@ #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 "rshb/CommonScript.h" +#include "rshb/GetLastDaysHistoryScript.h" +#include "rshb/LoginAndCheckAccountsScript.h" #include "rshb/PayByPhoneScript.h" EventHandler::EventHandler(QObject *parent) : QObject(parent) { @@ -44,60 +50,110 @@ void EventHandler::checkAndRestart() { } } +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; } - 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); + // qDebug() << "Start thread for key" << eventTypeToString(event.type); + if (event.type == EventType::Balance) { + auto *worker = new GetLastDaysHistoryScript(account); + worker->moveToThread(thr); - 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(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); - 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; - } + connect(worker, &GetLastDaysHistoryScript::finishedWithResult, this, + [this, key, event](const QString &result) { + updateEventThread(key, event, result); } + ); + } - if (const auto tm = m_timers.take(key)) { - tm->stop(); - tm->deleteLater(); - } - m_threads.remove(key); - }); + 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); diff --git a/services/EventHandler.h b/services/EventHandler.h index c6f1380..31f8a69 100644 --- a/services/EventHandler.h +++ b/services/EventHandler.h @@ -3,6 +3,8 @@ #include #include +#include "db/EventInfo.h" + class EventHandler final : public QObject { Q_OBJECT @@ -32,5 +34,7 @@ private: void clearAll(); + void updateEventThread(const QString &key, const EventInfo &event, const QString &result); + void startThreadForKey(const QString &key); }; diff --git a/services/net/NetworkService.cpp b/services/net/NetworkService.cpp index 5e7382b..db33e13 100644 --- a/services/net/NetworkService.cpp +++ b/services/net/NetworkService.cpp @@ -11,6 +11,8 @@ #include #include "dao/AccountDAO.h" +#include "dao/ApplicationDAO.h" +#include "dao/DeviceDAO.h" #include "dao/EventDAO.h" #include "dao/TransactionDAO.h" #include "db/EventInfo.h" @@ -20,7 +22,9 @@ NetworkService::NetworkService(QObject *parent) : QObject(parent) { m_urlAccountUpdate = settings.value("net/accountUpdate").toString(); m_urlPaymentsGet = settings.value("net/paymentsGet").toString(); m_urlTransactionUpdate = settings.value("net/transactionUpdate").toString(); + m_urlAppStatusUpdate = settings.value("net/appStatusUpdate").toString(); m_urlTransactionInsert = settings.value("net/transactionInsert").toString(); + m_urlEventStatusUpdate = settings.value("net/eventStatusUpdate").toString(); m_manager = new QNetworkAccessManager(this); m_desktopId = "DT88bokcwQ"; } @@ -145,6 +149,7 @@ void NetworkService::getPayments() { QJsonObject obj = item.toObject(); EventInfo eventInfo; eventInfo.status = EventStatus::Wait; + eventInfo.type = eventTypeFromString(obj["type"].toString()); eventInfo.externalId = obj["id"].toInt(); eventInfo.timestamp = QDateTime::fromMSecsSinceEpoch(obj["timestamp"].toDouble(), QTimeZone::utc()); eventInfo.amount = obj["amount"].toDouble(); @@ -169,6 +174,33 @@ void NetworkService::getPayments() { } } +void NetworkService::postAppStatus() { + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + addFormMetadata(multiPart, m_json); + addFormField(multiPart, "desktopUniqueName", m_desktopId); + addFormField(multiPart, "androidUniqueName", m_deviceId); + + const bool isSuccess = post(multiPart, m_urlAppStatusUpdate).toBool(); + if (!isSuccess) { + qCritical() << "Cant post data: postAppStatus"; + } + + emit finished(); +} + +void NetworkService::postEventStatus() { + QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); + + addFormMetadata(multiPart, m_json); + + const bool isSuccess = post(multiPart, m_urlEventStatusUpdate).toBool(); + if (!isSuccess) { + qCritical() << "Cant post data: postEventStatus"; + } + + emit finished(); +} void NetworkService::updateTransactionData() { QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); @@ -205,6 +237,38 @@ void NetworkService::insertTransactionData() { } void NetworkService::start() { + QList devices = DeviceDAO::getAllDevices(true); + for (DeviceInfo &device: devices) { + QList applications = ApplicationDAO::getApplicationsByDeviceId(device.id); + for (ApplicationInfo &app: applications) { + if (app.pinCodeChecked) { + QList accounts = AccountDAO::getAccounts(device.id, app.code); + QJsonArray list; + + for (const AccountInfo &account: accounts) { + QJsonObject obj; + obj["appName"] = account.appCode; + obj["lastNumbers"] = account.lastNumbers; + obj["amount"] = account.amount; + obj["description"] = account.description; + obj["status"] = accountStatusToString(account.status); + list.append(obj); + } + + auto *paymentThread = new QThread; + auto *networkService = new NetworkService; + networkService->setDeviceId(device.id); + networkService->setPayload(QJsonDocument(list).toJson()); + networkService->moveToThread(paymentThread); + QObject::connect(paymentThread, &QThread::started, networkService, &NetworkService::postAccountsData); + 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(); + } + } + } + QTimer *poller = new QTimer(this); poller->setInterval(1000); connect(poller, &QTimer::timeout, this, &NetworkService::getPayments); diff --git a/services/net/NetworkService.h b/services/net/NetworkService.h index cd89196..e6374e8 100644 --- a/services/net/NetworkService.h +++ b/services/net/NetworkService.h @@ -25,6 +25,10 @@ public slots: void getPayments(); + void postAppStatus(); + + void postEventStatus(); + void insertTransactionData(); void updateTransactionData(); @@ -45,7 +49,9 @@ private: QString m_urlAccountUpdate; QString m_urlPaymentsGet; QString m_urlTransactionUpdate; + QString m_urlAppStatusUpdate; QString m_urlTransactionInsert; + QString m_urlEventStatusUpdate; QNetworkAccessManager *m_manager; diff --git a/views/bank/BankSettingsWindow.cpp b/views/bank/BankSettingsWindow.cpp index dc6e6d1..f8d2b63 100644 --- a/views/bank/BankSettingsWindow.cpp +++ b/views/bank/BankSettingsWindow.cpp @@ -11,12 +11,15 @@ #include #include #include +#include +#include #include #include "dao/ApplicationDAO.h" #include "db/ApplicationInfo.h" #include "common/PaddedItemDelegate.h" #include "device/EditBankDialog.h" +#include "net/NetworkService.h" #include "rshb/LoginAndCheckAccountsScript.h" #include "widget/common/CollapsibleSection.h" @@ -54,6 +57,23 @@ void BankSettingsWindow::startCheckPinCode(const QString &appCode) { thread->start(); } +void sendAppStatus(const QString &deviceId, const QString &code, const QString &status) { + QJsonObject obj; + obj["code"] = code; + obj["status"] = status; + + 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::postAppStatus); + 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 BankSettingsWindow::reloadContent(QTableWidget *tableWidget) { QList applications = ApplicationDAO::getApplicationsByDeviceId(m_deviceId); @@ -173,6 +193,7 @@ void BankSettingsWindow::reloadContent(QTableWidget *tableWidget) { if (!ApplicationDAO::update(app.id, app.pinCode, app.comment, "off")) { qDebug() << "Cant update bank data"; } else { + sendAppStatus(app.deviceId, app.code, "off"); tableWidget->clearContents(); reloadContent(tableWidget); tableWidget->resizeRowsToContents(); @@ -201,6 +222,7 @@ void BankSettingsWindow::reloadContent(QTableWidget *tableWidget) { if (!ApplicationDAO::update(app.id, pinCode, comment, on ? "active" : "off")) { qDebug() << "Cant update bank data"; } else { + sendAppStatus(app.deviceId, app.code, on ? "active" : "off"); tableWidget->clearContents(); reloadContent(tableWidget); tableWidget->resizeRowsToContents(); diff --git a/views/device/DeviceWidget.cpp b/views/device/DeviceWidget.cpp index 12429ff..374429b 100644 --- a/views/device/DeviceWidget.cpp +++ b/views/device/DeviceWidget.cpp @@ -8,10 +8,14 @@ #include #include #include +#include #include +#include + #include "bank/BankSettingsWindow.h" #include "dao/ApplicationDAO.h" #include "db/ApplicationInfo.h" +#include "net/NetworkService.h" QString getColorByStatus(const ApplicationInfo &app) { @@ -30,6 +34,23 @@ QString getColorByStatus(const ApplicationInfo &app) { } } +void sendBankStatus(const QString &deviceId, const QString &code, const QString &status) { + QJsonObject obj; + obj["code"] = code; + obj["status"] = status; + + 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::postAppStatus); + 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 DeviceWidget::buildBankRow(const ApplicationInfo &app, QWidget *rowWidget, QWidget *parent) { rowWidget->setContentsMargins(0, 0, 0, 0); rowWidget->setStyleSheet( @@ -104,6 +125,7 @@ void DeviceWidget::buildBankRow(const ApplicationInfo &app, QWidget *rowWidget, if (!ApplicationDAO::update(app.id, app.pinCode, app.comment, newStatus)) { qDebug() << "Cant update bank data"; } else { + sendBankStatus(app.deviceId, app.code, newStatus); } } else { // просто закрыли — ничего не делаем