1
0
forked from BRT/arc

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.

This commit is contained in:
trnsmkot 2026-05-15 16:54:02 +07:00
parent 685b2de9ca
commit 93f286a8f8
12 changed files with 130 additions and 37 deletions

View File

@ -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<QString, int> 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() {

View File

@ -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. Безопасно

View File

@ -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<MaterialInfo> allAccounts = MaterialDAO::getAccounts(device.id, m_appCode);

View File

@ -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<QString, int> 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() {

View File

@ -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 если баннер был закрыт.

View File

@ -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<MaterialInfo> allAccounts = MaterialDAO::getAccounts(device.id, m_appCode);
QList<QPair<MaterialInfo, Node>> accountsToPost;

View File

@ -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<QString, int> 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) {

View File

@ -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);

View File

@ -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");

View File

@ -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();

View File

@ -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<QPair<MaterialInfo, Node> > accounts = xmlScreenParser.parseAccountsInfo();

View File

@ -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<MaterialInfo> ScreenXmlParser::parseCardsOnHomeScreen() {
if (commonAmount == 0.0)
qWarning() << "[parseCards] Balance button not found or amount=0";
// 2. Собираем все кнопки карт (текст заканчивается на 4 цифры)
// 2. Собираем все кнопки карт (текст заканчивается на 4 цифры).
// Записи из ленты переводов выглядят как кнопки и тоже заканчиваются на 4 цифры
// ("Евгений Дмитриевич Р. 100 ₽ Переводы • Карта **1092") — отсеиваем по маркерам.
QList<MaterialInfo> 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;