1
0
forked from BRT/arc

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.
This commit is contained in:
trnsmkot 2025-04-21 12:01:24 +07:00
parent 37c0f69d97
commit 60460751d6
7 changed files with 239 additions and 0 deletions

View File

@ -0,0 +1,74 @@
#include <QSqlQuery>
#include <QSqlError>
#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<AccountInfo> AccountDAO::getAccounts(const QString &deviceId, const QString &appName) {
QList<AccountInfo> 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;
}

11
database/dao/AccountDAO.h Normal file
View File

@ -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<AccountInfo> getAccounts(const QString &deviceId, const QString &appName);
};

View File

@ -0,0 +1,82 @@
#include <QSqlQuery>
#include <QSqlError>
#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<TransactionInfo> TransactionDAO::getTransactions(const int accountId) {
QList<TransactionInfo> 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;
}

View File

@ -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<TransactionInfo> getTransactions(int accountId);
};

37
migrations.sql Normal file
View File

@ -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)
);

13
models/AccountInfo.h Normal file
View File

@ -0,0 +1,13 @@
#pragma once
#include <QDateTime>
struct AccountInfo {
int id;
QString deviceId;
QString appName;
QString cardName;
double amount;
QDateTime updateTime;
QString status;
};

11
models/TransactionInfo.h Normal file
View File

@ -0,0 +1,11 @@
#pragma once
#include <QDateTime>
struct TransactionInfo {
int id;
int accountId;
double amount;
QString description;
QDateTime timestamp;
};