diff --git a/android/rshb/GetLastDaysHistoryScript.cpp b/android/rshb/GetLastDaysHistoryScript.cpp new file mode 100644 index 0000000..ac4e930 --- /dev/null +++ b/android/rshb/GetLastDaysHistoryScript.cpp @@ -0,0 +1,273 @@ +#include "GetLastDaysHistoryScript.h" + +#include +#include +#include +#include + +#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 findSavedTransaction( + const QList &transactions, + const TransactionInfo &parsed +) { + QList 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 &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(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 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 > 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 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 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 + */ diff --git a/android/rshb/GetLastDaysHistoryScript.h b/android/rshb/GetLastDaysHistoryScript.h new file mode 100644 index 0000000..4ab370b --- /dev/null +++ b/android/rshb/GetLastDaysHistoryScript.h @@ -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); +}; diff --git a/android/rshb/PayByPhoneScript.cpp b/android/rshb/PayByPhoneScript.cpp index ce6921e..0ae0210 100644 --- a/android/rshb/PayByPhoneScript.cpp +++ b/android/rshb/PayByPhoneScript.cpp @@ -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; diff --git a/android/rshb/PayByPhoneScript.h b/android/rshb/PayByPhoneScript.h index 4e648da..6a9f878 100644 --- a/android/rshb/PayByPhoneScript.h +++ b/android/rshb/PayByPhoneScript.h @@ -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; diff --git a/android/rshb/ScreenXmlParser.cpp b/android/rshb/ScreenXmlParser.cpp index 79f7dd4..b8a8a64 100644 --- a/android/rshb/ScreenXmlParser.cpp +++ b/android/rshb/ScreenXmlParser.cpp @@ -8,12 +8,12 @@ #include #include #include -#include #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 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 ScreenXmlParser::parseTodayAndYesterdayHistory() { QList transactions; if (!parentNode.isNull()) { + QDate date = {}; for (QDomNode transactionNode: parentNode.childNodes()) { QString transactionShortText = transactionNode.toElement().attribute("text"); @@ -527,8 +527,6 @@ QList ScreenXmlParser::parseTodayAndYesterdayHistory() { // только сегодня и вчера парсим, позавчера не трогаем break; } - - QDate date = {}; if (titleLowerCase.startsWith(todayTitle)) { date = today; transactionShortText = transactionShortText.mid(todayTitle.length() + 1); @@ -557,8 +555,7 @@ QList 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 > 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)); } diff --git a/database/app_data.db b/database/app_data.db index 64524c5..fdec5d7 100644 Binary files a/database/app_data.db and b/database/app_data.db differ diff --git a/database/dao/AccountDAO.cpp b/database/dao/AccountDAO.cpp index 2ee24f1..aff69a0 100644 --- a/database/dao/AccountDAO.cpp +++ b/database/dao/AccountDAO.cpp @@ -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 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; diff --git a/database/dao/CommonDataDAO.cpp b/database/dao/CommonDataDAO.cpp index 9287635..f68b381 100644 --- a/database/dao/CommonDataDAO.cpp +++ b/database/dao/CommonDataDAO.cpp @@ -4,6 +4,8 @@ #include #include +#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(); diff --git a/database/dao/DeviceDAO.cpp b/database/dao/DeviceDAO.cpp index 2f61f80..69cdbe8 100644 --- a/database/dao/DeviceDAO.cpp +++ b/database/dao/DeviceDAO.cpp @@ -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(); diff --git a/database/dao/TransactionDAO.cpp b/database/dao/TransactionDAO.cpp index 575154c..ab1182b 100644 --- a/database/dao/TransactionDAO.cpp +++ b/database/dao/TransactionDAO.cpp @@ -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 TransactionDAO::getTransactions(const int accountId) { QList list; QSqlQuery query(DatabaseManager::instance().database()); @@ -142,27 +168,7 @@ QList 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 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 TransactionDAO::getTransactionsWithinHours(const int accountId, const int hours) { + QList 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; } diff --git a/database/dao/TransactionDAO.h b/database/dao/TransactionDAO.h index 0a84bbb..cd805a1 100644 --- a/database/dao/TransactionDAO.h +++ b/database/dao/TransactionDAO.h @@ -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 getTransactionsWithinHours(int accountId, int hours); }; diff --git a/database/time/DateUtils.cpp b/database/time/DateUtils.cpp new file mode 100644 index 0000000..0c35890 --- /dev/null +++ b/database/time/DateUtils.cpp @@ -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 ×tamp) { + 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"); +} diff --git a/database/time/DateUtils.h b/database/time/DateUtils.h new file mode 100644 index 0000000..40d38c7 --- /dev/null +++ b/database/time/DateUtils.h @@ -0,0 +1,17 @@ +#pragma once +#include + +class DateUtils final : public QObject { + Q_OBJECT + +public: + explicit DateUtils(QObject *parent = nullptr); + + static QDateTime getMoscowNow(); + + static QDateTime getUtcNow(); + + static QDateTime fromUtcString(const QString ×tamp); + + static QString toUtcString(const QDateTime &dateTime); +}; diff --git a/main.cpp b/main.cpp index 7cb84e9..cbbb90e 100644 --- a/main.cpp +++ b/main.cpp @@ -15,6 +15,7 @@ #include #include +#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, [=]() { diff --git a/migrations.sql b/migrations.sql index 02e83db..c66c44f 100644 --- a/migrations.sql +++ b/migrations.sql @@ -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) ); \ No newline at end of file