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 "CommonScript.h"
#include <QJsonArray>
#include <QJsonObject>
#include <QRandomGenerator> #include <QRandomGenerator>
#include <QThread> #include <QThread>
#include "adb/AdbUtils.h" #include "adb/AdbUtils.h"
#include "net/NetworkService.h"
CommonScript::CommonScript(QObject *parent) CommonScript::CommonScript(QObject *parent)
: 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() { void CommonScript::stop() {
m_running = false; m_running = false;
} }

View File

@ -25,6 +25,18 @@ protected:
bool findAndTapOnAccount(const QString &deviceId, const QString &accountNumbers); 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( Node swipeToButton(
const QString &deviceId, const QString &deviceId,
const QString &text, const QString &text,
@ -49,6 +61,7 @@ protected:
signals: signals:
void finished(); void finished();
void finishedWithResult(const QString &result); void finishedWithResult(const QString &result);
public slots: public slots:

View File

@ -1,6 +1,5 @@
#include "GetLastDaysHistoryScript.h" #include "GetLastDaysHistoryScript.h"
#include <QJsonArray>
#include <QJsonObject> #include <QJsonObject>
#include <QRandomGenerator> #include <QRandomGenerator>
#include <QThread> #include <QThread>
@ -84,89 +83,104 @@ QString formatPrice(const double value) {
return QString("%1 %2").arg(sign, formatted); return QString("%1 %2").arg(sign, formatted);
} }
bool GetLastDaysHistoryScript::openAndSaveDitailInfo(TransactionInfo &transaction, int width, int height) { // bool GetLastDaysHistoryScript::openAndSaveDitailInfo(TransactionInfo &transaction, int width, int height) {
QString text = QString("%1 %2").arg(transaction.shortDescription, formatPrice(transaction.amount)); // 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); bool GetLastDaysHistoryScript::saveAndPostNewTransactionData(
if (!trButton.isEmpty()) { const AccountInfo &account,
AdbUtils::makeTap(m_account.deviceId, trButton.x(), trButton.y()); const TransactionInfo &transaction
QThread::msleep(500); ) {
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); bool GetLastDaysHistoryScript::updateAndPostTransactionData(
if (!moreDetailBtn.isEmpty()) { const AccountInfo &account,
AdbUtils::makeTap(m_account.deviceId, moreDetailBtn.x(), moreDetailBtn.y()); const int transactionId,
QThread::msleep(200); const TransactionStatus status,
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter)); const QString &oldDesc,
TransactionInfo info = xmlScreenParser.parseTransactionInfoHistory(width, height); const QString &newDesc
) {
if (transaction.id == -1) { if (TransactionDAO::updateTransaction(transactionId, status, oldDesc, newDesc)) {
info.accountId = m_account.id; updateTransactionData(account, transactionId, status, oldDesc, newDesc);
info.shortDescription = transaction.shortDescription; return true;
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; 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() { void GetLastDaysHistoryScript::doStart() {
return;
// берем все транзакции из БД // берем все транзакции из БД
QList<TransactionInfo> localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48); QList<TransactionInfo> localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48);
const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId);
@ -230,8 +244,6 @@ void GetLastDaysHistoryScript::doStart() {
// все поддвержденные // все поддвержденные
QList<TransactionInfo> parsedTransactions = xmlScreenParser.parseTodayAndYesterdayReportHistory(); QList<TransactionInfo> parsedTransactions = xmlScreenParser.parseTodayAndYesterdayReportHistory();
QSet<int> parsedTransactionIds; QSet<int> parsedTransactionIds;
qDebug() << "--- parsedTransactions: " << parsedTransactions.length();
qDebug() << "--- localTransactions: " << localTransactions.length();
for (const TransactionInfo &transaction: parsedTransactions) { for (const TransactionInfo &transaction: parsedTransactions) {
bool updated = false; bool updated = false;
@ -353,25 +365,8 @@ void GetLastDaysHistoryScript::doStart() {
} }
if (!updated) { if (!updated) {
qDebug() << "--- insert: " << transaction.description; qDebug() << "--- insert: " << transaction.description;
// не нашлась транзакция if (!saveAndPostNewTransactionData(m_account, transaction)) {
TransactionInfo tr; qDebug() << "--- saveAndPostNewTransactionData failed";
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;
} }
} }

View File

@ -18,5 +18,13 @@ protected:
private: private:
const AccountInfo m_account; 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 DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId);
const ApplicationInfo app = ApplicationDAO::getApplicationsByDeviceId(m_account.deviceId, m_account.appName); 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()) { if (device.id.isEmpty() || app.id.isEmpty()) {
qDebug() << "Device or app not found...."; qDebug() << "Device or app not found....";
return; return;

View File

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

Binary file not shown.

View File

@ -41,12 +41,12 @@ bool TransactionDAO::insertTransactionIfNotExists(const TransactionInfo &info) {
INSERT INTO transactions ( INSERT INTO transactions (
account_id, amount, fee, status, phone, type, account_id, amount, fee, status, phone, type,
description, updated_description, short_description, updated_short_description, 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 update_time, complete_time, timestamp
) VALUES ( ) VALUES (
:account_id, :amount, :fee, :status, :phone, :type, :account_id, :amount, :fee, :status, :phone, :type,
:description, :updated_description, :short_description, :updated_short_description, :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 :update_time, :complete_time, :timestamp
) )
)"); )");
@ -64,7 +64,7 @@ bool TransactionDAO::insertTransactionIfNotExists(const TransactionInfo &info) {
query.bindValue(":updated_short_description", info.updatedShortDescription); query.bindValue(":updated_short_description", info.updatedShortDescription);
query.bindValue(":bank_name", info.bankName); query.bindValue(":bank_name", info.bankName);
query.bindValue(":bank_time", info.bankTime); 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(":update_time", DateUtils::toUtcString(info.updateTime));
query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime)); query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));
@ -83,12 +83,12 @@ int TransactionDAO::insertTransaction(const TransactionInfo &info) {
INSERT INTO transactions ( INSERT INTO transactions (
account_id, amount, fee, status, phone, type, account_id, amount, fee, status, phone, type,
description, updated_description, short_description, updated_short_description, 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 update_time, complete_time
) VALUES ( ) VALUES (
:account_id, :amount, :fee, :status, :phone, :type, :account_id, :amount, :fee, :status, :phone, :type,
:description, :updated_description, :short_description, :updated_short_description, :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 :update_time, :complete_time
) )
)"); )");
@ -106,7 +106,7 @@ int TransactionDAO::insertTransaction(const TransactionInfo &info) {
query.bindValue(":updated_short_description", info.updatedShortDescription); query.bindValue(":updated_short_description", info.updatedShortDescription);
query.bindValue(":bank_name", info.bankName); query.bindValue(":bank_name", info.bankName);
query.bindValue(":bank_time", info.bankTime); 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(":update_time", DateUtils::toUtcString(info.updateTime));
query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime)); 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.updatedShortDescription = query.value("updated_short_description").toString();
tx.bankName = query.value("bank_name").toString(); tx.bankName = query.value("bank_name").toString();
tx.bankTime = query.value("bank_time").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.updateTime = DateUtils::fromUtcString(query.value("update_time").toString());
tx.completeTime = DateUtils::fromUtcString(query.value("complete_time").toString()); tx.completeTime = DateUtils::fromUtcString(query.value("complete_time").toString());
tx.timestamp = DateUtils::fromUtcString(query.value("timestamp").toString()); tx.timestamp = DateUtils::fromUtcString(query.value("timestamp").toString());
@ -193,7 +193,7 @@ bool TransactionDAO::updateTransaction(const TransactionInfo &info) {
bank_name = :bank_name, bank_name = :bank_name,
bank_time = :bank_time, bank_time = :bank_time,
name = :name, name = :name,
tr_external_id = :tr_external_id, bank_tr_external_id = :bank_tr_external_id,
update_time = :update_time, update_time = :update_time,
complete_time = :complete_time, complete_time = :complete_time,
timestamp = :timestamp timestamp = :timestamp
@ -213,7 +213,7 @@ bool TransactionDAO::updateTransaction(const TransactionInfo &info) {
query.bindValue(":updated_short_description", info.updatedShortDescription); query.bindValue(":updated_short_description", info.updatedShortDescription);
query.bindValue(":bank_name", info.bankName); query.bindValue(":bank_name", info.bankName);
query.bindValue(":bank_time", info.bankTime); 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(":update_time", DateUtils::toUtcString(info.updateTime));
query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime)); query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));
query.bindValue(":timestamp", DateUtils::toUtcString(info.timestamp)); query.bindValue(":timestamp", DateUtils::toUtcString(info.timestamp));
@ -335,3 +335,22 @@ bool TransactionDAO::updateTransaction(
} }
return true; 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 &oldDesc,
const QString &newDesc const QString &newDesc
); );
[[nodiscard]] static bool updateExternalTransactionId(int id, int externalId);
}; };

View File

@ -14,6 +14,7 @@
#include <cstdlib> #include <cstdlib>
#include <thread> #include <thread>
#include <chrono> #include <chrono>
#include <QFile>
#include "rshb/GetLastDaysHistoryScript.h" #include "rshb/GetLastDaysHistoryScript.h"
#include "rshb/HomeScreenScript.h" #include "rshb/HomeScreenScript.h"
@ -59,7 +60,8 @@ void setupWorkerAndThreadHistory(QCoreApplication &app) {
deviceScreenerWorker->moveToThread(thread); deviceScreenerWorker->moveToThread(thread);
QObject::connect(thread, &QThread::started, deviceScreenerWorker, &GetLastDaysHistoryScript::start); QObject::connect(thread, &QThread::started, deviceScreenerWorker, &GetLastDaysHistoryScript::start);
QObject::connect(deviceScreenerWorker, &GetLastDaysHistoryScript::finished, thread, &QThread::quit); 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(thread, &QThread::finished, thread, &QThread::deleteLater);
QObject::connect(&app, &QCoreApplication::aboutToQuit, [=]() { QObject::connect(&app, &QCoreApplication::aboutToQuit, [=]() {
deviceScreenerWorker->stop(); deviceScreenerWorker->stop();
@ -75,7 +77,7 @@ void setupScriptAutoPaymentAndThread(QCoreApplication &app) {
const QString phoneNumber = "79641815146"; const QString phoneNumber = "79641815146";
const QString bankName = "Альфа-Банк"; const QString bankName = "Альфа-Банк";
// const QString bankName = "Сбербанк"; // const QString bankName = "Сбербанк";
const double amount = 520; const double amount = 320;
const AccountInfo account = AccountDAO::findAppAccount(appName, cardNumber); const AccountInfo account = AccountDAO::findAppAccount(appName, cardNumber);
if (account.id != -1) { if (account.id != -1) {
@ -104,25 +106,29 @@ void setupScriptAutoPaymentAndThread(QCoreApplication &app) {
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater); QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
// Обработка результата первого потока // Обработка результата первого потока
QObject::connect(scriptWorker, &PayByPhoneScript::finishedWithResult, &app, [account, connectStopOnQuit](const QString &result) { QObject::connect(scriptWorker, &PayByPhoneScript::finishedWithResult, &app,
qDebug() << "Получен результат: [" << result << "]"; [account, connectStopOnQuit](const QString &result) {
qDebug() << "Получен результат: [" << result << "]";
// Создаем второй поток после завершения первого // Создаем второй поток после завершения первого
auto *thread2 = new QThread; auto *thread2 = new QThread;
auto *scriptWorker2 = new GetLastDaysHistoryScript(account); auto *scriptWorker2 = new GetLastDaysHistoryScript(account);
scriptWorker2->moveToThread(thread2); scriptWorker2->moveToThread(thread2);
// Запуск второго потока // Запуск второго потока
QObject::connect(thread2, &QThread::started, scriptWorker2, &GetLastDaysHistoryScript::start); QObject::connect(thread2, &QThread::started, scriptWorker2,
QObject::connect(scriptWorker2, &GetLastDaysHistoryScript::finished, thread2, &QThread::quit); &GetLastDaysHistoryScript::start);
QObject::connect(scriptWorker2, &GetLastDaysHistoryScript::finished, scriptWorker2, &QObject::deleteLater); QObject::connect(scriptWorker2, &GetLastDaysHistoryScript::finished, thread2,
QObject::connect(thread2, &QThread::finished, thread2, &QThread::deleteLater); &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); 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[]) { int main(int argc, char *argv[]) {
QApplication app(argc, argv); QApplication app(argc, argv);
setupWorkerAndThreadHistory(app); logFile.setFileName("application.log");
setupWorkerAndThread(app); if (!logFile.open(QIODevice::Append | QIODevice::Text)) {
qWarning() << "Не удалось открыть лог-файл для записи";
}
// Устанавливаем свой обработчик
qInstallMessageHandler(messageHandler);
setupScriptAutoPaymentAndThread(app);
// setupWorkerAndThread(app);
MainWindow window; MainWindow window;
window.show(); window.show();

View File

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

View File

@ -63,7 +63,7 @@ struct TransactionInfo {
QString bankName; QString bankName;
QString bankTime; QString bankTime;
QString trExternalId; QString bankTrExternalId;
QDateTime updateTime; QDateTime updateTime;
QDateTime completeTime; 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.bankName.isEmpty()) lines << " bankName: " + tx.bankName; else emptyFields << "bankName";
if (!tx.bankTime.isEmpty()) lines << " bankTime: " + tx.bankTime; else emptyFields << "bankTime"; if (!tx.bankTime.isEmpty()) lines << " bankTime: " + tx.bankTime; else emptyFields << "bankTime";
if (!tx.name.isEmpty()) lines << " name: " + tx.name; else emptyFields << "name"; 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.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.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"; 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; QHttpPart uniquePart;
uniquePart.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="uniqueName")"); uniquePart.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="desktopUniqueName")");
uniquePart.setBody("DT88bokcwQ"); uniquePart.setBody("DT88bokcwQ");
multiPart->append(uniquePart); multiPart->append(uniquePart);

View File

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

View File

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