1
0
forked from BRT/arc
arc/android/dushanbe/GetCardInfoScript.cpp
trnsmkot 81b8ac6d92 Add account number parsing and database integration:
- Extend `ScreenXmlParser` with methods for detecting and extracting account numbers from multiple screens.
- Update database schema and DAO to support `account_number` field.
- Modify card information scripts to collect, save, and utilize account numbers.
- Enhance `GetCardInfoScript` logic to fetch account numbers via screen parsing and interaction.
2026-05-08 20:29:00 +07:00

291 lines
13 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 "GetCardInfoScript.h"
#include <QThread>
#include <QJsonDocument>
#include <QJsonObject>
#include "adb/AdbUtils.h"
#include "dao/MaterialDAO.h"
#include "dao/BankProfileDAO.h"
#include "dao/DeviceDAO.h"
#include "net/NetworkService.h"
namespace Dushanbe {
GetCardInfoScript::GetCardInfoScript(
QString deviceId,
QString appCode,
QObject *parent
) : CommonScript(parent),
m_deviceId(std::move(deviceId)),
m_appCode(std::move(appCode)) {
}
GetCardInfoScript::~GetCardInfoScript() = default;
void GetCardInfoScript::doStart() {
DeviceInfo device = DeviceDAO::getDeviceById(m_deviceId); if (device.id.isEmpty()) device = DeviceDAO::findByAdbSerial(m_deviceId); if (!device.adbSerial.isEmpty()) m_deviceId = device.adbSerial;
const BankProfileInfo app = BankProfileDAO::getApplication(device.id, m_appCode);
if (device.id.isEmpty() || app.id == -1) {
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 (!xmlScreenParser.isHomeScreen()) {
qDebug() << "[Dushanbe::GetCardInfo] Not on home screen, restarting app...";
if (!runAppAndGoToHomeScreen(m_deviceId, app.package, app.pinCode,
device.screenWidth, device.screenHeight)) {
m_error = "Cannot reach home screen: " + device.name + " (" + m_deviceId + ") " + m_appCode;
qWarning() << m_error;
AdbUtils::tryToKillApplication(m_deviceId, app.package);
emit finishedWithResult(m_error);
return;
}
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
}
// Парсим баланс с домашнего экрана
const double balance = xmlScreenParser.parseBalanceFromHomeScreen();
qDebug() << "[Dushanbe::GetCardInfo] balance:" << balance;
if (!BankProfileDAO::updateAmount(app.id, balance)) {
qWarning() << "[Dushanbe::GetCardInfo] Failed to update balance for app" << app.id;
}
// Тапаем "Карты" на домашнем экране
Node cardsButton = xmlScreenParser.findNodeByContentDesc(QString::fromUtf8("Карты"));
if (cardsButton.x() == 0 && cardsButton.y() == 0) {
m_error = "Cannot find Cards button: " + m_deviceId;
qWarning() << m_error;
AdbUtils::tryToKillApplication(m_deviceId, app.package);
emit finishedWithResult(m_error);
return;
}
AdbUtils::makeTap(m_deviceId, cardsButton.x(), cardsButton.y());
QThread::msleep(3000);
// Ждём экран "Мои карты"
bool cardsScreenFound = false;
for (int attempt = 0; attempt < 5; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (xmlScreenParser.isCardsScreen()) {
cardsScreenFound = true;
break;
}
QThread::msleep(1000);
}
if (!cardsScreenFound) {
m_error = "Cards screen not detected: " + m_deviceId;
qWarning() << m_error;
AdbUtils::tryToKillApplication(m_deviceId, app.package);
emit finishedWithResult(m_error);
return;
}
// Парсим карты из списка (lastNumbers, баланс)
const QList<ScreenXmlParser::CardInfo> cards = xmlScreenParser.parseCards();
qDebug() << "[Dushanbe::GetCardInfo] Found" << cards.size() << "cards in list";
if (cards.isEmpty()) {
m_error = "No cards found: " + m_deviceId;
qWarning() << m_error;
AdbUtils::tryToKillApplication(m_deviceId, app.package);
emit finishedWithResult(m_error);
return;
}
// Находим все clickable ImageView с "****" — это карточки в списке
// Пропускаем кредитные карты
QList<Node> cardNodes;
for (const Node &node : xmlScreenParser.findAllNodes()) {
if (node.className == "android.widget.ImageView" && node.clickable &&
node.contentDesc.contains("****") &&
!node.contentDesc.contains(QString::fromUtf8("Кредитная карта"))) {
cardNodes.append(node);
}
}
// Кликаем по каждой карте для получения полного номера
for (int i = 0; i < cardNodes.size(); ++i) {
if (i >= cards.size()) break;
const Node &cardNode = cardNodes[i];
AdbUtils::makeTap(m_deviceId, cardNode.x(), cardNode.y());
QThread::msleep(2000);
// Ждём экран детали карты
bool detailFound = false;
for (int attempt = 0; attempt < 5; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (xmlScreenParser.isCardDetailScreen()) {
detailFound = true;
break;
}
QThread::msleep(1000);
}
if (!detailFound) {
qWarning() << "[Dushanbe::GetCardInfo] Card detail screen not found for card" << i;
continue;
}
// Ждём загрузки баланса (может быть 0 пока данные подтягиваются)
QThread::msleep(1500);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
QString fullNumber = xmlScreenParser.parseFullCardNumber();
const double cardBalance = xmlScreenParser.parseCardDetailBalance();
const QString holder = xmlScreenParser.parseCardDetailHolder();
const QString fullNumberClean = QString(fullNumber).remove(' ');
const QString lastNumbers = (fullNumberClean.length() >= 4)
? fullNumberClean.right(4)
: (cards[i].lastNumbers.isEmpty() ? QString() : cards[i].lastNumbers);
qDebug() << "[Dushanbe::GetCardInfo] Card" << i << ":" << fullNumber
<< "balance:" << cardBalance << "holder:" << holder;
if (lastNumbers.isEmpty()) {
qWarning() << "[Dushanbe::GetCardInfo] No card number for card" << i;
AdbUtils::goBack(m_deviceId);
QThread::msleep(1500);
continue;
}
// Достаём номер счёта: Настройки карты → Реквизиты карты → таб "Реквизиты счета".
QString accountNumber;
const Node settingsBtn = xmlScreenParser.findCardSettingsButton();
if (settingsBtn.x() > 0) {
AdbUtils::makeTap(m_deviceId, settingsBtn.x(), settingsBtn.y());
QThread::msleep(1500);
bool sheetFound = false;
for (int attempt = 0; attempt < 5; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (xmlScreenParser.isCardSettingsBottomSheet()) {
sheetFound = true;
break;
}
QThread::msleep(1000);
}
if (sheetFound) {
const Node requisitesItem = xmlScreenParser.findCardRequisitesMenuItem();
if (requisitesItem.x() > 0) {
AdbUtils::makeTap(m_deviceId, requisitesItem.x(), requisitesItem.y());
QThread::msleep(1500);
bool reqScreenFound = false;
for (int attempt = 0; attempt < 5; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (xmlScreenParser.isCardRequisitesScreen()) {
reqScreenFound = true;
break;
}
QThread::msleep(1000);
}
if (reqScreenFound) {
const Node accountTab = xmlScreenParser.findAccountRequisitesTab();
if (accountTab.x() > 0) {
AdbUtils::makeTap(m_deviceId, accountTab.x(), accountTab.y());
QThread::msleep(1500);
// Несколько попыток — данные могут подгружаться
for (int attempt = 0; attempt < 5; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
accountNumber = xmlScreenParser.parseAccountNumber();
if (!accountNumber.isEmpty()) break;
QThread::msleep(1000);
}
qDebug() << "[Dushanbe::GetCardInfo] accountNumber for card" << i
<< ":" << accountNumber;
} else {
qWarning() << "[Dushanbe::GetCardInfo] Account tab not found for card" << i;
}
} else {
qWarning() << "[Dushanbe::GetCardInfo] Requisites screen not detected for card" << i;
}
} else {
qWarning() << "[Dushanbe::GetCardInfo] 'Реквизиты карты' menu item not found for card" << i;
}
} else {
qWarning() << "[Dushanbe::GetCardInfo] Card settings bottom sheet not detected for card" << i;
}
// Возврат на экран детали карты — bottom sheet и/или экран Реквизитов
for (int attempt = 0; attempt < 4; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (xmlScreenParser.isCardDetailScreen()) break;
AdbUtils::goBack(m_deviceId);
QThread::msleep(1200);
}
} else {
qWarning() << "[Dushanbe::GetCardInfo] 'Настройки карты' button not found for card" << i;
}
MaterialInfo account;
account.deviceId = device.id;
account.appCode = m_appCode;
account.lastNumbers = lastNumbers;
account.cardNumber = QString(fullNumber).remove(' ');
account.accountNumber = accountNumber;
account.amount = cardBalance;
account.currency = "TJS";
account.description = cards[i].description.isEmpty() ? holder : cards[i].description;
account.status = MaterialStatus::Active;
account.updateTime = QDateTime::currentDateTimeUtc();
qDebug() << "[Dushanbe::GetCardInfo] Saving card:" << "lastNumbers=" << lastNumbers
<< "cardNumber=" << account.cardNumber << "amount=" << cardBalance;
if (!MaterialDAO::upsertAccount(account)) {
qWarning() << "[Dushanbe::GetCardInfo] Failed to upsert account" << lastNumbers;
}
// Отправляем на сервер если нет material_id
const BankProfileInfo updatedApp = BankProfileDAO::getApplication(device.id, m_appCode);
if (!updatedApp.bankProfileId.isEmpty()) {
const MaterialInfo savedAccount = MaterialDAO::findAppAccount(device.id, m_appCode, lastNumbers);
if (savedAccount.id != -1 && savedAccount.materialId.isEmpty()) {
NetworkService ns;
ns.setDeviceId(m_deviceId);
const QMap<QString, int> currencyCodes = {
{"RUB", 643}, {"USD", 840}, {"KRW", 410}, {"AZN", 944}, {"ARS", 32}, {"TJS", 972}
};
QJsonObject materialBody;
materialBody["bank_profile_id"] = updatedApp.bankProfileId;
materialBody["card_number"] = QString(fullNumber).remove(' ');
materialBody["account_number"] = accountNumber;
materialBody["name"] = updatedApp.fullName;
materialBody["state"] = materialStatusToString(savedAccount.status) == "active" ? "enabled" : "disabled";
materialBody["currency"] = currencyCodes.value(savedAccount.currency.toUpper(), 972);
ns.setPayload(QJsonDocument(materialBody).toJson());
ns.addExtra("account_id", savedAccount.id);
ns.postMaterial();
qDebug() << "[Dushanbe::GetCardInfo] postMaterial done for" << lastNumbers;
}
}
// Назад к списку карт
AdbUtils::goBack(m_deviceId);
QThread::msleep(1500);
// Ждём возврата на экран "Мои карты"
for (int attempt = 0; attempt < 5; ++attempt) {
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
if (xmlScreenParser.isCardsScreen()) break;
QThread::msleep(1000);
}
}
AdbUtils::tryToKillApplication(m_deviceId, app.package);
emit finishedWithResult(m_error);
}
} // namespace Dushanbe