1
0
forked from BRT/arc

Include appVersion and clientVersion in error reporting for task failures. Add fallback logic for bottom navigation taps in GetLastTransactionsScript using icon-based detection.

This commit is contained in:
slava 2026-06-02 21:56:42 +05:00
parent b486e922b3
commit 3391591e33
2 changed files with 54 additions and 29 deletions

View File

@ -1,7 +1,6 @@
#include "GetLastTransactionsScript.h" #include "GetLastTransactionsScript.h"
#include <QJsonDocument> #include <QJsonDocument>
#include <QHash>
#include <QRegularExpression> #include <QRegularExpression>
#include <QSet> #include <QSet>
#include <QThread> #include <QThread>
@ -437,34 +436,51 @@ void GetLastTransactionsScript::doStart() {
} }
QPoint GetLastTransactionsScript::bottomNavPoint(const QString &label, const DeviceInfo &device) { QPoint GetLastTransactionsScript::bottomNavPoint(const QString &label, const DeviceInfo &device) {
const Node n = xmlScreenParser.findNodeByContentDesc(label); // 1) Норма: у таба есть реальные bounds в нижней полосе экрана. Проверка
// Реальные bounds принимаем только если они «вменяемые» для НИЖНЕГО меню: // «центр в нижних 80% высоты» отсекает и вырожденные [0,0][0,0], и
// непустой узел, ненулевой центр И центр в нижней полосе экрана (>= 80% // НЕнулевые-но-негодные координаты (одноимённый элемент не в нав-баре).
// высоты). Это отсекает не только вырожденные [0,0][0,0] (Infinix XOS), но и const Node lbl = xmlScreenParser.findNodeByContentDesc(label);
// НЕнулевые, но негодные координаты — напр. одноимённый элемент не в нав-баре if (!lbl.isEmpty() && lbl.x() > 0
// или частичные bounds. Иначе нав-бар у самого низа, центр таба ~95% высоты. && lbl.y() >= static_cast<int>(device.screenHeight * 0.80))
if (!n.isEmpty() && n.x() > 0 && n.y() >= static_cast<int>(device.screenHeight * 0.80)) return {lbl.x(), lbl.y()};
return {n.x(), n.y()};
// Узел нав-таба отсутствует или негоден. На части устройств // 2) Лейбл нав-таба вырожден ([0,0][0,0] — гибридный UI, напр. Infinix XOS не
// (наблюдалось на Infinix X669D, app 3.2.41 — гибридный UI) uiautomator не // отдаёт bounds нижнего меню). Но ИКОНКИ табов имеют реальные координаты.
// отдаёт bounds нижнего меню, хотя оно отрисовано в полосе у самого низа // Берём самый нижний ряд небольших нод и в нём крайнюю по X: История —
// экрана (ниже всего дерева доступности). Тапаем по геометрии: 5 равных // самая правая иконка, Главная — самая левая (FAB по центру никогда не
// слотов — Главная · Переводы · [FAB] · MiniApps · История; центр слота — // крайний; контентные плитки выше нижнего ряда и отсекаются по y2). Это
// доля ширины, высота — у самого низа экрана. // фактические координаты → не зависит от размера экрана, плотности и
static const QHash<QString, double> kFx{ // высоты системной навигации Android (её фикс-высота в dp ломала бы доли).
{QString::fromUtf8("Главная"), 0.10}, const bool rightmost = (label == QString::fromUtf8("История"));
{QString::fromUtf8("Переводы"), 0.30}, const bool leftmost = (label == QString::fromUtf8("Главная"));
{QString::fromUtf8("MiniApps"), 0.70}, if (rightmost || leftmost) {
{QString::fromUtf8("История"), 0.90}, const int H = device.screenHeight, W = device.screenWidth;
}; const auto nodes = xmlScreenParser.findAllNodes();
const auto it = kFx.constFind(label); auto isNavIcon = [&](const Node &n) {
if (it == kFx.constEnd()) return {-1, -1}; return n.x1() >= 0 && n.width() > 0 && n.height() >= 12
const int x = static_cast<int>(device.screenWidth * it.value()); && n.width() <= W / 3 && n.y() >= H / 2; // не контейнер, нижняя половина
const int y = static_cast<int>(device.screenHeight * 0.95); };
qWarning() << "[Dushanbe::GetLastTransactions] nav tab" << label int rowBottom = -1;
<< "has no usable bounds — geometric fallback tap at" << x << y; for (const Node &n : nodes)
return {x, y}; if (isNavIcon(n)) rowBottom = qMax(rowBottom, n.y2());
if (rowBottom > 0) {
const int tol = qMax(8, H / 100); // «тот же ряд» — у самой нижней кромки
Node pick;
for (const Node &n : nodes) {
if (!isNavIcon(n) || n.y2() < rowBottom - tol) continue;
if (pick.isEmpty()
|| (rightmost && n.x() > pick.x())
|| (leftmost && n.x() < pick.x()))
pick = n;
}
if (!pick.isEmpty()) {
qWarning() << "[Dushanbe::GetLastTransactions] nav tab" << label
<< "— icon-row fallback tap at" << pick.x() << pick.y();
return {pick.x(), pick.y()};
}
}
}
return {-1, -1};
} }
void GetLastTransactionsScript::searchAll( void GetLastTransactionsScript::searchAll(

View File

@ -6,6 +6,7 @@
#include <QJsonObject> #include <QJsonObject>
#include <QJsonArray> #include <QJsonArray>
#include <QMap> #include <QMap>
#include <QSettings>
#include <QThread> #include <QThread>
#include <QTimer> #include <QTimer>
#include <QTimeZone> #include <QTimeZone>
@ -230,27 +231,35 @@ static void sendTaskError(const QString &type, const QString &materialId, const
// api-токен общий на нескольких операторов — по нему не отличить, чей // api-токен общий на нескольких операторов — по нему не отличить, чей
// профиль упал. Резолвим банковский профиль по accountId и пишем его // профиль упал. Резолвим банковский профиль по accountId и пишем его
// серверный UUID + материал + телефон профиля. // серверный UUID + материал + телефон профиля.
QString profileUuid, profilePhone; QString profileUuid, profilePhone, appVersion;
if (accountId > 0) { if (accountId > 0) {
const MaterialInfo acc = MaterialDAO::getAccountById(accountId); const MaterialInfo acc = MaterialDAO::getAccountById(accountId);
if (acc.id != -1) { if (acc.id != -1) {
const BankProfileInfo app = BankProfileDAO::getApplication(acc.deviceId, acc.appCode); const BankProfileInfo app = BankProfileDAO::getApplication(acc.deviceId, acc.appCode);
profileUuid = app.bankProfileId; profileUuid = app.bankProfileId;
profilePhone = app.phone; profilePhone = app.phone;
appVersion = app.appVersion;
} }
} }
const QString desktopId = SettingsDAO::get("desktop_id"); const QString desktopId = SettingsDAO::get("desktop_id");
// Версия самого ARC-десктопа (клиента), не банковского приложения.
const QString clientVersion =
QSettings("config.ini", QSettings::IniFormat).value("common/version").toString();
QStringList lines; QStringList lines;
lines << QStringLiteral("<b>[TASK_ERROR] [%1] [%2]</b>") lines << QStringLiteral("<b>[TASK_ERROR] [%1] [%2]</b>")
.arg(type.toHtmlEscaped(), bankName.toUpper().toHtmlEscaped()); .arg(type.toHtmlEscaped(), bankName.toUpper().toHtmlEscaped());
if (!desktopId.isEmpty()) if (!desktopId.isEmpty())
lines << QStringLiteral("Desktop: %1").arg(desktopId.toHtmlEscaped()); lines << QStringLiteral("Desktop: %1").arg(desktopId.toHtmlEscaped());
if (!clientVersion.isEmpty())
lines << QStringLiteral("DesktopVersion: %1").arg(clientVersion.toHtmlEscaped());
if (!profileUuid.isEmpty()) if (!profileUuid.isEmpty())
lines << QStringLiteral("Profile: %1").arg(profileUuid.toHtmlEscaped()); lines << QStringLiteral("Profile: %1").arg(profileUuid.toHtmlEscaped());
if (!materialId.isEmpty()) if (!materialId.isEmpty())
lines << QStringLiteral("Material: %1").arg(materialId.toHtmlEscaped()); lines << QStringLiteral("Material: %1").arg(materialId.toHtmlEscaped());
if (!profilePhone.isEmpty()) if (!profilePhone.isEmpty())
lines << QStringLiteral("Phone: %1").arg(profilePhone.toHtmlEscaped()); lines << QStringLiteral("Phone: %1").arg(profilePhone.toHtmlEscaped());
if (!appVersion.isEmpty())
lines << QStringLiteral("AppVersion: %1").arg(appVersion.toHtmlEscaped());
lines << QStringLiteral("Error: %1").arg(description.toHtmlEscaped()); lines << QStringLiteral("Error: %1").arg(description.toHtmlEscaped());
const QString message = lines.join(QStringLiteral("\n")); const QString message = lines.join(QStringLiteral("\n"));
sendMonitoringLog("error", "task_error", message, screenshot, eventId); sendMonitoringLog("error", "task_error", message, screenshot, eventId);