Removed redundant "numbers" field from Account model and database queries to simplify account structure. Introduced a TutorialWindow class for displaying application tutorials, integrated into the main window, and updated file paths accordingly. Improved string handling and logic in Payment routines and enhanced overall UI structure.
314 lines
14 KiB
C++
314 lines
14 KiB
C++
#include "PayByPhoneScript.h"
|
||
|
||
#include <QRandomGenerator>
|
||
#include <QThread>
|
||
#include <utility>
|
||
|
||
#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/ApplicationInfo.h"
|
||
#include "db/DeviceInfo.h"
|
||
|
||
PayByPhoneScript::PayByPhoneScript(
|
||
AccountInfo account,
|
||
QString phone,
|
||
QString bankName,
|
||
const double amount,
|
||
QObject *parent
|
||
) : CommonScript(parent),
|
||
m_account(std::move(account)), m_phone(std::move(phone)),
|
||
m_bankName(std::move(bankName)), m_amount(amount) {
|
||
}
|
||
|
||
PayByPhoneScript::~PayByPhoneScript() = default;
|
||
|
||
|
||
void PayByPhoneScript::makePayment(
|
||
const QString &deviceId,
|
||
const QString &pinCode,
|
||
int width,
|
||
int height
|
||
) {
|
||
// Оплата со страницы карты
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
Node payBtn = xmlScreenParser.findButtonNode("Оплатить", false);
|
||
if (!payBtn.isEmpty()) {
|
||
AdbUtils::makeTap(deviceId, payBtn.x(), payBtn.y());
|
||
QThread::msleep(1000);
|
||
} else {
|
||
m_error = "Кнопка 'Оплатить' не нашлась";
|
||
qDebug() << m_error;
|
||
return;
|
||
}
|
||
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
// TODO - По номеру карты В РСХБ и другие банки
|
||
Node payTitle = xmlScreenParser.findTextNode("Платежи и переводы");
|
||
if (!payTitle.isEmpty() && payTitle.y() < 300) {
|
||
Node payByPhoneBtn = xmlScreenParser.findButtonNode("По номеру телефона В РСХБ и через", false);
|
||
if (!payByPhoneBtn.isEmpty()) {
|
||
AdbUtils::makeTap(deviceId, payByPhoneBtn.x(), payByPhoneBtn.y());
|
||
QThread::msleep(1000);
|
||
} else {
|
||
m_error = "Не смогли найти пункт 'По номеру телефона В РСХБ и через'";
|
||
qDebug() << m_error;
|
||
return;
|
||
}
|
||
} else {
|
||
m_error = "Не смогли перейти на экран 'Платежи и переводы'";
|
||
qDebug() << m_error;
|
||
return;
|
||
}
|
||
|
||
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
Node payByPhoneTitle = xmlScreenParser.findTextNode("Перевод по телефону");
|
||
Node continueBtn = xmlScreenParser.findButtonNode("Продолжить", false);
|
||
if (!payByPhoneTitle.isEmpty() && payByPhoneTitle.y() < 300 && continueBtn.isEmpty()) {
|
||
Node inputPhone = xmlScreenParser.findFirstEditText();
|
||
AdbUtils::makeTap(deviceId, inputPhone.x(), inputPhone.y());
|
||
QThread::msleep(500);
|
||
AdbUtils::inputText(deviceId, m_phone);
|
||
QThread::msleep(1000);
|
||
} else {
|
||
m_error = "Не смогли найти пункт 'Перевод по телефону'";
|
||
qDebug() << m_error;
|
||
return;
|
||
}
|
||
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
QString payBankName = m_bankName == QStringLiteral("Россельхозбанк") ? "Россельхозбанк" : "Другой банк через СБП";
|
||
Node payOtherBankBtn = xmlScreenParser.findButtonNode(payBankName, false);
|
||
if (!payOtherBankBtn.isEmpty()) {
|
||
AdbUtils::makeTap(deviceId, payOtherBankBtn.x(), payOtherBankBtn.y());
|
||
QThread::msleep(1000);
|
||
} else {
|
||
m_error = "Не смогли нажать на первичный выбор банка";
|
||
qDebug() << m_error;
|
||
return;
|
||
}
|
||
|
||
if (m_bankName != QStringLiteral("Россельхозбанк")) {
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
for (int i = 0; i < 5; ++i) {
|
||
Node allBanksTitle = xmlScreenParser.findTextNode("Банки участники");
|
||
if (!allBanksTitle.isEmpty() && allBanksTitle.y() < 300) {
|
||
Node inputBankName = xmlScreenParser.findFirstEditText();
|
||
AdbUtils::makeTap(deviceId, inputBankName.x(), inputBankName.y());
|
||
QThread::msleep(500);
|
||
AdbUtils::enableAdbIMEKeyboard(deviceId);
|
||
AdbUtils::inputAdbIMEText(deviceId, m_bankName);
|
||
AdbUtils::disableAdbIMEKeyboard(deviceId);
|
||
QThread::msleep(1000);
|
||
break;
|
||
} else {
|
||
qDebug() << "--- allBanksTitle not found, i: " << i;
|
||
}
|
||
QThread::msleep(1000);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
}
|
||
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
for (int i = 0; i < 5; ++i) {
|
||
Node bankNameListItem = xmlScreenParser.findTextNode(m_bankName);
|
||
if (!bankNameListItem.isEmpty()) {
|
||
AdbUtils::makeTap(deviceId, bankNameListItem.x(), bankNameListItem.y());
|
||
QThread::msleep(1000);
|
||
break;
|
||
} else {
|
||
qDebug() << "--- bankNameListItem not found, i: " << i;
|
||
}
|
||
QThread::msleep(1000);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
}
|
||
}
|
||
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
|
||
const bool isRshbBank = m_bankName == QStringLiteral("Россельхозбанк");
|
||
QString payByPhoneScreenTitleText = isRshbBank ? "Перевод клиенту РСХБ" : "Перевод по телефону";
|
||
Node payByPhoneScreenTitle = xmlScreenParser.findTextNode(payByPhoneScreenTitleText);
|
||
Node continuePayBtn = xmlScreenParser.findButtonNode("Продолжить", false);
|
||
|
||
if (isRshbBank ||
|
||
!payByPhoneScreenTitle.isEmpty() && payByPhoneScreenTitle.y() < 300 && !continuePayBtn.isEmpty()) {
|
||
Node sumBtn = xmlScreenParser.findButtonNode("0", false);
|
||
if (isRshbBank || !sumBtn.isEmpty()) {
|
||
if (!isRshbBank) {
|
||
AdbUtils::makeTap(deviceId, sumBtn.x(), sumBtn.y());
|
||
QThread::msleep(500);
|
||
}
|
||
AdbUtils::inputText(deviceId, QString::number(m_amount));
|
||
QThread::msleep(500);
|
||
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
Node readyBtn = xmlScreenParser.findTextNode("Готово");
|
||
if (!readyBtn.isEmpty()) {
|
||
AdbUtils::makeTap(deviceId, readyBtn.x(), readyBtn.y());
|
||
QThread::msleep(500);
|
||
if (isRshbBank) {
|
||
QThread::msleep(1000);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
continuePayBtn = xmlScreenParser.findButtonNode("Продолжить", false);
|
||
}
|
||
AdbUtils::makeTap(deviceId, continuePayBtn.x(), continuePayBtn.y());
|
||
QThread::msleep(1000);
|
||
} else {
|
||
m_error = "Не смогли нажать 'Готово'";
|
||
qDebug() << m_error;
|
||
return;
|
||
}
|
||
} else {
|
||
m_error = "Не смогли нажать 'Продолжить'";
|
||
qDebug() << m_error;
|
||
return;
|
||
}
|
||
} else {
|
||
m_error = "Не смогли найти заголовок: " + payByPhoneScreenTitleText;
|
||
qDebug() << m_error;
|
||
return;
|
||
}
|
||
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
bool inputedPinCode = false;
|
||
for (int i = 0; i < 5; ++i) {
|
||
// TODO кнопка содержит "Перевести 10 ₽" если будет от 1000 то скорее всвего будет "Перевести 1 000 ₽",
|
||
// TODO не ясно как форматирование будет, поэтому пока на начало зашьем
|
||
Node makePayBtn = xmlScreenParser.findButtonNode("Перевести ", true);
|
||
if (!makePayBtn.isEmpty()) {
|
||
AdbUtils::makeTap(deviceId, makePayBtn.x(), makePayBtn.y());
|
||
QThread::msleep(500);
|
||
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
if (const QList<Node> pincode = xmlScreenParser.tryToFindPinCode(pinCode, "Введите код доступа");
|
||
pincode.size() == 4) {
|
||
inputPinCode(deviceId, pincode);
|
||
inputedPinCode = true;
|
||
// После ввода пин-кода, ждем 3с
|
||
QThread::msleep(3000);
|
||
}
|
||
break;
|
||
} else {
|
||
qWarning() << "--- makePayBtn not found, i: " << i;
|
||
}
|
||
QThread::msleep(1000);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
}
|
||
|
||
if (!isRshbBank && !inputedPinCode) {
|
||
m_error = "Не смогли ввести пин-код!'";
|
||
qDebug() << m_error;
|
||
return;
|
||
}
|
||
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
|
||
for (int i = 0; i < 10; ++i) {
|
||
Node moreDetailBtn = xmlScreenParser.findButtonNode("Детали операции", false);
|
||
if (!moreDetailBtn.isEmpty()) {
|
||
AdbUtils::makeTap(deviceId, moreDetailBtn.x(), moreDetailBtn.y());
|
||
QThread::msleep(200);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
|
||
// TODO считываем все данные по переводу
|
||
TransactionInfo transaction = xmlScreenParser.parseTransactionInfo(width, height);
|
||
if (isRshbBank) {
|
||
transaction.bankName = m_bankName;
|
||
}
|
||
transaction.accountId = m_account.id;
|
||
transaction.amount = -m_amount;
|
||
transaction.type = TransactionType::Phone;
|
||
transaction.id = TransactionDAO::insertTransaction(transaction);
|
||
if (transaction.id == -1) {
|
||
// FIXME alert!!
|
||
qDebug() << "Transaction has not inserted...";
|
||
}
|
||
|
||
// ждем 10 секунд смену статуса
|
||
for (int k = 0; k < 10; ++k) {
|
||
Node paymentCompleted = xmlScreenParser.findTextNode("Исполнен", 0, 0, width, height / 2);
|
||
if (!paymentCompleted.isEmpty()) {
|
||
transaction.status = TransactionStatus::Complete;
|
||
if (!TransactionDAO::updateTransactionStatus(transaction.id, TransactionStatus::Complete)) {
|
||
qDebug() << "Not updated: " << transaction.id;
|
||
}
|
||
// TODO обновляем данные
|
||
break;
|
||
}
|
||
|
||
QThread::msleep(1000);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
}
|
||
|
||
postNewTransactionData(m_account, transaction);
|
||
break;
|
||
} else {
|
||
qWarning() << "--- moreDetailBtn not found, i: " << i;
|
||
}
|
||
QThread::msleep(1000);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
|
||
}
|
||
|
||
Node homeBtn = xmlScreenParser.findButtonNode("Вернуться на главную", false);
|
||
AdbUtils::makeTap(deviceId, homeBtn.x(), homeBtn.y());
|
||
QThread::msleep(1000);
|
||
}
|
||
|
||
void PayByPhoneScript::doStart() {
|
||
const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId);
|
||
const ApplicationInfo app = ApplicationDAO::getApplicationsByDeviceId(m_account.deviceId, m_account.appName);
|
||
|
||
if (device.id.isEmpty() || app.id.isEmpty()) {
|
||
m_error = "Device or app not found....";
|
||
qDebug() << m_error;
|
||
emit finishedWithResult(m_error);
|
||
return;
|
||
}
|
||
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
|
||
if (!xmlScreenParser.isHomeScreen()) {
|
||
if (!runAppAndGoToHomeScreen(device.id, app.package, app.pinCode, device.screenWidth, device.screenHeight)) {
|
||
m_error = "Не смогли дойти до домашней страницы....";
|
||
qDebug() << m_error;
|
||
emit finishedWithResult(m_error);
|
||
return;
|
||
}
|
||
}
|
||
|
||
if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) {
|
||
qDebug() << "======== 1. All products";
|
||
QList<QPair<AccountInfo, Node> > accounts = xmlScreenParser.parseAccountsInfo();
|
||
for (auto &[account, node]: accounts) {
|
||
account.deviceId = m_account.deviceId;
|
||
account.appName = m_account.appName;
|
||
if (!AccountDAO::upsertAccount(account)) {
|
||
qDebug() << "Not updated: " << account.lastNumbers;
|
||
}
|
||
}
|
||
|
||
postAccountsData(accounts);
|
||
|
||
for (auto &[account, node]: accounts) {
|
||
// FIXME будем считать что числа последние везде уникальные
|
||
if (m_account.lastNumbers == account.lastNumbers) {
|
||
qDebug() << "======== 2. Tap by card";
|
||
AdbUtils::makeTap(device.id, node.x(), node.y());
|
||
QThread::msleep(1000);
|
||
break;
|
||
}
|
||
}
|
||
|
||
qDebug() << "======== 3. Make payment";
|
||
makePayment(device.id, app.pinCode, device.screenWidth, device.screenHeight);
|
||
} else {
|
||
m_error = "Не смогли открыть 'Все продукты'";
|
||
qDebug() << m_error;
|
||
}
|
||
|
||
emit finishedWithResult(m_error);
|
||
}
|