From 93f286a8f8391633e3512bff3d54728d13ca4f8a Mon Sep 17 00:00:00 2001 From: trnsmkot Date: Fri, 15 May 2026 16:54:02 +0700 Subject: [PATCH] Refactor `createBankProfileIfNeeded` to return error messages instead of `void`. Update call sites to handle and propagate errors appropriately, improving error handling across card info and profile scripts. --- android/black/CommonScript.cpp | 11 ++++- android/black/CommonScript.h | 5 +- android/black/GetProfileInfoScript.cpp | 6 ++- android/dushanbe/CommonScript.cpp | 11 ++++- android/dushanbe/CommonScript.h | 2 +- android/dushanbe/GetProfileInfoScript.cpp | 6 ++- android/ozon/CommonScript.cpp | 11 ++++- android/ozon/CommonScript.h | 5 +- android/ozon/GetCardInfoScript.cpp | 48 +++++++++++++------- android/ozon/GetProfileInfoScript.cpp | 6 ++- android/ozon/LoginAndCheckAccountsScript.cpp | 25 +++++++++- android/ozon/ScreenXmlParser.cpp | 31 +++++++++++-- 12 files changed, 130 insertions(+), 37 deletions(-) diff --git a/android/black/CommonScript.cpp b/android/black/CommonScript.cpp index 9933374..8cdf649 100644 --- a/android/black/CommonScript.cpp +++ b/android/black/CommonScript.cpp @@ -200,12 +200,12 @@ void CommonScript::updateTransactionData( thread->start(); } -void CommonScript::createBankProfileIfNeeded(const QString &deviceId, const QString &appCode, double parsedBalance) { +QString CommonScript::createBankProfileIfNeeded(const QString &deviceId, const QString &appCode, double parsedBalance) { const BankProfileInfo app = BankProfileDAO::getApplication(deviceId, appCode); if (app.fullName.trimmed().isEmpty() || app.phone.trimmed().isEmpty()) { qWarning() << "[Black] createBankProfileIfNeeded: skipping — fullName or phone is empty"; - return; + return {}; } const QMap currencyCodes = { @@ -249,12 +249,19 @@ void CommonScript::createBankProfileIfNeeded(const QString &deviceId, const QStr if (app.bankProfileId.isEmpty()) { ns.addExtra("app_id", app.id); ns.postBankProfile(); + const BankProfileInfo afterPost = BankProfileDAO::getApplication(deviceId, appCode); + if (afterPost.bankProfileId.isEmpty()) { + const QString err = "postBankProfile failed for " + deviceId + " " + appCode; + qWarning() << "[Black] createBankProfileIfNeeded:" << err; + return err; + } qDebug() << "[Black] createBankProfileIfNeeded: postBankProfile done"; } else { ns.addExtra("bank_profile_id", app.bankProfileId); ns.patchBankProfile(); qDebug() << "[Black] createBankProfileIfNeeded: patchBankProfile done"; } + return {}; } void CommonScript::stop() { diff --git a/android/black/CommonScript.h b/android/black/CommonScript.h index 29d3635..d061d29 100644 --- a/android/black/CommonScript.h +++ b/android/black/CommonScript.h @@ -46,8 +46,9 @@ protected: const QString &newDesc ); - // Создаёт bank_profile на сервере если ещё не создан - void createBankProfileIfNeeded(const QString &deviceId, const QString &appCode, double parsedBalance = -1.0); + // Создаёт bank_profile на сервере если ещё не создан. + // Возвращает пустую строку при успехе, текст ошибки при провале. + QString createBankProfileIfNeeded(const QString &deviceId, const QString &appCode, double parsedBalance = -1.0); // Если виден баннер Google Play "Доступно обновление" или системный // диалог разрешений — закрывает его и перепарсивает XML. Безопасно diff --git a/android/black/GetProfileInfoScript.cpp b/android/black/GetProfileInfoScript.cpp index a51a519..23bf45e 100644 --- a/android/black/GetProfileInfoScript.cpp +++ b/android/black/GetProfileInfoScript.cpp @@ -164,7 +164,11 @@ void GetProfileInfoScript::doStart() { } // 7. Создаём bank profile если нет - createBankProfileIfNeeded(m_deviceId, m_appCode); + if (const QString err = createBankProfileIfNeeded(m_deviceId, m_appCode); !err.isEmpty()) { + m_error = err; + emit finishedWithResult(m_error); + return; + } // 8. Отправляем данные аккаунтов на сервер если нет material_id const QList allAccounts = MaterialDAO::getAccounts(device.id, m_appCode); diff --git a/android/dushanbe/CommonScript.cpp b/android/dushanbe/CommonScript.cpp index d15cbad..518b577 100644 --- a/android/dushanbe/CommonScript.cpp +++ b/android/dushanbe/CommonScript.cpp @@ -235,12 +235,12 @@ void CommonScript::updateTransactionData( thread->start(); } -void CommonScript::createBankProfileIfNeeded(const QString &deviceId, const QString &appCode, double parsedBalance) { +QString CommonScript::createBankProfileIfNeeded(const QString &deviceId, const QString &appCode, double parsedBalance) { const BankProfileInfo app = BankProfileDAO::getApplication(deviceId, appCode); if (app.fullName.trimmed().isEmpty() || app.phone.trimmed().isEmpty()) { qWarning() << "[Dushanbe] createBankProfileIfNeeded: skipping — fullName or phone is empty"; - return; + return {}; } const QMap currencyCodes = { @@ -283,12 +283,19 @@ void CommonScript::createBankProfileIfNeeded(const QString &deviceId, const QStr if (app.bankProfileId.isEmpty()) { ns.addExtra("app_id", app.id); ns.postBankProfile(); + const BankProfileInfo afterPost = BankProfileDAO::getApplication(deviceId, appCode); + if (afterPost.bankProfileId.isEmpty()) { + const QString err = "postBankProfile failed for " + deviceId + " " + appCode; + qWarning() << "[Dushanbe] createBankProfileIfNeeded:" << err; + return err; + } qDebug() << "[Dushanbe] createBankProfileIfNeeded: postBankProfile done"; } else { ns.addExtra("bank_profile_id", app.bankProfileId); ns.patchBankProfile(); qDebug() << "[Dushanbe] createBankProfileIfNeeded: patchBankProfile done"; } + return {}; } void CommonScript::stop() { diff --git a/android/dushanbe/CommonScript.h b/android/dushanbe/CommonScript.h index 68c72de..f3f925e 100644 --- a/android/dushanbe/CommonScript.h +++ b/android/dushanbe/CommonScript.h @@ -45,7 +45,7 @@ protected: const QString &newDesc ); - void createBankProfileIfNeeded(const QString &deviceId, const QString &appCode, double parsedBalance = -1.0); + QString createBankProfileIfNeeded(const QString &deviceId, const QString &appCode, double parsedBalance = -1.0); // Если виден баннер Google Play "Доступно обновление" — закрывает его и перепарсивает XML. // Безопасно вызывать на любом экране; возвращает true если баннер был закрыт. diff --git a/android/dushanbe/GetProfileInfoScript.cpp b/android/dushanbe/GetProfileInfoScript.cpp index 591a49e..2bd7fb6 100644 --- a/android/dushanbe/GetProfileInfoScript.cpp +++ b/android/dushanbe/GetProfileInfoScript.cpp @@ -129,7 +129,11 @@ void GetProfileInfoScript::doStart() { BankProfileDAO::updateProfileInfo(app.id, fullName, phone, ""); } - createBankProfileIfNeeded(device.id, m_appCode, parsedBalance); + if (const QString err = createBankProfileIfNeeded(device.id, m_appCode, parsedBalance); !err.isEmpty()) { + m_error = err; + emit finishedWithResult(m_error); + return; + } const QList allAccounts = MaterialDAO::getAccounts(device.id, m_appCode); QList> accountsToPost; diff --git a/android/ozon/CommonScript.cpp b/android/ozon/CommonScript.cpp index d3823e4..c77ea3d 100644 --- a/android/ozon/CommonScript.cpp +++ b/android/ozon/CommonScript.cpp @@ -401,12 +401,12 @@ void CommonScript::updateTransactionData( thread->start(); } -void CommonScript::createBankProfileIfNeeded(const QString &deviceId, const QString &appCode, double parsedBalance) { +QString CommonScript::createBankProfileIfNeeded(const QString &deviceId, const QString &appCode, double parsedBalance) { const BankProfileInfo app = BankProfileDAO::getApplication(deviceId, appCode); if (app.fullName.trimmed().isEmpty() || app.phone.trimmed().isEmpty()) { qWarning() << "[Ozon] createBankProfileIfNeeded: skipping — fullName or phone is empty"; - return; + return {}; } const QMap currencyCodes = { @@ -450,12 +450,19 @@ void CommonScript::createBankProfileIfNeeded(const QString &deviceId, const QStr if (app.bankProfileId.isEmpty()) { ns.addExtra("app_id", app.id); ns.postBankProfile(); + const BankProfileInfo afterPost = BankProfileDAO::getApplication(deviceId, appCode); + if (afterPost.bankProfileId.isEmpty()) { + const QString err = "postBankProfile failed for " + deviceId + " " + appCode; + qWarning() << "[Ozon] createBankProfileIfNeeded:" << err; + return err; + } qDebug() << "[Ozon] createBankProfileIfNeeded: postBankProfile done"; } else { ns.addExtra("bank_profile_id", app.bankProfileId); ns.patchBankProfile(); qDebug() << "[Ozon] createBankProfileIfNeeded: patchBankProfile done"; } + return {}; } bool CommonScript::isAppRunning(const QString &deviceId, const QString &packageName) { diff --git a/android/ozon/CommonScript.h b/android/ozon/CommonScript.h index 4641002..8afcb6c 100644 --- a/android/ozon/CommonScript.h +++ b/android/ozon/CommonScript.h @@ -63,8 +63,9 @@ protected: const QString &deviceId, int width, int height ); - // Создаёт bank_profile на сервере если ещё не создан - void createBankProfileIfNeeded(const QString &deviceId, const QString &appCode, double parsedBalance = -1.0); + // Создаёт bank_profile на сервере если ещё не создан. + // Возвращает пустую строку при успехе, текст ошибки при провале. + QString createBankProfileIfNeeded(const QString &deviceId, const QString &appCode, double parsedBalance = -1.0); // Проверяет что приложение ещё запущено на устройстве static bool isAppRunning(const QString &deviceId, const QString &packageName); diff --git a/android/ozon/GetCardInfoScript.cpp b/android/ozon/GetCardInfoScript.cpp index 016d613..d8cb0bd 100644 --- a/android/ozon/GetCardInfoScript.cpp +++ b/android/ozon/GetCardInfoScript.cpp @@ -152,17 +152,30 @@ void GetCardInfoScript::doStart() { 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; - if (!MaterialDAO::upsertAccount(card)) { - qWarning() << "[GetCardInfo] Failed to upsert account for card" << card.lastNumbers; + + 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] Upserted account:" << card.lastNumbers - << card.description << card.amount; + qDebug() << "[GetCardInfo] Refreshed amount for card" + << card.lastNumbers << "to" << card.amount; } } @@ -226,9 +239,11 @@ void GetCardInfoScript::doStart() { AppLogger::log("ozon/GetCardInfo", m_deviceId, err, AdbUtils::captureScreenshotBytes(m_deviceId), "FETCH_PROFILE"); - } else { - qDebug() << "[GetCardInfo] postBankProfile done"; + m_error = err; + emit finishedWithResult(m_error); + return; } + qDebug() << "[GetCardInfo] postBankProfile done"; } else { ns.addExtra("bank_profile_id", freshApp.bankProfileId); ns.patchBankProfile(); @@ -398,19 +413,20 @@ void GetCardInfoScript::doStart() { if (!cardNumber.isEmpty()) { qDebug() << "[GetCardInfo] Card" << card.lastNumbers << "full number:" << cardNumber; - // Получаем id аккаунта из БД и сохраняем номер карты - MaterialInfo saved = MaterialDAO::findAppAccount(device.id, m_appCode, card.lastNumbers); - if (saved.id != -1) { - MaterialDAO::updateCardNumber(saved.id, 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; - MaterialInfo saved = MaterialDAO::findAppAccount(device.id, m_appCode, card.lastNumbers); - if (saved.id != -1) { - MaterialDAO::updateAccountById(saved.id, materialStatusToString(MaterialStatus::Off), - saved.description, saved.currency); - } + // Без полного номера материал в БД не создаём. AppLogger::log("ozon/GetCardInfo", m_deviceId, err, AdbUtils::captureScreenshotBytes(m_deviceId), "FETCH_PROFILE"); diff --git a/android/ozon/GetProfileInfoScript.cpp b/android/ozon/GetProfileInfoScript.cpp index 851353e..0273976 100644 --- a/android/ozon/GetProfileInfoScript.cpp +++ b/android/ozon/GetProfileInfoScript.cpp @@ -174,9 +174,11 @@ void GetProfileInfoScript::doStart() { AppLogger::log("ozon/GetProfileInfo", m_deviceId, err, AdbUtils::captureScreenshotBytes(m_deviceId), "FETCH_PROFILE"); - } else { - qDebug() << "[GetProfileInfo] postBankProfile done"; + m_error = err; + emit finishedWithResult(m_error); + return; } + qDebug() << "[GetProfileInfo] postBankProfile done"; } else { ns.addExtra("bank_profile_id", freshApp.bankProfileId); ns.patchBankProfile(); diff --git a/android/ozon/LoginAndCheckAccountsScript.cpp b/android/ozon/LoginAndCheckAccountsScript.cpp index 377f4e1..258c7d5 100644 --- a/android/ozon/LoginAndCheckAccountsScript.cpp +++ b/android/ozon/LoginAndCheckAccountsScript.cpp @@ -76,8 +76,31 @@ void LoginAndCheckAccountsScript::doStart() { BankProfileDAO::updateAmount(app.id, parsedBalance); } + // Освежаем amount только для уже существующих материалов (cardNumber/materialId + // и прочие поля сохраняем). Новые карты здесь не создаём — запись появится + // только после успешного OCR полного номера в GetCardInfoScript. + // Если ниже goToAllProducts + parseAccountsInfo отработают — они уточнят + // per-card сумму поверх. + for (const MaterialInfo &card : parsedCards) { + 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 = QDateTime::currentDateTimeUtc(); + if (!MaterialDAO::upsertAccount(refresh)) { + qWarning() << "[LoginAndCheck] Failed to refresh amount for card" << card.lastNumbers; + } else { + qDebug() << "[LoginAndCheck] Refreshed amount for card" + << card.lastNumbers << "to" << card.amount; + } + } + // Создаём/обновляем bank profile на сервере - createBankProfileIfNeeded(device.id, m_appCode, parsedBalance); + if (const QString err = createBankProfileIfNeeded(device.id, m_appCode, parsedBalance); !err.isEmpty()) { + m_error = err; + emit finishedWithResult(m_error); + return; + } if (goToAllProducts(m_deviceId, device.screenWidth, device.screenHeight)) { QList > accounts = xmlScreenParser.parseAccountsInfo(); diff --git a/android/ozon/ScreenXmlParser.cpp b/android/ozon/ScreenXmlParser.cpp index eccb5ed..0d1e12b 100644 --- a/android/ozon/ScreenXmlParser.cpp +++ b/android/ozon/ScreenXmlParser.cpp @@ -426,11 +426,19 @@ Node ScreenXmlParser::findCardButtonOnHomeScreen() { Node ScreenXmlParser::findCardButtonByLastNumbers(const QString &lastNumbers) { for (const Node &node : m_nodes) { if (node.className != "android.widget.Button" || !node.clickable) continue; - if (node.text.trimmed().endsWith(lastNumbers)) { - qDebug() << "[findCardBtn] Found by lastNumbers" << lastNumbers - << "text=" << node.text << "at" << node.x() << node.y(); - return node; + const QString t = node.text.trimmed(); + if (!t.endsWith(lastNumbers)) continue; + // Отсекаем строки из ленты переводов (см. parseCardsOnHomeScreen) + if (t.contains(QString::fromUtf8("Переводы")) + || t.contains(QStringLiteral("**")) + || t.contains(QChar(0x2212)) || t.contains(QChar('+'))) { + qDebug() << "[findCardBtn] Skipping transfer-feed row for lastNumbers" + << lastNumbers << "text=" << node.text; + continue; } + qDebug() << "[findCardBtn] Found by lastNumbers" << lastNumbers + << "text=" << node.text << "at" << node.x() << node.y(); + return node; } qWarning() << "[findCardBtn] Not found for lastNumbers=" << lastNumbers << ". Clickable buttons:"; @@ -514,13 +522,26 @@ QList ScreenXmlParser::parseCardsOnHomeScreen() { if (commonAmount == 0.0) qWarning() << "[parseCards] Balance button not found or amount=0"; - // 2. Собираем все кнопки карт (текст заканчивается на 4 цифры) + // 2. Собираем все кнопки карт (текст заканчивается на 4 цифры). + // Записи из ленты переводов выглядят как кнопки и тоже заканчиваются на 4 цифры + // ("Евгений Дмитриевич Р. − 100 ₽ Переводы • Карта **1092") — отсеиваем по маркерам. QList cards; for (const Node &node : m_nodes) { if (node.className != "android.widget.Button" || !node.clickable) continue; QString t = node.text.trimmed(); if (!endsWithFourDigits.match(t).hasMatch()) continue; + // Маркеры записи из ленты переводов: + // - слово "Переводы" + // - маска "**" перед цифрами + // - знак суммы транзакции (минус U+2212 или плюс) + if (t.contains(QString::fromUtf8("Переводы")) + || t.contains(QStringLiteral("**")) + || t.contains(QChar(0x2212)) || t.contains(QChar('+'))) { + qDebug() << "[parseCards] Skipping transfer-feed row:" << t; + continue; + } + MaterialInfo acc; acc.lastNumbers = t.right(4); acc.amount = commonAmount;