211 lines
8.7 KiB
C++
211 lines
8.7 KiB
C++
#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;
|
||
BankProfileDAO::updateAmount(app.id, balance);
|
||
|
||
// Тапаем "Карты" на домашнем экране
|
||
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;
|
||
}
|
||
|
||
MaterialInfo account;
|
||
account.deviceId = device.id;
|
||
account.appCode = m_appCode;
|
||
account.lastNumbers = lastNumbers;
|
||
account.cardNumber = QString(fullNumber).remove(' ');
|
||
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);
|
||
QJsonObject materialBody;
|
||
materialBody["bank_profile_id"] = updatedApp.bankProfileId;
|
||
materialBody["card_number"] = QString(fullNumber).remove(' ');
|
||
materialBody["name"] = updatedApp.fullName;
|
||
materialBody["state"] = materialStatusToString(savedAccount.status) == "active" ? "enabled" : "disabled";
|
||
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
|