1
0
forked from BRT/arc

Refactor and enhance transaction handling and logging

Introduced new logging system with QInstallMessageHandler. Refactored NetworkService for better modularity and added new methods to handle transaction data. Updated database schema and TransactionDAO to support external transaction ID. Cleaned up and streamlined transaction processing in GetLastDaysHistoryScript.
This commit is contained in:
slava 2025-05-21 16:06:18 +07:00
parent edcd931e66
commit a506bca01d
15 changed files with 413 additions and 154 deletions

View File

@ -1,8 +1,12 @@
#include "CommonScript.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QRandomGenerator>
#include <QThread>
#include "adb/AdbUtils.h"
#include "net/NetworkService.h"
CommonScript::CommonScript(QObject *parent)
: QObject(parent) {
@ -205,6 +209,95 @@ bool CommonScript::findAndTapOnAccount(const QString &deviceId, const QString &a
}
void CommonScript::postAccountsData(const QList<QPair<AccountInfo, Node> > &accounts) {
if (accounts.isEmpty()) {
return;
}
auto *thread = new QThread;
auto *worker = new NetworkService;
QJsonArray list;
for (const QPair<AccountInfo, Node> &pair: accounts) {
QJsonObject obj;
AccountInfo account = pair.first;
obj["appName"] = account.appName;
obj["lastNumbers"] = account.lastNumbers;
obj["amount"] = account.amount;
obj["description"] = account.description;
list.append(obj);
}
// FIXME доставить десктоп ид из настроеек
worker->setDeviceData("DT88bokcwQ", accounts.first().first.deviceId);
worker->setPayload(QJsonDocument(list).toJson());
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &NetworkService::postAccountsData);
connect(worker, &NetworkService::finished, thread, &QThread::quit);
connect(worker, &NetworkService::finished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void CommonScript::postNewTransactionData(const AccountInfo &account, const TransactionInfo &transaction) {
auto *thread = new QThread;
auto *worker = new NetworkService;
QJsonObject obj;
obj["id"] = transaction.id;
obj["amount"] = transaction.amount;
obj["name"] = transaction.name;
obj["fee"] = transaction.fee;
obj["status"] = transactionStatusToString(transaction.status);
obj["type"] = transactionTypeToString(transaction.type);
obj["phone"] = transaction.phone;
obj["description"] = transaction.description;
obj["updatedDescription"] = transaction.updatedDescription;
obj["bankName"] = transaction.bankName;
obj["bankTime"] = transaction.bankTime;
obj["bankTransactionId"] = transaction.bankTrExternalId;
// FIXME доставить десктоп ид из настроеек
worker->setDeviceData("DT88bokcwQ", account.deviceId);
worker->addExtra("lastNumbers", account.lastNumbers);
worker->addExtra("transactionId", transaction.id);
worker->setPayload(QJsonDocument(obj).toJson());
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &NetworkService::insertTransactionData);
connect(worker, &NetworkService::finished, thread, &QThread::quit);
connect(worker, &NetworkService::finished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void CommonScript::updateTransactionData(
const AccountInfo &account,
const int transactionId,
const TransactionStatus status,
const QString &oldDesc,
const QString &newDesc
) {
auto *thread = new QThread;
auto *worker = new NetworkService;
QJsonObject obj;
obj["id"] = transactionId;
obj["status"] = transactionStatusToString(status);
obj["description"] = newDesc;
obj["updatedDescription"] = oldDesc;
// FIXME доставить десктоп ид из настроеек
worker->setDeviceData("DT88bokcwQ", account.deviceId);
worker->addExtra("lastNumbers", account.lastNumbers);
worker->setPayload(QJsonDocument(obj).toJson());
worker->moveToThread(thread);
connect(thread, &QThread::started, worker, &NetworkService::updateTransactionData);
connect(worker, &NetworkService::finished, thread, &QThread::quit);
connect(worker, &NetworkService::finished, worker, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void CommonScript::stop() {
m_running = false;
}

View File

@ -25,6 +25,18 @@ protected:
bool findAndTapOnAccount(const QString &deviceId, const QString &accountNumbers);
void postAccountsData(const QList<QPair<AccountInfo, Node> > &accounts);
void postNewTransactionData(const AccountInfo &account, const TransactionInfo &transaction);
void updateTransactionData(
const AccountInfo &account,
int transactionId,
TransactionStatus status,
const QString &oldDesc,
const QString &newDesc
);
Node swipeToButton(
const QString &deviceId,
const QString &text,
@ -49,6 +61,7 @@ protected:
signals:
void finished();
void finishedWithResult(const QString &result);
public slots:

View File

@ -1,6 +1,5 @@
#include "GetLastDaysHistoryScript.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QRandomGenerator>
#include <QThread>
@ -84,89 +83,104 @@ QString formatPrice(const double value) {
return QString("%1 %2").arg(sign, formatted);
}
bool GetLastDaysHistoryScript::openAndSaveDitailInfo(TransactionInfo &transaction, int width, int height) {
QString text = QString("%1 %2").arg(transaction.shortDescription, formatPrice(transaction.amount));
// bool GetLastDaysHistoryScript::openAndSaveDitailInfo(TransactionInfo &transaction, int width, int height) {
// QString text = QString("%1 %2").arg(transaction.shortDescription, formatPrice(transaction.amount));
//
// Node trButton = swipeToButton(m_account.deviceId, text, m_counter, width, height);
// if (!trButton.isEmpty()) {
// AdbUtils::makeTap(m_account.deviceId, trButton.x(), trButton.y());
// QThread::msleep(500);
//
// xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
//
// Node moreDetailBtn = xmlScreenParser.findButtonNode("Детали операции", false);
// if (!moreDetailBtn.isEmpty()) {
// AdbUtils::makeTap(m_account.deviceId, moreDetailBtn.x(), moreDetailBtn.y());
// QThread::msleep(200);
// xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
// TransactionInfo info = xmlScreenParser.parseTransactionInfoHistory(width, height);
//
// if (transaction.id == -1) {
// info.accountId = m_account.id;
// info.shortDescription = transaction.shortDescription;
// info.amount = transaction.amount;
// info.type = info.phone.isEmpty() ? TransactionType::Card : TransactionType::Phone;
// info.updateTime = DateUtils::getUtcNow();
// if (info.status == TransactionStatus::Complete) {
// info.completeTime = DateUtils::getUtcNow();
// }
// if (TransactionDAO::insertTransaction(info) == -1) {
// qWarning() << "--- insertTransaction failed";
// }
// } else {
// transaction.status = info.status;
// transaction.description = info.description;
// if (transaction.trExternalId.isEmpty()) {
// transaction.trExternalId = info.trExternalId;
// }
// transaction.updateTime = DateUtils::getUtcNow();
// if (transaction.status == TransactionStatus::Complete && !transaction.completeTime.isValid()) {
// transaction.completeTime = DateUtils::getUtcNow();
// }
// if (!TransactionDAO::updateTransaction(transaction)) {
// qWarning() << "--- updateTransaction failed";
// }
// }
// }
//
// AdbUtils::goBack(m_account.deviceId);
// QThread::msleep(200);
// } else {
// qWarning() << "--- trButton not found..........";
// }
// return false;
// }
Node trButton = swipeToButton(m_account.deviceId, text, m_counter, width, height);
if (!trButton.isEmpty()) {
AdbUtils::makeTap(m_account.deviceId, trButton.x(), trButton.y());
QThread::msleep(500);
bool GetLastDaysHistoryScript::saveAndPostNewTransactionData(
const AccountInfo &account,
const TransactionInfo &transaction
) {
TransactionInfo tr;
tr.accountId = account.id;
tr.description = transaction.description;
tr.bankTime = transaction.bankTime;
tr.bankName = transaction.bankName;
tr.amount = transaction.amount;
tr.phone = transaction.phone;
tr.updateTime = DateUtils::getUtcNow();
tr.fee = transaction.fee;
tr.name = transaction.name;
tr.bankTrExternalId = transaction.bankTrExternalId;
tr.status = TransactionStatus::Complete;
tr.type = transaction.description.startsWith("Перевод") ? TransactionType::Phone : TransactionType::Card;
tr.completeTime = DateUtils::getUtcNow();
tr.id = TransactionDAO::insertTransaction(tr);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
if (tr.id == -1) {
return false;
}
postNewTransactionData(account, tr);
return true;
}
Node moreDetailBtn = xmlScreenParser.findButtonNode("Детали операции", false);
if (!moreDetailBtn.isEmpty()) {
AdbUtils::makeTap(m_account.deviceId, moreDetailBtn.x(), moreDetailBtn.y());
QThread::msleep(200);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
TransactionInfo info = xmlScreenParser.parseTransactionInfoHistory(width, height);
if (transaction.id == -1) {
info.accountId = m_account.id;
info.shortDescription = transaction.shortDescription;
info.amount = transaction.amount;
info.type = info.phone.isEmpty() ? TransactionType::Card : TransactionType::Phone;
info.updateTime = DateUtils::getUtcNow();
if (info.status == TransactionStatus::Complete) {
info.completeTime = DateUtils::getUtcNow();
}
if (TransactionDAO::insertTransaction(info) == -1) {
qWarning() << "--- insertTransaction failed";
}
} else {
transaction.status = info.status;
transaction.description = info.description;
if (transaction.trExternalId.isEmpty()) {
transaction.trExternalId = info.trExternalId;
}
transaction.updateTime = DateUtils::getUtcNow();
if (transaction.status == TransactionStatus::Complete && !transaction.completeTime.isValid()) {
transaction.completeTime = DateUtils::getUtcNow();
}
if (!TransactionDAO::updateTransaction(transaction)) {
qWarning() << "--- updateTransaction failed";
}
}
}
AdbUtils::goBack(m_account.deviceId);
QThread::msleep(200);
} else {
qWarning() << "--- trButton not found..........";
bool GetLastDaysHistoryScript::updateAndPostTransactionData(
const AccountInfo &account,
const int transactionId,
const TransactionStatus status,
const QString &oldDesc,
const QString &newDesc
) {
if (TransactionDAO::updateTransaction(transactionId, status, oldDesc, newDesc)) {
updateTransactionData(account, transactionId, status, oldDesc, newDesc);
return true;
}
return false;
}
void postAccountsData(const QList<QPair<AccountInfo, Node> > &accounts) {
if (accounts.isEmpty()) {
return;
}
auto *thread = new QThread;
auto *worker = new NetworkService;
QJsonArray list;
for (const QPair<AccountInfo, Node> &pair: accounts) {
QJsonObject obj;
AccountInfo account = pair.first;
obj["appName"] = account.appName;
obj["lastNumbers"] = account.lastNumbers;
obj["amount"] = account.amount;
obj["description"] = account.description;
list.append(obj);
}
worker->setDeviceData("DT88bokcwQ", accounts.first().first.deviceId);
worker->setPayload(QJsonDocument(list).toJson());
worker->moveToThread(thread);
QObject::connect(thread, &QThread::started, worker, &NetworkService::postAccountsData);
QObject::connect(worker, &NetworkService::finished, thread, &QThread::quit);
QObject::connect(worker, &NetworkService::finished, worker, &QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
thread->start();
}
void GetLastDaysHistoryScript::doStart() {
return;
// берем все транзакции из БД
QList<TransactionInfo> localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48);
const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId);
@ -230,8 +244,6 @@ void GetLastDaysHistoryScript::doStart() {
// все поддвержденные
QList<TransactionInfo> parsedTransactions = xmlScreenParser.parseTodayAndYesterdayReportHistory();
QSet<int> parsedTransactionIds;
qDebug() << "--- parsedTransactions: " << parsedTransactions.length();
qDebug() << "--- localTransactions: " << localTransactions.length();
for (const TransactionInfo &transaction: parsedTransactions) {
bool updated = false;
@ -353,25 +365,8 @@ void GetLastDaysHistoryScript::doStart() {
}
if (!updated) {
qDebug() << "--- insert: " << transaction.description;
// не нашлась транзакция
TransactionInfo tr;
tr.accountId = m_account.id;
tr.description = transaction.description;
tr.bankTime = transaction.bankTime;
tr.bankName = transaction.bankName;
tr.amount = transaction.amount;
tr.phone = transaction.phone;
tr.updateTime = DateUtils::getUtcNow();
tr.fee = transaction.fee;
tr.name = transaction.name;
tr.trExternalId = transaction.trExternalId;
tr.status = TransactionStatus::Complete;
tr.type = transaction.description.startsWith("Перевод")
? TransactionType::Phone
: TransactionType::Card;
tr.completeTime = DateUtils::getUtcNow();
if (TransactionDAO::insertTransaction(tr) == -1) {
qDebug() << "--- not inserted: " << transaction.description;
if (!saveAndPostNewTransactionData(m_account, transaction)) {
qDebug() << "--- saveAndPostNewTransactionData failed";
}
}

View File

@ -18,5 +18,13 @@ protected:
private:
const AccountInfo m_account;
bool openAndSaveDitailInfo(TransactionInfo &transaction, int width, int height);
bool saveAndPostNewTransactionData(const AccountInfo &account, const TransactionInfo &transaction);
bool updateAndPostTransactionData(
const AccountInfo &account,
int transactionId,
TransactionStatus status,
const QString &oldDesc,
const QString &newDesc
);
};

View File

@ -244,6 +244,13 @@ void PayByPhoneScript::doStart() {
const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId);
const ApplicationInfo app = ApplicationDAO::getApplicationsByDeviceId(m_account.deviceId, m_account.appName);
for (TransactionInfo &transaction: TransactionDAO::getTransactions(m_account.id)) {
// postNewTransactionData(m_account, transaction);
updateTransactionData(m_account, transaction.id, transaction.status, transaction.updatedDescription, transaction.description);
}
emit finishedWithResult(m_error);
return;
if (device.id.isEmpty() || app.id.isEmpty()) {
qDebug() << "Device or app not found....";
return;

View File

@ -339,9 +339,9 @@ void parseTransferInfo(const QString &text, TransactionInfo &info) {
}
if (const auto m = reSbpId.match(text); m.hasMatch()) {
info.trExternalId = m.captured(1);
info.bankTrExternalId = m.captured(1);
} else if (const auto m1 = reIdAlt.match(text); m1.hasMatch()) {
info.trExternalId = m1.captured(1);
info.bankTrExternalId = m1.captured(1);
}
if (const auto m = reDate.match(text); m.hasMatch()) {
@ -864,7 +864,7 @@ TransactionInfo parseExpandedInfo(const QDomElement &root) {
break;
case 10:
ee = findSubElement(infoElement, "1");
transaction.trExternalId = findSubInfo(ee, "1");
transaction.bankTrExternalId = findSubInfo(ee, "1");
break;
default:
break;

Binary file not shown.

View File

@ -41,12 +41,12 @@ bool TransactionDAO::insertTransactionIfNotExists(const TransactionInfo &info) {
INSERT INTO transactions (
account_id, amount, fee, status, phone, type,
description, updated_description, short_description, updated_short_description,
bank_name, bank_time, name, tr_external_id,
bank_name, bank_time, name, bank_tr_external_id,
update_time, complete_time, timestamp
) VALUES (
:account_id, :amount, :fee, :status, :phone, :type,
:description, :updated_description, :short_description, :updated_short_description,
:bank_name, :bank_time, :name, :tr_external_id,
:bank_name, :bank_time, :name, :bank_tr_external_id,
:update_time, :complete_time, :timestamp
)
)");
@ -64,7 +64,7 @@ bool TransactionDAO::insertTransactionIfNotExists(const TransactionInfo &info) {
query.bindValue(":updated_short_description", info.updatedShortDescription);
query.bindValue(":bank_name", info.bankName);
query.bindValue(":bank_time", info.bankTime);
query.bindValue(":tr_external_id", info.trExternalId);
query.bindValue(":bank_tr_external_id", info.bankTrExternalId);
query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));
@ -83,12 +83,12 @@ int TransactionDAO::insertTransaction(const TransactionInfo &info) {
INSERT INTO transactions (
account_id, amount, fee, status, phone, type,
description, updated_description, short_description, updated_short_description,
bank_name, bank_time, name, tr_external_id,
bank_name, bank_time, name, bank_tr_external_id,
update_time, complete_time
) VALUES (
:account_id, :amount, :fee, :status, :phone, :type,
:description, :updated_description, :short_description, :updated_short_description,
:bank_name, :bank_time, :name, :tr_external_id,
:bank_name, :bank_time, :name, :bank_tr_external_id,
:update_time, :complete_time
)
)");
@ -106,7 +106,7 @@ int TransactionDAO::insertTransaction(const TransactionInfo &info) {
query.bindValue(":updated_short_description", info.updatedShortDescription);
query.bindValue(":bank_name", info.bankName);
query.bindValue(":bank_time", info.bankTime);
query.bindValue(":tr_external_id", info.trExternalId);
query.bindValue(":bank_tr_external_id", info.bankTrExternalId);
query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));
@ -142,7 +142,7 @@ TransactionInfo parseTransaction(const QSqlQuery &query) {
tx.updatedShortDescription = query.value("updated_short_description").toString();
tx.bankName = query.value("bank_name").toString();
tx.bankTime = query.value("bank_time").toString();
tx.trExternalId = query.value("tr_external_id").toString();
tx.bankTrExternalId = query.value("bank_tr_external_id").toString();
tx.updateTime = DateUtils::fromUtcString(query.value("update_time").toString());
tx.completeTime = DateUtils::fromUtcString(query.value("complete_time").toString());
tx.timestamp = DateUtils::fromUtcString(query.value("timestamp").toString());
@ -193,7 +193,7 @@ bool TransactionDAO::updateTransaction(const TransactionInfo &info) {
bank_name = :bank_name,
bank_time = :bank_time,
name = :name,
tr_external_id = :tr_external_id,
bank_tr_external_id = :bank_tr_external_id,
update_time = :update_time,
complete_time = :complete_time,
timestamp = :timestamp
@ -213,7 +213,7 @@ bool TransactionDAO::updateTransaction(const TransactionInfo &info) {
query.bindValue(":updated_short_description", info.updatedShortDescription);
query.bindValue(":bank_name", info.bankName);
query.bindValue(":bank_time", info.bankTime);
query.bindValue(":tr_external_id", info.trExternalId);
query.bindValue(":bank_tr_external_id", info.bankTrExternalId);
query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));
query.bindValue(":timestamp", DateUtils::toUtcString(info.timestamp));
@ -335,3 +335,22 @@ bool TransactionDAO::updateTransaction(
}
return true;
}
bool TransactionDAO::updateExternalTransactionId(const int id, const int externalId) {
QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"(
UPDATE transactions SET
external_id = :external_id
WHERE id = :id
)");
query.bindValue(":id", id);
query.bindValue(":external_id", externalId);
if (!query.exec()) {
qWarning() << "Ошибка при обновлении external_id:" << query.lastError().text();
return false;
}
return true;
}

View File

@ -25,4 +25,6 @@ public:
const QString &oldDesc,
const QString &newDesc
);
[[nodiscard]] static bool updateExternalTransactionId(int id, int externalId);
};

View File

@ -14,6 +14,7 @@
#include <cstdlib>
#include <thread>
#include <chrono>
#include <QFile>
#include "rshb/GetLastDaysHistoryScript.h"
#include "rshb/HomeScreenScript.h"
@ -59,7 +60,8 @@ void setupWorkerAndThreadHistory(QCoreApplication &app) {
deviceScreenerWorker->moveToThread(thread);
QObject::connect(thread, &QThread::started, deviceScreenerWorker, &GetLastDaysHistoryScript::start);
QObject::connect(deviceScreenerWorker, &GetLastDaysHistoryScript::finished, thread, &QThread::quit);
QObject::connect(deviceScreenerWorker, &GetLastDaysHistoryScript::finished, deviceScreenerWorker, &QObject::deleteLater);
QObject::connect(deviceScreenerWorker, &GetLastDaysHistoryScript::finished, deviceScreenerWorker,
&QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
QObject::connect(&app, &QCoreApplication::aboutToQuit, [=]() {
deviceScreenerWorker->stop();
@ -75,7 +77,7 @@ void setupScriptAutoPaymentAndThread(QCoreApplication &app) {
const QString phoneNumber = "79641815146";
const QString bankName = "Альфа-Банк";
// const QString bankName = "Сбербанк";
const double amount = 520;
const double amount = 320;
const AccountInfo account = AccountDAO::findAppAccount(appName, cardNumber);
if (account.id != -1) {
@ -104,25 +106,29 @@ void setupScriptAutoPaymentAndThread(QCoreApplication &app) {
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
// Обработка результата первого потока
QObject::connect(scriptWorker, &PayByPhoneScript::finishedWithResult, &app, [account, connectStopOnQuit](const QString &result) {
qDebug() << "Получен результат: [" << result << "]";
QObject::connect(scriptWorker, &PayByPhoneScript::finishedWithResult, &app,
[account, connectStopOnQuit](const QString &result) {
qDebug() << "Получен результат: [" << result << "]";
// Создаем второй поток после завершения первого
auto *thread2 = new QThread;
auto *scriptWorker2 = new GetLastDaysHistoryScript(account);
scriptWorker2->moveToThread(thread2);
// Создаем второй поток после завершения первого
auto *thread2 = new QThread;
auto *scriptWorker2 = new GetLastDaysHistoryScript(account);
scriptWorker2->moveToThread(thread2);
// Запуск второго потока
QObject::connect(thread2, &QThread::started, scriptWorker2, &GetLastDaysHistoryScript::start);
QObject::connect(scriptWorker2, &GetLastDaysHistoryScript::finished, thread2, &QThread::quit);
QObject::connect(scriptWorker2, &GetLastDaysHistoryScript::finished, scriptWorker2, &QObject::deleteLater);
QObject::connect(thread2, &QThread::finished, thread2, &QThread::deleteLater);
// Запуск второго потока
QObject::connect(thread2, &QThread::started, scriptWorker2,
&GetLastDaysHistoryScript::start);
QObject::connect(scriptWorker2, &GetLastDaysHistoryScript::finished, thread2,
&QThread::quit);
QObject::connect(scriptWorker2, &GetLastDaysHistoryScript::finished, scriptWorker2,
&QObject::deleteLater);
QObject::connect(thread2, &QThread::finished, thread2, &QThread::deleteLater);
// Остановка второго потока при завершении приложения
connectStopOnQuit(scriptWorker2, thread2);
// Остановка второго потока при завершении приложения
connectStopOnQuit(scriptWorker2, thread2);
thread2->start();
});
thread2->start();
});
// Остановка первого потока при завершении приложения
connectStopOnQuit(scriptWorker, thread);
@ -132,12 +138,63 @@ void setupScriptAutoPaymentAndThread(QCoreApplication &app) {
}
}
static QFile logFile;
static QMutex logMutex;
void messageHandler(
QtMsgType type,
const QMessageLogContext &context,
const QString &msg
) {
Q_UNUSED(context) // можно раскомментировать, если нужны file/line/function
// Формируем строку с типом и временем
const QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
QString level;
switch (type) {
case QtDebugMsg: level = "DEBUG";
break;
case QtInfoMsg: level = "INFO";
break;
case QtWarningMsg: level = "WARNING";
break;
case QtCriticalMsg: level = "CRITICAL";
break;
case QtFatalMsg: level = "FATAL";
break;
}
const QString line = QString("%1 [%2] %3\n").arg(timestamp, level, msg);
// Записываем в файл (потокобезопасно)
{
QMutexLocker locker(&logMutex);
if (logFile.isOpen()) {
QTextStream(&logFile) << line;
logFile.flush();
}
}
// По желанию — дублировать в консоль
QByteArray localMsg = line.toLocal8Bit();
fprintf(stderr, "%s", localMsg.constData());
if (type == QtFatalMsg) {
abort();
}
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
setupWorkerAndThreadHistory(app);
setupWorkerAndThread(app);
logFile.setFileName("application.log");
if (!logFile.open(QIODevice::Append | QIODevice::Text)) {
qWarning() << "Не удалось открыть лог-файл для записи";
}
// Устанавливаем свой обработчик
qInstallMessageHandler(messageHandler);
setupScriptAutoPaymentAndThread(app);
// setupWorkerAndThread(app);
MainWindow window;
window.show();

View File

@ -45,6 +45,7 @@ CREATE TABLE IF NOT EXISTS transactions
(
id INTEGER PRIMARY KEY AUTOINCREMENT,
account_id INTEGER,
external_id INTEGER,
amount REAL,
fee REAL,
@ -60,9 +61,7 @@ CREATE TABLE IF NOT EXISTS transactions
bank_name TEXT,
bank_time TEXT,
tr_external_id TEXT,
bank_tr_external_id TEXT,
update_time DATETIME,
complete_time DATETIME,

View File

@ -63,7 +63,7 @@ struct TransactionInfo {
QString bankName;
QString bankTime;
QString trExternalId;
QString bankTrExternalId;
QDateTime updateTime;
QDateTime completeTime;
@ -90,7 +90,7 @@ inline QString convertTransactionToString(const TransactionInfo &tx) {
if (!tx.bankName.isEmpty()) lines << " bankName: " + tx.bankName; else emptyFields << "bankName";
if (!tx.bankTime.isEmpty()) lines << " bankTime: " + tx.bankTime; else emptyFields << "bankTime";
if (!tx.name.isEmpty()) lines << " name: " + tx.name; else emptyFields << "name";
if (!tx.trExternalId.isEmpty()) lines << " trExternalId: " + tx.trExternalId; else emptyFields << "trExternalId";
if (!tx.bankTrExternalId.isEmpty()) lines << " bankTrExternalId: " + tx.bankTrExternalId; else emptyFields << "bankTrExternalId";
if (tx.updateTime.isValid()) lines << " updateTime: " + tx.updateTime.toString(Qt::ISODate); else emptyFields << "updateTime";
if (tx.completeTime.isValid()) lines << " completeTime: " + tx.completeTime.toString(Qt::ISODate); else emptyFields << "completeTime";
if (tx.timestamp.isValid()) lines << " timestamp: " + tx.timestamp.toString(Qt::ISODate); else emptyFields << "timestamp";

View File

@ -54,7 +54,7 @@ void postDevicesData(const QList<DeviceInfo> &devices) {
}
QHttpPart uniquePart;
uniquePart.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="uniqueName")");
uniquePart.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="desktopUniqueName")");
uniquePart.setBody("DT88bokcwQ");
multiPart->append(uniquePart);

View File

@ -5,36 +5,38 @@
#include <QNetworkReply>
#include <QJsonObject>
#include "dao/TransactionDAO.h"
NetworkService::NetworkService(QObject *parent) : QObject(parent) {
}
NetworkService::~NetworkService() = default;
// FIXME доделать работу с экстра
void NetworkService::addExtra(const QString &key, const QVariant &value) {
m_extraData[key] = value;
}
void NetworkService::postAccountsData() {
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
void addFormField(QHttpMultiPart *multiPart, const QString &fieldName, const QString &fieldValue) {
QHttpPart formField;
formField.setHeader(
QNetworkRequest::ContentDispositionHeader,QStringLiteral("form-data; name=\"%1\"").arg(fieldName)
);
formField.setBody(fieldValue.toUtf8());
multiPart->append(formField);
}
void addFormMetadata(QHttpMultiPart *multiPart, const QByteArray &fieldValue) {
QHttpPart jsonPart;
jsonPart.setHeader(QNetworkRequest::ContentTypeHeader, "application/json; charset=UTF-8");
jsonPart.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="metadata")");
jsonPart.setBody(m_json);
jsonPart.setBody(fieldValue);
multiPart->append(jsonPart);
}
QHttpPart desktopUniqueName;
desktopUniqueName.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="desktopUniqueName")");
desktopUniqueName.setBody(m_desktopId.toUtf8());
multiPart->append(desktopUniqueName);
QHttpPart androidUniqueName;
androidUniqueName.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="androidUniqueName")");
androidUniqueName.setBody(m_deviceId.toUtf8());
multiPart->append(androidUniqueName);
QJsonValue NetworkService::post(QHttpMultiPart *multiPart, const QString &path) {
QNetworkAccessManager manager; // создаём внутри потока
const QUrl url("http://localhost:9999/api/v1/account/update");
const QUrl url(path);
const QNetworkRequest request(url);
QNetworkReply *reply = manager.post(request, multiPart);
@ -47,20 +49,71 @@ void NetworkService::postAccountsData() {
});
loop.exec(); // <- здесь и запускается event-loop потока
// обрабатываем результат
QJsonValue result;
if (reply->error() == QNetworkReply::NoError) {
const QByteArray raw = reply->readAll();
QJsonParseError err;
const QJsonDocument doc = QJsonDocument::fromJson(raw, &err);
if (err.error == QJsonParseError::NoError && doc.isObject()) {
const QJsonObject root = doc.object();
qDebug() << "Успешный ответ: " << root.value("success").toBool();
if (root.value("success").toBool()) {
result = root.value("data");
} else {
qWarning() << "Ошибка:" << root;
}
}
} else {
qWarning() << "Ошибка:" << reply->errorString();
}
reply->deleteLater();
return result;
}
void NetworkService::postAccountsData() {
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormMetadata(multiPart, m_json);
addFormField(multiPart, "desktopUniqueName", m_desktopId);
addFormField(multiPart, "androidUniqueName", m_deviceId);
const bool isSuccess = post(multiPart, "http://localhost:9999/api/v1/account/update").toBool();
if (!isSuccess) {
qDebug() << "Cant post accounts data";
}
emit finished();
}
void NetworkService::updateTransactionData() {
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormMetadata(multiPart, m_json);
addFormField(multiPart, "desktopUniqueName", m_desktopId);
addFormField(multiPart, "androidUniqueName", m_deviceId);
addFormField(multiPart, "lastNumbers", m_extraData["lastNumbers"].toString());
const bool isSuccess = post(multiPart, "http://localhost:9999/api/v1/transaction/update").toBool();
if (!isSuccess) {
qDebug() << "Cant update transaction";
}
emit finished();
}
void NetworkService::insertTransactionData() {
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormMetadata(multiPart, m_json);
addFormField(multiPart, "desktopUniqueName", m_desktopId);
addFormField(multiPart, "androidUniqueName", m_deviceId);
addFormField(multiPart, "lastNumbers", m_extraData["lastNumbers"].toString());
const int id = post(multiPart, "http://localhost:9999/api/v1/transaction/insert").toInt();
if (id > 0) {
const int transactionId = m_extraData["transactionId"].toInt();
if (!TransactionDAO::updateExternalTransactionId(transactionId, id)) {
qDebug() << "Cant update transaction id";
}
}
emit finished();
}

View File

@ -1,5 +1,7 @@
#pragma once
#include <qhttpmultipart.h>
#include <QObject>
#include <QMap>
class NetworkService final : public QObject {
Q_OBJECT
@ -16,9 +18,16 @@ public:
m_deviceId = deviceId;
}
void addExtra(const QString &key, const QVariant &value);
public slots:
void postAccountsData();
void insertTransactionData();
void updateTransactionData();
void stop();
signals:
@ -28,5 +37,9 @@ private:
QByteArray m_json;
QString m_deviceId;
QString m_desktopId;
QMap<QString, QVariant> m_extraData = {};
bool m_running = true;
QJsonValue post(QHttpMultiPart *multiPart, const QString &path);
};