Introduce global transaction cache to prevent duplicate confirmations across tasks and enhance workflow consistency. Optimize cache cleanup and integrate validation in "Get
This commit is contained in:
parent
cd98644f32
commit
615febc8b5
@ -986,6 +986,20 @@ void GetLastTransactionsScript::searchAll(
|
||||
txJson["real_created_at"] = txTime.isValid()
|
||||
? txTime.toUTC().toString(Qt::ISODate) : QJsonValue(QJsonValue::Null);
|
||||
|
||||
// Проверяем глобальный кэш: если транзакция уже была найдена
|
||||
// в рамках другой задачи этого материала — пропускаем её.
|
||||
const QString realAt = txJson["real_created_at"].toString();
|
||||
if (!realAt.isEmpty()) {
|
||||
const QString ck = realAt + "|" + QString::number(qAbs(txJson["amount"].toInt()));
|
||||
if (m_foundCache.contains(ck)) {
|
||||
qDebug() << "[Dushanbe::GetLastTransactions] TX already found in another task, skip:" << ck;
|
||||
AdbUtils::goBack(m_deviceId);
|
||||
QThread::msleep(500);
|
||||
continue;
|
||||
}
|
||||
m_foundCache.insert(ck);
|
||||
}
|
||||
|
||||
qDebug() << "[Dushanbe::GetLastTransactions] Emitting parsed tx:"
|
||||
<< QJsonDocument(txJson).toJson(QJsonDocument::Compact);
|
||||
emit transactionParsed(txJson);
|
||||
|
||||
@ -5,6 +5,7 @@
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <QPoint>
|
||||
#include <QSet>
|
||||
#include "db/DeviceInfo.h"
|
||||
#include "db/BankProfileInfo.h"
|
||||
|
||||
@ -30,6 +31,12 @@ signals:
|
||||
// повторного чтения из БД.
|
||||
void transactionParsed(const QJsonObject &tx);
|
||||
|
||||
public:
|
||||
// Снимок глобального кэша найденных транзакций (от EventHandler).
|
||||
// Ключи: "real_created_at|abs_amount_cents". Транзакции из этого набора
|
||||
// пропускаются при поиске, чтобы одна и та же TX не подтверждала два задания.
|
||||
void setFoundCache(const QSet<QString> &cache) { m_foundCache = cache; }
|
||||
|
||||
protected:
|
||||
void doStart() override;
|
||||
|
||||
@ -57,6 +64,7 @@ private:
|
||||
const QList<QPair<double, QDateTime>> m_targets;
|
||||
const QString m_materialId; // UUID задачи (для сохранения дампов)
|
||||
QString m_error;
|
||||
QSet<QString> m_foundCache; // снимок глобального кэша (от EventHandler)
|
||||
};
|
||||
|
||||
} // namespace Dushanbe
|
||||
|
||||
@ -636,6 +636,20 @@ bool GetLastTransactionsScript::searchOne(const DeviceInfo &device, int accountI
|
||||
<< "amount=" << parsed.amount << "time=" << dtStr
|
||||
<< "name=" << detail.name;
|
||||
|
||||
// Проверяем глобальный кэш: если транзакция уже была найдена
|
||||
// в рамках другой задачи этого материала — пропускаем её.
|
||||
if (txTime.isValid()) {
|
||||
const QString ck = txTime.toUTC().toString(Qt::ISODate)
|
||||
+ "|" + QString::number(qAbs(static_cast<int>(parsed.amount * 100)));
|
||||
if (m_foundCache.contains(ck)) {
|
||||
qDebug() << "[FetchTransactions] TX already found in another task, skip:" << ck;
|
||||
AdbUtils::goBack(m_deviceId);
|
||||
QThread::msleep(500);
|
||||
continue; // следующая кнопка в for(bi) — ищем другую транзакцию
|
||||
}
|
||||
m_foundCache.insert(ck);
|
||||
}
|
||||
|
||||
// Скриншот экрана деталей транзакции — отдаём в EventHandler.
|
||||
// Раньше дополнительно тапали "Документы" + OCR ради ID операции,
|
||||
// но это медленно и нестабильно. Теперь полагаемся на дату+время+
|
||||
|
||||
@ -4,6 +4,7 @@
|
||||
#include <QJsonObject>
|
||||
#include <QList>
|
||||
#include <QPair>
|
||||
#include <QSet>
|
||||
#include "db/DeviceInfo.h"
|
||||
#include "db/BankProfileInfo.h"
|
||||
|
||||
@ -16,6 +17,12 @@ signals:
|
||||
// Испускается на КАЖДУЮ найденную транзакцию из списка целей.
|
||||
void transactionParsed(const QJsonObject &tx);
|
||||
|
||||
public:
|
||||
// Снимок глобального кэша найденных транзакций (от EventHandler).
|
||||
// Ключи: "real_created_at|abs_amount_cents". Транзакции из этого набора
|
||||
// пропускаются при поиске, чтобы одна и та же TX не подтверждала два задания.
|
||||
void setFoundCache(const QSet<QString> &cache) { m_foundCache = cache; }
|
||||
|
||||
public:
|
||||
explicit GetLastTransactionsScript(
|
||||
QString deviceId,
|
||||
@ -54,6 +61,7 @@ private:
|
||||
const QList<QPair<double, QDateTime>> m_targets;
|
||||
const QString m_materialId; // UUID задачи (для сохранения дампов)
|
||||
QString m_error;
|
||||
QSet<QString> m_foundCache; // снимок глобального кэша (от EventHandler)
|
||||
};
|
||||
|
||||
} // namespace Ozon
|
||||
|
||||
@ -38,6 +38,11 @@ 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 = -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);
|
||||
|
||||
static QString foundTxKey(const QString &materialId, const QJsonObject &tx) {
|
||||
return materialId + "|" + tx["real_created_at"].toString()
|
||||
+ "|" + QString::number(qAbs(tx["amount"].toInt()));
|
||||
}
|
||||
|
||||
EventHandler::EventHandler(QObject *parent) : QObject(parent) {}
|
||||
|
||||
EventHandler::~EventHandler() = default;
|
||||
@ -53,6 +58,13 @@ void EventHandler::start() {
|
||||
// На время установки обновления не трогаем устройства и не запрашиваем задачи.
|
||||
if (UpdatePause::isPaused()) return;
|
||||
|
||||
// Удаляем устаревшие записи кэша найденных транзакций (TTL 20 минут)
|
||||
{
|
||||
const qint64 now = QDateTime::currentSecsSinceEpoch();
|
||||
for (auto it = m_foundTxCache.begin(); it != m_foundTxCache.end(); )
|
||||
it = (now - it.value() > 1200) ? m_foundTxCache.erase(it) : ++it;
|
||||
}
|
||||
|
||||
// Пробуем запустить задачи из очереди для свободных устройств
|
||||
const QStringList onlineDevices = DeviceDAO::getOnlineDeviceCodes();
|
||||
for (const QString &deviceId : onlineDevices) {
|
||||
@ -794,11 +806,28 @@ void EventHandler::startThreadForTask(const MaterialInfo &account, const EventIn
|
||||
arr.append(tx);
|
||||
sendTaskResult(eventTypeToString(EventType::FetchTransactions),
|
||||
event.externalId, event.bankName, arr, event.id, /*updateStatus=*/false);
|
||||
// Фиксируем в глобальном кэше: будущие задачи того же материала
|
||||
// не будут повторно подтверждать эту транзакцию (TTL 20 минут).
|
||||
const QString txKey = foundTxKey(event.externalId, tx);
|
||||
if (!txKey.startsWith("|")) // real_created_at непустой
|
||||
m_foundTxCache[txKey] = QDateTime::currentSecsSinceEpoch();
|
||||
};
|
||||
|
||||
// Снимок кэша найденных транзакций для этого материала (без prefix).
|
||||
// Скрипт использует его для пропуска уже подтверждённых транзакций.
|
||||
QSet<QString> foundCacheSnapshot;
|
||||
{
|
||||
const qint64 now = QDateTime::currentSecsSinceEpoch();
|
||||
const QString prefix = event.externalId + "|";
|
||||
for (auto it = m_foundTxCache.cbegin(); it != m_foundTxCache.cend(); ++it)
|
||||
if (now - it.value() < 1200 && it.key().startsWith(prefix))
|
||||
foundCacheSnapshot.insert(it.key().mid(prefix.size()));
|
||||
}
|
||||
|
||||
if (appCode == "ozon") {
|
||||
auto *w = new Ozon::GetLastTransactionsScript(adbSerial, appCode, 0, targets, account.materialId);
|
||||
setup(w); connectScreenshot(w);
|
||||
w->setFoundCache(foundCacheSnapshot);
|
||||
connect(w, &Ozon::GetLastTransactionsScript::transactionParsed, this, streamParsedTx);
|
||||
}
|
||||
else if (appCode == "black")
|
||||
@ -806,6 +835,7 @@ void EventHandler::startThreadForTask(const MaterialInfo &account, const EventIn
|
||||
else if (appCode == "dushanbe_city_bank") {
|
||||
auto *w = new Dushanbe::GetLastTransactionsScript(adbSerial, appCode, targets, account.materialId);
|
||||
setup(w); connectScreenshotDushanbe(w);
|
||||
w->setFoundCache(foundCacheSnapshot);
|
||||
connect(w, &Dushanbe::GetLastTransactionsScript::transactionParsed, this, streamParsedTx);
|
||||
}
|
||||
else qWarning() << "[EventHandler] FETCH_TRANSACTIONS: unknown appCode:" << appCode;
|
||||
|
||||
@ -3,6 +3,7 @@
|
||||
#include <QJsonObject>
|
||||
#include <QMap>
|
||||
#include <QObject>
|
||||
#include <QSet>
|
||||
#include <QTimer>
|
||||
|
||||
#include "db/MaterialInfo.h"
|
||||
@ -36,6 +37,7 @@ private:
|
||||
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<QString, qint64> m_foundTxCache; // "materialId|real_created_at|abs_amount_cents" → unix timestamp; защита от двойного подтверждения одной TX двумя задачами
|
||||
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