579 lines
30 KiB
C++
579 lines
30 KiB
C++
#include "GetCardInfoScript.h"
|
||
|
||
#include <QDateTime>
|
||
#include <QJsonDocument>
|
||
#include <QJsonObject>
|
||
#include <QSettings>
|
||
#include <QThread>
|
||
|
||
#include "adb/AdbUtils.h"
|
||
#include "dao/MaterialDAO.h"
|
||
#include "dao/BankProfileDAO.h"
|
||
#include "dao/DeviceDAO.h"
|
||
#include "dao/SettingsDAO.h"
|
||
#include "dump/TaskDumpRecorder.h"
|
||
#include "net/NetworkService.h"
|
||
#include "AppLogger.h"
|
||
|
||
namespace Ozon {
|
||
|
||
GetCardInfoScript::GetCardInfoScript(
|
||
QString deviceId,
|
||
QString appCode,
|
||
QString lastNumbers,
|
||
QObject *parent
|
||
) : CommonScript(parent),
|
||
m_deviceId(std::move(deviceId)),
|
||
m_appCode(std::move(appCode)),
|
||
m_lastNumbers(std::move(lastNumbers)) {
|
||
}
|
||
|
||
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;
|
||
}
|
||
|
||
const TaskDumpMeta dumpMeta{};
|
||
TaskDumpRecorder::Scope dumpScope("ozon", "FETCH_CARDS", m_deviceId, dumpMeta, &m_error);
|
||
|
||
// Читаем текущий экран
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
|
||
// Закрываем рекламный bottom sheet, если открыт
|
||
if (Node adNode = xmlScreenParser.tryToFindAdBanner(); !adNode.isEmpty()) {
|
||
qDebug() << "[GetCardInfo] Ad banner detected, closing...";
|
||
AdbUtils::makeTap(m_deviceId, adNode.x(), adNode.y());
|
||
QThread::msleep(1000);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
}
|
||
|
||
// Свежий дамп перед проверкой домашнего экрана
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
|
||
// Если не на домашнем экране — перезапускаем приложение
|
||
if (!xmlScreenParser.isHomeScreen()) {
|
||
qDebug() << "[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 + ")";
|
||
qWarning() << m_error;
|
||
AdbUtils::tryToKillApplication(m_deviceId, app.package);
|
||
emit finishedWithResult(m_error);
|
||
return;
|
||
}
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
}
|
||
|
||
// Закрываем баннеры на главной
|
||
tryToCloseAllBannersOnHomePage(m_deviceId, device.screenWidth, device.screenHeight);
|
||
QThread::msleep(500);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
|
||
// Переходим в профиль и парсим имя и телефон (только если ещё нет в БД)
|
||
const bool hasProfileInfo = !app.fullName.isEmpty() && !app.phone.isEmpty();
|
||
Node userNameNode;
|
||
if (!hasProfileInfo) {
|
||
userNameNode = xmlScreenParser.findUserNameOnHomeScreen(device.screenHeight);
|
||
}
|
||
if (!hasProfileInfo && !userNameNode.isEmpty()) {
|
||
qDebug() << "[GetCardInfo] Navigating to profile to parse name/phone...";
|
||
AdbUtils::makeTap(m_deviceId, userNameNode.x(), userNameNode.y());
|
||
QThread::msleep(2500);
|
||
|
||
for (int attempt = 0; attempt < 5; ++attempt) {
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
|
||
if (xmlScreenParser.isProfileScreen()) break;
|
||
QThread::msleep(1000);
|
||
}
|
||
|
||
if (xmlScreenParser.isProfileScreen()) {
|
||
const QString fullName = xmlScreenParser.parseProfileFullName();
|
||
const QString phone = xmlScreenParser.parseProfilePhone();
|
||
qDebug() << "[GetCardInfo] Profile parsed - fullName:" << fullName << "phone:" << phone;
|
||
BankProfileDAO::updateProfileInfo(app.id, fullName, phone);
|
||
} else {
|
||
qWarning() << "[GetCardInfo] Profile screen not reached, skipping profile parse";
|
||
}
|
||
|
||
AdbUtils::goBack(m_deviceId);
|
||
QThread::msleep(1500);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
|
||
} else {
|
||
qDebug() << "[GetCardInfo] Profile info already in DB or username not found, skipping profile parse";
|
||
}
|
||
|
||
// Проверяем что на домашнем экране перед поиском карт
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
|
||
if (!xmlScreenParser.isHomeScreen()) {
|
||
qDebug() << "[GetCardInfo] Not on home screen before card search, restarting...";
|
||
if (!runAppAndGoToHomeScreen(m_deviceId, app.package, app.pinCode,
|
||
device.screenWidth, device.screenHeight)) {
|
||
m_error = "Cannot reach home screen before card search: " + device.name + " (" + m_deviceId + ")";
|
||
qWarning() << m_error;
|
||
AdbUtils::tryToKillApplication(m_deviceId, app.package);
|
||
emit finishedWithResult(m_error);
|
||
return;
|
||
}
|
||
tryToCloseAllBannersOnHomePage(m_deviceId, device.screenWidth, device.screenHeight);
|
||
QThread::msleep(500);
|
||
}
|
||
|
||
// Ждём пока все карты подгрузятся — до 5 попыток
|
||
QList<MaterialInfo> cards;
|
||
for (int attempt = 0; attempt < 5; ++attempt) {
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
if (closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight)) {
|
||
// баннер закрыт, дамп уже обновлён внутри helper'а
|
||
}
|
||
cards = xmlScreenParser.parseCardsOnHomeScreen();
|
||
if (!cards.isEmpty()) break;
|
||
qDebug() << "[GetCardInfo] No cards found on home screen, attempt" << attempt + 1 << "/ 5";
|
||
QThread::msleep(1500);
|
||
}
|
||
|
||
if (cards.isEmpty()) {
|
||
m_error = "No cards found on home screen: " + m_deviceId;
|
||
qWarning() << m_error;
|
||
AdbUtils::tryToKillApplication(m_deviceId, app.package);
|
||
emit finishedWithResult(m_error);
|
||
return;
|
||
}
|
||
|
||
qDebug() << "[GetCardInfo] Found" << cards.size() << "card(s) on home screen";
|
||
|
||
// Заполняем метаданные на найденных картах. Новые карты (которых ещё нет в БД)
|
||
// не создаём здесь — запись появится только после успешного OCR полного номера
|
||
// в success-ветке ниже. Это защищает от записей из ленты переводов и не оставляет
|
||
// в БД материал с пустым card_number.
|
||
//
|
||
// Для карт, которые уже в БД, обновляем только amount/update_time —
|
||
// прочие поля (cardNumber, materialId, status, ...) сохраняем как было.
|
||
for (MaterialInfo &card : cards) {
|
||
card.deviceId = device.id;
|
||
card.appCode = m_appCode;
|
||
card.updateTime = QDateTime::currentDateTimeUtc();
|
||
card.status = MaterialStatus::Off;
|
||
|
||
const MaterialInfo existing = MaterialDAO::findAppAccount(device.id, m_appCode, card.lastNumbers);
|
||
if (existing.id == -1) continue;
|
||
|
||
MaterialInfo refresh = existing;
|
||
refresh.amount = card.amount;
|
||
refresh.updateTime = card.updateTime;
|
||
if (!MaterialDAO::upsertAccount(refresh)) {
|
||
qWarning() << "[GetCardInfo] Failed to refresh amount for card" << card.lastNumbers;
|
||
} else {
|
||
qDebug() << "[GetCardInfo] Refreshed amount for card"
|
||
<< card.lastNumbers << "to" << card.amount;
|
||
}
|
||
}
|
||
|
||
// Сохраняем баланс домашнего экрана в application
|
||
const double totalAmount = cards.isEmpty() ? 0.0 : cards[0].amount;
|
||
if (totalAmount > 0.0) {
|
||
BankProfileDAO::updateAmount(app.id, totalAmount);
|
||
}
|
||
|
||
// Синхронная отправка на сервер (NetworkService блокируется через QEventLoop внутри)
|
||
NetworkService ns;
|
||
ns.setDeviceId(m_deviceId);
|
||
|
||
// 1. Создаём девайс если нет api_id
|
||
if (device.apiId.isEmpty()) {
|
||
QJsonObject deviceBody;
|
||
deviceBody["desktop_id"] = SettingsDAO::get("desktop_id");
|
||
deviceBody["status"] = "ONLINE";
|
||
deviceBody["name"] = device.name;
|
||
deviceBody["screen_width"] = device.screenWidth;
|
||
deviceBody["screen_height"]= device.screenHeight;
|
||
deviceBody["battery"] = device.battery;
|
||
ns.setPayload(QJsonDocument(deviceBody).toJson());
|
||
ns.createDevice();
|
||
qDebug() << "[GetCardInfo] createDevice done";
|
||
}
|
||
|
||
// 2. Отправляем банковский профиль (используем свежие данные из БД — имя/телефон уже спарсены)
|
||
{
|
||
const BankProfileInfo freshApp = BankProfileDAO::getApplication(device.id, m_appCode);
|
||
const QStringList parts = freshApp.fullName.split(' ', Qt::SkipEmptyParts);
|
||
|
||
// ISO 4217 numeric currency codes
|
||
const QMap<QString, int> currencyCodes = {
|
||
{"RUB", 643}, {"USD", 840}, {"KRW", 410}, {"AZN", 944}, {"ARS", 32}, {"TJS", 972}
|
||
};
|
||
const int currencyCode = currencyCodes.value(freshApp.currency.toUpper(), 643);
|
||
|
||
QJsonObject profileBody;
|
||
profileBody["first_name"] = parts.value(0);
|
||
profileBody["middle_name"] = parts.size() >= 3 ? parts.value(1) : "";
|
||
profileBody["last_name"] = parts.size() >= 3 ? parts.value(2) : parts.value(1);
|
||
profileBody["bank_name"] = m_appCode;
|
||
// Телефон: убираем пробелы, оставляем "+"
|
||
const QString phone = freshApp.phone.simplified().remove(' ');
|
||
profileBody["phone_number"] = phone;
|
||
profileBody["balance"] = QString::number(qRound64(totalAmount * 100));
|
||
profileBody["state"] = (freshApp.status == "active") ? "enabled" : "disabled";
|
||
profileBody["device_id"] = device.apiId;
|
||
profileBody["currency"] = currencyCode;
|
||
ns.setPayload(QJsonDocument(profileBody).toJson());
|
||
|
||
if (freshApp.bankProfileId.isEmpty()) {
|
||
ns.addExtra("app_id", freshApp.id);
|
||
ns.postBankProfile();
|
||
// Проверяем что bankProfileId сохранился
|
||
const BankProfileInfo afterPost = BankProfileDAO::getApplication(device.id, m_appCode);
|
||
if (afterPost.bankProfileId.isEmpty()) {
|
||
const QString err = "postBankProfile failed for " + m_deviceId;
|
||
qWarning() << "[GetCardInfo]" << err;
|
||
AppLogger::log("ozon/GetCardInfo", m_deviceId, err,
|
||
AdbUtils::captureScreenshotBytes(m_deviceId),
|
||
"FETCH_PROFILE");
|
||
m_error = err;
|
||
emit finishedWithResult(m_error);
|
||
return;
|
||
}
|
||
qDebug() << "[GetCardInfo] postBankProfile done";
|
||
} else {
|
||
ns.addExtra("bank_profile_id", freshApp.bankProfileId);
|
||
ns.patchBankProfile();
|
||
qDebug() << "[GetCardInfo] patchBankProfile done";
|
||
}
|
||
}
|
||
|
||
// Для каждой карты переходим на её экран и получаем полный номер через OCR
|
||
for (int i = 0; i < cards.size(); ++i) {
|
||
const MaterialInfo &card = cards[i];
|
||
|
||
// Если передан конкретный lastNumbers — обрабатываем только его
|
||
if (!m_lastNumbers.isEmpty() && card.lastNumbers != m_lastNumbers) continue;
|
||
|
||
qDebug() << "[GetCardInfo] Getting card number for" << card.lastNumbers;
|
||
|
||
// Если номер карты уже есть в БД — сумма уже обновлена через upsert, навигация не нужна
|
||
MaterialInfo existing = MaterialDAO::findAppAccount(device.id, m_appCode, card.lastNumbers);
|
||
if (existing.id != -1 && !existing.cardNumber.isEmpty()) {
|
||
qDebug() << "[GetCardInfo] Card" << card.lastNumbers << "already has number, skipping";
|
||
continue;
|
||
}
|
||
|
||
// Тапаем по кнопке конкретной карты — каждая открывает свой экран
|
||
Node cardBtn = xmlScreenParser.findCardButtonByLastNumbers(card.lastNumbers);
|
||
if (cardBtn.isEmpty()) {
|
||
qWarning() << "[GetCardInfo] Card button not found for" << card.lastNumbers;
|
||
continue;
|
||
}
|
||
|
||
AdbUtils::makeTap(m_deviceId, cardBtn.x(), cardBtn.y());
|
||
|
||
// Скроллим WebView вверх, пока "Показать" не окажется в верхней трети экрана.
|
||
// На устройствах с короткой высотой (например, SM-A165F, h=2340) кнопка "Показать"
|
||
// находится за bottom navigation, и тап уходит в нав-бар.
|
||
const int targetTopY = device.screenHeight / 3;
|
||
for (int scrollAttempt = 0; scrollAttempt < 6; ++scrollAttempt) {
|
||
QThread::msleep(scrollAttempt == 0 ? 2000 : 600);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
|
||
|
||
Node candidate = xmlScreenParser.findButtonNode("Показать", false);
|
||
if (candidate.isEmpty()) {
|
||
if (xmlScreenParser.isHomeScreen()) {
|
||
cardBtn = xmlScreenParser.findCardButtonByLastNumbers(card.lastNumbers);
|
||
if (!cardBtn.isEmpty()) {
|
||
qDebug() << "[GetCardInfo] Still on home before scroll, re-tapping card" << card.lastNumbers;
|
||
AdbUtils::makeTap(m_deviceId, cardBtn.x(), cardBtn.y());
|
||
}
|
||
}
|
||
continue;
|
||
}
|
||
if (candidate.y() <= targetTopY) {
|
||
qDebug() << "[GetCardInfo] 'Показать' already at top y=" << candidate.y();
|
||
break;
|
||
}
|
||
const int swipeFromY = candidate.y();
|
||
const int swipeToY = device.screenHeight / 6;
|
||
qDebug() << "[GetCardInfo] Scrolling 'Показать' up:" << swipeFromY << "->" << swipeToY;
|
||
AdbUtils::makeSwipe(m_deviceId, device.screenWidth / 2, swipeFromY,
|
||
device.screenWidth / 2, swipeToY);
|
||
}
|
||
|
||
// Ждём экран карты и стабильной геометрии блока реквизитов.
|
||
// Без этого тап по "Показать" во время reflow уезжает ниже — в "Защиту от мошенников".
|
||
// Стабильность: "Показать" совпадает по y1 с "Реквизиты карты" И координаты не меняются в двух подряд дампах.
|
||
Node showBtn;
|
||
int prevShowY = -1;
|
||
for (int attempt = 0; attempt < 10; ++attempt) {
|
||
QThread::msleep(attempt == 0 ? 2000 : 500);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
|
||
|
||
Node candidate = xmlScreenParser.findButtonNode("Показать", false);
|
||
Node requisites = xmlScreenParser.findTextNode("Реквизиты карты");
|
||
|
||
if (candidate.isEmpty() || requisites.isEmpty()) {
|
||
// Если всё ещё на домашнем экране — тап по карточке не сработал, повторяем
|
||
if (xmlScreenParser.isHomeScreen()) {
|
||
cardBtn = xmlScreenParser.findCardButtonByLastNumbers(card.lastNumbers);
|
||
if (!cardBtn.isEmpty()) {
|
||
qDebug() << "[GetCardInfo] Still on home screen, re-tapping card" << card.lastNumbers;
|
||
AdbUtils::makeTap(m_deviceId, cardBtn.x(), cardBtn.y());
|
||
}
|
||
}
|
||
prevShowY = -1;
|
||
qDebug() << "[GetCardInfo] Requisites not ready, attempt" << attempt + 1 << "/ 10";
|
||
continue;
|
||
}
|
||
|
||
// "Показать" должен быть в одной строке с "Реквизиты карты"
|
||
if (qAbs(candidate.y1() - requisites.y1()) > 10) {
|
||
prevShowY = -1;
|
||
qDebug() << "[GetCardInfo] 'Показать' not aligned with 'Реквизиты карты', waiting reflow";
|
||
continue;
|
||
}
|
||
|
||
// Y должна совпасть в двух подряд дампах (layout устаканился)
|
||
if (prevShowY >= 0 && qAbs(candidate.y() - prevShowY) <= 5) {
|
||
showBtn = candidate;
|
||
break;
|
||
}
|
||
prevShowY = candidate.y();
|
||
qDebug() << "[GetCardInfo] 'Показать' seen at y=" << candidate.y() << ", waiting for stable frame";
|
||
}
|
||
|
||
if (showBtn.isEmpty()) {
|
||
qWarning() << "[GetCardInfo] 'Показать' not stable for card" << card.lastNumbers;
|
||
AdbUtils::goBack(m_deviceId);
|
||
QThread::msleep(1500);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
|
||
continue;
|
||
}
|
||
|
||
qDebug() << "[GetCardInfo] 'Показать' at" << showBtn.x() << showBtn.y();
|
||
AdbUtils::makeTap(m_deviceId, showBtn.x(), showBtn.y());
|
||
|
||
// Ждём появления полного номера карты в XML дампе
|
||
QString cardNumber;
|
||
for (int attempt = 0; attempt < 10; ++attempt) {
|
||
QThread::msleep(2000);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
|
||
cardNumber = xmlScreenParser.findFullCardNumber();
|
||
if (!cardNumber.isEmpty()) {
|
||
qDebug() << "[GetCardInfo] Card number found on attempt" << attempt + 1;
|
||
break;
|
||
}
|
||
|
||
// Закрываем баннер "Понятно" и повторно нажимаем "Показать"
|
||
Node bannerBtn = xmlScreenParser.findTextNode("Понятно");
|
||
if (!bannerBtn.isEmpty()) {
|
||
qDebug() << "[GetCardInfo] Banner 'Понятно' detected, closing...";
|
||
AdbUtils::makeTap(m_deviceId, bannerBtn.x(), bannerBtn.y());
|
||
QThread::msleep(1500);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
Node retryShowBtn = xmlScreenParser.findButtonNode("Показать", false);
|
||
if (!retryShowBtn.isEmpty()) {
|
||
qDebug() << "[GetCardInfo] Re-tapping 'Показать' after banner close";
|
||
AdbUtils::makeTap(m_deviceId, retryShowBtn.x(), retryShowBtn.y());
|
||
}
|
||
continue;
|
||
}
|
||
|
||
// "Подождите" — загрузка в процессе, просто ждём
|
||
if (!xmlScreenParser.findButtonNode("Подождите", false).isEmpty()) {
|
||
qDebug() << "[GetCardInfo] 'Подождите' visible, waiting... attempt" << attempt + 1 << "/ 10";
|
||
continue;
|
||
}
|
||
|
||
// "Скрыть" видна — номер должен быть раскрыт, но парсер не нашёл
|
||
if (!xmlScreenParser.findButtonNode("Скрыть", false).isEmpty()) {
|
||
qDebug() << "[GetCardInfo] 'Скрыть' visible but card number not parsed, attempt" << attempt + 1 << "/ 10";
|
||
continue;
|
||
}
|
||
|
||
// Повторно нажимаем "Показать" если тап не сработал
|
||
Node retryShowBtn = xmlScreenParser.findButtonNode("Показать", false);
|
||
if (!retryShowBtn.isEmpty()) {
|
||
qDebug() << "[GetCardInfo] Re-tapping 'Показать', attempt" << attempt + 1 << "/ 10";
|
||
AdbUtils::makeTap(m_deviceId, retryShowBtn.x(), retryShowBtn.y());
|
||
} else {
|
||
qDebug() << "[GetCardInfo] Card number not visible, attempt" << attempt + 1 << "/ 10";
|
||
}
|
||
}
|
||
|
||
if (!cardNumber.isEmpty()) {
|
||
qDebug() << "[GetCardInfo] Card" << card.lastNumbers << "full number:" << cardNumber;
|
||
// Апсёртим запись только теперь — с полным номером карты.
|
||
MaterialInfo toSave = card;
|
||
toSave.cardNumber = cardNumber;
|
||
toSave.updateTime = QDateTime::currentDateTimeUtc();
|
||
if (!MaterialDAO::upsertAccount(toSave)) {
|
||
qWarning() << "[GetCardInfo] Failed to upsert account for card" << card.lastNumbers;
|
||
} else {
|
||
qDebug() << "[GetCardInfo] Upserted account:" << card.lastNumbers
|
||
<< card.description << card.amount;
|
||
}
|
||
} else {
|
||
const QString err = "Card number not found for " + card.lastNumbers;
|
||
qWarning() << "[GetCardInfo]" << err;
|
||
// Без полного номера материал в БД не создаём.
|
||
AppLogger::log("ozon/GetCardInfo", m_deviceId, err,
|
||
AdbUtils::captureScreenshotBytes(m_deviceId),
|
||
"FETCH_PROFILE");
|
||
}
|
||
|
||
// Возвращаемся на домашний экран для следующей карты
|
||
if (i < cards.size() - 1) {
|
||
AdbUtils::goBack(m_deviceId);
|
||
QThread::msleep(2000);
|
||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
|
||
}
|
||
}
|
||
|
||
// 2.5. Достаём номер счёта (общий для всех карт): Основной счёт → Посмотреть реквизиты
|
||
// ВРЕМЕННО ОТКЛЮЧЕНО
|
||
// QString accountNumber;
|
||
// {
|
||
// bool needFetch = false;
|
||
// for (const MaterialInfo &c : cards) {
|
||
// const MaterialInfo dbCard = MaterialDAO::findAppAccount(device.id, m_appCode, c.lastNumbers);
|
||
// if (dbCard.id != -1 && dbCard.accountNumber.isEmpty()) { needFetch = true; break; }
|
||
// }
|
||
//
|
||
// if (needFetch) {
|
||
// // Возвращаемся на главный экран — критерий именно по наличию кнопки
|
||
// // "Основной счёт ... ₽". isHomeScreen() для Озона нестрогий
|
||
// // (Перевести/Пополнить присутствуют и на других экранах).
|
||
// Node mainAcc;
|
||
// for (int attempt = 0; attempt < 6; ++attempt) {
|
||
// xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
// mainAcc = xmlScreenParser.findMainAccountButton();
|
||
// if (mainAcc.x() > 0) break;
|
||
// AdbUtils::goBack(m_deviceId);
|
||
// QThread::msleep(1500);
|
||
// }
|
||
//
|
||
// if (mainAcc.x() > 0) {
|
||
// AdbUtils::makeTap(m_deviceId, mainAcc.x(), mainAcc.y());
|
||
// QThread::msleep(2000);
|
||
//
|
||
// // Скроллим WebView, пока "Посмотреть реквизиты" не окажется в верхней половине,
|
||
// // чтобы тап не ушёл в системный нав-бар.
|
||
// Node showReq;
|
||
// const int targetMaxY = device.screenHeight / 2;
|
||
// for (int attempt = 0; attempt < 6; ++attempt) {
|
||
// xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
// Node candidate = xmlScreenParser.findShowRequisitesButton();
|
||
// if (candidate.x() > 0 && candidate.y() <= targetMaxY) {
|
||
// showReq = candidate;
|
||
// break;
|
||
// }
|
||
// AdbUtils::makeSwipe(m_deviceId, device.screenWidth / 2,
|
||
// device.screenHeight * 2 / 3,
|
||
// device.screenWidth / 2,
|
||
// device.screenHeight / 3);
|
||
// QThread::msleep(800);
|
||
// }
|
||
//
|
||
// if (showReq.x() > 0) {
|
||
// AdbUtils::makeTap(m_deviceId, showReq.x(), showReq.y());
|
||
// QThread::msleep(2000);
|
||
//
|
||
// bool reqScreen = false;
|
||
// for (int attempt = 0; attempt < 6; ++attempt) {
|
||
// xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
// if (xmlScreenParser.isAccountRequisitesScreen()) {
|
||
// reqScreen = true;
|
||
// break;
|
||
// }
|
||
// QThread::msleep(1000);
|
||
// }
|
||
//
|
||
// if (reqScreen) {
|
||
// for (int attempt = 0; attempt < 5; ++attempt) {
|
||
// xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
// accountNumber = xmlScreenParser.parseAccountNumberFromRequisites();
|
||
// if (!accountNumber.isEmpty()) break;
|
||
// QThread::msleep(800);
|
||
// }
|
||
// qDebug() << "[GetCardInfo] accountNumber:" << accountNumber;
|
||
// } else {
|
||
// qWarning() << "[GetCardInfo] Account requisites screen not detected";
|
||
// }
|
||
// } else {
|
||
// qWarning() << "[GetCardInfo] 'Посмотреть реквизиты' not reached";
|
||
// }
|
||
// } else {
|
||
// qWarning() << "[GetCardInfo] 'Основной счёт' button not found on home";
|
||
// }
|
||
//
|
||
// // Возврат на главный — критерий по кнопке "Основной счёт ... ₽"
|
||
// for (int attempt = 0; attempt < 5; ++attempt) {
|
||
// xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||
// if (xmlScreenParser.findMainAccountButton().x() > 0) break;
|
||
// AdbUtils::goBack(m_deviceId);
|
||
// QThread::msleep(1500);
|
||
// }
|
||
//
|
||
// // Записываем номер счёта во все материалы device/app
|
||
// if (!accountNumber.isEmpty()) {
|
||
// for (const MaterialInfo &c : cards) {
|
||
// MaterialInfo upd = MaterialDAO::findAppAccount(device.id, m_appCode, c.lastNumbers);
|
||
// if (upd.id == -1) continue;
|
||
// upd.accountNumber = accountNumber;
|
||
// upd.updateTime = QDateTime::currentDateTimeUtc();
|
||
// if (!MaterialDAO::upsertAccount(upd)) {
|
||
// qWarning() << "[GetCardInfo] Failed to save accountNumber for" << upd.lastNumbers;
|
||
// }
|
||
// }
|
||
// }
|
||
// }
|
||
// }
|
||
|
||
// 3. Отправляем материалы (карты) — после OCR, когда номера карт уже в БД
|
||
const BankProfileInfo updatedApp = BankProfileDAO::getApplication(device.id, m_appCode);
|
||
if (!updatedApp.bankProfileId.isEmpty()) {
|
||
for (const MaterialInfo &card : cards) {
|
||
const MaterialInfo dbCard = MaterialDAO::findAppAccount(device.id, m_appCode, card.lastNumbers);
|
||
if (dbCard.cardNumber.isEmpty()) continue;
|
||
if (!dbCard.materialId.isEmpty()) {
|
||
qDebug() << "[GetCardInfo] material already exists for card" << dbCard.lastNumbers << ", skipping";
|
||
continue;
|
||
}
|
||
|
||
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"] = dbCard.cardNumber;
|
||
materialBody["account_number"] = dbCard.accountNumber;
|
||
materialBody["name"] = updatedApp.fullName;
|
||
materialBody["state"] = materialStatusToString(dbCard.status) == "active" ? "enabled" : "disabled";
|
||
materialBody["currency"] = currencyCodes.value(dbCard.currency.toUpper(), 643);
|
||
ns.setPayload(QJsonDocument(materialBody).toJson());
|
||
ns.addExtra("account_id", dbCard.id);
|
||
ns.postMaterial();
|
||
qDebug() << "[GetCardInfo] postMaterial done for card" << dbCard.lastNumbers;
|
||
}
|
||
} else {
|
||
qWarning() << "[GetCardInfo] bankProfileId empty, skipping materials";
|
||
}
|
||
|
||
AdbUtils::tryToKillApplication(m_deviceId, app.package);
|
||
emit finishedWithResult(m_error);
|
||
}
|
||
|
||
} // namespace Ozon
|