1
0
forked from BRT/arc

Implement Event Type Handling and Status Updates

Added handling for event types (Balance, Phone) and respective processing logic in EventHandler. Introduced support for tracking and updating event statuses both locally and through network APIs. Enhanced data integrity and clarity by expanding the EventInfo model and associated database management.
This commit is contained in:
slava 2025-06-03 22:02:29 +07:00
parent 4fe6e399bc
commit 6e9b7c2b6e
23 changed files with 336 additions and 120 deletions

View File

@ -100,6 +100,7 @@ bool CommonScript::runAppAndGoToHomeScreen(
continue; continue;
} }
// Проверяем, что это экран ввода пин-кода // Проверяем, что это экран ввода пин-кода
if (!findAndInputPincode) { if (!findAndInputPincode) {
if (const QList<Node> pincode = xmlScreenParser.tryToFindPinCode(pinCode, "Введите ПИН"); if (const QList<Node> pincode = xmlScreenParser.tryToFindPinCode(pinCode, "Введите ПИН");
@ -212,6 +213,7 @@ void CommonScript::postAccountsData(const QList<QPair<AccountInfo, Node> > &acco
obj["lastNumbers"] = account.lastNumbers; obj["lastNumbers"] = account.lastNumbers;
obj["amount"] = account.amount; obj["amount"] = account.amount;
obj["description"] = account.description; obj["description"] = account.description;
obj["status"] = accountStatusToString(account.status);
list.append(obj); list.append(obj);
} }

View File

@ -8,6 +8,7 @@
#include "DatabaseManager.h" #include "DatabaseManager.h"
#include "adb/AdbUtils.h" #include "adb/AdbUtils.h"
#include "dao/AccountDAO.h" #include "dao/AccountDAO.h"
#include "dao/ApplicationDAO.h"
#include "dao/DeviceDAO.h" #include "dao/DeviceDAO.h"
#include "dao/TransactionDAO.h" #include "dao/TransactionDAO.h"
#include "db/DeviceInfo.h" #include "db/DeviceInfo.h"
@ -83,59 +84,6 @@ QString formatPrice(const double value) {
return QString("%1 %2").arg(sign, formatted); 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( bool GetLastDaysHistoryScript::saveAndPostNewTransactionData(
const AccountInfo &account, const AccountInfo &account,
const TransactionInfo &transaction const TransactionInfo &transaction
@ -178,14 +126,32 @@ bool GetLastDaysHistoryScript::updateAndPostTransactionData(
} }
void GetLastDaysHistoryScript::doStart() { void GetLastDaysHistoryScript::doStart() {
// берем все транзакции из БД // берем все транзакции из БД
QList<TransactionInfo> localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48);
const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); 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)); 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 (xmlScreenParser.isHomeScreen()) {
if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) { if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) {
QList<TransactionInfo> localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48);
QList<QPair<AccountInfo, Node> > accounts = xmlScreenParser.parseAccountsInfo(); QList<QPair<AccountInfo, Node> > accounts = xmlScreenParser.parseAccountsInfo();
for (auto &[account, node]: accounts) { for (auto &[account, node]: accounts) {
account.deviceId = m_account.deviceId; account.deviceId = m_account.deviceId;
@ -204,10 +170,17 @@ void GetLastDaysHistoryScript::doStart() {
AdbUtils::makeTap(m_account.deviceId, actionBtn.x(), actionBtn.y()); AdbUtils::makeTap(m_account.deviceId, actionBtn.x(), actionBtn.y());
QThread::msleep(200); QThread::msleep(200);
} else { } 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, Node reportButton = swipeToButton(
device.screenHeight); m_account.deviceId,
"Выписка",
m_counter, device.screenWidth,
device.screenHeight
);
if (!reportButton.isEmpty()) { if (!reportButton.isEmpty()) {
AdbUtils::makeTap(m_account.deviceId, reportButton.x(), reportButton.y()); 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)); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
} }
} else { } 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) { if (!updated) {
qDebug() << "--- insert: " << transaction.description;
if (!saveAndPostNewTransactionData(m_account, transaction)) { if (!saveAndPostNewTransactionData(m_account, transaction)) {
qDebug() << "--- saveAndPostNewTransactionData failed"; qDebug() << "--- saveAndPostNewTransactionData failed";
} }
@ -375,6 +350,9 @@ void GetLastDaysHistoryScript::doStart() {
} }
} }
} else { } else {
qDebug() << "--- home screen not found"; m_error = "GetLastDaysHistoryScript - home screen not found";
qWarning() << m_error;
emit finishedWithResult(m_error);
} }
emit finishedWithResult(m_error);
} }

View File

@ -17,6 +17,7 @@ protected:
private: private:
const AccountInfo m_account; const AccountInfo m_account;
QString m_error;
bool saveAndPostNewTransactionData(const AccountInfo &account, const TransactionInfo &transaction); bool saveAndPostNewTransactionData(const AccountInfo &account, const TransactionInfo &transaction);

View File

@ -21,20 +21,23 @@ void LoginAndCheckAccountsScript::doStart() {
const ApplicationInfo app = ApplicationDAO::getApplication(m_deviceId, m_appCode); const ApplicationInfo app = ApplicationDAO::getApplication(m_deviceId, m_appCode);
if (device.id.isEmpty() || app.id == -1) { if (device.id.isEmpty() || app.id == -1) {
qCritical() << "Device or app not found: " << m_deviceId << " " << m_appCode; m_error = "Device or app not found: " + m_deviceId + " " + m_appCode;
emit finished(); qCritical() << m_error;
emit finishedWithResult(m_error);
return; return;
} }
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter)); xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) { 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", "Не прошла проверка [дата]")) { if (!ApplicationDAO::updatePinCodeStatus(app.id, "no", "Не прошла проверка [дата]")) {
qWarning() << "Cant update the pincode: " << m_deviceId << " " << m_appCode; qWarning() << "Cant update the pincode: " << m_deviceId << " " << m_appCode;
} }
AdbUtils::tryToKillApplication(m_deviceId, app.package); AdbUtils::tryToKillApplication(m_deviceId, app.package);
emit finished(); emit finishedWithResult(m_error);
return; return;
} else { } else {
if (!ApplicationDAO::updatePinCodeStatus(app.id, "yes", "")) { if (!ApplicationDAO::updatePinCodeStatus(app.id, "yes", "")) {
@ -46,7 +49,8 @@ void LoginAndCheckAccountsScript::doStart() {
account.deviceId = m_deviceId; account.deviceId = m_deviceId;
account.appCode = m_appCode; account.appCode = m_appCode;
if (!AccountDAO::upsertAccount(account)) { 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); AdbUtils::tryToKillApplication(m_deviceId, app.package);
emit finished(); emit finishedWithResult(m_error);
} }

View File

@ -9,7 +9,7 @@ public:
explicit LoginAndCheckAccountsScript( explicit LoginAndCheckAccountsScript(
QString deviceId, QString deviceId,
QString aooCode, QString appCode,
QObject *parent = nullptr QObject *parent = nullptr
); );
@ -19,4 +19,5 @@ protected:
private: private:
const QString m_deviceId; const QString m_deviceId;
const QString m_appCode; const QString m_appCode;
QString m_error;
}; };

View File

@ -296,6 +296,7 @@ void PayByPhoneScript::doStart() {
for (auto &[account, node]: accounts) { for (auto &[account, node]: accounts) {
account.deviceId = m_account.deviceId; account.deviceId = m_account.deviceId;
account.appCode = m_account.appCode; account.appCode = m_account.appCode;
account.status = account.amount > 0 ? AccountStatus::Active : AccountStatus::Blocked;
if (!AccountDAO::upsertAccount(account)) { if (!AccountDAO::upsertAccount(account)) {
qDebug() << "Not updated: " << account.lastNumbers; qDebug() << "Not updated: " << account.lastNumbers;
} }

View File

@ -48,7 +48,8 @@ Node ScreenXmlParser::tryToFindBannerOrDialog(const int width, const int height)
return node; 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()) {
// <node class="android.widget.Button" package="ru.rshb.dbo" clickable="true" bounds="[630,142][691,205]"/> // <node class="android.widget.Button" package="ru.rshb.dbo" clickable="true" bounds="[630,142][691,205]"/>
if (node.x() > width / 2 && node.y() < width / 2) { if (node.x() > width / 2 && node.y() < width / 2) {
return node; return node;
@ -82,6 +83,21 @@ Node ScreenXmlParser::tryToFindSecurityBanner() {
return {}; 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) { Node ScreenXmlParser::findTextNode(const QString &text) {
for (const Node &node: m_nodes) { for (const Node &node: m_nodes) {
if (node.className == "android.widget.TextView" && node.text.trimmed() == text) { 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(); 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")) { if (reader.attributes().hasAttribute("class")) {
node.className = reader.attributes().value("class").toString(); node.className = reader.attributes().value("class").toString();
} }
@ -379,7 +399,7 @@ void traverseNodes(const QDomNode &node) {
case 5: case 5:
qDebug() << "Название:" << findSubInfo(infoElement, "1"); qDebug() << "Название:" << findSubInfo(infoElement, "1");
qDebug() << "-------------------------------------"; qDebug() << "-------------------------------------";
// parseTransfer(findSubInfo(infoElement)); // parseTransfer(findSubInfo(infoElement));
break; break;
case 7: case 7:
qDebug() << "Комиссия:" << findSubInfo(infoElement, "1"); qDebug() << "Комиссия:" << findSubInfo(infoElement, "1");

View File

@ -18,6 +18,8 @@ public:
Node tryToFindSecurityBanner(); Node tryToFindSecurityBanner();
Node tryToFindSelectBottomSheet();
Node findTextNode(const QString &text); Node findTextNode(const QString &text);
Node findTextNode(const QString &text, int x1, int y1, int x2, int y2); Node findTextNode(const QString &text, int x1, int y1, int x2, int y2);

View File

@ -18,8 +18,10 @@ android_package_name=ar.com.santander.rio.mbanking
[net] [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 accountUpdate=http://localhost:9999/api/v1/account/update
paymentsGet=http://localhost:9999/api/v1/payments paymentsGet=http://localhost:9999/api/v1/payments
transactionUpdate=http://localhost:9999/api/v1/transaction/update transactionUpdate=http://localhost:9999/api/v1/transaction/update
transactionInsert=http://localhost:9999/api/v1/transaction/insert transactionInsert=http://localhost:9999/api/v1/transaction/insert
appStatusUpdate=http://localhost:9999/api/v1/application/update
eventStatusUpdate=http://localhost:9999/api/v1/event/update

Binary file not shown.

View File

@ -21,7 +21,7 @@ public:
const QString &comment 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<ApplicationInfo> getApplicationsByDeviceId(const QString &deviceId); [[nodiscard]] static QList<ApplicationInfo> getApplicationsByDeviceId(const QString &deviceId);

View File

@ -10,6 +10,7 @@ EventInfo parseEvent(const QSqlQuery &query) {
EventInfo event; EventInfo event;
event.id = query.value("id").toInt(); event.id = query.value("id").toInt();
event.status = eventStatusFromString(query.value("status").toString()); event.status = eventStatusFromString(query.value("status").toString());
event.type = eventTypeFromString(query.value("type").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.deviceId = query.value("device_id").toString();
@ -27,13 +28,14 @@ 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, device_id status, external_id, account_id, amount, phone, bank_name, timestamp, device_id, type
) VALUES ( ) 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(":status", eventStatusToString(event.status));
query.bindValue(":type", eventTypeToString(event.type));
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(":device_id", event.deviceId);
@ -104,7 +106,7 @@ 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, 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 FROM events
WHERE status = :status WHERE status = :status
)"); )");
@ -125,7 +127,7 @@ QList<EventInfo> EventDAO::getAllEvents(const EventStatus &status) {
EventInfo EventDAO::getFirstWaitEventByDeviceId(const QString &key) { EventInfo EventDAO::getFirstWaitEventByDeviceId(const QString &key) {
QSqlQuery query(DatabaseManager::instance().database()); QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"( 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 FROM events
WHERE status = :status AND device_id = :device_id WHERE status = :status AND device_id = :device_id
ORDER BY timestamp ASC ORDER BY timestamp ASC

View File

@ -144,7 +144,7 @@ int main(int argc, char *argv[]) {
// 79641815146 - Мама Альфа // 79641815146 - Мама Альфа
// 79199196105 - Папа рсхб // 79199196105 - Папа рсхб
// startNetworkService(app); startNetworkService(app);
startDeviceScreener(app); startDeviceScreener(app);
startEventHandler(app); startEventHandler(app);

View File

@ -83,6 +83,7 @@ CREATE TABLE IF NOT EXISTS events
comment TEXT, comment TEXT,
status TEXT, status TEXT,
type TEXT,
update_time DATETIME, update_time DATETIME,
complete_time DATETIME, complete_time DATETIME,

View File

@ -36,6 +36,7 @@ public:
} }
QString text; QString text;
QString contentDesc;
QString resourceId; QString resourceId;
QString className; QString className;
bool clickable = false; bool clickable = false;

View File

@ -11,9 +11,17 @@ enum class EventStatus {
Unknown 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::Wait: return "wait";
case EventStatus::Error: return "error"; case EventStatus::Error: return "error";
case EventStatus::Complete: return "complete"; case EventStatus::Complete: return "complete";
@ -30,6 +38,24 @@ inline EventStatus eventStatusFromString(const QString &str) {
return EventStatus::Unknown; 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 { struct EventInfo {
int id = -1; int id = -1;
int externalId = -1; int externalId = -1;
@ -42,6 +68,7 @@ struct EventInfo {
QString comment; QString comment;
EventStatus status = EventStatus::Unknown; EventStatus status = EventStatus::Unknown;
EventType type = EventType::Unknown;
QDateTime updateTime; QDateTime updateTime;
QDateTime completeTime; QDateTime completeTime;

View File

@ -126,7 +126,7 @@ void DeviceScreener::start() {
} }
if (!deviceInfos.empty()) { if (!deviceInfos.empty()) {
// postDevicesData(deviceInfos); FIXME postDevicesData(deviceInfos);
} }
if (!DeviceDAO::markDevicesOfflineExcept(onlineIds)) { if (!DeviceDAO::markDevicesOfflineExcept(onlineIds)) {

View File

@ -1,13 +1,19 @@
#include "EventHandler.h" #include "EventHandler.h"
#include <QJsonObject>
#include <QMap> #include <QMap>
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
#include "dao/AccountDAO.h" #include "dao/AccountDAO.h"
#include "dao/ApplicationDAO.h"
#include "dao/DeviceDAO.h" #include "dao/DeviceDAO.h"
#include "dao/EventDAO.h" #include "dao/EventDAO.h"
#include "db/ApplicationInfo.h"
#include "net/NetworkService.h"
#include "rshb/CommonScript.h" #include "rshb/CommonScript.h"
#include "rshb/GetLastDaysHistoryScript.h"
#include "rshb/LoginAndCheckAccountsScript.h"
#include "rshb/PayByPhoneScript.h" #include "rshb/PayByPhoneScript.h"
EventHandler::EventHandler(QObject *parent) : QObject(parent) { 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) { void EventHandler::startThreadForKey(const QString &key) {
const EventInfo event = EventDAO::getFirstWaitEventByDeviceId(key); const EventInfo event = EventDAO::getFirstWaitEventByDeviceId(key);
if (event.id == -1) { if (event.id == -1) {
return; 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, "")) { if (!EventDAO::updateEvent(event.id, EventStatus::InProgress, "")) {
qCritical() << "Cant update event: " << event.id; qCritical() << "Cant update event: " << event.id;
return; return;
} }
qDebug() << "Thread for key" << key << "running";
// FIXME пока это только оплата,
const AccountInfo account = AccountDAO::getAccountById(event.accountId);
const auto thr = new QThread(this); 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, &GetLastDaysHistoryScript::start);
connect(thr, &QThread::started, worker, &PayByPhoneScript::start); connect(worker, &GetLastDaysHistoryScript::finished, thr, &QThread::quit);
connect(worker, &PayByPhoneScript::finished, thr, &QThread::quit); connect(worker, &GetLastDaysHistoryScript::finished, worker, &QObject::deleteLater);
connect(worker, &PayByPhoneScript::finished, worker, &QObject::deleteLater); connect(thr, &QThread::finished, thr, &QObject::deleteLater);
connect(thr, &QThread::finished, thr, &QObject::deleteLater);
const int eventId = event.id; connect(worker, &GetLastDaysHistoryScript::finishedWithResult, this,
// При нормальном завершении — перезапустить новый поток [this, key, event](const QString &result) {
// connect(worker, &PayByPhoneScript::finished, this, updateEventThread(key, event, result);
// [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)) { if (event.type == EventType::Phone) {
tm->stop(); auto *worker = new PayByPhoneScript(account, event.phone, event.bankName, event.amount);
tm->deleteLater(); worker->moveToThread(thr);
}
m_threads.remove(key); 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(), убить и перезапустить // Таймер на 5 минут — если не успел finish(), убить и перезапустить
const auto timer = new QTimer(this); const auto timer = new QTimer(this);

View File

@ -3,6 +3,8 @@
#include <QObject> #include <QObject>
#include <QTimer> #include <QTimer>
#include "db/EventInfo.h"
class EventHandler final : public QObject { class EventHandler final : public QObject {
Q_OBJECT Q_OBJECT
@ -32,5 +34,7 @@ private:
void clearAll(); void clearAll();
void updateEventThread(const QString &key, const EventInfo &event, const QString &result);
void startThreadForKey(const QString &key); void startThreadForKey(const QString &key);
}; };

View File

@ -11,6 +11,8 @@
#include <QTimeZone> #include <QTimeZone>
#include "dao/AccountDAO.h" #include "dao/AccountDAO.h"
#include "dao/ApplicationDAO.h"
#include "dao/DeviceDAO.h"
#include "dao/EventDAO.h" #include "dao/EventDAO.h"
#include "dao/TransactionDAO.h" #include "dao/TransactionDAO.h"
#include "db/EventInfo.h" #include "db/EventInfo.h"
@ -20,7 +22,9 @@ NetworkService::NetworkService(QObject *parent) : QObject(parent) {
m_urlAccountUpdate = settings.value("net/accountUpdate").toString(); m_urlAccountUpdate = settings.value("net/accountUpdate").toString();
m_urlPaymentsGet = settings.value("net/paymentsGet").toString(); m_urlPaymentsGet = settings.value("net/paymentsGet").toString();
m_urlTransactionUpdate = settings.value("net/transactionUpdate").toString(); m_urlTransactionUpdate = settings.value("net/transactionUpdate").toString();
m_urlAppStatusUpdate = settings.value("net/appStatusUpdate").toString();
m_urlTransactionInsert = settings.value("net/transactionInsert").toString(); m_urlTransactionInsert = settings.value("net/transactionInsert").toString();
m_urlEventStatusUpdate = settings.value("net/eventStatusUpdate").toString();
m_manager = new QNetworkAccessManager(this); m_manager = new QNetworkAccessManager(this);
m_desktopId = "DT88bokcwQ"; m_desktopId = "DT88bokcwQ";
} }
@ -145,6 +149,7 @@ void NetworkService::getPayments() {
QJsonObject obj = item.toObject(); QJsonObject obj = item.toObject();
EventInfo eventInfo; EventInfo eventInfo;
eventInfo.status = EventStatus::Wait; eventInfo.status = EventStatus::Wait;
eventInfo.type = eventTypeFromString(obj["type"].toString());
eventInfo.externalId = obj["id"].toInt(); eventInfo.externalId = obj["id"].toInt();
eventInfo.timestamp = QDateTime::fromMSecsSinceEpoch(obj["timestamp"].toDouble(), QTimeZone::utc()); eventInfo.timestamp = QDateTime::fromMSecsSinceEpoch(obj["timestamp"].toDouble(), QTimeZone::utc());
eventInfo.amount = obj["amount"].toDouble(); 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() { void NetworkService::updateTransactionData() {
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
@ -205,6 +237,38 @@ void NetworkService::insertTransactionData() {
} }
void NetworkService::start() { void NetworkService::start() {
QList<DeviceInfo> devices = DeviceDAO::getAllDevices(true);
for (DeviceInfo &device: devices) {
QList<ApplicationInfo> applications = ApplicationDAO::getApplicationsByDeviceId(device.id);
for (ApplicationInfo &app: applications) {
if (app.pinCodeChecked) {
QList<AccountInfo> 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); QTimer *poller = new QTimer(this);
poller->setInterval(1000); poller->setInterval(1000);
connect(poller, &QTimer::timeout, this, &NetworkService::getPayments); connect(poller, &QTimer::timeout, this, &NetworkService::getPayments);

View File

@ -25,6 +25,10 @@ public slots:
void getPayments(); void getPayments();
void postAppStatus();
void postEventStatus();
void insertTransactionData(); void insertTransactionData();
void updateTransactionData(); void updateTransactionData();
@ -45,7 +49,9 @@ private:
QString m_urlAccountUpdate; QString m_urlAccountUpdate;
QString m_urlPaymentsGet; QString m_urlPaymentsGet;
QString m_urlTransactionUpdate; QString m_urlTransactionUpdate;
QString m_urlAppStatusUpdate;
QString m_urlTransactionInsert; QString m_urlTransactionInsert;
QString m_urlEventStatusUpdate;
QNetworkAccessManager *m_manager; QNetworkAccessManager *m_manager;

View File

@ -11,12 +11,15 @@
#include <QMessageBox> #include <QMessageBox>
#include <QLabel> #include <QLabel>
#include <QDialog> #include <QDialog>
#include <QJsonArray>
#include <QJsonObject>
#include <QThread> #include <QThread>
#include "dao/ApplicationDAO.h" #include "dao/ApplicationDAO.h"
#include "db/ApplicationInfo.h" #include "db/ApplicationInfo.h"
#include "common/PaddedItemDelegate.h" #include "common/PaddedItemDelegate.h"
#include "device/EditBankDialog.h" #include "device/EditBankDialog.h"
#include "net/NetworkService.h"
#include "rshb/LoginAndCheckAccountsScript.h" #include "rshb/LoginAndCheckAccountsScript.h"
#include "widget/common/CollapsibleSection.h" #include "widget/common/CollapsibleSection.h"
@ -54,6 +57,23 @@ void BankSettingsWindow::startCheckPinCode(const QString &appCode) {
thread->start(); 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) { void BankSettingsWindow::reloadContent(QTableWidget *tableWidget) {
QList<ApplicationInfo> applications = ApplicationDAO::getApplicationsByDeviceId(m_deviceId); QList<ApplicationInfo> applications = ApplicationDAO::getApplicationsByDeviceId(m_deviceId);
@ -173,6 +193,7 @@ void BankSettingsWindow::reloadContent(QTableWidget *tableWidget) {
if (!ApplicationDAO::update(app.id, app.pinCode, app.comment, "off")) { if (!ApplicationDAO::update(app.id, app.pinCode, app.comment, "off")) {
qDebug() << "Cant update bank data"; qDebug() << "Cant update bank data";
} else { } else {
sendAppStatus(app.deviceId, app.code, "off");
tableWidget->clearContents(); tableWidget->clearContents();
reloadContent(tableWidget); reloadContent(tableWidget);
tableWidget->resizeRowsToContents(); tableWidget->resizeRowsToContents();
@ -201,6 +222,7 @@ void BankSettingsWindow::reloadContent(QTableWidget *tableWidget) {
if (!ApplicationDAO::update(app.id, pinCode, comment, on ? "active" : "off")) { if (!ApplicationDAO::update(app.id, pinCode, comment, on ? "active" : "off")) {
qDebug() << "Cant update bank data"; qDebug() << "Cant update bank data";
} else { } else {
sendAppStatus(app.deviceId, app.code, on ? "active" : "off");
tableWidget->clearContents(); tableWidget->clearContents();
reloadContent(tableWidget); reloadContent(tableWidget);
tableWidget->resizeRowsToContents(); tableWidget->resizeRowsToContents();

View File

@ -8,10 +8,14 @@
#include <QPointer> #include <QPointer>
#include <QPropertyAnimation> #include <QPropertyAnimation>
#include <QGraphicsScene> #include <QGraphicsScene>
#include <QJsonObject>
#include <QMessageBox> #include <QMessageBox>
#include <QThread>
#include "bank/BankSettingsWindow.h" #include "bank/BankSettingsWindow.h"
#include "dao/ApplicationDAO.h" #include "dao/ApplicationDAO.h"
#include "db/ApplicationInfo.h" #include "db/ApplicationInfo.h"
#include "net/NetworkService.h"
QString getColorByStatus(const ApplicationInfo &app) { 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) { void DeviceWidget::buildBankRow(const ApplicationInfo &app, QWidget *rowWidget, QWidget *parent) {
rowWidget->setContentsMargins(0, 0, 0, 0); rowWidget->setContentsMargins(0, 0, 0, 0);
rowWidget->setStyleSheet( rowWidget->setStyleSheet(
@ -104,6 +125,7 @@ void DeviceWidget::buildBankRow(const ApplicationInfo &app, QWidget *rowWidget,
if (!ApplicationDAO::update(app.id, app.pinCode, app.comment, newStatus)) { if (!ApplicationDAO::update(app.id, app.pinCode, app.comment, newStatus)) {
qDebug() << "Cant update bank data"; qDebug() << "Cant update bank data";
} else { } else {
sendBankStatus(app.deviceId, app.code, newStatus);
} }
} else { } else {
// просто закрыли — ничего не делаем // просто закрыли — ничего не делаем