Add bank profile and phone metadata for improved task tracking and error reporting. Update TaskDumpMeta structure and associated scripts to include bankProfileId and profilePhone. Enhance LogArchiveSender and monitoring outputs for better operator identification and debugging.
This commit is contained in:
parent
65d3b00bb9
commit
8d86d5f835
@ -37,7 +37,7 @@ void GetCardInfoScript::doStart() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TaskDumpMeta dumpMeta{};
|
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("black", "FETCH_CARDS", m_deviceId, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("black", "FETCH_CARDS", m_deviceId, dumpMeta, &m_error);
|
||||||
|
|
||||||
// Читаем текущий экран
|
// Читаем текущий экран
|
||||||
|
|||||||
@ -35,7 +35,7 @@ void GetProfileInfoScript::doStart() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TaskDumpMeta dumpMeta{};
|
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("black", "FETCH_PROFILE_INFO", m_deviceId, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("black", "FETCH_PROFILE_INFO", m_deviceId, dumpMeta, &m_error);
|
||||||
|
|
||||||
// Читаем текущий экран
|
// Читаем текущий экран
|
||||||
|
|||||||
@ -36,7 +36,7 @@ void LoginAndCheckAccountsScript::doStart() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TaskDumpMeta dumpMeta{};
|
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("black", "FETCH_PROFILE", m_deviceId, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("black", "FETCH_PROFILE", m_deviceId, dumpMeta, &m_error);
|
||||||
|
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||||
|
|||||||
@ -166,22 +166,30 @@ TaskDumpRecorder::TaskDumpRecorder(const QString &bank, const QString &taskKind,
|
|||||||
}
|
}
|
||||||
|
|
||||||
QString TaskDumpRecorder::buildCaption(const QString &errorMessage) const {
|
QString TaskDumpRecorder::buildCaption(const QString &errorMessage) const {
|
||||||
const QString apiToken = SettingsDAO::get("api_key");
|
|
||||||
const QString timestamp = QDateTime::currentDateTime().toString(Qt::ISODate);
|
const QString timestamp = QDateTime::currentDateTime().toString(Qt::ISODate);
|
||||||
|
|
||||||
|
// api-токен в caption больше не пишем: он общий на нескольких операторов и
|
||||||
|
// не позволяет отличить, чей профиль упал. Идентифицируем по UUID профиля,
|
||||||
|
// материалу и телефону профиля (см. m_meta).
|
||||||
QStringList head{QStringLiteral("[TASK_ERROR]")};
|
QStringList head{QStringLiteral("[TASK_ERROR]")};
|
||||||
if (!apiToken.isEmpty())
|
|
||||||
head << QStringLiteral("[%1]").arg(apiToken.toHtmlEscaped());
|
|
||||||
head << QStringLiteral("[%1]").arg(m_bank.toHtmlEscaped());
|
head << QStringLiteral("[%1]").arg(m_bank.toHtmlEscaped());
|
||||||
head << QStringLiteral("[%1]").arg(m_taskKind.toHtmlEscaped());
|
head << QStringLiteral("[%1]").arg(m_taskKind.toHtmlEscaped());
|
||||||
|
|
||||||
QString body = QStringLiteral("Device: %1").arg(m_adbDeviceIdRaw.toHtmlEscaped());
|
const QString desktopId = SettingsDAO::get("desktop_id");
|
||||||
|
QString body;
|
||||||
|
if (!desktopId.isEmpty())
|
||||||
|
body += QStringLiteral("Desktop: %1\n").arg(desktopId.toHtmlEscaped());
|
||||||
|
body += QStringLiteral("Device: %1").arg(m_adbDeviceIdRaw.toHtmlEscaped());
|
||||||
|
if (!m_meta.bankProfileId.isEmpty())
|
||||||
|
body += QStringLiteral("\nProfile: %1").arg(m_meta.bankProfileId.toHtmlEscaped());
|
||||||
if (!m_meta.materialId.isEmpty())
|
if (!m_meta.materialId.isEmpty())
|
||||||
body += QStringLiteral("\nMaterial: %1").arg(m_meta.materialId.toHtmlEscaped());
|
body += QStringLiteral("\nMaterial: %1").arg(m_meta.materialId.toHtmlEscaped());
|
||||||
|
if (!m_meta.profilePhone.isEmpty())
|
||||||
|
body += QStringLiteral("\nPhone: %1").arg(m_meta.profilePhone.toHtmlEscaped());
|
||||||
if (m_meta.amount != 0)
|
if (m_meta.amount != 0)
|
||||||
body += QStringLiteral("\nAmount: %1").arg(static_cast<qint64>(m_meta.amount));
|
body += QStringLiteral("\nAmount: %1").arg(static_cast<qint64>(m_meta.amount));
|
||||||
if (!m_meta.phone.isEmpty())
|
if (!m_meta.phone.isEmpty())
|
||||||
body += QStringLiteral("\nPhone: %1").arg(m_meta.phone.toHtmlEscaped());
|
body += QStringLiteral("\nRecipient: %1").arg(m_meta.phone.toHtmlEscaped());
|
||||||
if (!m_meta.card.isEmpty())
|
if (!m_meta.card.isEmpty())
|
||||||
body += QStringLiteral("\nCard: %1").arg(m_meta.card.toHtmlEscaped());
|
body += QStringLiteral("\nCard: %1").arg(m_meta.card.toHtmlEscaped());
|
||||||
body += QStringLiteral("\nError: %1").arg(errorMessage.left(800).toHtmlEscaped());
|
body += QStringLiteral("\nError: %1").arg(errorMessage.left(800).toHtmlEscaped());
|
||||||
@ -238,9 +246,13 @@ void TaskDumpRecorder::finishInternal(const QString &errorMessage) {
|
|||||||
if (ef.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
if (ef.open(QIODevice::WriteOnly | QIODevice::Truncate)) {
|
||||||
QTextStream ts(&ef);
|
QTextStream ts(&ef);
|
||||||
ts.setEncoding(QStringConverter::Utf8);
|
ts.setEncoding(QStringConverter::Utf8);
|
||||||
|
const QString desktopId = SettingsDAO::get("desktop_id");
|
||||||
|
if (!desktopId.isEmpty()) ts << "desktop_id: " << desktopId << "\n";
|
||||||
ts << "bank: " << m_bank << "\n"
|
ts << "bank: " << m_bank << "\n"
|
||||||
<< "task: " << m_taskKind << "\n"
|
<< "task: " << m_taskKind << "\n"
|
||||||
<< "device: " << m_adbDeviceIdRaw << "\n";
|
<< "device: " << m_adbDeviceIdRaw << "\n";
|
||||||
|
if (!m_meta.bankProfileId.isEmpty()) ts << "profile_id: " << m_meta.bankProfileId << "\n";
|
||||||
|
if (!m_meta.profilePhone.isEmpty()) ts << "profile_phone: " << m_meta.profilePhone << "\n";
|
||||||
if (!m_meta.materialId.isEmpty()) ts << "material_id: " << m_meta.materialId << "\n";
|
if (!m_meta.materialId.isEmpty()) ts << "material_id: " << m_meta.materialId << "\n";
|
||||||
if (m_meta.amount != 0) ts << "amount: " << static_cast<qint64>(m_meta.amount) << "\n";
|
if (m_meta.amount != 0) ts << "amount: " << static_cast<qint64>(m_meta.amount) << "\n";
|
||||||
if (!m_meta.phone.isEmpty()) ts << "phone: " << m_meta.phone << "\n";
|
if (!m_meta.phone.isEmpty()) ts << "phone: " << m_meta.phone << "\n";
|
||||||
@ -255,6 +267,17 @@ void TaskDumpRecorder::finishInternal(const QString &errorMessage) {
|
|||||||
const QString archivePath = m_folder + ".zip";
|
const QString archivePath = m_folder + ".zip";
|
||||||
const QString text = buildCaption(errorMessage);
|
const QString text = buildCaption(errorMessage);
|
||||||
|
|
||||||
|
// Тот же идентификатор профиля/материала/телефона добавляем в caption архива
|
||||||
|
// логов ([LOGS]), чтобы по общему api-токену можно было разделить операторов.
|
||||||
|
QStringList logsIdLines;
|
||||||
|
if (!m_meta.bankProfileId.isEmpty())
|
||||||
|
logsIdLines << QStringLiteral("Profile: %1").arg(m_meta.bankProfileId.toHtmlEscaped());
|
||||||
|
if (!m_meta.materialId.isEmpty())
|
||||||
|
logsIdLines << QStringLiteral("Material: %1").arg(m_meta.materialId.toHtmlEscaped());
|
||||||
|
if (!m_meta.profilePhone.isEmpty())
|
||||||
|
logsIdLines << QStringLiteral("Phone: %1").arg(m_meta.profilePhone.toHtmlEscaped());
|
||||||
|
const QString logsIdentity = logsIdLines.join(QStringLiteral("\n"));
|
||||||
|
|
||||||
// Финальный скриншот шлём отдельным полем `image`, поэтому сразу читаем
|
// Финальный скриншот шлём отдельным полем `image`, поэтому сразу читаем
|
||||||
// его в память, пока папка ещё цела.
|
// его в память, пока папка ещё цела.
|
||||||
QByteArray finalScreenshot;
|
QByteArray finalScreenshot;
|
||||||
@ -265,7 +288,7 @@ void TaskDumpRecorder::finishInternal(const QString &errorMessage) {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
QThread *bg = QThread::create([folder, archivePath, text, finalScreenshot]() {
|
QThread *bg = QThread::create([folder, archivePath, text, finalScreenshot, logsIdentity]() {
|
||||||
QByteArray archiveBytes;
|
QByteArray archiveBytes;
|
||||||
if (zipFolder(folder, archivePath)) {
|
if (zipFolder(folder, archivePath)) {
|
||||||
archiveBytes = readFileBytes(archivePath);
|
archiveBytes = readFileBytes(archivePath);
|
||||||
@ -301,7 +324,7 @@ void TaskDumpRecorder::finishInternal(const QString &errorMessage) {
|
|||||||
// Следом — application.log + БД (+ WAL/SHM) отдельным архивом.
|
// Следом — application.log + БД (+ WAL/SHM) отдельным архивом.
|
||||||
// Запускаем уже ПОСЛЕ того, как task-дамп поставлен в очередь
|
// Запускаем уже ПОСЛЕ того, как task-дамп поставлен в очередь
|
||||||
// отправки, чтобы порядок прихода на relay был "дамп → логи".
|
// отправки, чтобы порядок прихода на relay был "дамп → логи".
|
||||||
LogArchiveSender::dispatch(QStringLiteral("TASK_ERROR"));
|
LogArchiveSender::dispatch(QStringLiteral("TASK_ERROR"), -1, logsIdentity);
|
||||||
});
|
});
|
||||||
QObject::connect(bg, &QThread::finished, bg, &QObject::deleteLater);
|
QObject::connect(bg, &QThread::finished, bg, &QObject::deleteLater);
|
||||||
bg->start();
|
bg->start();
|
||||||
|
|||||||
@ -9,8 +9,10 @@ class NetworkService;
|
|||||||
struct TaskDumpMeta {
|
struct TaskDumpMeta {
|
||||||
QString materialId;
|
QString materialId;
|
||||||
double amount = 0; // 0 = нет
|
double amount = 0; // 0 = нет
|
||||||
QString phone; // пусто = нет
|
QString phone; // пусто = нет (телефон получателя в транзакции)
|
||||||
QString card; // пусто = нет
|
QString card; // пусто = нет
|
||||||
|
QString bankProfileId; // серверный UUID банковского профиля (детект в общих логах)
|
||||||
|
QString profilePhone; // телефон банковского профиля (не получателя)
|
||||||
};
|
};
|
||||||
|
|
||||||
class TaskDumpRecorder {
|
class TaskDumpRecorder {
|
||||||
|
|||||||
@ -36,7 +36,7 @@ void GetCardInfoScript::doStart() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TaskDumpMeta dumpMeta{};
|
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_CARDS", m_deviceId, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_CARDS", m_deviceId, dumpMeta, &m_error);
|
||||||
|
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||||
|
|||||||
@ -276,7 +276,7 @@ void GetLastTransactionsScript::doStart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const double firstAmount = m_targets.isEmpty() ? 0.0 : m_targets.first().first;
|
const double firstAmount = m_targets.isEmpty() ? 0.0 : m_targets.first().first;
|
||||||
const TaskDumpMeta dumpMeta{m_materialId, firstAmount, {}, {}};
|
const TaskDumpMeta dumpMeta{m_materialId, firstAmount, {}, {}, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_TRANSACTIONS", m_deviceId, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_TRANSACTIONS", m_deviceId, dumpMeta, &m_error);
|
||||||
|
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||||
|
|||||||
@ -39,7 +39,7 @@ void GetProfileInfoScript::doStart() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TaskDumpMeta dumpMeta{};
|
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_PROFILE_INFO", m_deviceId, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_PROFILE_INFO", m_deviceId, dumpMeta, &m_error);
|
||||||
|
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||||
|
|||||||
@ -41,7 +41,7 @@ void LoginAndCheckAccountsScript::doStart() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TaskDumpMeta dumpMeta{};
|
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_PROFILE", m_deviceId, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_PROFILE", m_deviceId, dumpMeta, &m_error);
|
||||||
|
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||||
|
|||||||
@ -123,7 +123,7 @@ void PayByCardScript::doStart() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, {}, m_cardNumber};
|
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, {}, m_cardNumber, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("dushanbe", "CREATE_TRANSACTION_CARD", device.adbSerial, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("dushanbe", "CREATE_TRANSACTION_CARD", device.adbSerial, dumpMeta, &m_error);
|
||||||
|
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.adbSerial, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.adbSerial, ++m_counter));
|
||||||
|
|||||||
@ -120,7 +120,7 @@ void PayByPhoneScript::doStart() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, m_phone, {}};
|
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, m_phone, {}, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("dushanbe", "CREATE_TRANSACTION_PHONE", device.adbSerial, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("dushanbe", "CREATE_TRANSACTION_PHONE", device.adbSerial, dumpMeta, &m_error);
|
||||||
|
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.adbSerial, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.adbSerial, ++m_counter));
|
||||||
|
|||||||
@ -42,7 +42,7 @@ void GetCardInfoScript::doStart() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TaskDumpMeta dumpMeta{};
|
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("ozon", "FETCH_CARDS", m_deviceId, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("ozon", "FETCH_CARDS", m_deviceId, dumpMeta, &m_error);
|
||||||
|
|
||||||
// Читаем текущий экран
|
// Читаем текущий экран
|
||||||
|
|||||||
@ -109,7 +109,7 @@ void GetLastTransactionsScript::doStart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const double firstAmount = m_targets.isEmpty() ? 0.0 : m_targets.first().first;
|
const double firstAmount = m_targets.isEmpty() ? 0.0 : m_targets.first().first;
|
||||||
const TaskDumpMeta dumpMeta{m_materialId, firstAmount, {}, {}};
|
const TaskDumpMeta dumpMeta{m_materialId, firstAmount, {}, {}, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("ozon", "FETCH_TRANSACTIONS", m_deviceId, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("ozon", "FETCH_TRANSACTIONS", m_deviceId, dumpMeta, &m_error);
|
||||||
|
|
||||||
if (!navigateToTransactionList(device, app)) {
|
if (!navigateToTransactionList(device, app)) {
|
||||||
|
|||||||
@ -37,7 +37,7 @@ void GetProfileInfoScript::doStart() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TaskDumpMeta dumpMeta{};
|
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("ozon", "FETCH_PROFILE_INFO", m_deviceId, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("ozon", "FETCH_PROFILE_INFO", m_deviceId, dumpMeta, &m_error);
|
||||||
|
|
||||||
// Читаем текущий экран
|
// Читаем текущий экран
|
||||||
|
|||||||
@ -40,7 +40,7 @@ void LoginAndCheckAccountsScript::doStart() {
|
|||||||
return;
|
return;
|
||||||
}
|
}
|
||||||
|
|
||||||
const TaskDumpMeta dumpMeta{};
|
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("ozon", "FETCH_PROFILE", m_deviceId, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("ozon", "FETCH_PROFILE", m_deviceId, dumpMeta, &m_error);
|
||||||
|
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||||
|
|||||||
@ -91,7 +91,7 @@ void PayByCardScript::doStart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QString &adbId = device.adbSerial;
|
const QString &adbId = device.adbSerial;
|
||||||
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, {}, m_cardNumber};
|
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, {}, m_cardNumber, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("ozon", "CREATE_TRANSACTION_CARD", adbId, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("ozon", "CREATE_TRANSACTION_CARD", adbId, dumpMeta, &m_error);
|
||||||
|
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(adbId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(adbId, ++m_counter));
|
||||||
|
|||||||
@ -100,7 +100,7 @@ void PayByPhoneScript::doStart() {
|
|||||||
}
|
}
|
||||||
|
|
||||||
const QString &adbId = device.adbSerial;
|
const QString &adbId = device.adbSerial;
|
||||||
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, m_phone, {}};
|
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, m_phone, {}, app.bankProfileId, app.phone};
|
||||||
TaskDumpRecorder::Scope dumpScope("ozon", "CREATE_TRANSACTION_PHONE", adbId, dumpMeta, &m_error);
|
TaskDumpRecorder::Scope dumpScope("ozon", "CREATE_TRANSACTION_PHONE", adbId, dumpMeta, &m_error);
|
||||||
|
|
||||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(adbId, ++m_counter));
|
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(adbId, ++m_counter));
|
||||||
|
|||||||
@ -34,7 +34,7 @@
|
|||||||
#include "dushanbe/PayByPhoneScript.h"
|
#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 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 = {});
|
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 sendMonitoringLog(const QString &type, const QString &event, const QString &message, const QByteArray &image = {}, int eventId = -1, bool toApiBase = false);
|
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) {}
|
EventHandler::EventHandler(QObject *parent) : QObject(parent) {}
|
||||||
@ -78,7 +78,7 @@ void EventHandler::start() {
|
|||||||
if (e.sentToServer) continue;
|
if (e.sentToServer) continue;
|
||||||
EventDAO::updateEvent(e.id, EventStatus::Error, "App restarted");
|
EventDAO::updateEvent(e.id, EventStatus::Error, "App restarted");
|
||||||
EventDAO::markSentToServer(e.id);
|
EventDAO::markSentToServer(e.id);
|
||||||
sendTaskError(eventTypeToString(e.type), e.externalId, e.bankName, "App restarted, event was pending", {}, -1, e.transactionId);
|
sendTaskError(eventTypeToString(e.type), e.externalId, e.bankName, "App restarted, event was pending", {}, -1, e.transactionId, e.accountId);
|
||||||
qDebug() << "[EventHandler] Closed stale event id:" << e.id;
|
qDebug() << "[EventHandler] Closed stale event id:" << e.id;
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -127,7 +127,7 @@ void EventHandler::onTasksReceived(const QJsonArray &tasks) {
|
|||||||
if (eventType == EventType::FetchProfile) {
|
if (eventType == EventType::FetchProfile) {
|
||||||
const QList<EventInfo> cancelled = EventDAO::cancelWaitingByMaterial(materialId, EventType::FetchProfile);
|
const QList<EventInfo> cancelled = EventDAO::cancelWaitingByMaterial(materialId, EventType::FetchProfile);
|
||||||
for (const EventInfo &c : cancelled) {
|
for (const EventInfo &c : cancelled) {
|
||||||
sendTaskError(eventTypeToString(c.type), c.externalId, c.bankName, "Cancelled: replaced by new task", {}, -1, c.transactionId);
|
sendTaskError(eventTypeToString(c.type), c.externalId, c.bankName, "Cancelled: replaced by new task", {}, -1, c.transactionId, c.accountId);
|
||||||
EventDAO::markSentToServer(c.id);
|
EventDAO::markSentToServer(c.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@ -168,7 +168,7 @@ void EventHandler::onTasksReceived(const QJsonArray &tasks) {
|
|||||||
if (!onlineDevices.contains(account.deviceId)) {
|
if (!onlineDevices.contains(account.deviceId)) {
|
||||||
qWarning() << "[EventHandler] Device offline for material_id:" << materialId << "device:" << account.deviceId;
|
qWarning() << "[EventHandler] Device offline for material_id:" << materialId << "device:" << account.deviceId;
|
||||||
EventDAO::updateEvent(eventId, EventStatus::Error, "Device is offline");
|
EventDAO::updateEvent(eventId, EventStatus::Error, "Device is offline");
|
||||||
sendTaskError(typeStr, materialId, bankName, "Device is offline", {}, -1, incomingTransactionId);
|
sendTaskError(typeStr, materialId, bankName, "Device is offline", {}, -1, incomingTransactionId, account.id);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -215,7 +215,7 @@ static void sendTaskResult(const QString &type, const QString &materialId, const
|
|||||||
|
|
||||||
static void sendTaskError(const QString &type, const QString &materialId, const QString &bankName,
|
static void sendTaskError(const QString &type, const QString &materialId, const QString &bankName,
|
||||||
const QString &description, const QByteArray &screenshot, int eventId,
|
const QString &description, const QByteArray &screenshot, int eventId,
|
||||||
const QString &transactionId) {
|
const QString &transactionId, int accountId) {
|
||||||
QJsonObject errorData;
|
QJsonObject errorData;
|
||||||
errorData["status"] = QStringLiteral("error");
|
errorData["status"] = QStringLiteral("error");
|
||||||
errorData["event_type"] = type;
|
errorData["event_type"] = type;
|
||||||
@ -224,18 +224,36 @@ static void sendTaskError(const QString &type, const QString &materialId, const
|
|||||||
errorData["transaction_id"] = transactionId;
|
errorData["transaction_id"] = transactionId;
|
||||||
sendTaskResult(QStringLiteral("ERROR"), materialId, bankName, errorData, eventId);
|
sendTaskResult(QStringLiteral("ERROR"), materialId, bankName, errorData, eventId);
|
||||||
|
|
||||||
if (!screenshot.isEmpty()) {
|
// Шлём в мониторинг всегда — даже без скриншота (offline / expired /
|
||||||
const QString apiToken = SettingsDAO::get("api_key");
|
// disabled / account not found и т.п.), чтобы такие ошибки не «молчали» на
|
||||||
|
// релее. Скриншот прикладываем, если он есть.
|
||||||
|
// api-токен общий на нескольких операторов — по нему не отличить, чей
|
||||||
|
// профиль упал. Резолвим банковский профиль по accountId и пишем его
|
||||||
|
// серверный UUID + материал + телефон профиля.
|
||||||
|
QString profileUuid, profilePhone;
|
||||||
|
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;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
const QString desktopId = SettingsDAO::get("desktop_id");
|
||||||
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 (!apiToken.isEmpty())
|
if (!desktopId.isEmpty())
|
||||||
lines << QStringLiteral("<b>[%1]</b>").arg(apiToken.toHtmlEscaped());
|
lines << QStringLiteral("Desktop: %1").arg(desktopId.toHtmlEscaped());
|
||||||
lines << QStringLiteral("<b>[%1]</b>").arg(materialId.toHtmlEscaped());
|
if (!profileUuid.isEmpty())
|
||||||
lines << description.toHtmlEscaped();
|
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());
|
||||||
|
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);
|
||||||
}
|
|
||||||
|
|
||||||
// application.log + БД отправляются из TaskDumpRecorder::finishInternal,
|
// application.log + БД отправляются из TaskDumpRecorder::finishInternal,
|
||||||
// чтобы покрывать также non-event-driven задачи (login flow и т.п.).
|
// чтобы покрывать также non-event-driven задачи (login flow и т.п.).
|
||||||
@ -359,7 +377,7 @@ void EventHandler::updateEventThread(const QString &key, const EventInfo &event,
|
|||||||
.arg(eventTypeToString(event.type), deviceLabel, event.bankName, result);
|
.arg(eventTypeToString(event.type), deviceLabel, event.bankName, result);
|
||||||
GeneralLogDAO::insert("CRITICAL", "EventHandler", logMsg, screenshot);
|
GeneralLogDAO::insert("CRITICAL", "EventHandler", logMsg, screenshot);
|
||||||
|
|
||||||
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, result, screenshot, event.id, event.transactionId);
|
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, result, screenshot, event.id, event.transactionId, event.accountId);
|
||||||
|
|
||||||
if (event.type == EventType::CreateTransaction) {
|
if (event.type == EventType::CreateTransaction) {
|
||||||
const MaterialInfo declAcc = MaterialDAO::getAccountById(event.accountId);
|
const MaterialInfo declAcc = MaterialDAO::getAccountById(event.accountId);
|
||||||
@ -750,7 +768,7 @@ void EventHandler::startThreadForTask(const MaterialInfo &account, const EventIn
|
|||||||
const QString logMsg = QString("[%1] bank=%2 material=%3: Script timeout")
|
const QString logMsg = QString("[%1] bank=%2 material=%3: Script timeout")
|
||||||
.arg(eventTypeToString(event.type), event.bankName, event.externalId);
|
.arg(eventTypeToString(event.type), event.bankName, event.externalId);
|
||||||
GeneralLogDAO::insert("CRITICAL", "EventHandler", logMsg);
|
GeneralLogDAO::insert("CRITICAL", "EventHandler", logMsg);
|
||||||
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, "Script timeout (5 min)", {}, -1, event.transactionId);
|
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, "Script timeout (5 min)", {}, -1, event.transactionId, event.accountId);
|
||||||
EventDAO::markSentToServer(event.id);
|
EventDAO::markSentToServer(event.id);
|
||||||
|
|
||||||
// Убиваем банковское приложение на устройстве после таймаута
|
// Убиваем банковское приложение на устройстве после таймаута
|
||||||
@ -787,7 +805,7 @@ void EventHandler::processNextTask(const QString &deviceId) {
|
|||||||
if (waitSecs > 5 * 60) {
|
if (waitSecs > 5 * 60) {
|
||||||
qWarning() << "[EventHandler] Task expired after" << waitSecs << "s, event id:" << event.id;
|
qWarning() << "[EventHandler] Task expired after" << waitSecs << "s, event id:" << event.id;
|
||||||
EventDAO::updateEvent(event.id, EventStatus::Error, "Task expired: waited " + QString::number(waitSecs) + "s");
|
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", {}, -1, event.transactionId);
|
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName, "Task expired: not started within 5 minutes", {}, -1, event.transactionId, event.accountId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -797,7 +815,7 @@ void EventHandler::processNextTask(const QString &deviceId) {
|
|||||||
qWarning() << "[EventHandler] Bank profile disabled for" << account.appCode << "device:" << deviceId;
|
qWarning() << "[EventHandler] Bank profile disabled for" << account.appCode << "device:" << deviceId;
|
||||||
EventDAO::updateEvent(event.id, EventStatus::Error, "Bank profile is disabled");
|
EventDAO::updateEvent(event.id, EventStatus::Error, "Bank profile is disabled");
|
||||||
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName,
|
sendTaskError(eventTypeToString(event.type), event.externalId, event.bankName,
|
||||||
"Bank profile is disabled: " + account.appCode, {}, -1, event.transactionId);
|
"Bank profile is disabled: " + account.appCode, {}, -1, event.transactionId, event.accountId);
|
||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@ -90,16 +90,22 @@ QByteArray buildArchive() {
|
|||||||
|
|
||||||
} // namespace
|
} // namespace
|
||||||
|
|
||||||
bool LogArchiveSender::dispatch(const QString &label, int eventId) {
|
bool LogArchiveSender::dispatch(const QString &label, int eventId, const QString &extraCaptionHtml) {
|
||||||
const QString apiToken = SettingsDAO::get("api_key");
|
const QString apiToken = SettingsDAO::get("api_key");
|
||||||
const QDateTime now = QDateTime::currentDateTime();
|
const QDateTime now = QDateTime::currentDateTime();
|
||||||
const QString timestamp = now.toString(Qt::ISODate);
|
const QString timestamp = now.toString(Qt::ISODate);
|
||||||
|
|
||||||
|
// api-токен в caption больше не пишем (общий на нескольких операторов);
|
||||||
|
// идентификация идёт по Profile/Material/Phone из extraCaptionHtml.
|
||||||
|
const QString desktopId = SettingsDAO::get("desktop_id");
|
||||||
QStringList head{QStringLiteral("[LOGS]")};
|
QStringList head{QStringLiteral("[LOGS]")};
|
||||||
if (!apiToken.isEmpty()) head << QStringLiteral("[%1]").arg(apiToken.toHtmlEscaped());
|
|
||||||
if (!label.isEmpty()) head << QStringLiteral("[%1]").arg(label.toHtmlEscaped());
|
if (!label.isEmpty()) head << QStringLiteral("[%1]").arg(label.toHtmlEscaped());
|
||||||
const QString text = QStringLiteral("<b>%1</b>\n%2")
|
QString text = QStringLiteral("<b>%1</b>\n%2")
|
||||||
.arg(head.join(QStringLiteral(" ")), timestamp.toHtmlEscaped());
|
.arg(head.join(QStringLiteral(" ")), timestamp.toHtmlEscaped());
|
||||||
|
if (!desktopId.isEmpty())
|
||||||
|
text += QStringLiteral("\nDesktop: %1").arg(desktopId.toHtmlEscaped());
|
||||||
|
if (!extraCaptionHtml.isEmpty())
|
||||||
|
text += QStringLiteral("\n") + extraCaptionHtml;
|
||||||
|
|
||||||
QString tokenSafe = apiToken;
|
QString tokenSafe = apiToken;
|
||||||
tokenSafe.replace(QRegularExpression(R"([^\w\-.])"), "_");
|
tokenSafe.replace(QRegularExpression(R"([^\w\-.])"), "_");
|
||||||
|
|||||||
@ -10,7 +10,10 @@ namespace LogArchiveSender {
|
|||||||
// в собственных background-потоках (worker для zip + thread для HTTP).
|
// в собственных background-потоках (worker для zip + thread для HTTP).
|
||||||
// `label` — короткий тег ("TASK_ERROR" и т.п.), попадает в caption и в имя файла.
|
// `label` — короткий тег ("TASK_ERROR" и т.п.), попадает в caption и в имя файла.
|
||||||
// `eventId` (если > 0) тоже уходит в имя файла для трассировки.
|
// `eventId` (если > 0) тоже уходит в имя файла для трассировки.
|
||||||
|
// `extraCaptionHtml` (если не пусто) — дополнительные строки caption'а
|
||||||
|
// (Profile/Material/Phone), уже экранированные; нужны чтобы по общему
|
||||||
|
// api-токену различать операторов.
|
||||||
// Возвращает true если задача поставлена в очередь; реальный результат отправки
|
// Возвращает true если задача поставлена в очередь; реальный результат отправки
|
||||||
// не отслеживается (fire-and-forget, как у sendMonitoringLog).
|
// не отслеживается (fire-and-forget, как у sendMonitoringLog).
|
||||||
bool dispatch(const QString &label, int eventId = -1);
|
bool dispatch(const QString &label, int eventId = -1, const QString &extraCaptionHtml = {});
|
||||||
}
|
}
|
||||||
|
|||||||
Loading…
Reference in New Issue
Block a user