From d21e7f40486dc095330652e6844c4d575d532eaa Mon Sep 17 00:00:00 2001 From: slava Date: Mon, 21 Apr 2025 12:01:24 +0700 Subject: [PATCH] Add database schema and DAO implementation for accounts and transactions Introduced tables for devices, accounts, and transactions in the schema, along with associated models. Implemented DAO methods for CRUD operations, including upsert functionality for accounts and deduplication for transactions. These changes enable structured management of account and transaction data. --- database/dao/AccountDAO.cpp | 74 +++++++++++++++++++++++++++++ database/dao/AccountDAO.h | 11 +++++ database/dao/TransactionDAO.cpp | 82 +++++++++++++++++++++++++++++++++ database/dao/TransactionDAO.h | 11 +++++ migrations.sql | 37 +++++++++++++++ models/AccountInfo.h | 13 ++++++ models/TransactionInfo.h | 11 +++++ 7 files changed, 239 insertions(+) create mode 100644 database/dao/AccountDAO.cpp create mode 100644 database/dao/AccountDAO.h create mode 100644 database/dao/TransactionDAO.cpp create mode 100644 database/dao/TransactionDAO.h create mode 100644 migrations.sql create mode 100644 models/AccountInfo.h create mode 100644 models/TransactionInfo.h 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