Add taskJson parameter to sendTaskError, introduce sendTaskSuccess, enhance error and success logging with detailed JSON data, and refine time mismatch handling.
This commit is contained in:
parent
66279d4767
commit
cd98644f32
@ -740,13 +740,15 @@ void GetLastTransactionsScript::searchAll(
|
||||
}
|
||||
}
|
||||
|
||||
// Проверка времени по HH:MM:SS (±10 минут, с учётом wraparound полуночи)
|
||||
// Проверка времени по HH:MM:SS (+10 минут, одностороннее окно, с учётом wraparound полуночи)
|
||||
if (cand.isValid() && !tx.time.isEmpty()) {
|
||||
const QTime listTime = QTime::fromString(tx.time, "HH:mm:ss");
|
||||
const QTime searchLocalTime = cand.toTimeZone(tjTz).time();
|
||||
if (listTime.isValid() && searchLocalTime.isValid()) {
|
||||
const int diffSecs = qAbs(listTime.secsTo(searchLocalTime));
|
||||
if (diffSecs > 600 && diffSecs < 85800) continue; // 85800 = 23h50m
|
||||
int diffSecs = searchLocalTime.secsTo(listTime); // >0 = listTime (tx) позже цели
|
||||
if (diffSecs < -43200) diffSecs += 86400; // wraparound через полночь вперёд
|
||||
else if (diffSecs > 43200) diffSecs -= 86400; // wraparound назад
|
||||
if (diffSecs < 0 || diffSecs > 600) continue; // tx раньше цели или позже неё на >10 мин
|
||||
}
|
||||
}
|
||||
|
||||
@ -915,7 +917,7 @@ void GetLastTransactionsScript::searchAll(
|
||||
<< "date=" << detail.date << "time=" << detail.time
|
||||
<< "amount=" << detail.amount << "receiver=" << detail.receiverAccount;
|
||||
|
||||
// Проверяем время ±10 минут
|
||||
// Проверяем время: txTime не раньше цели и не позже неё на 10 минут (+10м, одностороннее окно)
|
||||
if (searchTime.isValid() && !detail.date.isEmpty() && !detail.time.isEmpty()) {
|
||||
QDateTime txTime = QDateTime::fromString(
|
||||
detail.date + " " + detail.time, "dd.MM.yyyy HH:mm:ss");
|
||||
@ -924,9 +926,9 @@ void GetLastTransactionsScript::searchAll(
|
||||
}
|
||||
if (txTime.isValid()) {
|
||||
txTime.setTimeZone(tjTz);
|
||||
const qint64 diffSecs = qAbs(txTime.toUTC().secsTo(searchTime.toUTC()));
|
||||
if (diffSecs > 600) {
|
||||
qDebug() << "[Dushanbe::GetLastTransactions] Time mismatch: diff=" << diffSecs << "sec (>10min), skipping";
|
||||
const qint64 diffSecs = searchTime.toUTC().secsTo(txTime.toUTC()); // >0 = txTime позже цели
|
||||
if (diffSecs < 0 || diffSecs > 600) {
|
||||
qDebug() << "[Dushanbe::GetLastTransactions] Time mismatch: diff=" << diffSecs << "sec (out of +10min window), skipping";
|
||||
AdbUtils::goBack(m_deviceId);
|
||||
QThread::msleep(1000);
|
||||
continue;
|
||||
@ -956,9 +958,15 @@ void GetLastTransactionsScript::searchAll(
|
||||
if (txTime.isValid()) txTime.setTimeZone(tjTz);
|
||||
|
||||
QJsonObject txJson;
|
||||
txJson["bank_transaction_id"] = detail.operationId.isEmpty()
|
||||
? QString("dushanbe_") + detail.date + "_" + detail.time
|
||||
: detail.operationId;
|
||||
// Фолбэк-id (когда «Номер операции» не распарсился) строим из
|
||||
// created_at задачи — он детерминирован и стабилен между запусками,
|
||||
// что важно для дедупликации на сервере. Раньше брали распарсенные
|
||||
// с экрана дату+время, которые могли «плавать» из-за OCR.
|
||||
txJson["bank_transaction_id"] = !detail.operationId.isEmpty()
|
||||
? detail.operationId
|
||||
: (searchTime.isValid()
|
||||
? QStringLiteral("dushanbe_") + searchTime.toUTC().toString(Qt::ISODate)
|
||||
: QStringLiteral("dushanbe_") + detail.date + "_" + detail.time);
|
||||
// Списание хранится с отрицательным знаком, пополнение — положительным.
|
||||
// detail.amount всегда положителен (OCR), знак берём из списка Выписки.
|
||||
const double signedAmount = (tx.amount < 0 ? -1.0 : 1.0) * qAbs(finalAmount);
|
||||
@ -971,7 +979,11 @@ void GetLastTransactionsScript::searchAll(
|
||||
QJsonObject extraObj;
|
||||
extraObj["fee"] = static_cast<int>(finalFee * 100);
|
||||
txJson["extra"] = extraObj;
|
||||
txJson["created_at"] = txTime.isValid()
|
||||
// created_at — время из задачи (поисковый target), real_created_at —
|
||||
// фактическое время транзакции, спарсенное с экрана приложения.
|
||||
txJson["created_at"] = searchTime.isValid()
|
||||
? searchTime.toUTC().toString(Qt::ISODate) : QJsonValue(QJsonValue::Null);
|
||||
txJson["real_created_at"] = txTime.isValid()
|
||||
? txTime.toUTC().toString(Qt::ISODate) : QJsonValue(QJsonValue::Null);
|
||||
|
||||
qDebug() << "[Dushanbe::GetLastTransactions] Emitting parsed tx:"
|
||||
|
||||
@ -618,10 +618,10 @@ bool GetLastTransactionsScript::searchOne(const DeviceInfo &device, int accountI
|
||||
}
|
||||
}
|
||||
|
||||
// Проверяем время (±10 минут)
|
||||
// Проверяем время: txTime не раньше цели и не позже неё на 10 минут (+10м, одностороннее окно)
|
||||
if (txTime.isValid() && searchTime.isValid()) {
|
||||
const qint64 diffSecs = qAbs(txTime.toUTC().secsTo(searchTime.toUTC()));
|
||||
if (diffSecs > 600) {
|
||||
const qint64 diffSecs = searchTime.toUTC().secsTo(txTime.toUTC()); // >0 = txTime позже цели
|
||||
if (diffSecs < 0 || diffSecs > 600) {
|
||||
qDebug() << "[FetchTransactions] Time mismatch:" << dtStr
|
||||
<< "expected" << searchLocal.toString("dd.MM.yyyy, HH:mm")
|
||||
<< "diff=" << diffSecs << "sec";
|
||||
@ -644,6 +644,16 @@ bool GetLastTransactionsScript::searchOne(const DeviceInfo &device, int accountI
|
||||
|
||||
QString externalId = detail.bankTrExternalId;
|
||||
|
||||
// Фолбэк-id (когда реальный ID банка не распарсился) строим из
|
||||
// created_at задачи — он детерминирован и стабилен между запусками,
|
||||
// что важно для дедупликации на сервере. Раньше брали распарсенное
|
||||
// с экрана время (dtStr), которое могло «плавать» из-за OCR.
|
||||
const QString finalExternalId = externalId.isEmpty()
|
||||
? QStringLiteral("ozon_") + (searchTime.isValid()
|
||||
? searchTime.toUTC().toString(Qt::ISODate)
|
||||
: QString(dtStr).remove(',').trimmed().replace(' ', '_'))
|
||||
: externalId;
|
||||
|
||||
// Сохраняем
|
||||
if (accountId != -1) {
|
||||
TransactionInfo tx;
|
||||
@ -651,9 +661,7 @@ bool GetLastTransactionsScript::searchOne(const DeviceInfo &device, int accountI
|
||||
tx.amount = parsed.amount;
|
||||
tx.bankName = m_appCode;
|
||||
tx.bankTime = dtStr;
|
||||
tx.bankTrExternalId = externalId.isEmpty()
|
||||
? QString("ozon_") + QString(dtStr).remove(',').trimmed().replace(' ', '_')
|
||||
: externalId;
|
||||
tx.bankTrExternalId = finalExternalId;
|
||||
tx.status = TransactionStatus::Complete;
|
||||
tx.timestamp = DateUtils::getUtcNow();
|
||||
if (txTime.isValid()) tx.completeTime = txTime;
|
||||
@ -671,10 +679,6 @@ bool GetLastTransactionsScript::searchOne(const DeviceInfo &device, int accountI
|
||||
|
||||
// Отдаём результат в EventHandler через сигнал (как Dushanbe)
|
||||
{
|
||||
const QString finalExternalId = externalId.isEmpty()
|
||||
? QString("ozon_") + QString(dtStr).remove(',').trimmed().replace(' ', '_')
|
||||
: externalId;
|
||||
|
||||
QJsonObject txJson;
|
||||
txJson["bank_transaction_id"] = finalExternalId;
|
||||
txJson["amount"] = static_cast<int>(parsed.amount * 100);
|
||||
@ -687,7 +691,11 @@ bool GetLastTransactionsScript::searchOne(const DeviceInfo &device, int accountI
|
||||
QJsonObject extraObj;
|
||||
extraObj["fee"] = 0;
|
||||
txJson["extra"] = extraObj;
|
||||
txJson["created_at"] = txTime.isValid()
|
||||
// created_at — время из задачи (поисковый target), real_created_at —
|
||||
// фактическое время транзакции, спарсенное с экрана приложения.
|
||||
txJson["created_at"] = searchTime.isValid()
|
||||
? searchTime.toUTC().toString(Qt::ISODate) : QJsonValue(QJsonValue::Null);
|
||||
txJson["real_created_at"] = txTime.isValid()
|
||||
? txTime.toUTC().toString(Qt::ISODate) : QJsonValue(QJsonValue::Null);
|
||||
|
||||
qDebug() << "[FetchTransactions] Emitting parsed tx:"
|
||||
|
||||
@ -21,6 +21,8 @@ pathDevice=/api/v1/device/
|
||||
pathDeviceScreenshot=/api/v1/device/add_screenshot/
|
||||
pathMaterial=/api/v1/profile/material
|
||||
pathProfilePing=/api/v1/profile/ping
|
||||
pathGetTasks=/api/v1/task/get_tasks
|
||||
pathTaskResult=/api/v1/task/task_result
|
||||
pathMonitoringLog=/monitoring/log
|
||||
pathMonitoringDump=/monitoring/dump
|
||||
pathMonitoringScreenshot=/monitoring/screenshot
|
||||
|
||||
@ -35,7 +35,7 @@
|
||||
#include "dushanbe/PayByPhoneScript.h"
|
||||
|
||||
static void sendTaskResult(const QString &type, const QString &materialId, const QString &bankName, const QJsonValue &data, int eventId = -1, bool updateStatus = true);
|
||||
static void sendTaskError(const QString &type, const QString &materialId, const QString &bankName, const QString &description, const QByteArray &screenshot = {}, int eventId = -1, const QString &transactionId = {}, int accountId = -1);
|
||||
static void sendTaskError(const QString &type, const QString &materialId, const QString &bankName, const QString &description, const QByteArray &screenshot = {}, int eventId = -1, const QString &transactionId = {}, int accountId = -1, const QString &taskJson = {});
|
||||
static void sendMonitoringLog(const QString &type, const QString &event, const QString &message, const QByteArray &image = {}, int eventId = -1, bool toApiBase = false);
|
||||
|
||||
EventHandler::EventHandler(QObject *parent) : QObject(parent) {}
|
||||
@ -79,7 +79,7 @@ void EventHandler::start() {
|
||||
if (e.sentToServer) continue;
|
||||
EventDAO::updateEvent(e.id, EventStatus::Error, "App restarted");
|
||||
EventDAO::markSentToServer(e.id);
|
||||
sendTaskError(eventTypeToString(e.type), e.externalId, e.bankName, "App restarted, event was pending", {}, e.id, e.transactionId, e.accountId);
|
||||
sendTaskError(eventTypeToString(e.type), e.externalId, e.bankName, "App restarted, event was pending", {}, e.id, e.transactionId, e.accountId, e.json);
|
||||
qDebug() << "[EventHandler] Closed stale event id:" << e.id;
|
||||
}
|
||||
}
|
||||
@ -120,7 +120,7 @@ void EventHandler::onTasksReceived(const QJsonArray &tasks) {
|
||||
const MaterialInfo account = MaterialDAO::findByMaterialId(materialId);
|
||||
if (account.id == -1) {
|
||||
qWarning() << "[EventHandler] Account not found for bank:" << bankName << "material_id:" << materialId;
|
||||
sendTaskError(typeStr, materialId, bankName, "Account not found for material_id: " + materialId, {}, -1, incomingTransactionId);
|
||||
sendTaskError(typeStr, materialId, bankName, "Account not found for material_id: " + materialId, {}, -1, incomingTransactionId, -1, QString::fromUtf8(QJsonDocument(task).toJson(QJsonDocument::Compact)));
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -128,7 +128,7 @@ void EventHandler::onTasksReceived(const QJsonArray &tasks) {
|
||||
if (eventType == EventType::FetchProfile) {
|
||||
const QList<EventInfo> cancelled = EventDAO::cancelWaitingByMaterial(materialId, EventType::FetchProfile);
|
||||
for (const EventInfo &c : cancelled) {
|
||||
sendTaskError(eventTypeToString(c.type), c.externalId, c.bankName, "Cancelled: replaced by new task", {}, c.id, c.transactionId, c.accountId);
|
||||
sendTaskError(eventTypeToString(c.type), c.externalId, c.bankName, "Cancelled: replaced by new task", {}, c.id, c.transactionId, c.accountId, c.json);
|
||||
EventDAO::markSentToServer(c.id);
|
||||
}
|
||||
}
|
||||
@ -169,7 +169,7 @@ void EventHandler::onTasksReceived(const QJsonArray &tasks) {
|
||||
if (!onlineDevices.contains(account.deviceId)) {
|
||||
qWarning() << "[EventHandler] Device offline for material_id:" << materialId << "device:" << account.deviceId;
|
||||
EventDAO::updateEvent(eventId, EventStatus::Error, "Device is offline");
|
||||
sendTaskError(typeStr, materialId, bankName, "Device is offline", {}, eventId, incomingTransactionId, account.id);
|
||||
sendTaskError(typeStr, materialId, bankName, "Device is offline", {}, eventId, incomingTransactionId, account.id, event.json);
|
||||
}
|
||||
}
|
||||
|
||||
@ -216,7 +216,7 @@ static void sendTaskResult(const QString &type, const QString &materialId, const
|
||||
|
||||
static void sendTaskError(const QString &type, const QString &materialId, const QString &bankName,
|
||||
const QString &description, const QByteArray &screenshot, int eventId,
|
||||
const QString &transactionId, int accountId) {
|
||||
const QString &transactionId, int accountId, const QString &taskJson) {
|
||||
QJsonObject errorData;
|
||||
errorData["status"] = QStringLiteral("error");
|
||||
errorData["event_type"] = type;
|
||||
@ -261,6 +261,15 @@ static void sendTaskError(const QString &type, const QString &materialId, const
|
||||
if (!appVersion.isEmpty())
|
||||
lines << QStringLiteral("AppVersion: %1").arg(appVersion.toHtmlEscaped());
|
||||
lines << QStringLiteral("Error: %1").arg(description.toHtmlEscaped());
|
||||
// JSON задачи, пришедшей с сервера (pretty-print). НЕ экранируем — админка
|
||||
// (_render_msg) экранирует один раз и выносит блок после «JSON: » в <pre>.
|
||||
if (!taskJson.isEmpty()) {
|
||||
const QJsonDocument doc = QJsonDocument::fromJson(taskJson.toUtf8());
|
||||
const QString pretty = doc.isNull()
|
||||
? taskJson
|
||||
: QString::fromUtf8(doc.toJson(QJsonDocument::Indented));
|
||||
lines << QStringLiteral("JSON: %1").arg(pretty);
|
||||
}
|
||||
const QString message = lines.join(QStringLiteral("\n"));
|
||||
sendMonitoringLog("error", "task_error", message, screenshot, eventId);
|
||||
|
||||
@ -268,6 +277,50 @@ static void sendTaskError(const QString &type, const QString &materialId, const
|
||||
// чтобы покрывать также non-event-driven задачи (login flow и т.п.).
|
||||
}
|
||||
|
||||
// Успешное завершение задачи в arc-monitoring (мониторинговый дроплет, не apiBase).
|
||||
// Бизнес-результат уходит отдельно (sendTaskResult / per-tx streaming) — здесь
|
||||
// только человекочитаемый лог в админку с теми же метаданными, что у task_error
|
||||
// (Desktop/Profile/Material/Phone/AppVersion), чтобы корректно парсились и
|
||||
// фильтровались. `summary` — операционные детали (Amount/Found/…).
|
||||
static void sendTaskSuccess(const QString &type, const QString &materialId, const QString &bankName,
|
||||
const QString &summary, const QByteArray &screenshot, int eventId,
|
||||
int accountId) {
|
||||
QString profileUuid, profilePhone, appVersion;
|
||||
if (accountId > 0) {
|
||||
const MaterialInfo acc = MaterialDAO::getAccountById(accountId);
|
||||
if (acc.id != -1) {
|
||||
const BankProfileInfo app = BankProfileDAO::getApplication(acc.deviceId, acc.appCode);
|
||||
profileUuid = app.bankProfileId;
|
||||
profilePhone = app.phone;
|
||||
appVersion = app.appVersion;
|
||||
}
|
||||
}
|
||||
const QString desktopId = SettingsDAO::get("desktop_id");
|
||||
const QString clientVersion =
|
||||
QSettings("config.ini", QSettings::IniFormat).value("common/version").toString();
|
||||
QStringList lines;
|
||||
lines << QStringLiteral("<b>[TASK_SUCCESS] [%1] [%2]</b>")
|
||||
.arg(type.toHtmlEscaped(), bankName.toUpper().toHtmlEscaped());
|
||||
if (!desktopId.isEmpty())
|
||||
lines << QStringLiteral("Desktop: %1").arg(desktopId.toHtmlEscaped());
|
||||
if (!clientVersion.isEmpty())
|
||||
lines << QStringLiteral("DesktopVersion: %1").arg(clientVersion.toHtmlEscaped());
|
||||
if (!profileUuid.isEmpty())
|
||||
lines << QStringLiteral("Profile: %1").arg(profileUuid.toHtmlEscaped());
|
||||
if (!materialId.isEmpty())
|
||||
lines << QStringLiteral("Material: %1").arg(materialId.toHtmlEscaped());
|
||||
if (!profilePhone.isEmpty())
|
||||
lines << QStringLiteral("Phone: %1").arg(profilePhone.toHtmlEscaped());
|
||||
if (!appVersion.isEmpty())
|
||||
lines << QStringLiteral("AppVersion: %1").arg(appVersion.toHtmlEscaped());
|
||||
// summary содержит JSON — НЕ экранируем: админка (_render_msg) экранирует
|
||||
// ровно один раз сама. Двойной toHtmlEscaped превратил бы " в " в тексте.
|
||||
if (!summary.isEmpty())
|
||||
lines << summary;
|
||||
const QString message = lines.join(QStringLiteral("\n"));
|
||||
sendMonitoringLog("info", "task_success", message, screenshot, eventId, /*toApiBase=*/false);
|
||||
}
|
||||
|
||||
static void sendMonitoringLog(const QString &type, const QString &event, const QString &message,
|
||||
const QByteArray &image, int eventId, bool toApiBase) {
|
||||
auto *thread = new QThread;
|
||||
@ -327,6 +380,7 @@ bool EventHandler::tryRetry(const QString &key, const EventInfo &event, const QS
|
||||
// счётчик отправленных в 0.
|
||||
m_screenshots.remove(key);
|
||||
m_parsedTxCount.remove(key);
|
||||
m_parsedTxJson.remove(key);
|
||||
|
||||
// Снимаем текущий поток/таймер (в timeout-ветке они уже сняты — take() вернёт null)
|
||||
if (const auto tm = m_timers.take(key)) {
|
||||
@ -397,7 +451,7 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
|
||||
.arg(eventTypeToString(event.type), deviceLabel, event.bankName, result);
|
||||
GeneralLogDAO::insert("CRITICAL", "EventHandler", logMsg, screenshot);
|
||||
|
||||
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, result, screenshot, event.id, event.transactionId, event.accountId);
|
||||
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, result, screenshot, event.id, event.transactionId, event.accountId, event.json);
|
||||
} else {
|
||||
qDebug() << "[EventHandler] Interrupted — server error report skipped (cause already reported by interrupter), event id:" << event.id;
|
||||
}
|
||||
@ -445,6 +499,9 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
|
||||
EventDAO::markSentToServer(event.id);
|
||||
}
|
||||
|
||||
// JSON результата, ушедшего на apiBase — кладём в текст task_success ниже.
|
||||
QString apiResultJson;
|
||||
|
||||
if (newStatus != EventStatus::Error) {
|
||||
const MaterialInfo account = MaterialDAO::getAccountById(event.accountId);
|
||||
const QMap<QString, int> currencyCodes = {
|
||||
@ -505,6 +562,8 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
|
||||
obj["extra"] = extraObj;
|
||||
|
||||
taskData = obj;
|
||||
apiResultJson = QString::fromUtf8(
|
||||
QJsonDocument(obj).toJson(QJsonDocument::Indented));
|
||||
} else {
|
||||
QJsonObject obj;
|
||||
obj["status"] = "completed";
|
||||
@ -528,8 +587,10 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
|
||||
const QString adbSerial = device.adbSerial.isEmpty() ? event.deviceId : device.adbSerial;
|
||||
|
||||
if (event.type == EventType::CreateTransaction) {
|
||||
const QString msg = QStringLiteral("[CREATE_TRANSACTION] bank=%1 material=%2 amount=%3 phone=%4")
|
||||
QString msg = QStringLiteral("[CREATE_TRANSACTION] bank=%1 material=%2 amount=%3 phone=%4")
|
||||
.arg(event.bankName, event.externalId, QString::number(event.amount, 'f', 2), event.phone);
|
||||
if (!apiResultJson.isEmpty())
|
||||
msg += QStringLiteral("\nJSON: %1").arg(apiResultJson);
|
||||
QByteArray screenshot;
|
||||
const QString tmpPath = QDir::tempPath() + "/arc_tx_" + QString::number(event.id);
|
||||
if (AdbUtils::takeScreenshot(event.deviceId, tmpPath)) {
|
||||
@ -541,6 +602,14 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
|
||||
f.remove();
|
||||
}
|
||||
sendMonitoringLog("info", "task_success", msg, screenshot, event.id, /*toApiBase=*/true);
|
||||
// Дублируем успех в arc-monitoring (см. sendTaskSuccess) — туда же,
|
||||
// куда уходят ошибки, чтобы видеть и удачные создания транзакций.
|
||||
QString createSummary = QStringLiteral("Amount: %1\nRecipient: %2")
|
||||
.arg(QString::number(event.amount, 'f', 2), event.phone);
|
||||
if (!apiResultJson.isEmpty())
|
||||
createSummary += QStringLiteral("\nJSON: %1").arg(apiResultJson);
|
||||
sendTaskSuccess("CREATE_TRANSACTION", event.externalId, event.bankName,
|
||||
createSummary, screenshot, event.id, event.accountId);
|
||||
|
||||
// Декрементируем материальный/профильный баланс на сумму расхода + комиссию.
|
||||
// Между запусками FETCH_PROFILE это держит UI/локальный баланс
|
||||
@ -586,10 +655,22 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
|
||||
}
|
||||
|
||||
if (event.type == EventType::FetchTransactions) {
|
||||
const QString msg = QStringLiteral("[FETCH_TRANSACTIONS] bank=%1 material=%2")
|
||||
.arg(event.bankName, event.externalId);
|
||||
// JSON для текста: массив транзакций, отправленных потоково на apiBase.
|
||||
const QJsonArray txArr = m_parsedTxJson.value(key);
|
||||
const QString txJson = QString::fromUtf8(
|
||||
QJsonDocument(txArr).toJson(QJsonDocument::Indented));
|
||||
QString msg = QStringLiteral("[FETCH_TRANSACTIONS] bank=%1 material=%2 found=%3")
|
||||
.arg(event.bankName, event.externalId, QString::number(txArr.size()));
|
||||
if (!txArr.isEmpty())
|
||||
msg += QStringLiteral("\nJSON: %1").arg(txJson);
|
||||
const QByteArray scr = m_screenshots.take(key);
|
||||
sendMonitoringLog("info", "task_success", msg, scr, event.id, /*toApiBase=*/true);
|
||||
// Дублируем успех в arc-monitoring с теми же метаданными, что у ошибок.
|
||||
QString fetchSummary = QStringLiteral("Found: %1").arg(txArr.size());
|
||||
if (!txArr.isEmpty())
|
||||
fetchSummary += QStringLiteral("\nJSON: %1").arg(txJson);
|
||||
sendTaskSuccess("FETCH_TRANSACTIONS", event.externalId, event.bankName,
|
||||
fetchSummary, scr, event.id, event.accountId);
|
||||
}
|
||||
|
||||
if (event.type == EventType::FetchProfile) {
|
||||
@ -608,6 +689,7 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
|
||||
m_threads.remove(key);
|
||||
m_retryCount.remove(event.id);
|
||||
m_parsedTxCount.remove(key);
|
||||
m_parsedTxJson.remove(key);
|
||||
|
||||
// Убиваем банковское приложение на устройстве после завершения задачи
|
||||
{
|
||||
@ -707,6 +789,7 @@ void EventHandler::startThreadForTask(const MaterialInfo &account, const EventIn
|
||||
// ретраить уже частично отработанную задачу.
|
||||
auto streamParsedTx = [this, key, event](const QJsonObject &tx) {
|
||||
m_parsedTxCount[key] = m_parsedTxCount.value(key, 0) + 1;
|
||||
m_parsedTxJson[key].append(tx); // копим для показа JSON в task_success
|
||||
QJsonArray arr;
|
||||
arr.append(tx);
|
||||
sendTaskResult(eventTypeToString(EventType::FetchTransactions),
|
||||
@ -796,7 +879,7 @@ void EventHandler::startThreadForTask(const MaterialInfo &account, const EventIn
|
||||
const QString logMsg = QString("[%1] bank=%2 material=%3: Script timeout")
|
||||
.arg(eventTypeToString(event.type), event.bankName, event.externalId);
|
||||
GeneralLogDAO::insert("CRITICAL", "EventHandler", logMsg);
|
||||
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, timeoutMsg, {}, event.id, event.transactionId, event.accountId);
|
||||
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, timeoutMsg, {}, event.id, event.transactionId, event.accountId, event.json);
|
||||
EventDAO::markSentToServer(event.id);
|
||||
|
||||
// Убиваем банковское приложение на устройстве после таймаута
|
||||
@ -833,7 +916,7 @@ void EventHandler::processNextTask(const QString &deviceId) {
|
||||
if (waitSecs > 5 * 60) {
|
||||
qWarning() << "[EventHandler] Task expired after" << waitSecs << "s, event id:" << event.id;
|
||||
EventDAO::updateEvent(event.id, EventStatus::Error, "Task expired: waited " + QString::number(waitSecs) + "s");
|
||||
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, "Task expired: not started within 5 minutes", {}, event.id, event.transactionId, event.accountId);
|
||||
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, "Task expired: not started within 5 minutes", {}, event.id, event.transactionId, event.accountId, event.json);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -843,7 +926,7 @@ void EventHandler::processNextTask(const QString &deviceId) {
|
||||
qWarning() << "[EventHandler] Bank profile disabled for" << account.appCode << "device:" << deviceId;
|
||||
EventDAO::updateEvent(event.id, EventStatus::Error, "Bank profile is disabled");
|
||||
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName,
|
||||
"Bank profile is disabled: " + account.appCode, {}, event.id, event.transactionId, event.accountId);
|
||||
"Bank profile is disabled: " + account.appCode, {}, event.id, event.transactionId, event.accountId, event.json);
|
||||
continue;
|
||||
}
|
||||
|
||||
@ -886,6 +969,7 @@ void EventHandler::clearAll() {
|
||||
m_timers.clear();
|
||||
m_screenshots.clear();
|
||||
m_parsedTxCount.clear();
|
||||
m_parsedTxJson.clear();
|
||||
}
|
||||
|
||||
void EventHandler::stop() {
|
||||
|
||||
@ -35,6 +35,7 @@ private:
|
||||
QMap<QString, QTimer *> m_timers;
|
||||
QMap<QString, QByteArray> m_screenshots; // deviceId → скриншот от скрипта
|
||||
QMap<QString, int> m_parsedTxCount; // key → сколько транзакций уже отправлено на сервер (FETCH_TRANSACTIONS, per-tx streaming)
|
||||
QMap<QString, QJsonArray> m_parsedTxJson; // key → сами отправленные tx (для показа JSON в task_success мониторинга)
|
||||
QMap<int, int> m_retryCount; // event.id → число уже сделанных ретраев (только FETCH_TRANSACTIONS)
|
||||
QTimer *m_pollTimer = nullptr;
|
||||
NetworkService *m_network = nullptr;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user