1
0
forked from BRT/arc

Daily commit

This commit is contained in:
trnsmkot 2025-07-27 14:27:41 +05:00
parent 1659d9633e
commit 053dc94203
30 changed files with 868 additions and 102 deletions

View File

@ -213,6 +213,7 @@ void CommonScript::postAccountsData(const QList<QPair<AccountInfo, Node> > &acco
obj["lastNumbers"] = account.lastNumbers;
obj["amount"] = account.amount;
obj["description"] = account.description;
obj["currency"] = account.currency;
obj["status"] = accountStatusToString(account.status);
list.append(obj);

View File

@ -296,7 +296,7 @@ void PayByPhoneScript::doStart() {
for (auto &[account, node]: accounts) {
account.deviceId = m_account.deviceId;
account.appCode = m_account.appCode;
account.status = account.amount > 0 ? AccountStatus::Active : AccountStatus::Blocked;
account.status = account.amount > 0 ? AccountStatus::Active : AccountStatus::Off;
if (!AccountDAO::upsertAccount(account)) {
qDebug() << "Not updated: " << account.lastNumbers;
}

View File

@ -142,6 +142,7 @@ void CommonZiraatScript::postAccountsData(const QList<AccountInfo> &accounts) {
obj["appName"] = account.appCode;
obj["lastNumbers"] = account.lastNumbers;
obj["amount"] = account.amount;
obj["currency"] = account.currency;
obj["description"] = account.description;
obj["status"] = accountStatusToString(account.status);

View File

@ -1,33 +1,34 @@
[common]
varsion=0.0.1
[apps]
list=rshb, alfabank, santanderbank, ziraat
list=birbank, toss
[rshb]
name=Росcельхозбанк
android_package_name=ru.rshb.dbo
[birbank]
android_package_name=az.kapitalbank.mbanking
name=Birbank
[alfabank]
name=Альфа-Банк
android_package_name=ru.alfabank.mobile.android
[ziraat]
name=Ziraat
android_package_name=com.ziraat.ziraatmobil
[santanderbank]
name=Santander
android_package_name=ar.com.santander.rio.mbanking
[db]
dbPath=C:/Users/User/CLionProjects/ARCDeskProject/database/app_data.db
[common]
currencies=USD, KRW, AZN, RUB
token=token1753605586797
version=0.0.1
[net]
appInfoUpdate=http://localhost:9999/api/v1/device/update
accountUpdate=http://localhost:9999/api/v1/account/update
paymentsGet=http://localhost:9999/api/v1/payments
transactionUpdate=http://localhost:9999/api/v1/transaction/update
transactionInsert=http://localhost:9999/api/v1/transaction/insert
appInfoUpdate=http://localhost:9999/api/v1/device/update
appStatusUpdate=http://localhost:9999/api/v1/application/update
eventStatusUpdate=http://localhost:9999/api/v1/event/update
checkAppStatus=http://localhost:9999/api/v1/application/status
eventStatusUpdate=http://localhost:9999/api/v1/event/update
paymentsGet=http://localhost:9999/api/v1/payments
registerNewUser=http://localhost:9999/api/v1/application/register
transactionInsert=http://localhost:9999/api/v1/transaction/insert
transactionUpdate=http://localhost:9999/api/v1/transaction/update
[rshb]
android_package_name=ru.rshb.dbo
name=Росcельхозбанк
[toss]
android_package_name=viva.republica.toss
name=Toss
[ziraat]
android_package_name=com.ziraat.ziraatmobil
name=Ziraat

Binary file not shown.

View File

@ -10,23 +10,25 @@ bool AccountDAO::upsertAccount(const AccountInfo &info) {
QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"(
INSERT INTO accounts (device_id, app_name, last_numbers, amount, update_time, status, description)
VALUES (:device_id, :app_name, :last_numbers, :amount, :update_time, :status, :description)
INSERT INTO accounts (device_id, app_name, last_numbers, amount, update_time, status, description, currency)
VALUES (:device_id, :app_name, :last_numbers, :amount, :update_time, :status, :description, :currency)
ON CONFLICT(device_id, app_name, last_numbers)
DO UPDATE SET
amount = excluded.amount,
update_time = excluded.update_time,
status = excluded.status,
description = excluded.description
description = excluded.description,
currency = excluded.currency
)");
query.bindValue(":device_id", info.deviceId);
query.bindValue(":app_name", info.appCode);
query.bindValue(":last_numbers", info.lastNumbers);
query.bindValue(":currency", info.currency);
query.bindValue(":description", info.description);
query.bindValue(":amount", info.amount);
query.bindValue(":update_time", DateUtils::toUtcString(info.updateTime));
query.bindValue(":status", accountStatusToString(info.status));
query.bindValue(":description", info.description);
if (!query.exec()) {
qWarning() << "Ошибка при вставке/обновлении account:" << query.lastError().text();
@ -36,6 +38,35 @@ bool AccountDAO::upsertAccount(const AccountInfo &info) {
return true;
}
bool AccountDAO::updateAccountById(
const int id,
const QString &status,
const QString &description,
const QString &currency
) {
QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"(
UPDATE accounts
SET status = :status,
description = :description,
currency = :currency
WHERE id = :id
)");
query.bindValue(":status", status);
query.bindValue(":description", description);
query.bindValue(":currency", currency);
query.bindValue(":id", id);
if (!query.exec()) {
qWarning() << "Ошибка при обновлении account по id:" << 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");
@ -53,6 +84,7 @@ AccountInfo parseAccount(const QSqlQuery &query) {
acc.updateTime = DateUtils::fromUtcString(query.value("update_time").toString());
acc.status = accountStatusFromString(query.value("status").toString());
acc.description = query.value("description").toString();
acc.currency = query.value("currency").toString();
return acc;
}
@ -61,7 +93,7 @@ QList<AccountInfo> AccountDAO::getAccounts(const QString &deviceId, const QStrin
QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"(
SELECT id, device_id, app_name, last_numbers, amount, update_time, status, description
SELECT id, device_id, app_name, last_numbers, amount, update_time, status, description, currency
FROM accounts
WHERE device_id = :device_id AND app_name = :app_name
)");
@ -85,7 +117,7 @@ AccountInfo AccountDAO::getAccountById(const int accountId) {
QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"(
SELECT id, device_id, app_name, last_numbers, amount, update_time, status, description
SELECT id, device_id, app_name, last_numbers, amount, update_time, status, description, currency
FROM accounts
WHERE id = :id
LIMIT 1
@ -108,7 +140,7 @@ AccountInfo AccountDAO::findAppAccount(const QString &deviceId, const QString &a
QSqlQuery query(DatabaseManager::instance().database());
query.prepare(R"(
SELECT id, device_id, app_name, last_numbers, amount, update_time, status, description
SELECT id, device_id, app_name, last_numbers, amount, update_time, status, description, currency
FROM accounts
WHERE app_name = :appName AND last_numbers = :lastNumber AND device_id = :deviceId
LIMIT 1

View File

@ -5,6 +5,13 @@ class AccountDAO {
public:
[[nodiscard]] static bool upsertAccount(const AccountInfo &info);
[[nodiscard]] static bool updateAccountById(
int id,
const QString &status,
const QString &description,
const QString &currency
);
[[nodiscard]] static bool deleteAccount(int id);
[[nodiscard]] static QList<AccountInfo> getAccounts(const QString &deviceId, const QString &appName);

BIN
images/birbank_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 1.4 KiB

BIN
images/toss_icon.png Normal file

Binary file not shown.

After

Width:  |  Height:  |  Size: 4.7 KiB

View File

@ -13,6 +13,7 @@
#include <thread>
#include <chrono>
#include <QFile>
#include <QSettings>
#include "EventHandler.h"
#include "adb/AdbUtils.h"
@ -20,6 +21,9 @@
#include "rshb/GetLastDaysHistoryScript.h"
#include "rshb/HomeScreenScript.h"
#include "rshb/PayByPhoneScript.h"
#include "widget/loader/ErrorWindow.h"
#include "widget/loader/LoaderWindow.h"
#include "widget/loader/RegistrationWindow.h"
void startDeviceScreener(const QCoreApplication &app) {
auto *thread = new QThread();
@ -139,16 +143,67 @@ int main(int argc, char *argv[]) {
qInstallMessageHandler(messageHandler);
// 7927296033 - Амир
// 79829976987 - Егор
// 79641815146 - Мама Альфа
// 79199196105 - Папа рсхб
// 1. Если есть токен, проверяем его
// - ошибка сети или сайта [code -1]
// - протух токен, надо заново [code 0]
// - все ок, запуск программы [code 1]
// 2. Если нет токена, выводим, что нет и надо зарегаться [code 0]
const QSettings settings("config.ini", QSettings::IniFormat);
const QString token = settings.value("common/token").toString();
startNetworkService(app);
startDeviceScreener(app);
startEventHandler(app);
if (false) {
auto *mainWindow = new MainWindow();
mainWindow->show();
} else {
if (token.isEmpty()) {
auto *registrationWindow = new RegistrationWindow();
registrationWindow->show();
MainWindow window;
window.show();
QObject::connect(
registrationWindow, &RegistrationWindow::onRegisteredSuccess, registrationWindow,
[registrationWindow, &app]() {
auto *mainWindow = new MainWindow();
mainWindow->show();
registrationWindow->close();
startNetworkService(app);
startDeviceScreener(app);
}, Qt::QueuedConnection);
} else {
auto *loader = new LoaderWindow(token);
loader->show();
QObject::connect(loader, &LoaderWindow::loadingFinished, loader, [loader, &app](const int result) {
if (result == -1) {
auto *errorWindow = new ErrorWindow(
"Нет подключения к интернету или сервер не отвечает.\n\nПожалуйста, попробуйте позже."
);
errorWindow->show();
} else if (result == 1) {
auto *mainWindow = new MainWindow();
mainWindow->show();
startNetworkService(app);
startDeviceScreener(app);
} else if (result == 0) {
auto *registrationWindow = new RegistrationWindow();
registrationWindow->show();
QObject::connect(
registrationWindow, &RegistrationWindow::onRegisteredSuccess, registrationWindow,
[registrationWindow, &app]() {
auto *mainWindow = new MainWindow();
mainWindow->show();
registrationWindow->close();
startNetworkService(app);
startDeviceScreener(app);
}, Qt::QueuedConnection);
}
loader->close();
}, Qt::QueuedConnection);
// loader.show();
}
}
// startEventHandler(app);
return QApplication::exec();
}

View File

@ -4,20 +4,28 @@
enum class AccountStatus {
Active,
Blocked
Off
};
inline QString accountStatusToString(const AccountStatus status) {
switch (status) {
case AccountStatus::Active: return "active";
case AccountStatus::Blocked: return "blocked";
case AccountStatus::Off: return "off";
default: return "active";
}
}
inline QString accountStatusToUiString(const AccountStatus status) {
switch (status) {
case AccountStatus::Active: return "в работе";
case AccountStatus::Off: return "выключена";
default: return "в работе";
}
}
inline AccountStatus accountStatusFromString(const QString &str) {
if (str == "active") return AccountStatus::Active;
if (str == "blocked") return AccountStatus::Blocked;
if (str == "off") return AccountStatus::Off;
return AccountStatus::Active;
}
@ -26,32 +34,9 @@ struct AccountInfo {
QString deviceId; // code
QString appCode;
QString lastNumbers;
QString currency;
QString description;
double amount = 0.0;
QDateTime updateTime;
AccountStatus status = AccountStatus::Active;
QString description;
};
inline QString convertAccountToString(const AccountInfo &info) {
QStringList lines;
QStringList emptyFields;
lines << "AccountInfo {";
if (info.id != -1) lines << " id: " + QString::number(info.id); else emptyFields << "id";
if (!info.deviceId.isEmpty()) lines << " deviceId: " + info.deviceId; else emptyFields << "deviceId";
if (!info.appCode.isEmpty()) lines << " appCode: " + info.appCode; else emptyFields << "appCode";
if (!info.lastNumbers.isEmpty()) lines << " lastNumbers: " + info.lastNumbers; else emptyFields << "lastNumbers";
if (info.amount != 0.0) lines << " amount: " + QString::number(info.amount, 'f', 2); else emptyFields << "amount";
if (info.updateTime.isValid()) lines << " updateTime: " + info.updateTime.toString(Qt::ISODate); else emptyFields << "updateTime";
if (info.status != AccountStatus::Active) lines << " status: " + accountStatusToString(info.status); else emptyFields << "status";
if (!info.description.isEmpty()) lines << " description: " + info.description; else emptyFields << "description";
if (!emptyFields.isEmpty()) {
lines << " --- empty: " + emptyFields.join(", ");
}
lines << "}";
return lines.join('\n');
}
};

View File

@ -21,6 +21,7 @@
DeviceScreener::DeviceScreener(QObject *parent) : QObject(parent) {
const QSettings settings("config.ini", QSettings::IniFormat);
m_urlAppUpdate = settings.value("net/appInfoUpdate").toString();
m_token = settings.value("common/token").toString();
}
DeviceScreener::~DeviceScreener() = default;
@ -59,8 +60,8 @@ void DeviceScreener::postDevicesData(const QList<DeviceInfo> &devices) {
}
QHttpPart uniquePart;
uniquePart.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="desktopUniqueName")");
uniquePart.setBody("DT88bokcwQ");
uniquePart.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="token")");
uniquePart.setBody(m_token.toUtf8());
multiPart->append(uniquePart);
@ -125,9 +126,9 @@ void DeviceScreener::start() {
}
}
if (!deviceInfos.empty()) {
// if (!deviceInfos.empty()) {
postDevicesData(deviceInfos);
}
// }
if (!DeviceDAO::markDevicesOfflineExcept(onlineIds)) {
qDebug() << "Cant mark devices offline";

View File

@ -22,6 +22,7 @@ signals:
private:
bool m_running = true;
QString m_urlAppUpdate;
QString m_token;
static QList<QPair<QString, QString> > readAdbDevices();

View File

@ -25,8 +25,10 @@ NetworkService::NetworkService(QObject *parent) : QObject(parent) {
m_urlAppStatusUpdate = settings.value("net/appStatusUpdate").toString();
m_urlTransactionInsert = settings.value("net/transactionInsert").toString();
m_urlEventStatusUpdate = settings.value("net/eventStatusUpdate").toString();
m_urlCheckAppStatus = settings.value("net/checkAppStatus").toString();
m_urlRegisterNewUser = settings.value("net/registerNewUser").toString();
m_manager = new QNetworkAccessManager(this);
m_desktopId = "DT88bokcwQ";
m_token = settings.value("common/token").toString();
}
NetworkService::~NetworkService() = default;
@ -130,7 +132,7 @@ void NetworkService::postAccountsData() {
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormMetadata(multiPart, m_json);
addFormField(multiPart, "desktopUniqueName", m_desktopId);
addFormField(multiPart, "token", m_token);
addFormField(multiPart, "androidUniqueName", m_deviceId);
const bool isSuccess = post(multiPart, m_urlAccountUpdate).toBool();
if (!isSuccess) {
@ -142,7 +144,7 @@ void NetworkService::postAccountsData() {
void NetworkService::getPayments() {
QMap<QString, QString> params;
params["desktopUniqueName"] = m_desktopId;
params["token"] = m_token;
const QJsonArray data = get(m_urlPaymentsGet, params);
for (QJsonValue item: data) {
@ -178,7 +180,7 @@ void NetworkService::postAppStatus() {
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormMetadata(multiPart, m_json);
addFormField(multiPart, "desktopUniqueName", m_desktopId);
addFormField(multiPart, "token", m_token);
addFormField(multiPart, "androidUniqueName", m_deviceId);
const bool isSuccess = post(multiPart, m_urlAppStatusUpdate).toBool();
@ -189,6 +191,38 @@ void NetworkService::postAppStatus() {
emit finished();
}
void NetworkService::checkAppStatus() {
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormField(multiPart, "token", m_extraData["token"].toString());
const QJsonValue result = post(multiPart, m_urlCheckAppStatus);
if (result.isNull()) {
emit finishedWithResult(-1);
} else {
emit finishedWithResult(result.toInt());
}
emit finished();
}
void NetworkService::registerNewUser() {
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
QSettings settings("config.ini", QSettings::IniFormat);
addFormField(multiPart, "version", settings.value("common/version").toString());
addFormField(multiPart, "name", m_extraData["name"].toString());
addFormField(multiPart, "email", m_extraData["email"].toString());
const QJsonValue result = post(multiPart, m_urlRegisterNewUser);
if (result.isNull()) {
emit finishedWithResult(-1);
} else {
settings.setValue("common/token", result.toString());
emit finishedWithResult(1);
}
emit finished();
}
void NetworkService::postEventStatus() {
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
@ -206,7 +240,7 @@ void NetworkService::updateTransactionData() {
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormMetadata(multiPart, m_json);
addFormField(multiPart, "desktopUniqueName", m_desktopId);
addFormField(multiPart, "token", m_token);
addFormField(multiPart, "androidUniqueName", m_deviceId);
addFormField(multiPart, "lastNumbers", m_extraData["lastNumbers"].toString());
@ -222,7 +256,7 @@ void NetworkService::insertTransactionData() {
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
addFormMetadata(multiPart, m_json);
addFormField(multiPart, "desktopUniqueName", m_desktopId);
addFormField(multiPart, "token", m_token);
addFormField(multiPart, "androidUniqueName", m_deviceId);
addFormField(multiPart, "lastNumbers", m_extraData["lastNumbers"].toString());
@ -251,6 +285,7 @@ void NetworkService::start() {
obj["lastNumbers"] = account.lastNumbers;
obj["amount"] = account.amount;
obj["description"] = account.description;
obj["currency"] = account.currency;
obj["status"] = accountStatusToString(account.status);
list.append(obj);
}

View File

@ -33,16 +33,21 @@ public slots:
void updateTransactionData();
void checkAppStatus();
void registerNewUser();
void start();
void stop();
signals:
void finished();
void finishedWithResult(int value);
private:
QByteArray m_json;
QString m_deviceId;
QString m_desktopId;
QString m_token;
QMap<QString, QVariant> m_extraData = {};
bool m_running = true;
@ -52,6 +57,8 @@ private:
QString m_urlAppStatusUpdate;
QString m_urlTransactionInsert;
QString m_urlEventStatusUpdate;
QString m_urlCheckAppStatus;
QString m_urlRegisterNewUser;
QNetworkAccessManager *m_manager;

View File

@ -0,0 +1,313 @@
#include "AccountSettingsWindow.h"
#include <QLabel>
#include <QMessageBox>
#include <QPushButton>
#include <QScrollArea>
#include <QCoreApplication>
#include <QVBoxLayout>
#include <QHeaderView>
#include <QJsonArray>
#include <QJsonObject>
#include <QThread>
#include "common/PaddedItemDelegate.h"
#include "dao/AccountDAO.h"
#include "dao/ApplicationDAO.h"
#include "device/EditBankAccountDialog.h"
#include "device/EditBankDialog.h"
#include "net/NetworkService.h"
AccountSettingsWindow::AccountSettingsWindow(
QWidget *parent, QString deviceId, QString deviceName, const ApplicationInfo &app
) : QWidget(parent,
Qt::Window
| Qt::WindowTitleHint
| Qt::WindowSystemMenuHint
| Qt::WindowCloseButtonHint
| Qt::WindowMinMaxButtonsHint),
m_deviceId(std::move(deviceId)),
m_deviceName(std::move(deviceName)),
m_applicationInfo(app) {
setWindowTitle("Настройки банка [" + app.name + "]");
setMinimumSize(800, 400);
setMaximumSize(1000, 800);
resize(800, 400);
// 1. Редактирование пин-кода
// 2. Включение/отключения банка
// 3. Работа с картами (вкл/выкл) и редактирование цифр
// 4. Запуск проверки пин-код и парсинга аккаунтов
auto *mainLayout = new QVBoxLayout(this);
// Скролл-область
auto *scrollArea = new QScrollArea(this);
scrollArea->setWidgetResizable(true);
// Виджет-контейнер, в котором будет layout с таблицами и лейблами
auto *contentWidget = new QWidget;
auto *layout = new QVBoxLayout(contentWidget);
// Убираем отступы и рамку
contentWidget->setContentsMargins(0, 0, 0, 0); // Убираем отступы
layout->setContentsMargins(4, 4, 4, 4); // Убираем отступы у layout
// scrollArea->setStyleSheet("border: none;"); // Убираем рамку
layout->setAlignment(Qt::AlignTop);
QHBoxLayout *row1 = new QHBoxLayout();
QString status = QString("Статус: ") + (m_applicationInfo.status == "active"
? "<span style='color:green;'>Активен</span>"
: "<span style='color:red;'>Неактивен</span>") +
QString(", пин-код: ") + (m_applicationInfo.pinCodeChecked
? "<span style='color:green;'>Проверен</span>"
: "<span style='color:red;'>Не проверен</span>");
m_statusLabel = new QLabel(status);
m_statusLabel->setTextFormat(Qt::RichText);
row1->addWidget(m_statusLabel);
QPushButton *editBankBtn = new QPushButton("Редактировать");
editBankBtn->setStyleSheet(
"background-color: green;"
"color: white;"
);
editBankBtn->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
row1->addWidget(editBankBtn);
connect(editBankBtn, &QPushButton::clicked, this, [this]() {
BankEditDialog dlg(m_applicationInfo, this);
if (dlg.exec() == QDialog::Accepted) {
const QString pinCode = dlg.firstValue();
const QString comment = dlg.secondValue();
bool on = dlg.toggled();
if (!ApplicationDAO::update(m_applicationInfo.id, pinCode, comment, on ? "active" : "off")) {
qDebug() << "Cant update bank data";
} else {
m_applicationInfo.pinCode = pinCode;
m_applicationInfo.comment = comment;
m_applicationInfo.status = on ? "active" : "off";
QString status = QString("Статус: ") + (on
? "<span style='color:green;'>Активен</span>"
: "<span style='color:red;'>Неактивен</span>") +
QString(", пин-код: ") + (m_applicationInfo.pinCodeChecked
? "<span style='color:green;'>Проверен</span>"
: "<span style='color:red;'>Не проверен</span>");
m_statusLabel->setText(status);
m_commentLabel->setText(comment);
sendAppStatus(m_applicationInfo.deviceId, m_applicationInfo.code, on ? "active" : "off");
}
}
});
layout->addLayout(row1);
QHBoxLayout *commentRow = new QHBoxLayout();
m_commentLabel = new QLabel(m_applicationInfo.comment);
m_commentLabel->setStyleSheet(
"color: gray;"
"margin-bottom: 8px;"
);
commentRow->addWidget(m_commentLabel);
layout->addLayout(commentRow);
QPushButton *soloButton = new QPushButton("Запустить проверку пин-кода");
soloButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
// soloButton->setStyleSheet("");
soloButton->setStyleSheet(
"background-color: orange;"
"color: white;"
);
QObject::connect(soloButton, &QPushButton::clicked, []() {
qDebug() << "Кнопка 3 нажата";
});
layout->addWidget(soloButton);
layout->addWidget(new QLabel("Счета"));
QTableWidget *tableWidget = new QTableWidget(this);
tableWidget->setSelectionMode(QAbstractItemView::NoSelection);
tableWidget->setFocusPolicy(Qt::NoFocus);
reloadContent(tableWidget);
tableWidget->resizeColumnsToContents();
layout->addWidget(tableWidget);
QPushButton *addAccountButton = new QPushButton("Добавить аккаунт");
addAccountButton->setSizePolicy(QSizePolicy::Fixed, QSizePolicy::Preferred);
AccountInfo account;
connect(addAccountButton, &QPushButton::clicked, this, [this, tableWidget, account]() {
EditBankAccountDialog dlg(account, this);
if (dlg.exec() == QDialog::Accepted) {
const QString cardNumber = dlg.cardNumberValue();
const QString currency = dlg.currencyValue();
bool on = dlg.toggled();
AccountInfo accountInfo;
accountInfo.currency = currency;
accountInfo.description = cardNumber;
accountInfo.amount = 0;
accountInfo.appCode = m_applicationInfo.code;
accountInfo.deviceId = m_deviceId;
accountInfo.status = on ? AccountStatus::Active : AccountStatus::Off;
accountInfo.lastNumbers = cardNumber.right(4);
if (!AccountDAO::upsertAccount(accountInfo)) {
qDebug() << "Cant insert bank account data";
} else {
reloadContent(tableWidget);
updateAccountData(accountInfo);
}
}
});
layout->addWidget(addAccountButton);
tableWidget->resizeRowsToContents();
// Добавим вертикальный Spacer в конец, чтобы прокручиваемая область не растягивала контент
// QSpacerItem *spacer = new QSpacerItem(20, 40, QSizePolicy::Minimum, QSizePolicy::Expanding);
// layout->addItem(spacer);
contentWidget->setLayout(layout);
scrollArea->setWidget(contentWidget);
mainLayout->addWidget(scrollArea);
setLayout(mainLayout);
}
void AccountSettingsWindow::startCheckPinCode(const QString &appCode) {
}
void AccountSettingsWindow::sendAppStatus(const QString &deviceId, const QString &code, const QString &status) {
QJsonObject obj;
obj["code"] = code;
obj["status"] = status;
auto *paymentThread = new QThread;
auto *networkService = new NetworkService;
networkService->moveToThread(paymentThread);
networkService->setDeviceId(deviceId);
networkService->setPayload(QJsonDocument(obj).toJson());
QObject::connect(paymentThread, &QThread::started, networkService, &NetworkService::postAppStatus);
QObject::connect(networkService, &NetworkService::finished, paymentThread, &QThread::quit);
QObject::connect(networkService, &NetworkService::finished, networkService, &QObject::deleteLater);
QObject::connect(paymentThread, &QThread::finished, paymentThread, &QThread::deleteLater);
paymentThread->start();
}
void AccountSettingsWindow::updateAccountData(const AccountInfo &account) {
QJsonArray list;
QJsonObject obj;
obj["appName"] = account.appCode;
obj["lastNumbers"] = account.lastNumbers;
obj["amount"] = account.amount;
obj["description"] = account.description;
obj["currency"] = account.currency;
obj["status"] = accountStatusToString(account.status);
list.append(obj);
auto *paymentThread = new QThread;
auto *networkService = new NetworkService;
networkService->moveToThread(paymentThread);
networkService->setDeviceId(account.deviceId);
networkService->setPayload(QJsonDocument(list).toJson());
QObject::connect(paymentThread, &QThread::started, networkService, &NetworkService::postAccountsData);
QObject::connect(networkService, &NetworkService::finished, paymentThread, &QThread::quit);
QObject::connect(networkService, &NetworkService::finished, networkService, &QObject::deleteLater);
QObject::connect(paymentThread, &QThread::finished, paymentThread, &QThread::deleteLater);
paymentThread->start();
}
void AccountSettingsWindow::reloadContent(QTableWidget *tableWidget) {
QList<AccountInfo> accounts = AccountDAO::getAccounts(m_deviceId, m_applicationInfo.code);
tableWidget->clearContents();
tableWidget->setRowCount(accounts.length());
tableWidget->setColumnCount(7);
tableWidget->setHorizontalHeaderLabels({
"Время обновления",
"Номер",
"",
"Сумма",
"Валюта",
"Статус",
""
});
tableWidget->setWordWrap(true);
tableWidget->setTextElideMode(Qt::ElideNone);
tableWidget->horizontalHeader()->setDefaultAlignment(Qt::AlignLeft | Qt::AlignVCenter);
tableWidget->horizontalHeader()->setSectionResizeMode(5, QHeaderView::ResizeToContents);
tableWidget->verticalHeader()->setSectionResizeMode(6, QHeaderView::ResizeToContents);
tableWidget->horizontalHeader()->setSectionResizeMode(1, QHeaderView::Stretch);
for (int k = 0; k < accounts.size(); ++k) {
const AccountInfo &account = accounts[k];
QTableWidgetItem *time = new QTableWidgetItem(account.updateTime.toString("dd.MM.yyyy HH:mm:ss"));
time->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
time->setFlags(time->flags() & ~Qt::ItemIsEditable);
tableWidget->setItem(k, 0, time);
QTableWidgetItem *desc = new QTableWidgetItem(account.description);
desc->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
desc->setFlags(desc->flags() & ~Qt::ItemIsEditable);
tableWidget->setItem(k, 1, desc);
QTableWidgetItem *lastNumbers = new QTableWidgetItem(account.lastNumbers);
lastNumbers->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
lastNumbers->setFlags(lastNumbers->flags() & ~Qt::ItemIsEditable);
tableWidget->setItem(k, 2, lastNumbers);
QTableWidgetItem *amount = new QTableWidgetItem(QString::number(account.amount));
amount->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
amount->setFlags(amount->flags() & ~Qt::ItemIsEditable);
tableWidget->setItem(k, 3, amount);
QTableWidgetItem *currency = new QTableWidgetItem(account.currency);
currency->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
currency->setFlags(currency->flags() & ~Qt::ItemIsEditable);
tableWidget->setItem(k, 4, currency);
QTableWidgetItem *status = new QTableWidgetItem(accountStatusToUiString(account.status));
status->setTextAlignment(Qt::AlignLeft | Qt::AlignVCenter);
status->setFlags(status->flags() & ~Qt::ItemIsEditable);
if (account.status != AccountStatus::Active) {
status->setForeground(QBrush(Qt::red));
}
tableWidget->setItem(k, 5, status);
auto *editBankAccountBtn = new QPushButton("Редактировать", tableWidget);
tableWidget->setCellWidget(k, 6, editBankAccountBtn);
connect(editBankAccountBtn, &QPushButton::clicked, this, [this, tableWidget, account]() {
EditBankAccountDialog dlg(account, this);
if (dlg.exec() == QDialog::Accepted) {
const QString cardNumber = dlg.cardNumberValue();
const QString currency = dlg.currencyValue();
bool on = dlg.toggled();
if (!AccountDAO::updateAccountById(account.id, on ? "active" : "off", cardNumber, currency)) {
qDebug() << "Cant update bank account data";
} else {
reloadContent(tableWidget);
AccountInfo accountInfo;
accountInfo.currency = currency;
accountInfo.description = cardNumber;
accountInfo.amount = account.amount;
accountInfo.appCode = m_applicationInfo.code;
accountInfo.deviceId = m_deviceId;
accountInfo.status = on ? AccountStatus::Active : AccountStatus::Off;
accountInfo.lastNumbers = cardNumber.right(4);
updateAccountData(accountInfo);
}
}
});
}
}

View File

@ -0,0 +1,30 @@
#pragma once
#include <QLabel>
#include <QTableWidget>
#include <QWidget>
#include "db/AccountInfo.h"
#include "db/ApplicationInfo.h"
class AccountSettingsWindow final : public QWidget {
Q_OBJECT
public:
AccountSettingsWindow(QWidget *parent, QString deviceId, QString deviceName, const ApplicationInfo &app);
private:
QString m_deviceId;
QString m_deviceName;
ApplicationInfo m_applicationInfo;
QLabel *m_statusLabel;
QLabel *m_commentLabel;
void startCheckPinCode(const QString &appCode);
void reloadContent(QTableWidget *tableWidget);
void sendAppStatus(const QString &deviceId, const QString &code, const QString &status);
void updateAccountData(const AccountInfo &account);
};

View File

@ -243,7 +243,7 @@ void BankSettingsWindow::reloadContent(QTableWidget *tableWidget) {
}
}
BankSettingsWindow::BankSettingsWindow(QWidget *parent, QString deviceId, QString deviceName)
BankSettingsWindow::BankSettingsWindow(QWidget *parent, QString deviceId, QString deviceName, const ApplicationInfo &app)
: QWidget(parent,
Qt::Window
| Qt::WindowTitleHint

View File

@ -1,12 +1,13 @@
#pragma once
#include <qtablewidget.h>
#include <QWidget>
#include "db/ApplicationInfo.h"
class BankSettingsWindow final : public QWidget {
Q_OBJECT
public:
BankSettingsWindow(QWidget *parent, QString deviceId, QString deviceName);
BankSettingsWindow(QWidget *parent, QString deviceId, QString deviceName, const ApplicationInfo &app);
private:
QString m_deviceId;

View File

@ -12,6 +12,7 @@
#include <QMessageBox>
#include <QThread>
#include "bank/AccountSettingsWindow.h"
#include "bank/BankSettingsWindow.h"
#include "dao/ApplicationDAO.h"
#include "db/ApplicationInfo.h"
@ -160,7 +161,7 @@ void DeviceWidget::buildBankRow(const ApplicationInfo &app, QWidget *rowWidget,
anim->start(QAbstractAnimation::DeleteWhenStopped);
});
setOpenBanksSettings(btn2);
setOpenBanksSettings(btn2, app);
hbox->addWidget(icon);
hbox->addWidget(label, 1);
@ -170,15 +171,15 @@ void DeviceWidget::buildBankRow(const ApplicationInfo &app, QWidget *rowWidget,
// hbox->addStretch();
}
void DeviceWidget::setOpenBanksSettings(QPushButton *button) {
void DeviceWidget::setOpenBanksSettings(QPushButton *button, const ApplicationInfo &app) {
QString deviceId = m_device.id;
QString deviceName = m_device.name;
connect(button, &QPushButton::clicked, this, [deviceId, deviceName]() {
static QPointer<BankSettingsWindow> bankSettingsWindow;
connect(button, &QPushButton::clicked, this, [deviceId, deviceName, app]() {
static QPointer<AccountSettingsWindow> bankSettingsWindow;
// Если окна нет (либо ещё не было создано, либо уже удалилось) — создаём заново
if (!bankSettingsWindow) {
bankSettingsWindow = new BankSettingsWindow(nullptr, deviceId, deviceName);
bankSettingsWindow = new AccountSettingsWindow(nullptr, deviceId, deviceName, app);
bankSettingsWindow->setWindowFlags(bankSettingsWindow->windowFlags() | Qt::Window);
bankSettingsWindow->setAttribute(Qt::WA_DeleteOnClose);
}
@ -249,16 +250,16 @@ DeviceWidget::DeviceWidget(const DeviceInfo &device, QWidget *parent): QWidget(p
layout->addWidget(appsView, 0, 0, Qt::AlignLeft | Qt::AlignBottom);
// layout->addWidget(appsView, 0, 0, Qt::AlignLeft); TODO так на всю ширину
auto *addBankBtn = new QPushButton(this);
addBankBtn->setMaximumWidth(300);
addBankBtn->setText("Добавить банк +");
addBankBtn->setStyleSheet(
"background-color: #fff;"
"margin-top: 2px;"
"padding: 4px;"
"color: #000;"
);
// auto *addBankBtn = new QPushButton(this);
// addBankBtn->setMaximumWidth(300);
// addBankBtn->setText("Добавить банк +");
// addBankBtn->setStyleSheet(
// "background-color: #fff;"
// "margin-top: 2px;"
// "padding: 4px;"
// "color: #000;"
// );
setOpenBanksSettings(addBankBtn);
layout->addWidget(addBankBtn, 1, 0);
// setOpenBanksSettings(addBankBtn);
// layout->addWidget(addBankBtn, 1, 0);
}

View File

@ -17,5 +17,5 @@ private:
void buildBankRow(const ApplicationInfo &app, QWidget *rowWidget, QWidget *parent);
void setOpenBanksSettings(QPushButton *button);
void setOpenBanksSettings(QPushButton *button, const ApplicationInfo &app);
};

View File

@ -0,0 +1,63 @@
#pragma once
#include <QDialog>
#include <QFormLayout>
#include <QLineEdit>
#include <QCheckBox>
#include <QDialogButtonBox>
#include <QComboBox>
#include <QSettings>
#include "db/AccountInfo.h"
class EditBankAccountDialog final : public QDialog {
Q_OBJECT
public:
explicit EditBankAccountDialog(const AccountInfo &account, QWidget *parent = nullptr) : QDialog(parent) {
if (account.id > 0) {
setWindowTitle("Настройка аккаунта *" + account.lastNumbers);
} else {
setWindowTitle("Настройка нового аккаунта");
}
setModal(true);
resize(300, 160);
const QSettings settings("config.ini", QSettings::IniFormat);
QStringList currencies = settings.value("common/currencies", QStringList()).toStringList();
auto *form = new QFormLayout(this);
cardNumber = new QLineEdit(this);
cardNumber->setText(account.description);
currency = new QComboBox(this);
for (QString c: currencies) {
currency->addItem(c);
}
currency->setCurrentText(account.currency);
toggle = new QCheckBox(tr("В работе"), this);
toggle->setChecked(account.status == AccountStatus::Active);
form->addRow(tr("Номер карты:"), cardNumber);
form->addRow(tr("Валюта:"), currency);
form->addRow(QString(), toggle);
auto *buttons = new QDialogButtonBox(Qt::Horizontal, this);
buttons->addButton(tr("Сохранить"), QDialogButtonBox::AcceptRole);
buttons->addButton(tr("Отмена"), QDialogButtonBox::RejectRole);
form->addRow(buttons);
connect(buttons, &QDialogButtonBox::accepted, this, &QDialog::accept);
connect(buttons, &QDialogButtonBox::rejected, this, &QDialog::reject);
}
QString cardNumberValue() const { return cardNumber->text(); }
QString currencyValue() const { return currency->currentText(); }
bool toggled() const { return toggle->isChecked(); }
private:
QLineEdit *cardNumber;
QComboBox *currency;
QCheckBox *toggle;
};

View File

@ -0,0 +1,21 @@
#include "ErrorWindow.h"
#include <QVBoxLayout>
#include <QPushButton>
#include <QLabel>
#include <QApplication>
ErrorWindow::ErrorWindow(const QString &errorMessage, QWidget *parent) : QWidget(parent) {
setWindowTitle("Ошибка");
resize(300, 150);
QVBoxLayout *layout = new QVBoxLayout(this);
QLabel *label = new QLabel(errorMessage);
label->setWordWrap(true);
layout->addWidget(label);
QPushButton *closeButton = new QPushButton("Закрыть");
layout->addWidget(closeButton);
connect(closeButton, &QPushButton::clicked, qApp, &QApplication::quit);
}

View File

@ -0,0 +1,10 @@
#pragma once
#include <QWidget>
class ErrorWindow final : public QWidget {
Q_OBJECT
public:
explicit ErrorWindow(const QString &errorMessage, QWidget *parent = nullptr);
};

View File

@ -0,0 +1,18 @@
#include "LoaderDialog.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QMovie>
LoaderDialog::LoaderDialog(QWidget *parent)
: QDialog(parent)
{
setWindowTitle("Загрузка...");
setModal(true);
setFixedSize(200, 100);
QVBoxLayout *layout = new QVBoxLayout(this);
QLabel *label = new QLabel("Пожалуйста, подождите...");
label->setAlignment(Qt::AlignCenter);
layout->addWidget(label);
}

View File

@ -0,0 +1,12 @@
#pragma once
#include <QDialog>
class QLabel;
class QMovie;
class LoaderDialog : public QDialog {
Q_OBJECT
public:
explicit LoaderDialog(QWidget *parent = nullptr);
};

View File

@ -0,0 +1,62 @@
#include "LoaderWindow.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QThread>
#include "net/NetworkService.h"
LoaderWindow::LoaderWindow(const QString &token, QWidget *parent) : QWidget(parent) {
setWindowTitle("Проверка приложения");
setFixedSize(300, 100);
QVBoxLayout *layout = new QVBoxLayout(this);
QLabel *label = new QLabel("Пожалуйста, подождите...");
layout->addWidget(label);
progressBar = new QProgressBar();
progressBar->setRange(0, 100);
layout->addWidget(progressBar);
timer = new QTimer(this);
connect(timer, &QTimer::timeout, this, &LoaderWindow::updateProgress);
timer->start(25);
auto *thread = new QThread;
auto *networkService = new NetworkService;
networkService->moveToThread(thread);
networkService->addExtra("token", token);
connect(thread, &QThread::started, networkService, &NetworkService::checkAppStatus);
connect(networkService, &NetworkService::finished, thread, &QThread::quit);
connect(networkService, &NetworkService::finished, networkService, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
connect(networkService, &NetworkService::finishedWithResult, [&](const int result) {
m_checkResult = result;
if (m_checkResult == -1) {
if (timer) {
QMetaObject::invokeMethod(timer, "stop", Qt::QueuedConnection);
}
QMetaObject::invokeMethod(this, [=]() {
emit loadingFinished(m_checkResult);
}, Qt::QueuedConnection);
} else if (progressBar->value() >= 100) {
QMetaObject::invokeMethod(this, [=]() {
emit loadingFinished(m_checkResult);
}, Qt::QueuedConnection);
}
});
thread->start();
}
void LoaderWindow::updateProgress() {
if (const int value = progressBar->value(); value < 100) {
progressBar->setValue(value + 1);
} else {
if (timer) {
timer->stop();
}
emit loadingFinished(m_checkResult);
}
}

View File

@ -0,0 +1,22 @@
#pragma once
#include <QProgressBar>
#include <QTimer>
class LoaderWindow final : public QWidget {
Q_OBJECT
public:
explicit LoaderWindow(const QString &token, QWidget *parent = nullptr);
signals:
void loadingFinished(int status);
private slots:
void updateProgress();
private:
QProgressBar *progressBar;
QTimer *timer;
int m_checkResult = 1000;
};

View File

@ -0,0 +1,74 @@
#include "RegistrationWindow.h"
#include <QVBoxLayout>
#include <QLabel>
#include <QLineEdit>
#include <QPushButton>
#include <QApplication>
#include <QDebug>
#include <QMessageBox>
#include <QThread>
#include "LoaderDialog.h"
#include "net/NetworkService.h"
RegistrationWindow::RegistrationWindow(QWidget *parent): QWidget(parent) {
setWindowTitle("Регистрация");
resize(300, 150);
auto *layout = new QVBoxLayout(this);
layout->addWidget(new QLabel("Введите данные:"));
QLineEdit *nameEdit = new QLineEdit();
nameEdit->setPlaceholderText("Имя");
layout->addWidget(nameEdit);
QLineEdit *emailEdit = new QLineEdit();
emailEdit->setPlaceholderText("Контакт");
layout->addWidget(emailEdit);
QHBoxLayout *buttonLayout = new QHBoxLayout();
QPushButton *registerButton = new QPushButton("Регистрация");
QPushButton *closeButton = new QPushButton("Закрыть");
buttonLayout->addWidget(registerButton);
buttonLayout->addWidget(closeButton);
layout->addLayout(buttonLayout);
connect(closeButton, &QPushButton::clicked, qApp, &QApplication::quit);
connect(registerButton, &QPushButton::clicked, this, [=]() {
QString name = nameEdit->text();
QString email = emailEdit->text();
auto *loader = new LoaderDialog(this);
loader->show();
auto *thread = new QThread;
auto *networkService = new NetworkService;
networkService->moveToThread(thread);
networkService->addExtra("name", name);
networkService->addExtra("email", email);
connect(thread, &QThread::started, networkService, &NetworkService::registerNewUser);
connect(networkService, &NetworkService::finished, thread, &QThread::quit);
connect(networkService, &NetworkService::finished, networkService, &QObject::deleteLater);
connect(thread, &QThread::finished, thread, &QThread::deleteLater);
connect(networkService, &NetworkService::finishedWithResult, [=](int result) {
QMetaObject::invokeMethod(this, [=]() {
loader->close();
loader->deleteLater();
if (result == 1) {
emit onRegisteredSuccess();
} else {
QMessageBox::critical(this, "Ошибка регистрации",
"Проверьте подключение к интернету или свяжитесь со службой поддержки");
}
}, Qt::QueuedConnection);
});
thread->start();
});
}

View File

@ -0,0 +1,13 @@
#pragma once
#include <QWidget>
class RegistrationWindow final : public QWidget {
Q_OBJECT
public:
explicit RegistrationWindow(QWidget *parent = nullptr);
signals:
void onRegisteredSuccess();
};