1
0
forked from BRT/arc
arc/android/rshb/CommonScript.cpp
trnsmkot 5878705ae0 Refactor and enhance transaction handling and logging
Introduced new logging system with QInstallMessageHandler. Refactored NetworkService for better modularity and added new methods to handle transaction data. Updated database schema and TransactionDAO to support external transaction ID. Cleaned up and streamlined transaction processing in GetLastDaysHistoryScript.
2025-05-21 16:06:18 +07:00

304 lines
12 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include "CommonScript.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QRandomGenerator>
#include <QThread>
#include "adb/AdbUtils.h"
#include "net/NetworkService.h"
CommonScript::CommonScript(QObject *parent)
: QObject(parent) {
}
CommonScript::~CommonScript() = default;
void CommonScript::start() {
doStart();
emit finished();
}
Node CommonScript::swipeToButton(
const QString &deviceId,
const QString &text, int &counter, const int width, const int height
) {
for (int i = 0; i < 10; ++i) {
if (Node button = xmlScreenParser.findButtonNode(text, true); button.x() > 0 && button.y() > 0) {
return button;
}
const int x = width / 2;
const int offset = height / 4;
AdbUtils::makeSwipe(deviceId, x, height - offset, x, offset);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++counter));
}
return {};
}
bool CommonScript::inputPinCode(const QString &deviceId, const QList<Node> &pincode) {
for (const Node &node: pincode) {
const bool completePincode = AdbUtils::makeTap(deviceId, node.x(), node.y());
if (!completePincode) {
return false;
}
QThread::msleep(QRandomGenerator::global()->bounded(100, 901));
}
return true;
}
bool CommonScript::runAppAndGoToHomeScreen(
const QString &deviceId,
const QString &packageName,
const QString &pinCode,
const int width,
const int height
) {
// Запустить устройство
if (const QString pid = AdbUtils::tryToGetRunningAppPid(deviceId, packageName); !pid.isEmpty()) {
qDebug() << "Process already running, pid: " << pid;
AdbUtils::getScreenDumpAsXml(deviceId, 1000);
AdbUtils::tryToKillApplication(deviceId, packageName);
}
if (!AdbUtils::tryToStartApplication(deviceId, packageName)) {
AdbUtils::takeScreenshot(deviceId, "run_app_error");
qWarning() << "Process has not started";
return false;
}
// Даем минимальное время на запуск
QThread::msleep(4500);
int counter = 0;
int restarted = 0;
int totalCounter = 0;
bool findAndInputPincode = false;
while (true) {
counter++;
totalCounter++;
// 3 раза уже перезапускали приложение, завершаем попытки
if (restarted > 2) {
AdbUtils::takeScreenshot(deviceId, "3_times_try_start");
return false;
}
// Если более 30 секунд мы пытаемся перейти на домашнюю, перезапускаем
if (counter > 30) {
AdbUtils::tryToKillApplication(deviceId, packageName);
QThread::msleep(4500);
++restarted;
counter = 0;
}
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
if (xmlScreenParser.isHomeScreen()) {
return true;
}
// Проверяем на банеры после пин-кодом
Node closeBannerOrDialogNode = xmlScreenParser.tryToFindBannerOrDialog(width, height);
if (closeBannerOrDialogNode.x() != -1 && closeBannerOrDialogNode.y() != -1) {
AdbUtils::makeTap(deviceId, closeBannerOrDialogNode.x(), closeBannerOrDialogNode.y());
QThread::msleep(1000);
continue;
}
// Проверяем на банеры перед пин-кодом
Node closeBannerNode = xmlScreenParser.tryToFindSecurityBanner();
if (!closeBannerNode.isEmpty()) {
AdbUtils::makeTap(deviceId, closeBannerNode.x(), closeBannerNode.y());
QThread::msleep(1000);
continue;
}
// Проверяем, что это экран ввода пин-кода
if (!findAndInputPincode) {
if (const QList<Node> pincode = xmlScreenParser.tryToFindPinCode(pinCode, "Введите ПИН");
pincode.size() == 4) {
inputPinCode(deviceId, pincode);
// После ввода пин-код нужно сделать задержку, пока подгрузка идет, чтобы снова не спарсить
findAndInputPincode = true;
QThread::msleep(3000);
continue;
}
}
QThread::msleep(1000);
// Если что-то пойдет не по плану...
if (totalCounter > 100) {
AdbUtils::takeScreenshot(deviceId, "too_many_attempts");
return false;
}
}
}
bool CommonScript::tryToCloseAllBannersOnHomePage(const QString &deviceId, const int width, const int height) {
Node closeBannerOrDialogNode = xmlScreenParser.tryToFindBannerOrDialog(width, height);
if (closeBannerOrDialogNode.x() != -1 && closeBannerOrDialogNode.y() != -1) {
AdbUtils::makeTap(deviceId, closeBannerOrDialogNode.x(), closeBannerOrDialogNode.y());
return true;
}
// TODO тут все другие банеры попробовать закрыть
return false;
}
bool CommonScript::goToAllProducts(
const QString &deviceId,
const int width,
const int height
) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, m_counter));
if (tryToCloseAllBannersOnHomePage(deviceId, width, height)) {
QThread::msleep(500);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
}
if (xmlScreenParser.isHomeScreen()) {
// идем на экран "Все продукты"
if (Node allCardTitle = xmlScreenParser.findTextNode("Счета и карты"); !allCardTitle.isEmpty()) {
// 1. Если нет кнопки видимой, свайпаем. Кликаем на "Все продукты"
for (int i = 0; i < 5; ++i) {
Node allProductsBtn = xmlScreenParser.findButtonNode("Все продукты", false);
if (allProductsBtn.x() > 0 && allProductsBtn.y() > 0) {
AdbUtils::makeTap(deviceId, allProductsBtn.x(), allProductsBtn.y());
QThread::msleep(1000);
break;
}
AdbUtils::makeSwipe(deviceId,
allCardTitle.x(), allCardTitle.y(),
allCardTitle.x(), allCardTitle.y() - 200);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
}
for (int i = 0; i < 5; ++i) {
Node allProductsTitle = xmlScreenParser.findTextNode("Все продукты");
// Проверяем что вверху заголовок
if (!allProductsTitle.isEmpty() && allProductsTitle.y() < 300) {
return true;
}
QThread::msleep(1000);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
}
} else {
// FIXME пишем в БД, что не дошли до домашней страницы, и завершаемся
// сохраняем скрин приложения
// дделаем алерт
}
} else {
// FIXME
qDebug() << "Not home screen...";
}
return false;
}
bool CommonScript::findAndTapOnAccount(const QString &deviceId, const QString &accountNumbers) {
QList<QPair<AccountInfo, Node> > accounts = xmlScreenParser.parseAccountsInfo();
for (auto &[account, node]: accounts) {
// FIXME будем считать что числа последние везде уникальные
if (accountNumbers == account.lastNumbers) {
qDebug() << "findAndTapOnAccount: " << accountNumbers;
AdbUtils::makeTap(deviceId, node.x(), node.y());
QThread::msleep(1000);
return true;
}
}
return false;
}
void CommonScript::postAccountsData(const QList<QPair<AccountInfo, Node> > &accounts) {
if (accounts.isEmpty()) {
return;
}
auto *thread = new QThread;
auto *worker = new NetworkService;
QJsonArray list;
for (const QPair<AccountInfo, Node> &pair: accounts) {
QJsonObject obj;
AccountInfo account = pair.first;
obj["appName"] = account.appName;
obj["lastNumbers"] = account.lastNumbers;
obj["amount"] = account.amount;
obj["description"] = account.description;
list.append(obj);
}
// FIXME доставить десктоп ид из настроеек
worker->setDeviceData("DT88bokcwQ", accounts.first().first.deviceId);
worker->setPayload(QJsonDocument(list).toJson());
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &NetworkService::postAccountsData);
connect(worker, &NetworkService::finished, thread, &QThread::quit);
connect(worker, &NetworkService::finished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void CommonScript::postNewTransactionData(const AccountInfo &account, const TransactionInfo &transaction) {
auto *thread = new QThread;
auto *worker = new NetworkService;
QJsonObject obj;
obj["id"] = transaction.id;
obj["amount"] = transaction.amount;
obj["name"] = transaction.name;
obj["fee"] = transaction.fee;
obj["status"] = transactionStatusToString(transaction.status);
obj["type"] = transactionTypeToString(transaction.type);
obj["phone"] = transaction.phone;
obj["description"] = transaction.description;
obj["updatedDescription"] = transaction.updatedDescription;
obj["bankName"] = transaction.bankName;
obj["bankTime"] = transaction.bankTime;
obj["bankTransactionId"] = transaction.bankTrExternalId;
// FIXME доставить десктоп ид из настроеек
worker->setDeviceData("DT88bokcwQ", account.deviceId);
worker->addExtra("lastNumbers", account.lastNumbers);
worker->addExtra("transactionId", transaction.id);
worker->setPayload(QJsonDocument(obj).toJson());
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &NetworkService::insertTransactionData);
connect(worker, &NetworkService::finished, thread, &QThread::quit);
connect(worker, &NetworkService::finished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void CommonScript::updateTransactionData(
const AccountInfo &account,
const int transactionId,
const TransactionStatus status,
const QString &oldDesc,
const QString &newDesc
) {
auto *thread = new QThread;
auto *worker = new NetworkService;
QJsonObject obj;
obj["id"] = transactionId;
obj["status"] = transactionStatusToString(status);
obj["description"] = newDesc;
obj["updatedDescription"] = oldDesc;
// FIXME доставить десктоп ид из настроеек
worker->setDeviceData("DT88bokcwQ", account.deviceId);
worker->addExtra("lastNumbers", account.lastNumbers);
worker->setPayload(QJsonDocument(obj).toJson());
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &NetworkService::updateTransactionData);
connect(worker, &NetworkService::finished, thread, &QThread::quit);
connect(worker, &NetworkService::finished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void CommonScript::stop() {
m_running = false;
}