1
0
forked from BRT/arc

Update transaction handling with a new history parsing script.

Replaced `PayByPhoneScript` with `GetLastDaysHistoryScript` in `main.cpp`. Introduced enhanced date/time utilities and improved database timestamp handling with `DateUtils`. Refactored `TransactionDAO` methods to separate parsing logic and added support for retrieving recent transactions within specified hours. Added the `GetLastDaysHistoryScript` class to parse and save recent transaction history.
This commit is contained in:
trnsmkot 2025-05-09 22:44:29 +07:00
parent ededac3152
commit 688a71c0b2
15 changed files with 444 additions and 96 deletions

View File

@ -0,0 +1,273 @@
#include "GetLastDaysHistoryScript.h"
#include <QRandomGenerator>
#include <QThread>
#include <QTimeZone>
#include <utility>
#include "DatabaseManager.h"
#include "adb/AdbUtils.h"
#include "dao/AccountDAO.h"
#include "dao/ApplicationDAO.h"
#include "dao/DeviceDAO.h"
#include "dao/TransactionDAO.h"
#include "db/ApplicationInfo.h"
#include "db/DeviceInfo.h"
#include "time/DateUtils.h"
GetLastDaysHistoryScript::GetLastDaysHistoryScript(
AccountInfo account,
QObject *parent
) : CommonScript(parent),
m_account(std::move(account)) {
}
GetLastDaysHistoryScript::~GetLastDaysHistoryScript() = default;
QList<TransactionInfo> findSavedTransaction(
const QList<TransactionInfo> &transactions,
const TransactionInfo &parsed
) {
QList<TransactionInfo> txs;
const QDateTime dateTime = QDateTime::fromString(parsed.bankTime, "dd.MM.yyyy"); // Московское время
for (const TransactionInfo &info: transactions) {
QDateTime timestamp = info.timestamp;
// если тот же день
if (dateTime.date() == timestamp.date() && parsed.amount == info.amount) {
// Перевод Надежда Юрьевна К** в Альфа-Банк через СБП, по номеру телефона +79641815...
if (parsed.shortDescription.contains(info.name)) {
txs.append(info);
}
}
}
return txs;
}
bool isSingle(const TransactionInfo &transaction, const QList<TransactionInfo> &transactions) {
int count = 0;
for (const TransactionInfo &info: transactions) {
if (info.amount == transaction.amount
&& info.shortDescription == transaction.shortDescription
&& info.bankTime == transaction.bankTime) {
count++;
}
}
return count == 1;
}
QString formatPrice(const double value) {
QString sign = value < 0 ? "Минус" : "Плюс";
const auto intValue = static_cast<qint64>(std::abs(value)); // округление до целого
QString number = QString::number(intValue);
QString formatted;
int count = 0;
for (int i = number.length() - 1; i >= 0; --i) {
formatted.prepend(number[i]);
++count;
if (count % 3 == 0 && i != 0) {
formatted.prepend(' ');
}
}
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));
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;
}
void GetLastDaysHistoryScript::doStart() {
// берем все транзакции из БД
QList<TransactionInfo> localTransactions = TransactionDAO::getTransactionsWithinHours(m_account.id, 48);
// for (const TransactionInfo& transaction: transactions) {
// qDebug().noquote().nospace() << convertTransactionToString(transaction);
// }
const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
if (xmlScreenParser.isHomeScreen()) {
// парсим все транзакции
if (goToAllProducts(device.id, device.screenWidth, device.screenHeight)) {
QList<QPair<AccountInfo, Node> > accounts = xmlScreenParser.parseAccountsInfo();
for (auto &[account, node]: accounts) {
// FIXME будем считать что числа последние везде уникальные
if (m_account.lastNumbers == account.lastNumbers) {
qDebug() << "Tap by card";
AdbUtils::makeTap(device.id, node.x(), node.y());
QThread::msleep(1000);
break;
}
}
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
Node filterBtn = xmlScreenParser.findButtonNode("РСХБ Онлайн", false);
if (!filterBtn.isEmpty()) {
AdbUtils::makeTap(m_account.deviceId, filterBtn.x(), filterBtn.y());
QThread::msleep(1000);
} else {
qWarning() << "--- filterBtn not found";
}
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
Node allTransactionFilerBtn = xmlScreenParser.findButtonNode("Все операции", false);
if (!allTransactionFilerBtn.isEmpty()) {
AdbUtils::makeTap(m_account.deviceId, allTransactionFilerBtn.x(), allTransactionFilerBtn.y());
QThread::msleep(1000);
} else {
qWarning() << "--- allTransactionFilerBtn not found";
}
QList<TransactionInfo> parsedTransactions;
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
for (int i = 0; i < 5; ++i) {
parsedTransactions = xmlScreenParser.parseTodayAndYesterdayHistory();
if (!parsedTransactions.empty()) {
break;
}
QThread::msleep(1000);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
}
qDebug() << "--- parsedTransactions: " << parsedTransactions.length();
// localTransactions
for (TransactionInfo &transaction: parsedTransactions) {
// если shortDescription + сумма + день и Complete совпадает и одна
bool single = isSingle(transaction, parsedTransactions);
QList<TransactionInfo> txs = findSavedTransaction(localTransactions, transaction);
if (single && txs.length() < 2) {
if (txs.length() == 1) {
// смотрим, надо ли обновление
if (txs[0].status != TransactionStatus::Complete || txs[0].shortDescription.isEmpty()) {
txs[0].shortDescription = transaction.shortDescription;
openAndSaveDitailInfo(txs[0], device.screenWidth, device.screenHeight);
}
} else {
// создаем новую
openAndSaveDitailInfo(transaction, device.screenWidth, device.screenHeight);
}
} else {
// в списке за день нашли более чем 1 одинаковых транзакций
// FIXME - что то делать если две одинаковые
}
if (txs.length() == 1) {
qDebug() << "--- one transaction with same amount";
qDebug().noquote().nospace() << convertTransactionToString(transaction);
if (transaction.status == TransactionStatus::Complete && !transaction.shortDescription.isEmpty()) {
continue;
}
// посмотреть подробности и обновить статус и описание
} else if (txs.length() > 1) {
// FIXME - что то делать если две одинаковые
// Alert !!!
qDebug() << "--- two transactions with same amount";
} else {
// Создаем новую, так как не найдено в БД
qDebug() << "--- new transaction";
qDebug().noquote().nospace() << convertTransactionToString(transaction);
// посмотреть подробности и создать
}
// qDebug() << transaction.amount;
// qDebug() << transaction.shortDescription;
// qDebug().noquote().nospace() << convertTransactionToString(transaction);
// TODO вот мы дошли до списка транзакций
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(m_account.deviceId, ++m_counter));
}
// сначала обработаем
}
} else {
qDebug() << "--- home screen not found";
}
}
/**
amount: -250
shortDescription: Перевод Надежда Юрьевна К** в Сбербанк через СБП, по номеру телефона +7964181514...
bankTime: 08.05.2025
amount: -305
shortDescription: Перевод Надежда Юрьевна К** в Альфа-Банк через СБП, по номеру телефона +79641815...
bankTime: 08.05.2025
amount: -250
shortDescription: Перевод Надежда Юрьевна К** в Альфа-Банк через СБП, по номеру телефона +79641815...
bankTime: 07.05.2025
amount: -250
shortDescription: Перевод Надежда Юрьевна К** в Сбербанк через СБП, по номеру телефона +7964181514...
bankTime: 07.05.2025
amount: -250
shortDescription: Перевод Надежда Юрьевна К** в Альфа-Банк через СБП, по номеру телефона +79641815...
bankTime: 07.05.2025
amount: 2100
shortDescription: Перевод через СБП от Надежда Юрьевна К (+7(964)-181-51-46) из Альфа-Банк на 4081...
bankTime: 07.05.2025
amount: 250
shortDescription: Перевод через СБП от Надежда Юрьевна К (+7(964)-181-51-46) из Сбербанк на 408178...
bankTime: 07.05.2025
*/

View File

@ -0,0 +1,22 @@
#pragma once
#include "CommonScript.h"
class GetLastDaysHistoryScript final : public CommonScript {
Q_OBJECT
public:
~GetLastDaysHistoryScript() override;
explicit GetLastDaysHistoryScript(
AccountInfo account,
QObject *parent = nullptr
);
protected:
void doStart() override;
private:
const AccountInfo m_account;
bool openAndSaveDitailInfo(TransactionInfo &transaction, int width, int height);
};

View File

@ -184,7 +184,8 @@ void PayByPhoneScript::makePayment(
transaction.accountId = m_account.id;
transaction.amount = -m_amount;
transaction.type = TransactionType::Phone;
if (bool inserted = TransactionDAO::insertTransaction(transaction); !inserted) {
transaction.id = TransactionDAO::insertTransaction(transaction);
if (transaction.id == -1) {
// FIXME alert!!
qDebug() << "Transaction has not inserted...";
}
@ -194,8 +195,7 @@ void PayByPhoneScript::makePayment(
Node paymentCompleted = xmlScreenParser.findTextNode("Исполнен", 0, 0, width, height / 2);
if (!paymentCompleted.isEmpty()) {
isPaymentCompleted = true;
transaction.status = TransactionStatus::Complete;
if (!TransactionDAO::updateTransactionStatus(transaction.id, transaction.status)) {
if (!TransactionDAO::updateTransactionStatus(transaction.id, TransactionStatus::Complete)) {
qDebug() << "Not updated: " << transaction.id;
}
// TODO обновляем данные
@ -205,10 +205,6 @@ void PayByPhoneScript::makePayment(
QThread::msleep(1000);
xmlScreenParser.parseAndSaveXml(AdbUtils::getScreenDumpAsXml(deviceId, ++m_counter));
}
transaction.updateTime = QDateTime::currentDateTime();
if (!TransactionDAO::updateTransaction(transaction)) {
qDebug() << "Not updated: " << transaction.id;
}
break;
} else {
qWarning() << "--- moreDetailBtn not found, i: " << i;

View File

@ -1,11 +1,12 @@
#pragma once
#include "CommonScript.h"
class PayByPhoneScript final : public CommonScript {
Q_OBJECT
public:
~PayByPhoneScript() override;
PayByPhoneScript(
AccountInfo account,
QString phone,
@ -14,8 +15,6 @@ public:
QObject *parent = nullptr
);
~PayByPhoneScript() override;
protected:
void doStart() override;

View File

@ -8,12 +8,12 @@
#include <QDomDocument>
#include <QFile>
#include <QDateTime>
#include <QTimeZone>
#include "adb/AdbUtils.h"
#include "android/xml/Node.h"
#include "db/AccountInfo.h"
#include "db/TransactionInfo.h"
#include "time/DateUtils.h"
ScreenXmlParser::ScreenXmlParser(QObject *parent) : QObject(parent) {
}
@ -492,8 +492,7 @@ QString beforeAmount(const QString &text) {
}
QList<TransactionInfo> ScreenXmlParser::parseTodayAndYesterdayHistory() {
QTimeZone tz("Europe/Moscow");
QDate today = QDateTime::currentDateTime(tz).date();
QDate today = DateUtils::getMoscowNow().date();
QDate yesterday = today.addDays(-1);
QDate beforeYesterday = today.addDays(-2);
@ -519,6 +518,7 @@ QList<TransactionInfo> ScreenXmlParser::parseTodayAndYesterdayHistory() {
QList<TransactionInfo> transactions;
if (!parentNode.isNull()) {
QDate date = {};
for (QDomNode transactionNode: parentNode.childNodes()) {
QString transactionShortText = transactionNode.toElement().attribute("text");
@ -527,8 +527,6 @@ QList<TransactionInfo> ScreenXmlParser::parseTodayAndYesterdayHistory() {
// только сегодня и вчера парсим, позавчера не трогаем
break;
}
QDate date = {};
if (titleLowerCase.startsWith(todayTitle)) {
date = today;
transactionShortText = transactionShortText.mid(todayTitle.length() + 1);
@ -557,8 +555,7 @@ QList<TransactionInfo> ScreenXmlParser::parseTodayAndYesterdayHistory() {
}
void parse2daysHistory(const QDomNode &node) {
QTimeZone tz("Europe/Moscow");
QDate today = QDateTime::currentDateTime(tz).date();
QDate today = DateUtils::getMoscowNow().date();
QDate tomorrow = today.addDays(-1);
QDate after2Days = today.addDays(-2);
@ -808,7 +805,7 @@ QList<QPair<AccountInfo, Node> > ScreenXmlParser::parseAccountsInfo() {
info.lastNumbers = match.captured(1).remove(" ");
info.amount = match.captured(2).remove(" ").toFloat();
info.description = text.mid(0, text.indexOf("Номер карты"));
info.updateTime = QDateTime::currentDateTime();
info.updateTime = DateUtils::getUtcNow();
accounts.append(qMakePair(info, node));
}

Binary file not shown.

View File

@ -4,6 +4,7 @@
#include "db/AccountInfo.h"
#include "DatabaseManager.h"
#include "time/DateUtils.h"
bool AccountDAO::upsertAccount(const AccountInfo &info) {
QSqlQuery query(DatabaseManager::instance().database());
@ -24,7 +25,7 @@ bool AccountDAO::upsertAccount(const AccountInfo &info) {
query.bindValue(":numbers", info.numbers);
query.bindValue(":last_numbers", info.lastNumbers);
query.bindValue(":amount", info.amount);
query.bindValue(":update_time", info.updateTime.toString(Qt::ISODate));
query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
query.bindValue(":status", accountStatusToString(info.status));
query.bindValue(":description", info.description);
@ -69,7 +70,7 @@ QList<AccountInfo> AccountDAO::getAccounts(const QString &deviceId, const QStrin
acc.numbers = query.value("numbers").toString();
acc.lastNumbers = query.value("last_numbers").toString();
acc.amount = query.value("amount").toDouble();
acc.updateTime = QDateTime::fromString(query.value("update_time").toString(), Qt::ISODate);
acc.updateTime = DateUtils::fromUtcString(query.value("update_time").toString());
acc.status = accountStatusFromString(query.value("status").toString());
acc.description = query.value("description").toString();
accounts.append(acc);
@ -86,7 +87,7 @@ AccountInfo parseAccount(const QSqlQuery &query) {
acc.lastNumbers = query.value("last_numbers").toString();
acc.numbers = query.value("numbers").toString();
acc.amount = query.value("amount").toDouble();
acc.updateTime = QDateTime::fromString(query.value("update_time").toString(), Qt::ISODate);
acc.updateTime = DateUtils::fromUtcString(query.value("update_time").toString());
acc.status = accountStatusFromString(query.value("status").toString());
acc.description = query.value("description").toString();
return acc;

View File

@ -4,6 +4,8 @@
#include <QSqlQuery>
#include <QSqlError>
#include "time/DateUtils.h"
QDateTime CommonDataDAO::getLastScan() {
QSqlQuery query(DatabaseManager::instance().database());
@ -16,16 +18,16 @@ QDateTime CommonDataDAO::getLastScan() {
if (query.next()) {
const QString timestamp = query.value(0).toString();
return QDateTime::fromString(timestamp, Qt::ISODate);
return DateUtils::fromUtcString(timestamp);
}
return {};
}
bool CommonDataDAO::updateLastScan(const QDateTime& newTime) {
bool CommonDataDAO::updateLastScan(const QDateTime &newTime) {
QSqlQuery query(DatabaseManager::instance().database());
query.prepare("UPDATE common_data SET update_time = :time WHERE id = 1");
query.bindValue(":time", newTime.toString(Qt::ISODate));
query.bindValue(":time", DateUtils::toUtcString(newTime));
if (!query.exec()) {
qWarning() << "Ошибка при обновлении lastScan:" << query.lastError().text();
@ -35,7 +37,7 @@ bool CommonDataDAO::updateLastScan(const QDateTime& newTime) {
if (query.numRowsAffected() == 0) {
QSqlQuery insertQuery(DatabaseManager::instance().database());
insertQuery.prepare("INSERT INTO common_data (id, update_time) VALUES (1, :time)");
insertQuery.bindValue(":time", newTime.toString(Qt::ISODate));
insertQuery.bindValue(":time", DateUtils::toUtcString(newTime));
if (!insertQuery.exec()) {
qWarning() << "Ошибка при INSERT lastScan:" << insertQuery.lastError().text();

View File

@ -3,6 +3,7 @@
#include "DeviceDAO.h"
#include "DatabaseManager.h"
#include "db/DeviceInfo.h"
#include "time/DateUtils.h"
DeviceInfo parseDevice(const QSqlQuery &query) {
DeviceInfo device;
@ -13,7 +14,7 @@ DeviceInfo parseDevice(const QSqlQuery &query) {
device.screenWidth = query.value("screen_width").toInt();
device.screenHeight = query.value("screen_height").toInt();
device.battery = query.value("battery").toInt();
device.updateTime = QDateTime::fromString(query.value("update_time").toString(), Qt::ISODate);
device.updateTime = DateUtils::fromUtcString(query.value("update_time").toString());
device.image = query.value("image").toByteArray();
return device;
}
@ -111,7 +112,7 @@ bool DeviceDAO::upsertDevice(const DeviceInfo &device) {
query.bindValue(":width", device.screenWidth);
query.bindValue(":height", device.screenHeight);
query.bindValue(":battery", device.battery);
query.bindValue(":update_time", device.updateTime.toString(Qt::ISODate));
query.bindValue(":update_time", DateUtils::toUtcString(device.updateTime));
if (!query.exec()) {
qWarning() << "Ошибка при вставке/обновлении устройства:" << query.lastError().text();

View File

@ -5,6 +5,8 @@
#include "DatabaseManager.h"
#include "TransactionDAO.h"
#include "time/DateUtils.h"
bool TransactionDAO::insertTransactionIfNotExists(const TransactionInfo &info) {
QSqlQuery query(DatabaseManager::instance().database());
@ -24,7 +26,7 @@ bool TransactionDAO::insertTransactionIfNotExists(const TransactionInfo &info) {
query.bindValue(":account_id", info.accountId);
query.bindValue(":amount", info.amount);
query.bindValue(":type", transactionTypeToString(info.type));
query.bindValue(":timestamp", info.timestamp.toString(Qt::ISODate));
query.bindValue(":timestamp", DateUtils::toUtcString(info.timestamp));
if (!query.exec() || !query.next()) {
qWarning() << "Ошибка при проверке существования transaction:" << query.lastError().text();
@ -63,9 +65,8 @@ bool TransactionDAO::insertTransactionIfNotExists(const TransactionInfo &info) {
query.bindValue(":bank_name", info.bankName);
query.bindValue(":bank_time", info.bankTime);
query.bindValue(":tr_external_id", info.trExternalId);
query.bindValue(":update_time", info.updateTime.toString(Qt::ISODate));
query.bindValue(":complete_time", info.completeTime.toString(Qt::ISODate));
query.bindValue(":timestamp", info.timestamp.toString(Qt::ISODate));
query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));
if (!query.exec()) {
qWarning() << "Ошибка при вставке transaction:" << query.lastError().text();
@ -75,7 +76,7 @@ bool TransactionDAO::insertTransactionIfNotExists(const TransactionInfo &info) {
return true;
}
bool TransactionDAO::insertTransaction(const TransactionInfo &info) {
int TransactionDAO::insertTransaction(const TransactionInfo &info) {
QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"(
@ -106,15 +107,15 @@ bool TransactionDAO::insertTransaction(const TransactionInfo &info) {
query.bindValue(":bank_name", info.bankName);
query.bindValue(":bank_time", info.bankTime);
query.bindValue(":tr_external_id", info.trExternalId);
query.bindValue(":update_time", info.updateTime.toString(Qt::ISODate));
query.bindValue(":complete_time", info.completeTime.toString(Qt::ISODate));
query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));
if (!query.exec()) {
qWarning() << "Ошибка при вставке transaction:" << query.lastError().text();
return false;
return -1;
}
return true;
return query.lastInsertId().toInt();
}
bool TransactionDAO::deleteTransaction(const int id) {
@ -124,6 +125,31 @@ bool TransactionDAO::deleteTransaction(const int id) {
return query.exec();
}
TransactionInfo parseTransaction(const QSqlQuery &query) {
TransactionInfo tx;
tx.id = query.value("id").toInt();
tx.accountId = query.value("account_id").toInt();
tx.amount = query.value("amount").toDouble();
tx.fee = query.value("fee").toDouble();
tx.status = transactionStatusFromString(query.value("status").toString());
tx.type = transactionTypeFromString(query.value("type").toString());
tx.phone = query.value("phone").toString();
tx.name = query.value("name").toString();
tx.description = query.value("description").toString();
tx.updatedDescription = query.value("updated_description").toString();
tx.shortDescription = query.value("short_description").toString();
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.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());
return tx;
}
QList<TransactionInfo> TransactionDAO::getTransactions(const int accountId) {
QList<TransactionInfo> list;
QSqlQuery query(DatabaseManager::instance().database());
@ -142,27 +168,7 @@ QList<TransactionInfo> TransactionDAO::getTransactions(const int accountId) {
}
while (query.next()) {
TransactionInfo tx;
tx.id = query.value("id").toInt();
tx.accountId = query.value("account_id").toInt();
tx.amount = query.value("amount").toDouble();
tx.fee = query.value("fee").toDouble();
tx.status = transactionStatusFromString(query.value("status").toString());
tx.type = transactionTypeFromString(query.value("type").toString());
tx.phone = query.value("phone").toString();
tx.name = query.value("name").toString();
tx.description = query.value("description").toString();
tx.updatedDescription = query.value("updated_description").toString();
tx.shortDescription = query.value("short_description").toString();
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.updateTime = QDateTime::fromString(query.value("update_time").toString(), Qt::ISODate);
tx.completeTime = QDateTime::fromString(query.value("complete_time").toString(), Qt::ISODate);
tx.timestamp = QDateTime::fromString(query.value("timestamp").toString(), Qt::ISODate);
list.append(tx);
list.append(parseTransaction(query));
}
return list;
@ -171,6 +177,8 @@ QList<TransactionInfo> TransactionDAO::getTransactions(const int accountId) {
bool TransactionDAO::updateTransaction(const TransactionInfo &info) {
QSqlQuery query(DatabaseManager::instance().database());
qDebug().noquote().nospace() << convertTransactionToString(info);
query.prepare(R"(
UPDATE transactions SET
amount = :amount,
@ -206,9 +214,9 @@ bool TransactionDAO::updateTransaction(const TransactionInfo &info) {
query.bindValue(":bank_name", info.bankName);
query.bindValue(":bank_time", info.bankTime);
query.bindValue(":tr_external_id", info.trExternalId);
query.bindValue(":update_time", info.updateTime.toString(Qt::ISODate));
query.bindValue(":complete_time", info.completeTime.toString(Qt::ISODate));
query.bindValue(":timestamp", info.timestamp.toString(Qt::ISODate));
query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
query.bindValue(":complete_time", DateUtils::toUtcString(info.completeTime));
query.bindValue(":timestamp", DateUtils::toUtcString(info.timestamp));
if (!query.exec()) {
qWarning() << "Ошибка при обновлении transaction:" << query.lastError().text();
@ -232,20 +240,21 @@ bool TransactionDAO::updateTransactionStatus(
WHERE id = :id
)");
// const QString currentTime = now.toString("yyyy-MM-dd HH:mm:ss");
query.bindValue(":id", id);
query.bindValue(":status", transactionStatusToString(status));
QString updateTime;
query.bindValue(":update_time", DateUtils::toUtcString(DateUtils::getUtcNow()));
if (status == TransactionStatus::Complete) {
updateTime = QDateTime::currentDateTime().toString(Qt::ISODate);
query.bindValue(":complete_time", DateUtils::toUtcString(DateUtils::getUtcNow()));
} else {
query.bindValue(":complete_time", QVariant(QMetaType(QMetaType::QString)));
}
query.bindValue(":update_time", updateTime);
query.bindValue(":complete_time", QDateTime::currentDateTime().toString(Qt::ISODate));
if (!query.exec()) {
qWarning() << "Ошибка при обновлении transaction:" << query.lastError().text();
return false;
}
return true;
}
@ -255,30 +264,31 @@ TransactionInfo TransactionDAO::getTransactionById(const int id) {
query.bindValue(":id", id);
TransactionInfo tx;
if (!query.exec() || !query.next()) {
qWarning() << "Ошибка или не найден transaction с id:" << id << query.lastError().text();
return tx;
return {};
}
return parseTransaction(query);
}
QList<TransactionInfo> TransactionDAO::getTransactionsWithinHours(const int accountId, const int hours) {
QList<TransactionInfo> list;
QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"(
SELECT * FROM transactions
WHERE account_id = :accountId
AND timestamp >= datetime('now', '-' || :hours || ' hours')
)");
query.bindValue(":accountId", accountId);
query.bindValue(":hours", hours);
if (!query.exec()) {
qWarning() << "Ошибка получения транзакций: " << query.lastError().text();
return {};
}
tx.id = query.value("id").toInt();
tx.accountId = query.value("account_id").toInt();
tx.amount = query.value("amount").toDouble();
tx.fee = query.value("fee").toDouble();
tx.status = transactionStatusFromString(query.value("status").toString());
tx.type = transactionTypeFromString(query.value("type").toString());
tx.phone = query.value("phone").toString();
tx.name = query.value("name").toString();
tx.description = query.value("description").toString();
tx.shortDescription = query.value("short_description").toString();
tx.updatedDescription = query.value("updated_description").toString();
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.updateTime = QDateTime::fromString(query.value("update_time").toString(), Qt::ISODate);
tx.completeTime = QDateTime::fromString(query.value("complete_time").toString(), Qt::ISODate);
tx.timestamp = QDateTime::fromString(query.value("timestamp").toString(), Qt::ISODate);
return tx;
while (query.next()) {
list.append(parseTransaction(query));
}
return list;
}

View File

@ -1,11 +1,12 @@
#pragma once
#include "db/TransactionInfo.h"
#include "rshb/GetLastDaysHistoryScript.h"
class TransactionDAO {
public:
[[nodiscard]] static bool insertTransactionIfNotExists(const TransactionInfo &info);
[[nodiscard]] static bool insertTransaction(const TransactionInfo &info);
[[nodiscard]] static int insertTransaction(const TransactionInfo &info);
[[nodiscard]] static bool deleteTransaction(int id);
@ -16,4 +17,6 @@ public:
[[nodiscard]] static bool updateTransactionStatus(int id, TransactionStatus status);
[[nodiscard]] static TransactionInfo getTransactionById(int id);
[[nodiscard]] static QList<TransactionInfo> getTransactionsWithinHours(int accountId, int hours);
};

View File

@ -0,0 +1,25 @@
#include "DateUtils.h"
DateUtils::DateUtils(QObject *parent) : QObject(parent) {
}
QDateTime DateUtils::getMoscowNow() {
const QTimeZone moscowTimeZone("Europe/Moscow");
return QDateTime::currentDateTime().toTimeZone(moscowTimeZone);
}
QDateTime DateUtils::getUtcNow() {
return QDateTime::currentDateTimeUtc();
}
QDateTime DateUtils::fromUtcString(const QString &timestamp) {
QDateTime dateTime = QDateTime::fromString(timestamp, "dd.MM.yyyy HH:mm:ss");
if (dateTime.isValid()) {
dateTime.setTimeZone(QTimeZone::utc());
}
return dateTime;
}
QString DateUtils::toUtcString(const QDateTime &dateTime) {
return dateTime.toString("dd.MM.yyyy HH:mm:ss");
}

17
database/time/DateUtils.h Normal file
View File

@ -0,0 +1,17 @@
#pragma once
#include <QTimeZone>
class DateUtils final : public QObject {
Q_OBJECT
public:
explicit DateUtils(QObject *parent = nullptr);
static QDateTime getMoscowNow();
static QDateTime getUtcNow();
static QDateTime fromUtcString(const QString &timestamp);
static QString toUtcString(const QDateTime &dateTime);
};

View File

@ -15,6 +15,7 @@
#include <thread>
#include <chrono>
#include "rshb/GetLastDaysHistoryScript.h"
#include "rshb/HomeScreenScript.h"
#include "rshb/PayByPhoneScript.h"
@ -55,17 +56,18 @@ void setupScriptAutoPaymentAndThread(QCoreApplication &app) {
const QString phoneNumber = "79641815146";
const QString bankName = "Альфа-Банк";
// const QString bankName = "Сбербанк";
const double ammount = 250;
const double ammount = 310;
AccountInfo account = AccountDAO::findAppAccount(appName, cardNumber);
if (account.id != -1) {
auto *thread = new QThread;
auto *scriptWorker = new PayByPhoneScript(account, phoneNumber, bankName, ammount);
auto *scriptWorker = new GetLastDaysHistoryScript(account);
// auto *scriptWorker = new PayByPhoneScript(account, phoneNumber, bankName, ammount);
scriptWorker->moveToThread(thread);
QObject::connect(thread, &QThread::started, scriptWorker, &PayByPhoneScript::start);
QObject::connect(scriptWorker, &PayByPhoneScript::finished, thread, &QThread::quit);
QObject::connect(scriptWorker, &PayByPhoneScript::finished, scriptWorker, &QObject::deleteLater);
QObject::connect(thread, &QThread::started, scriptWorker, &GetLastDaysHistoryScript::start);
QObject::connect(scriptWorker, &GetLastDaysHistoryScript::finished, thread, &QThread::quit);
QObject::connect(scriptWorker, &GetLastDaysHistoryScript::finished, scriptWorker, &QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
QObject::connect(&app, &QCoreApplication::aboutToQuit, [=]() {

View File

@ -8,7 +8,7 @@ CREATE TABLE IF NOT EXISTS devices
screen_height INTEGER,
battery INTEGER,
update_time DATETIME,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
timestamp DATETIME DEFAULT (strftime('%d.%m.%Y %H:%M:%S', 'now')),
image BLOB
);
@ -35,7 +35,7 @@ CREATE TABLE IF NOT EXISTS accounts
last_numbers TEXT,
description TEXT,
amount REAL,
update_time DATETIME DEFAULT CURRENT_TIMESTAMP,
update_time DATETIME DEFAULT (strftime('%d.%m.%Y %H:%M:%S', 'now')),
status TEXT,
FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE CASCADE,
UNIQUE (device_id, app_name, last_numbers)
@ -66,7 +66,7 @@ CREATE TABLE IF NOT EXISTS transactions
update_time DATETIME,
complete_time DATETIME,
timestamp DATETIME DEFAULT CURRENT_TIMESTAMP,
timestamp DATETIME DEFAULT (strftime('%d.%m.%Y %H:%M:%S', 'now')),
FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE,
UNIQUE (amount, timestamp)
);