diff --git a/android/rshb/PayByPhoneScript.cpp b/android/rshb/PayByPhoneScript.cpp index d5ea7d7..d0e8be6 100644 --- a/android/rshb/PayByPhoneScript.cpp +++ b/android/rshb/PayByPhoneScript.cpp @@ -244,13 +244,6 @@ void PayByPhoneScript::doStart() { const DeviceInfo device = DeviceDAO::getDeviceById(m_account.deviceId); const ApplicationInfo app = ApplicationDAO::getApplicationsByDeviceId(m_account.deviceId, m_account.appName); - for (TransactionInfo &transaction: TransactionDAO::getTransactions(m_account.id)) { - // postNewTransactionData(m_account, transaction); - updateTransactionData(m_account, transaction.id, transaction.status, transaction.updatedDescription, transaction.description); - } - - emit finishedWithResult(m_error); - return; if (device.id.isEmpty() || app.id.isEmpty()) { qDebug() << "Device or app not found...."; return; diff --git a/database/app_data.db b/database/app_data.db index 4e83f55..ba7d920 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 aff69a0..45af597 100644 --- a/database/dao/AccountDAO.cpp +++ b/database/dao/AccountDAO.cpp @@ -44,6 +44,20 @@ bool AccountDAO::deleteAccount(const int id) { return query.exec(); } +AccountInfo parseAccount(const QSqlQuery &query) { + AccountInfo acc; + acc.id = query.value("id").toInt(); + acc.deviceId = query.value("device_id").toString(); + acc.appName = query.value("app_name").toString(); + acc.lastNumbers = query.value("last_numbers").toString(); + acc.numbers = query.value("numbers").toString(); + acc.amount = query.value("amount").toDouble(); + acc.updateTime = DateUtils::fromUtcString(query.value("update_time").toString()); + acc.status = accountStatusFromString(query.value("status").toString()); + acc.description = query.value("description").toString(); + return acc; +} + QList AccountDAO::getAccounts(const QString &deviceId, const QString &appName) { QList accounts; QSqlQuery query(DatabaseManager::instance().database()); @@ -63,36 +77,12 @@ QList AccountDAO::getAccounts(const QString &deviceId, const QStrin } while (query.next()) { - AccountInfo acc; - acc.id = query.value("id").toInt(); - acc.deviceId = query.value("device_id").toString(); - acc.appName = query.value("app_name").toString(); - acc.numbers = query.value("numbers").toString(); - acc.lastNumbers = query.value("last_numbers").toString(); - acc.amount = query.value("amount").toDouble(); - 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); + accounts.append(parseAccount(query)); } return accounts; } -AccountInfo parseAccount(const QSqlQuery &query) { - AccountInfo acc; - acc.id = query.value("id").toInt(); - acc.deviceId = query.value("device_id").toString(); - acc.appName = query.value("app_name").toString(); - acc.lastNumbers = query.value("last_numbers").toString(); - acc.numbers = query.value("numbers").toString(); - acc.amount = query.value("amount").toDouble(); - acc.updateTime = DateUtils::fromUtcString(query.value("update_time").toString()); - acc.status = accountStatusFromString(query.value("status").toString()); - acc.description = query.value("description").toString(); - return acc; -} - AccountInfo AccountDAO::getAccountById(const int accountId) { QSqlQuery query(DatabaseManager::instance().database()); @@ -116,18 +106,19 @@ AccountInfo AccountDAO::getAccountById(const int accountId) { return {}; } -AccountInfo AccountDAO::findAppAccount(const QString &appName, const QString &cardNumber) { +AccountInfo AccountDAO::findAppAccount(const QString &deviceId, const QString &appName, const QString &lastNumber) { QSqlQuery query(DatabaseManager::instance().database()); query.prepare(R"( SELECT id, device_id, app_name, numbers, last_numbers, amount, update_time, status, description FROM accounts - WHERE app_name = :app_name AND numbers = :numbers + WHERE app_name = :appName AND last_numbers = :lastNumber AND device_id = :deviceId LIMIT 1 )"); - query.bindValue(":app_name", appName); - query.bindValue(":numbers", cardNumber); + query.bindValue(":deviceId", deviceId); + query.bindValue(":appName", appName); + query.bindValue(":lastNumber", lastNumber); if (!query.exec()) { qWarning() << "Ошибка при получении account по ID:" << query.lastError().text(); diff --git a/database/dao/AccountDAO.h b/database/dao/AccountDAO.h index ba6eefb..705f469 100644 --- a/database/dao/AccountDAO.h +++ b/database/dao/AccountDAO.h @@ -11,5 +11,9 @@ public: [[nodiscard]] static AccountInfo getAccountById(int accountId); - [[nodiscard]] static AccountInfo findAppAccount(const QString &appName, const QString &cardNumber); + [[nodiscard]] static AccountInfo findAppAccount( + const QString &deviceId, + const QString &appName, + const QString &lastNumber + ); }; diff --git a/database/dao/EventDAO.cpp b/database/dao/EventDAO.cpp new file mode 100644 index 0000000..82c2540 --- /dev/null +++ b/database/dao/EventDAO.cpp @@ -0,0 +1,108 @@ +#include "EventDAO.h" + +#include +#include + +#include "DatabaseManager.h" +#include "time/DateUtils.h" + +EventInfo parseEvent(const QSqlQuery &query) { + EventInfo event; + event.id = query.value("id").toInt(); + event.status = eventStatusFromString(query.value("status").toString()); + event.externalId = query.value("external_id").toInt(); + event.accountId = query.value("account_id").toInt(); + event.amount = query.value("amount").toDouble(); + event.phone = query.value("phone").toString(); + event.bankName = query.value("bank_name").toString(); + event.timestamp = DateUtils::fromUtcString(query.value("timestamp").toString()); + event.updateTime = DateUtils::fromUtcString(query.value("update_time").toString()); + event.completeTime = DateUtils::fromUtcString(query.value("complete_time").toString()); + return event; +} + +int EventDAO::insertEvent(const EventInfo &event) { + QSqlQuery query(DatabaseManager::instance().database()); + + query.prepare(R"( + INSERT INTO events ( + status, external_id, account_id, amount, phone, bank_name, timestamp + ) VALUES ( + :status, :external_id, :account_id, :amount, :phone, :bank_name, :timestamp + ) + )"); + + query.bindValue(":status", eventStatusToString(event.status)); + query.bindValue(":external_id", event.externalId); + query.bindValue(":account_id", event.accountId); + query.bindValue(":amount", event.amount); + query.bindValue(":phone", event.phone); + query.bindValue(":bank_name", event.bankName); + query.bindValue(":timestamp", DateUtils::toUtcString(DateUtils::getUtcNow())); + + if (!query.exec()) { + qWarning() << "Ошибка при вставке event:" << query.lastError().text(); + return -1; + } + + return query.lastInsertId().toInt(); +} + +bool EventDAO::updateEvent( + const int id, + const EventStatus &status +) { + QSqlQuery query(DatabaseManager::instance().database()); + + query.prepare(R"( + UPDATE events SET + status = :status, + update_time = :update_time, + complete_time = :complete_time + WHERE id = :id + )"); + + query.bindValue(":id", id); + query.bindValue(":status", eventStatusToString(status)); + + if (status == EventStatus::Wait) { + query.bindValue(":update_time", DateUtils::toUtcString(DateUtils::getUtcNow())); + } else { + query.bindValue(":update_time", QVariant(QMetaType(QMetaType::QString))); + } + + if (status == EventStatus::Complete) { + query.bindValue(":complete_time", DateUtils::toUtcString(DateUtils::getUtcNow())); + } else { + query.bindValue(":complete_time", QVariant(QMetaType(QMetaType::QString))); + } + + if (!query.exec()) { + qWarning() << "Ошибка при обновлении event:" << query.lastError().text(); + return false; + } + return true; +} + +QList EventDAO::getAllEvents(const EventStatus &status) { + QList events; + + QSqlQuery query(DatabaseManager::instance().database()); + query.prepare(R"( + SELECT id, account_id, external_id, amount, phone, bank_name, status, update_time, complete_time, timestamp + FROM events + WHERE status = :status + )"); + query.bindValue(":status", eventStatusToString(status)); + + if (!query.exec()) { + qWarning() << "Ошибка при получении списка устройств:" << query.lastError().text(); + return events; + } + + while (query.next()) { + events.append(parseEvent(query)); + } + + return events; +} diff --git a/database/dao/EventDAO.h b/database/dao/EventDAO.h new file mode 100644 index 0000000..9e4982b --- /dev/null +++ b/database/dao/EventDAO.h @@ -0,0 +1,15 @@ +#pragma once + +#include + +class EventDAO { +public: + [[nodiscard]] static int insertEvent(const EventInfo &event); + + [[nodiscard]] static bool updateEvent( + int id, + const EventStatus &status + ); + + [[nodiscard]] static QList getAllEvents(const EventStatus &status); +}; diff --git a/main.cpp b/main.cpp index a49c93a..82388d9 100644 --- a/main.cpp +++ b/main.cpp @@ -16,6 +16,7 @@ #include #include +#include "net/NetworkService.h" #include "rshb/GetLastDaysHistoryScript.h" #include "rshb/HomeScreenScript.h" #include "rshb/PayByPhoneScript.h" @@ -52,8 +53,9 @@ void setupWorkerAndThread(QCoreApplication &app) { void setupWorkerAndThreadHistory(QCoreApplication &app) { const QString appName = "rshb"; - const QString cardNumber = "2200380317107183"; - AccountInfo account = AccountDAO::findAppAccount(appName, cardNumber); + const QString lastNumber = "7183"; + const QString deviceId = "6edc4a47"; + AccountInfo account = AccountDAO::findAppAccount(deviceId, appName, lastNumber); auto *thread = new QThread; auto *deviceScreenerWorker = new GetLastDaysHistoryScript(account); @@ -72,14 +74,15 @@ void setupWorkerAndThreadHistory(QCoreApplication &app) { void setupScriptAutoPaymentAndThread(QCoreApplication &app) { const QString appName = "rshb"; - const QString cardNumber = "2200380317107183"; + const QString lastNumber = "7183"; const QString phoneNumber = "79641815146"; + const QString deviceId = "6edc4a47"; const QString bankName = "Альфа-Банк"; // const QString bankName = "Сбербанк"; const double amount = 320; - const AccountInfo account = AccountDAO::findAppAccount(appName, cardNumber); + const AccountInfo account = AccountDAO::findAppAccount(deviceId, appName, lastNumber); if (account.id != -1) { // Функция для подключения потоков к завершению приложения auto connectStopOnQuit = [&](QObject *worker, QThread *thread) { @@ -193,7 +196,20 @@ int main(int argc, char *argv[]) { // Устанавливаем свой обработчик qInstallMessageHandler(messageHandler); - setupScriptAutoPaymentAndThread(app); + + auto *thread = new QThread; + auto *worker = new NetworkService; + + // FIXME доставить десктоп ид из настроеек + worker->setDeviceData("DT88bokcwQ", ""); + worker->moveToThread(thread); + QObject::connect(thread, &QThread::started, worker, &NetworkService::getPayments); + 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(); + + // setupScriptAutoPaymentAndThread(app); // setupWorkerAndThread(app); MainWindow window; diff --git a/migrations.sql b/migrations.sql index 2d55758..987561f 100644 --- a/migrations.sql +++ b/migrations.sql @@ -67,4 +67,23 @@ CREATE TABLE IF NOT EXISTS transactions complete_time DATETIME, timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE +); + +CREATE TABLE IF NOT EXISTS events +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + account_id INTEGER, + external_id INTEGER, + + amount REAL, + phone TEXT, + bank_name TEXT, + + status TEXT, + + update_time DATETIME, + complete_time DATETIME, + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE, + UNIQUE (external_id) ); \ No newline at end of file diff --git a/models/db/EventInfo.h b/models/db/EventInfo.h new file mode 100644 index 0000000..6973f25 --- /dev/null +++ b/models/db/EventInfo.h @@ -0,0 +1,44 @@ +#pragma once + +#include +#include + +enum class EventStatus { + Wait, + InProgress, + Complete, + Unknown +}; + + +inline QString eventStatusToString(const EventStatus type) { + switch (type) { + case EventStatus::Wait: return "wait"; + case EventStatus::Complete: return "complete"; + case EventStatus::InProgress: return "iPprogress"; + default: return "unknown"; + } +} + +inline EventStatus eventStatusFromString(const QString &str) { + if (str == "wait") return EventStatus::Wait; + if (str == "complete") return EventStatus::Complete; + if (str == "iPprogress") return EventStatus::InProgress; + return EventStatus::Unknown; +} + +struct EventInfo { + int id = -1; + int externalId = -1; + int accountId = -1; + + double amount = 0; + QString phone; + QString bankName; + + EventStatus status = EventStatus::Unknown; + + QDateTime updateTime; + QDateTime completeTime; + QDateTime timestamp; +}; \ No newline at end of file diff --git a/services/net/NetworkService.cpp b/services/net/NetworkService.cpp index 2647c8a..0239fc2 100644 --- a/services/net/NetworkService.cpp +++ b/services/net/NetworkService.cpp @@ -1,11 +1,16 @@ #include "NetworkService.h" #include +#include #include #include #include +#include +#include "dao/AccountDAO.h" +#include "dao/EventDAO.h" #include "dao/TransactionDAO.h" +#include "db/EventInfo.h" NetworkService::NetworkService(QObject *parent) : QObject(parent) { } @@ -69,6 +74,47 @@ QJsonValue NetworkService::post(QHttpMultiPart *multiPart, const QString &path) return result; } + +QJsonArray NetworkService::get(const QString &path, const QMap ¶ms) { + QNetworkAccessManager manager; // создаём внутри потока + + QUrl url(path); + QUrlQuery query; + for (auto it = params.constBegin(); it != params.constEnd(); ++it) { + query.addQueryItem(it.key(), it.value()); + } + url.setQuery(query); + + const QNetworkRequest request(url); + QNetworkReply *reply = manager.get(request); + + QEventLoop loop; + connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit); + connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) { + loop.quit(); + }); + loop.exec(); // <- здесь и запускается event-loop потока + + QJsonArray result; + if (reply->error() == QNetworkReply::NoError) { + const QByteArray raw = reply->readAll(); + QJsonParseError err; + const QJsonDocument doc = QJsonDocument::fromJson(raw, &err); + if (err.error == QJsonParseError::NoError && doc.isObject()) { + const QJsonObject root = doc.object(); + if (root.value("success").toBool()) { + result = root.value("data").toArray(); + } else { + qWarning() << "Ошибка:" << root; + } + } + } else { + qWarning() << "Ошибка:" << reply->errorString(); + } + reply->deleteLater(); + return result; +} + void NetworkService::postAccountsData() { QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); @@ -83,6 +129,43 @@ void NetworkService::postAccountsData() { emit finished(); } +void NetworkService::getPayments() { + QMap params; + params["desktopUniqueName"] = m_desktopId; + const QJsonArray data = get("http://localhost:9999/api/v1/payments", params); + + for (QJsonValue item: data) { + QJsonObject obj = item.toObject(); + EventInfo eventInfo; + eventInfo.status = EventStatus::Wait; + eventInfo.externalId = obj["id"].toInt(); + eventInfo.amount = obj["amount"].toDouble(); + eventInfo.bankName = obj["bankName"].toString(); + eventInfo.phone = obj["phone"].toString(); + + QString deviceId = obj["deviceId"].toString(); + QString appName = obj["appName"].toString(); + QString lastNumbers = obj["lastNumbers"].toString(); + AccountInfo account = AccountDAO::findAppAccount(deviceId, appName, lastNumbers); + + eventInfo.accountId = account.id; + + + int id = EventDAO::insertEvent(eventInfo); + if (id == -1) { + qDebug() << "Cant insert event"; + } else { + if (!EventDAO::updateEvent(id, EventStatus::Wait)) { + qDebug() << "Cant update event"; + } + } + } + + + emit finished(); +} + + void NetworkService::updateTransactionData() { QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType); diff --git a/services/net/NetworkService.h b/services/net/NetworkService.h index c70d24c..41f35bd 100644 --- a/services/net/NetworkService.h +++ b/services/net/NetworkService.h @@ -23,6 +23,7 @@ public: public slots: void postAccountsData(); + void getPayments(); void insertTransactionData(); @@ -42,4 +43,6 @@ private: QJsonValue post(QHttpMultiPart *multiPart, const QString &path); + + QJsonArray get(const QString &path, const QMap ¶ms); };