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.
304 lines
12 KiB
C++
304 lines
12 KiB
C++
#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;
|
||
}
|