Add appVersion to TaskDumpMeta structure and update associated scripts for enhanced application version tracking in tasks and monitoring outputs.
This commit is contained in:
parent
f6d895ab34
commit
29ae105c64
@ -37,7 +37,7 @@ void GetCardInfoScript::doStart() {
|
||||
return;
|
||||
}
|
||||
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
TaskDumpRecorder::Scope dumpScope("black", "FETCH_CARDS", m_deviceId, dumpMeta, &m_error);
|
||||
|
||||
// Читаем текущий экран
|
||||
|
||||
@ -35,7 +35,7 @@ void GetProfileInfoScript::doStart() {
|
||||
return;
|
||||
}
|
||||
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
TaskDumpRecorder::Scope dumpScope("black", "FETCH_PROFILE_INFO", m_deviceId, dumpMeta, &m_error);
|
||||
|
||||
// Читаем текущий экран
|
||||
|
||||
@ -36,7 +36,7 @@ void LoginAndCheckAccountsScript::doStart() {
|
||||
return;
|
||||
}
|
||||
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
TaskDumpRecorder::Scope dumpScope("black", "FETCH_PROFILE", m_deviceId, dumpMeta, &m_error);
|
||||
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||
|
||||
@ -188,6 +188,8 @@ QString TaskDumpRecorder::buildCaption(const QString &errorMessage) const {
|
||||
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.appVersion.isEmpty())
|
||||
body += QStringLiteral("\nAppVersion: %1").arg(m_meta.appVersion.toHtmlEscaped());
|
||||
if (m_meta.amount != 0)
|
||||
body += QStringLiteral("\nAmount: %1").arg(static_cast<qint64>(m_meta.amount));
|
||||
if (!m_meta.phone.isEmpty())
|
||||
@ -256,6 +258,7 @@ void TaskDumpRecorder::finishInternal(const QString &errorMessage) {
|
||||
if (!m_meta.androidId.isEmpty()) ts << "android_id: " << m_meta.androidId << "\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.appVersion.isEmpty()) ts << "app_version: " << m_meta.appVersion << "\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.phone.isEmpty()) ts << "phone: " << m_meta.phone << "\n";
|
||||
|
||||
@ -14,6 +14,7 @@ struct TaskDumpMeta {
|
||||
QString bankProfileId; // серверный UUID банковского профиля (детект в общих логах)
|
||||
QString profilePhone; // телефон банковского профиля (не получателя)
|
||||
QString androidId; // стабильный android_id устройства (в отличие от adbSerial не меняется)
|
||||
QString appVersion; // версия банковского приложения (из pm dump versionName)
|
||||
};
|
||||
|
||||
class TaskDumpRecorder {
|
||||
|
||||
@ -36,7 +36,7 @@ void GetCardInfoScript::doStart() {
|
||||
return;
|
||||
}
|
||||
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_CARDS", m_deviceId, dumpMeta, &m_error);
|
||||
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||
|
||||
@ -278,7 +278,7 @@ void GetLastTransactionsScript::doStart() {
|
||||
}
|
||||
|
||||
const double firstAmount = m_targets.isEmpty() ? 0.0 : m_targets.first().first;
|
||||
const TaskDumpMeta dumpMeta{m_materialId, firstAmount, {}, {}, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{m_materialId, firstAmount, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_TRANSACTIONS", m_deviceId, dumpMeta, &m_error);
|
||||
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||
@ -407,12 +407,12 @@ void GetLastTransactionsScript::doStart() {
|
||||
// Проход идёт вниз от текущей позиции. Если цель не нашлась — возможно,
|
||||
// предыдущая (более новая) цель отсутствовала и увела список вниз, а эта
|
||||
// лежит выше. Тогда надёжно возвращаемся к началу Выписки и пробуем ещё раз.
|
||||
bool found = searchOne(device, accountId, amount, time);
|
||||
if (!found && m_error != "Interrupted") {
|
||||
SearchResult res = searchOne(device, accountId, amount, time);
|
||||
if (res == SearchResult::NotFound && m_error != "Interrupted") {
|
||||
scrollHistoryToTop(device);
|
||||
found = searchOne(device, accountId, amount, time);
|
||||
res = searchOne(device, accountId, amount, time);
|
||||
}
|
||||
if (!found) {
|
||||
if (res != SearchResult::Found) {
|
||||
if (m_error == "Interrupted") break;
|
||||
notFound << QString("amount=%1 time=%2")
|
||||
.arg(QString::number(amount, 'f', 2), time.toString(Qt::ISODate));
|
||||
@ -484,8 +484,9 @@ void GetLastTransactionsScript::scrollHistoryToTop(const DeviceInfo &device) {
|
||||
QThread::msleep(500);
|
||||
}
|
||||
|
||||
bool GetLastTransactionsScript::searchOne(const DeviceInfo &device, int accountId,
|
||||
double searchAmount, const QDateTime &searchTime) {
|
||||
GetLastTransactionsScript::SearchResult GetLastTransactionsScript::searchOne(
|
||||
const DeviceInfo &device, int accountId,
|
||||
double searchAmount, const QDateTime &searchTime) {
|
||||
// Скриншот списка транзакций — отправим при неудаче
|
||||
const QByteArray listScreenshot = AdbUtils::captureScreenshotBytes(m_deviceId);
|
||||
|
||||
@ -511,7 +512,7 @@ bool GetLastTransactionsScript::searchOne(const DeviceInfo &device, int accountI
|
||||
for (int scroll = 0; scroll <= maxScrolls; ++scroll) {
|
||||
if (interrupted()) {
|
||||
m_error = "Interrupted";
|
||||
return false;
|
||||
return SearchResult::NotFound;
|
||||
}
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
|
||||
@ -763,13 +764,13 @@ bool GetLastTransactionsScript::searchOne(const DeviceInfo &device, int accountI
|
||||
const int tapX = txNode.x1() + 80;
|
||||
AdbUtils::makeTap(m_deviceId, tapX, txNode.y());
|
||||
QThread::msleep(3000);
|
||||
if (interrupted()) { m_error = "Interrupted"; return false; }
|
||||
if (interrupted()) { m_error = "Interrupted"; return SearchResult::NotFound; }
|
||||
|
||||
// Ждём экран деталей Выписки — ImageView с content-desc начинающимся с "Успешный платеж"
|
||||
OcrTransactionDetail detail;
|
||||
bool detailLoaded = false;
|
||||
for (int attempt = 0; attempt < 10; ++attempt) {
|
||||
if (interrupted()) { m_error = "Interrupted"; return false; }
|
||||
if (interrupted()) { m_error = "Interrupted"; return SearchResult::NotFound; }
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||
closeGooglePlayBannerIfVisible(m_deviceId, device.screenWidth, device.screenHeight);
|
||||
// 1) Bottom-sheet формат (3.2.33, см. assets/dushanbe/3.2.33/tx_detail.xml):
|
||||
@ -975,7 +976,7 @@ bool GetLastTransactionsScript::searchOne(const DeviceInfo &device, int accountI
|
||||
}
|
||||
QThread::msleep(1000);
|
||||
|
||||
return true; // успех — транзакция найдена и отдана через transactionParsed
|
||||
return SearchResult::Found;
|
||||
}
|
||||
|
||||
// Окно поиска — только "сегодня + вчера". Список Выписки идёт сверху-вниз
|
||||
@ -997,13 +998,13 @@ bool GetLastTransactionsScript::searchOne(const DeviceInfo &device, int accountI
|
||||
" (newest visible=" << newestVisible.toString("dd.MM.yyyy")
|
||||
<< "< cutoff=" << cutoff.toString("dd.MM.yyyy")
|
||||
<< ") — stopping scroll";
|
||||
break;
|
||||
return SearchResult::PastWindow;
|
||||
}
|
||||
}
|
||||
|
||||
// Скроллим вниз — свайп внутри области транзакций
|
||||
if (scroll < maxScrolls) {
|
||||
if (interrupted()) { m_error = "Interrupted"; return false; }
|
||||
if (interrupted()) { m_error = "Interrupted"; return SearchResult::NotFound; }
|
||||
const int x = device.screenWidth / 2;
|
||||
// Определяем область контента из реальных Y позиций items
|
||||
int contentBottom = device.screenHeight * 2 / 3;
|
||||
@ -1031,7 +1032,7 @@ bool GetLastTransactionsScript::searchOne(const DeviceInfo &device, int accountI
|
||||
? AdbUtils::captureScreenshotBytes(m_deviceId) : listScreenshot;
|
||||
if (!m_resultScreenshot.isEmpty()) emit screenshotReady(m_resultScreenshot);
|
||||
|
||||
return false;
|
||||
return SearchResult::NotFound;
|
||||
}
|
||||
|
||||
} // namespace Dushanbe
|
||||
|
||||
@ -33,11 +33,15 @@ protected:
|
||||
void doStart() override;
|
||||
|
||||
private:
|
||||
// Поиск одной цели по сумме и времени на экране "Выписка". Возвращает true
|
||||
// и эмитит transactionParsed, если нашли; false — если нет. Прерывание
|
||||
// выставляет m_error="Interrupted".
|
||||
bool searchOne(const DeviceInfo &device, int accountId,
|
||||
double searchAmount, const QDateTime &searchTime);
|
||||
enum class SearchResult { Found, NotFound, PastWindow };
|
||||
|
||||
// Поиск одной цели по сумме и времени на экране "Выписка".
|
||||
// Found — нашли, transactionParsed эмитирован.
|
||||
// NotFound — не нашли, список прокручен до maxScrolls.
|
||||
// PastWindow — список старше окна today+yesterday, retry бессмысленен.
|
||||
// Прерывание выставляет m_error="Interrupted" и возвращает NotFound.
|
||||
SearchResult searchOne(const DeviceInfo &device, int accountId,
|
||||
double searchAmount, const QDateTime &searchTime);
|
||||
|
||||
// Возврат к началу списка "Выписка" между поисками целей.
|
||||
void scrollHistoryToTop(const DeviceInfo &device);
|
||||
|
||||
@ -39,7 +39,7 @@ void GetProfileInfoScript::doStart() {
|
||||
return;
|
||||
}
|
||||
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_PROFILE_INFO", m_deviceId, dumpMeta, &m_error);
|
||||
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||
|
||||
@ -41,7 +41,7 @@ void LoginAndCheckAccountsScript::doStart() {
|
||||
return;
|
||||
}
|
||||
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
TaskDumpRecorder::Scope dumpScope("dushanbe", "FETCH_PROFILE", m_deviceId, dumpMeta, &m_error);
|
||||
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||
|
||||
@ -123,7 +123,7 @@ void PayByCardScript::doStart() {
|
||||
return;
|
||||
}
|
||||
|
||||
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, {}, m_cardNumber, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, {}, m_cardNumber, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
TaskDumpRecorder::Scope dumpScope("dushanbe", "CREATE_TRANSACTION_CARD", device.adbSerial, dumpMeta, &m_error);
|
||||
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.adbSerial, ++m_counter));
|
||||
|
||||
@ -120,7 +120,7 @@ void PayByPhoneScript::doStart() {
|
||||
return;
|
||||
}
|
||||
|
||||
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, m_phone, {}, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, m_phone, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
TaskDumpRecorder::Scope dumpScope("dushanbe", "CREATE_TRANSACTION_PHONE", device.adbSerial, dumpMeta, &m_error);
|
||||
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(device.adbSerial, ++m_counter));
|
||||
|
||||
@ -42,7 +42,7 @@ void GetCardInfoScript::doStart() {
|
||||
return;
|
||||
}
|
||||
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
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 TaskDumpMeta dumpMeta{m_materialId, firstAmount, {}, {}, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{m_materialId, firstAmount, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
TaskDumpRecorder::Scope dumpScope("ozon", "FETCH_TRANSACTIONS", m_deviceId, dumpMeta, &m_error);
|
||||
|
||||
if (!navigateToTransactionList(device, app)) {
|
||||
|
||||
@ -37,7 +37,7 @@ void GetProfileInfoScript::doStart() {
|
||||
return;
|
||||
}
|
||||
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
TaskDumpRecorder::Scope dumpScope("ozon", "FETCH_PROFILE_INFO", m_deviceId, dumpMeta, &m_error);
|
||||
|
||||
// Читаем текущий экран
|
||||
|
||||
@ -40,7 +40,7 @@ void LoginAndCheckAccountsScript::doStart() {
|
||||
return;
|
||||
}
|
||||
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{{}, 0, {}, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
TaskDumpRecorder::Scope dumpScope("ozon", "FETCH_PROFILE", m_deviceId, dumpMeta, &m_error);
|
||||
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_deviceId, ++m_counter));
|
||||
|
||||
@ -91,7 +91,7 @@ void PayByCardScript::doStart() {
|
||||
}
|
||||
|
||||
const QString &adbId = device.adbSerial;
|
||||
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, {}, m_cardNumber, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, {}, m_cardNumber, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
TaskDumpRecorder::Scope dumpScope("ozon", "CREATE_TRANSACTION_CARD", adbId, dumpMeta, &m_error);
|
||||
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(adbId, ++m_counter));
|
||||
|
||||
@ -100,7 +100,7 @@ void PayByPhoneScript::doStart() {
|
||||
}
|
||||
|
||||
const QString &adbId = device.adbSerial;
|
||||
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, m_phone, {}, app.bankProfileId, app.phone, device.id};
|
||||
const TaskDumpMeta dumpMeta{m_account.materialId, m_amount, m_phone, {}, app.bankProfileId, app.phone, device.id, app.appVersion};
|
||||
TaskDumpRecorder::Scope dumpScope("ozon", "CREATE_TRANSACTION_PHONE", adbId, dumpMeta, &m_error);
|
||||
|
||||
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(adbId, ++m_counter));
|
||||
|
||||
@ -23,6 +23,7 @@ pathMaterial=/api/v1/profile/material
|
||||
pathProfilePing=/api/v1/profile/ping
|
||||
pathMonitoringLog=/monitoring/log
|
||||
pathMonitoringDump=/monitoring/dump
|
||||
pathMonitoringScreenshot=/monitoring/screenshot
|
||||
monitoringBase=http://142.93.102.40:8080
|
||||
monitoringToken=509b91592749959b9ff56f9ed9189e5b3e91e1260003f63c477255e1c187298a
|
||||
|
||||
|
||||
@ -9,6 +9,7 @@
|
||||
#include <QThread>
|
||||
#include <QTimer>
|
||||
#include <QImage>
|
||||
#include <QCryptographicHash>
|
||||
#include <QHttpMultiPart>
|
||||
#include <QJsonObject>
|
||||
#include <QJsonArray>
|
||||
@ -23,6 +24,7 @@
|
||||
#include "dao/MaterialDAO.h"
|
||||
#include "dao/SettingsDAO.h"
|
||||
#include "net/NetworkErrorUtils.h"
|
||||
#include "net/NetworkService.h"
|
||||
|
||||
DeviceScreener::DeviceScreener(QObject *parent) : QObject(parent) {
|
||||
const QSettings settings("config.ini", QSettings::IniFormat);
|
||||
@ -247,6 +249,28 @@ void DeviceScreener::postScreenshotToApi(const QString &apiId, const QByteArray
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void DeviceScreener::dispatchScreenshotToMonitoring(const QString &desktopId,
|
||||
const QString &deviceId,
|
||||
const QString &name,
|
||||
const QByteArray &jpeg) {
|
||||
if (jpeg.isEmpty()) return;
|
||||
|
||||
// Отдельный поток + свой NetworkService, чтобы таймаут отправки не тормозил
|
||||
// цикл сканера (паттерн LogArchiveSender::dispatch).
|
||||
auto *thread = new QThread;
|
||||
auto *svc = new NetworkService;
|
||||
svc->moveToThread(thread);
|
||||
QObject::connect(thread, &QThread::started, svc,
|
||||
[svc, desktopId, deviceId, name, jpeg]() {
|
||||
svc->postMonitoringScreenshot(desktopId, deviceId, name, jpeg);
|
||||
emit svc->finished();
|
||||
});
|
||||
QObject::connect(svc, &NetworkService::finished, thread, &QThread::quit);
|
||||
QObject::connect(svc, &NetworkService::finished, svc, &QObject::deleteLater);
|
||||
QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater);
|
||||
thread->start();
|
||||
}
|
||||
|
||||
void DeviceScreener::updateDeviceOnApi(const DeviceInfo &device) {
|
||||
// Находим первый активный bank_profile для этого устройства
|
||||
QJsonValue bankProfileId = QJsonValue::Null;
|
||||
@ -392,8 +416,8 @@ void DeviceScreener::start() {
|
||||
qDebug() << "Device registered on API:" << device.id << "->" << apiId;
|
||||
}
|
||||
} else if (status == "device") {
|
||||
// Обновляем не чаще раза в 60 секунд
|
||||
const qint64 now = QDateTime::currentSecsSinceEpoch();
|
||||
// Основной API обновляем не чаще раза в 60 секунд.
|
||||
if (now - m_lastApiUpdate.value(device.id, 0) >= 60) {
|
||||
device.apiId = saved.apiId;
|
||||
updateDeviceOnApi(device);
|
||||
@ -402,6 +426,21 @@ void DeviceScreener::start() {
|
||||
}
|
||||
m_lastApiUpdate[device.id] = now;
|
||||
}
|
||||
|
||||
// История экрана на сервере логов: проверяем не чаще раза в
|
||||
// kMonitoringScreenIntervalSecs и шлём кадр только если экран
|
||||
// изменился относительно последнего отправленного (дедуп по sha256).
|
||||
if (!device.image.isEmpty()
|
||||
&& now - m_lastMonitoringScreen.value(device.id, 0) >= kMonitoringScreenIntervalSecs) {
|
||||
m_lastMonitoringScreen[device.id] = now;
|
||||
const QByteArray hash =
|
||||
QCryptographicHash::hash(device.image, QCryptographicHash::Sha256);
|
||||
if (m_lastSentScreenHash.value(device.id) != hash) {
|
||||
m_lastSentScreenHash[device.id] = hash;
|
||||
dispatchScreenshotToMonitoring(SettingsDAO::get("desktop_id"),
|
||||
device.id, device.name, device.image);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ -15,6 +15,8 @@ public:
|
||||
~DeviceScreener() override;
|
||||
|
||||
static constexpr qint64 kOfflineDebounceSecs = 30;
|
||||
// Как часто проверять/слать кадр истории экрана на сервер логов (с дедупом).
|
||||
static constexpr qint64 kMonitoringScreenIntervalSecs = 30;
|
||||
|
||||
public slots:
|
||||
void start();
|
||||
@ -37,6 +39,8 @@ private:
|
||||
QMap<QString, qint64> m_lastApiUpdate; // deviceId → unix timestamp последнего PATCH
|
||||
QSet<QString> m_onlineDevices; // дебаунс-онлайн (true online после dedup)
|
||||
QMap<QString, qint64> m_offlineSince; // deviceId → когда пропал из ADB (для дебаунса)
|
||||
QMap<QString, qint64> m_lastMonitoringScreen; // deviceId → unix ts последней проверки кадра для истории экрана
|
||||
QMap<QString, QByteArray> m_lastSentScreenHash; // deviceId → sha256 последнего кадра, отправленного в историю экрана
|
||||
|
||||
static QList<QPair<QString, QString> > readAdbDevices();
|
||||
|
||||
@ -53,4 +57,11 @@ private:
|
||||
QString createDeviceOnApi(const DeviceInfo &device);
|
||||
void updateDeviceOnApi(const DeviceInfo &device);
|
||||
void postScreenshotToApi(const QString &apiId, const QByteArray &image);
|
||||
|
||||
// Fire-and-forget отправка кадра истории экрана на сервер логов (monitoringBase).
|
||||
// Создаёт временный NetworkService в отдельном потоке (паттерн LogArchiveSender).
|
||||
static void dispatchScreenshotToMonitoring(const QString &desktopId,
|
||||
const QString &deviceId,
|
||||
const QString &name,
|
||||
const QByteArray &jpeg);
|
||||
};
|
||||
|
||||
@ -78,6 +78,7 @@ NetworkService::NetworkService(QObject *parent) : QObject(parent) {
|
||||
m_pathTaskResult = settings.value("net/pathTaskResult", "/api/v1/task/task_result").toString();
|
||||
m_pathMonitoringLog = settings.value("net/pathMonitoringLog", "/monitoring/log").toString();
|
||||
m_pathMonitoringDump = settings.value("net/pathMonitoringDump", "/monitoring/dump").toString();
|
||||
m_pathMonitoringScreenshot = settings.value("net/pathMonitoringScreenshot", "/monitoring/screenshot").toString();
|
||||
m_monitoringBase = settings.value("net/monitoringBase", m_apiBase).toString();
|
||||
m_monitoringToken = settings.value("net/monitoringToken").toString();
|
||||
m_pathCurrentProfile = settings.value("net/pathCurrentProfile", "/api/v1/profile/current_profile").toString();
|
||||
@ -1766,6 +1767,54 @@ void NetworkService::postMonitoringDump(const QString &text, const QByteArray &a
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void NetworkService::postMonitoringScreenshot(const QString &desktopId,
|
||||
const QString &deviceId,
|
||||
const QString &name,
|
||||
const QByteArray &jpeg) {
|
||||
if (jpeg.isEmpty()) return;
|
||||
|
||||
QNetworkRequest request(QUrl(m_monitoringBase + m_pathMonitoringScreenshot));
|
||||
if (!m_monitoringToken.isEmpty()) {
|
||||
request.setRawHeader("X-Monitoring-Token", m_monitoringToken.toUtf8());
|
||||
}
|
||||
|
||||
QNetworkReply *reply = nullptr;
|
||||
bool timedOut = true;
|
||||
for (int attempt = 1; attempt <= kMaxRequestAttempts; ++attempt) {
|
||||
if (reply) { reply->deleteLater(); reply = nullptr; }
|
||||
|
||||
auto *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||||
addFormField(multiPart, "desktop_id", desktopId);
|
||||
addFormField(multiPart, "device_id", deviceId);
|
||||
addFormField(multiPart, "name", name);
|
||||
QHttpPart imagePart;
|
||||
imagePart.setHeader(QNetworkRequest::ContentTypeHeader, "image/jpeg");
|
||||
imagePart.setHeader(
|
||||
QNetworkRequest::ContentDispositionHeader,
|
||||
QStringLiteral("form-data; name=\"image\"; filename=\"screen.jpg\"")
|
||||
);
|
||||
imagePart.setBody(jpeg);
|
||||
multiPart->append(imagePart);
|
||||
|
||||
reply = m_manager->post(request, multiPart);
|
||||
multiPart->setParent(reply);
|
||||
|
||||
if (awaitReply(reply, kDefaultTimeoutMs)) { timedOut = false; break; }
|
||||
qWarning() << "[NetworkService] postMonitoringScreenshot timeout attempt"
|
||||
<< attempt << "/" << kMaxRequestAttempts;
|
||||
reply->abort();
|
||||
}
|
||||
|
||||
if (timedOut) {
|
||||
qWarning() << "[NetworkService] postMonitoringScreenshot timeout after"
|
||||
<< kMaxRequestAttempts << "attempts";
|
||||
} else if (reply->error() != QNetworkReply::NoError) {
|
||||
qWarning() << "[NetworkService] postMonitoringScreenshot failed:" << reply->errorString()
|
||||
<< "\n response body:" << reply->readAll();
|
||||
}
|
||||
reply->deleteLater();
|
||||
}
|
||||
|
||||
void NetworkService::start() {
|
||||
m_running = true;
|
||||
|
||||
|
||||
@ -117,6 +117,14 @@ public slots:
|
||||
const QByteArray &image,
|
||||
const QString &archiveFilename = QStringLiteral("task_dump.zip"));
|
||||
|
||||
// Отправляет JPEG-скриншот девайса на monitoringBase (/monitoring/screenshot,
|
||||
// X-Monitoring-Token) для истории экрана. Q_INVOKABLE — вызывается через
|
||||
// QueuedConnection из fire-and-forget потока DeviceScreener.
|
||||
Q_INVOKABLE void postMonitoringScreenshot(const QString &desktopId,
|
||||
const QString &deviceId,
|
||||
const QString &name,
|
||||
const QByteArray &jpeg);
|
||||
|
||||
void start();
|
||||
void stop();
|
||||
void disableAllProfilesOnServer();
|
||||
@ -165,6 +173,7 @@ private:
|
||||
QString m_pathTaskResult;
|
||||
QString m_pathMonitoringLog;
|
||||
QString m_pathMonitoringDump;
|
||||
QString m_pathMonitoringScreenshot;
|
||||
QString m_monitoringBase;
|
||||
QString m_monitoringToken;
|
||||
QString m_pathCurrentProfile;
|
||||
|
||||
Loading…
Reference in New Issue
Block a user