diff --git a/database/dao/AccountDAO.cpp b/database/dao/AccountDAO.cpp new file mode 100644 index 0000000..432c0bb --- /dev/null +++ b/database/dao/AccountDAO.cpp @@ -0,0 +1,74 @@ +#include +#include +#include "AccountDAO.h" + +#include "AccountInfo.h" +#include "DatabaseManager.h" + +bool AccountDAO::upsertAccount(const AccountInfo &info) { + QSqlQuery query(DatabaseManager::instance().database()); + + query.prepare(R"( + INSERT INTO accounts (device_id, app_name, card_name, amount, update_time, status) + VALUES (:device_id, :app_name, :card_name, :amount, :update_time, :status) + ON CONFLICT(device_id, app_name, card_name) + DO UPDATE SET + amount = excluded.amount, + update_time = excluded.update_time, + status = excluded.status + )"); + + query.bindValue(":device_id", info.deviceId); + query.bindValue(":app_name", info.appName); + query.bindValue(":card_name", info.cardName); + query.bindValue(":amount", info.amount); + query.bindValue(":update_time", info.updateTime.toString(Qt::ISODate)); + query.bindValue(":status", info.status); + + if (!query.exec()) { + qWarning() << "Ошибка при вставке/обновлении account:" << query.lastError().text(); + return false; + } + + return true; +} + +bool AccountDAO::deleteAccount(const int id) { + QSqlQuery query(DatabaseManager::instance().database()); + query.prepare("DELETE FROM accounts WHERE id = :id"); + query.bindValue(":id", id); + return query.exec(); +} + +QList AccountDAO::getAccounts(const QString &deviceId, const QString &appName) { + QList accounts; + QSqlQuery query(DatabaseManager::instance().database()); + + query.prepare(R"( + SELECT id, device_id, app_name, card_name, amount, update_time, status + FROM accounts + WHERE device_id = :device_id AND app_name = :app_name + )"); + + query.bindValue(":device_id", deviceId); + query.bindValue(":app_name", appName); + + if (!query.exec()) { + qWarning() << "Ошибка при получении accounts:" << query.lastError().text(); + return accounts; + } + + 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.cardName = query.value("card_name").toString(); + acc.amount = query.value("amount").toDouble(); + acc.updateTime = QDateTime::fromString(query.value("update_time").toString(), Qt::ISODate); + acc.status = query.value("status").toString(); + accounts.append(acc); + } + + return accounts; +} diff --git a/database/dao/AccountDAO.h b/database/dao/AccountDAO.h new file mode 100644 index 0000000..4489258 --- /dev/null +++ b/database/dao/AccountDAO.h @@ -0,0 +1,11 @@ +#pragma once +#include "AccountInfo.h" + +class AccountDAO { +public: + [[nodiscard]] static bool upsertAccount(const AccountInfo &info); + + [[nodiscard]] static bool deleteAccount(int id); + + [[nodiscard]] static QList getAccounts(const QString &deviceId, const QString &appName); +}; diff --git a/database/dao/TransactionDAO.cpp b/database/dao/TransactionDAO.cpp new file mode 100644 index 0000000..31de003 --- /dev/null +++ b/database/dao/TransactionDAO.cpp @@ -0,0 +1,82 @@ +#include +#include + +#include "DatabaseManager.h" +#include "TransactionDAO.h" + +bool TransactionDAO::insertTransactionIfNotExists(const TransactionInfo &info) { + QSqlQuery query(DatabaseManager::instance().database()); + + query.prepare(R"( + SELECT COUNT(*) FROM transactions + WHERE account_id = :account_id AND amount = :amount AND timestamp = :timestamp + )"); + + query.bindValue(":account_id", info.accountId); + query.bindValue(":amount", info.amount); + query.bindValue(":timestamp", info.timestamp.toString(Qt::ISODate)); + + if (!query.exec() || !query.next()) { + qWarning() << "Ошибка при проверке существования transaction:" << query.lastError().text(); + return false; + } + + if (query.value(0).toInt() > 0) { + return false; // Уже существует + } + + query.prepare(R"( + INSERT INTO transactions (account_id, amount, description, timestamp) + VALUES (:account_id, :amount, :description, :timestamp) + )"); + + query.bindValue(":account_id", info.accountId); + query.bindValue(":amount", info.amount); + query.bindValue(":description", info.description); + query.bindValue(":timestamp", info.timestamp.toString(Qt::ISODate)); + + if (!query.exec()) { + qWarning() << "Ошибка при вставке transaction:" << query.lastError().text(); + return false; + } + + return true; +} + +bool TransactionDAO::deleteTransaction(const int id) { + QSqlQuery query(DatabaseManager::instance().database()); + query.prepare("DELETE FROM transactions WHERE id = :id"); + query.bindValue(":id", id); + return query.exec(); +} + +QList TransactionDAO::getTransactions(const int accountId) { + QList list; + QSqlQuery query(DatabaseManager::instance().database()); + + query.prepare(R"( + SELECT id, account_id, amount, description, timestamp + FROM transactions + WHERE account_id = :account_id + ORDER BY timestamp DESC + )"); + + query.bindValue(":account_id", accountId); + + if (!query.exec()) { + qWarning() << "Ошибка при получении transactions:" << query.lastError().text(); + return list; + } + + 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.description = query.value("description").toString(); + tx.timestamp = QDateTime::fromString(query.value("timestamp").toString(), Qt::ISODate); + list.append(tx); + } + + return list; +} diff --git a/database/dao/TransactionDAO.h b/database/dao/TransactionDAO.h new file mode 100644 index 0000000..f75067e --- /dev/null +++ b/database/dao/TransactionDAO.h @@ -0,0 +1,11 @@ +#pragma once +#include "TransactionInfo.h" + +class TransactionDAO { +public: + [[nodiscard]] static bool insertTransactionIfNotExists(const TransactionInfo &info); + + [[nodiscard]] static bool deleteTransaction(int id); + + [[nodiscard]] static QList getTransactions(int accountId); +}; diff --git a/migrations.sql b/migrations.sql new file mode 100644 index 0000000..609b09b --- /dev/null +++ b/migrations.sql @@ -0,0 +1,37 @@ +CREATE TABLE IF NOT EXISTS devices +( + id TEXT PRIMARY KEY, + status TEXT, + name TEXT, + android TEXT, + screen_width INTEGER, + screen_height INTEGER, + battery INTEGER, + update_time DATETIME, + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, + image BLOB +); + +CREATE TABLE IF NOT EXISTS accounts +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + device_id TEXT, + app_name TEXT, + card_name TEXT, + amount REAL, + update_time DATETIME DEFAULT CURRENT_TIMESTAMP, + status TEXT, + FOREIGN KEY (device_id) REFERENCES devices (id) ON DELETE CASCADE, + UNIQUE (device_id, app_name, card_name) +); + +CREATE TABLE IF NOT EXISTS transactions +( + id INTEGER PRIMARY KEY AUTOINCREMENT, + account_id INTEGER, + amount REAL, + description TEXT, + timestamp DATETIME DEFAULT CURRENT_TIMESTAMP, + FOREIGN KEY (account_id) REFERENCES accounts (id) ON DELETE CASCADE, + UNIQUE (amount, timestamp) +); \ No newline at end of file diff --git a/models/AccountInfo.h b/models/AccountInfo.h new file mode 100644 index 0000000..bd06270 --- /dev/null +++ b/models/AccountInfo.h @@ -0,0 +1,13 @@ +#pragma once + +#include + +struct AccountInfo { + int id; + QString deviceId; + QString appName; + QString cardName; + double amount; + QDateTime updateTime; + QString status; +}; \ No newline at end of file diff --git a/models/TransactionInfo.h b/models/TransactionInfo.h new file mode 100644 index 0000000..fe791d3 --- /dev/null +++ b/models/TransactionInfo.h @@ -0,0 +1,11 @@ +#pragma once + +#include + +struct TransactionInfo { + int id; + int accountId; + double amount; + QString description; + QDateTime timestamp; +}; \ No newline at end of file