1
0
forked from BRT/arc
arc/main.cpp
trnsmkot 73c62495a2 Remove CommonBirbankScript and its derived classes from the codebase
Deleted `CommonBirbankScript` and its associated derived classes (`GetLastDaysHistoryBirbankScript`, `LoginAndCheckAccountsBirbankScript`, `PayByCardBirbankScript`). Removed dependencies on these scripts, including XML parsing, account handling, and transaction workflows related to the Birbank app.
2026-03-21 17:41:05 +07:00

374 lines
16 KiB
C++
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

#include <QApplication>
#include <QThread>
#include "dao/AccountDAO.h"
#include "views/MainWindow.h"
#include "database/DatabaseManager.h"
#include "database/dao/CommonDataDAO.h"
#include "database/dao/DeviceDAO.h"
#include "services/DeviceScreener.h"
#include <atomic>
#include <iostream>
#include <QPalette>
#include <QStyleFactory>
#include <thread>
#include <chrono>
#include <QFile>
#include <QSettings>
#include "AppLogger.h"
#include "dao/GeneralLogDAO.h"
#include "dao/SettingsDAO.h"
#include "EventHandler.h"
#include "net/NetworkService.h"
#include "black/PayByCardScript.h"
#include "ozon/PayByPhoneScript.h"
#include "widget/loader/ErrorWindow.h"
#include "widget/loader/LoaderWindow.h"
#include "widget/loader/RegistrationWindow.h"
#include <QMessageBox>
#include <QPushButton>
#include <QFileDialog>
void startDeviceScreener(const QCoreApplication &app) {
auto *thread = new QThread();
auto *deviceScreenerWorker = new DeviceScreener;
// Перемещаем worker в новый поток thread.
// Это значит, что все слоты worker, вызываемые через connect(), теперь будут исполняться в этом фоне-потоке.
deviceScreenerWorker->moveToThread(thread);
// Когда поток запустится, автоматически вызовется worker->start() внутри фонового потока.
QObject::connect(thread, &QThread::started, deviceScreenerWorker, &DeviceScreener::start);
// 🔁 Эти 3 строки делают автоматическую зачистку:
// Когда worker закончит работу (emit finished()), мы:
// - останавливаем поток (thread->quit())
// - удаляем worker (deleteLater)
// - удаляем thread (deleteLater)
// Это защищает от утечек памяти и оставшихся потоков после завершения.
QObject::connect(deviceScreenerWorker, &DeviceScreener::finished, thread, &QThread::quit);
QObject::connect(deviceScreenerWorker, &DeviceScreener::finished, deviceScreenerWorker, &QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
// Когда пользователь закрывает приложение (Ctrl+C, exit() и т.п.), мы вызываем worker->stop() — ты сам пишешь, что в этом методе.
//Обычно он просто ставит флаг m_running = false, чтобы остановить бесконечный цикл в start()
QObject::connect(&app, &QCoreApplication::aboutToQuit, [=]() {
deviceScreenerWorker->stop();
});
thread->start();
}
void startEventHandler(const QCoreApplication &app) {
auto *thread = new QThread;
auto *eventHandler = new EventHandler;
eventHandler->moveToThread(thread);
QObject::connect(thread, &QThread::started, eventHandler, &EventHandler::start);
QObject::connect(eventHandler, &EventHandler::finished, thread, &QThread::quit);
QObject::connect(eventHandler, &EventHandler::finished, eventHandler, &QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
QObject::connect(&app, &QCoreApplication::aboutToQuit, [=]() {
eventHandler->stop();
});
thread->start();
}
void startPayByPhoneTest() {
// Берём первый девайс с ozon аккаунтом
QList<AccountInfo> accounts = AccountDAO::getAccountsByAppCode("ozon");
if (accounts.isEmpty()) {
qWarning() << "[PayByPhoneTest] No ozon accounts found";
return;
}
AccountInfo account = accounts.first();
qDebug() << "[PayByPhoneTest] Using account:" << account.id << account.deviceId << account.lastNumbers;
auto *thread = new QThread(nullptr);
auto *worker = new Ozon::PayByPhoneScript(
account,
"+79855532896",
"Альфа-Банк",
150,
nullptr
);
worker->moveToThread(thread);
QObject::connect(thread, &QThread::started, worker, &Ozon::PayByPhoneScript::start);
QObject::connect(worker, &Ozon::PayByPhoneScript::finished, thread, &QThread::quit);
QObject::connect(worker, &Ozon::PayByPhoneScript::finished, worker, &QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater);
QObject::connect(worker, &Ozon::PayByPhoneScript::finishedWithResult, [](const QString &result) {
qDebug() << "[PayByPhoneTest] Result:" << result;
});
thread->start();
}
void startBlackPayByCardTest() {
QList<AccountInfo> accounts = AccountDAO::getAccountsByAppCode("black");
if (accounts.isEmpty()) {
qWarning() << "[BlackPayByCardTest] No black accounts found";
return;
}
AccountInfo account = accounts.first();
qDebug() << "[BlackPayByCardTest] Using account:" << account.id << account.deviceId << account.lastNumbers;
auto *thread = new QThread(nullptr);
auto *worker = new Black::PayByCardScript(
account,
"0070359630004039066690",
500,
nullptr
);
worker->moveToThread(thread);
QObject::connect(thread, &QThread::started, worker, &Black::PayByCardScript::start);
QObject::connect(worker, &Black::PayByCardScript::finished, thread, &QThread::quit);
QObject::connect(worker, &Black::PayByCardScript::finished, worker, &QObject::deleteLater);
QObject::connect(thread, &QThread::finished, thread, &QObject::deleteLater);
QObject::connect(worker, &Black::PayByCardScript::finishedWithResult, [](const QString &result) {
qDebug() << "[BlackPayByCardTest] Result:" << result;
});
thread->start();
}
void startNetworkService(const QCoreApplication &app) {
auto *paymentThread = new QThread;
auto *networkService = new NetworkService;
AppLogger::setNetworkService(networkService);
networkService->moveToThread(paymentThread);
QObject::connect(paymentThread, &QThread::started, networkService, &NetworkService::start);
QObject::connect(networkService, &NetworkService::finished, paymentThread, &QThread::quit);
QObject::connect(networkService, &NetworkService::finished, networkService, &QObject::deleteLater);
QObject::connect(paymentThread, &QThread::finished, paymentThread, &QThread::deleteLater);
QObject::connect(&app, &QCoreApplication::aboutToQuit, [=]() {
networkService->stop();
});
// Сигнал эмитируется из фонового потока → Qt::QueuedConnection доставит его в главный поток
QObject::connect(networkService, &NetworkService::tokenRefreshFailed, qApp, []() {
QMessageBox *box = new QMessageBox(
QMessageBox::Critical,
"Ошибка авторизации",
"Не удалось обновить токен авторизации.\n\n"
"Проверьте подключение к интернету.\n"
"Приложение продолжит попытки автоматически.",
QMessageBox::Ok
);
box->setAttribute(Qt::WA_DeleteOnClose);
box->show();
}, Qt::QueuedConnection);
paymentThread->start();
}
static QFile logFile;
static QMutex logMutex;
static std::atomic<bool> dbReady{false};
void messageHandler(
const QtMsgType type,
const QMessageLogContext &context,
const QString &msg
) {
Q_UNUSED(context)
const QString timestamp = QDateTime::currentDateTime().toString("yyyy-MM-dd hh:mm:ss.zzz");
QString level;
switch (type) {
case QtDebugMsg: level = "DEBUG";
break;
case QtInfoMsg: level = "INFO";
break;
case QtWarningMsg: level = "WARNING";
break;
case QtCriticalMsg: level = "CRITICAL";
break;
case QtFatalMsg: level = "FATAL";
break;
}
const QString source = context.function ? QString("%1:%2").arg(context.function).arg(context.line) : QString();
const QString line = QString("%1 [%2] [%3:%4] %5\n")
.arg(timestamp, level, context.function, QString::number(context.line), msg);
// Записываем в файл (потокобезопасно)
{
QMutexLocker locker(&logMutex);
if (logFile.isOpen()) {
QTextStream(&logFile) << line;
logFile.flush();
}
}
// Дублируем в БД (с защитой от рекурсии)
if (dbReady.load()) {
static thread_local bool writing = false;
if (!writing) {
writing = true;
GeneralLogDAO::insert(level, source, msg);
writing = false;
}
}
// По желанию — дублировать в консоль
const QByteArray localMsg = line.toLocal8Bit();
fprintf(stderr, "%s", localMsg.constData());
if (type == QtFatalMsg) {
abort();
}
}
int main(int argc, char *argv[]) {
QApplication app(argc, argv);
// Выбрать стиль Fusion + принудительная светлая тема
QApplication::setStyle(QStyleFactory::create("Fusion"));
QPalette lightPalette;
lightPalette.setColor(QPalette::Window, QColor(240, 240, 240));
lightPalette.setColor(QPalette::WindowText, Qt::black);
lightPalette.setColor(QPalette::Base, Qt::white);
lightPalette.setColor(QPalette::AlternateBase, QColor(233, 233, 233));
lightPalette.setColor(QPalette::ToolTipBase, QColor(255, 255, 220));
lightPalette.setColor(QPalette::ToolTipText, Qt::black);
lightPalette.setColor(QPalette::Text, Qt::black);
lightPalette.setColor(QPalette::Button, QColor(240, 240, 240));
lightPalette.setColor(QPalette::ButtonText, Qt::black);
lightPalette.setColor(QPalette::BrightText, Qt::red);
lightPalette.setColor(QPalette::Link, QColor(0, 0, 255));
lightPalette.setColor(QPalette::Highlight, QColor(76, 163, 224));
lightPalette.setColor(QPalette::HighlightedText, Qt::white);
QApplication::setPalette(lightPalette);
logFile.setFileName("application.log");
if (!logFile.open(QIODevice::Append | QIODevice::Text)) {
qWarning() << "Не удалось открыть лог-файл для записи";
}
// Устанавливаем свой обработчик
qInstallMessageHandler(messageHandler);
// Проверяем наличие файла БД, если нет — предлагаем загрузить
{
QSettings settings("config.ini", QSettings::IniFormat);
QString dbPath = settings.value("db/dbPath").toString();
if (dbPath.startsWith("~"))
dbPath.replace(0, 1, QDir::homePath());
if (!QFile::exists(dbPath)) {
QMessageBox msgBox;
msgBox.setWindowTitle("База данных не найдена");
msgBox.setText("Файл базы данных не найден:\n" + dbPath);
QPushButton *selectBtn = msgBox.addButton("Выбрать файл", QMessageBox::AcceptRole);
QPushButton *createBtn = msgBox.addButton("Создать новую", QMessageBox::ActionRole);
QPushButton *exitBtn = msgBox.addButton("Выход", QMessageBox::RejectRole);
msgBox.setDefaultButton(selectBtn);
msgBox.exec();
if (msgBox.clickedButton() == selectBtn) {
QString selected = QFileDialog::getOpenFileName(
nullptr, "Выберите файл базы данных", QDir::homePath(), "SQLite (*.db *.sqlite);;Все файлы (*)");
if (selected.isEmpty()) {
return 0;
}
QDir().mkpath(QFileInfo(dbPath).absolutePath());
if (!QFile::copy(selected, dbPath)) {
QMessageBox::critical(nullptr, "Ошибка", "Не удалось скопировать файл базы данных.");
return 1;
}
} else if (msgBox.clickedButton() == createBtn) {
// Просто продолжаем — initSchema создаст новую БД
} else {
return 0;
}
}
}
// Миграции БД
DatabaseManager::instance().initSchema();
dbReady.store(true);
// 1. Если есть api_key — логинимся и проверяем/создаём desktop
// - ошибка сети или сервера [code -1]
// - неверный api_key [code 0] — показываем окно ввода
// - всё ок [code 1] — запускаем приложение
// 2. Если нет api_key — показываем окно для ввода
const QString apiKey = SettingsDAO::get("api_key");
auto showRegistration = [&app]() {
auto *registrationWindow = new RegistrationWindow();
registrationWindow->setAttribute(Qt::WA_DeleteOnClose);
registrationWindow->show();
QObject::connect(
registrationWindow, &RegistrationWindow::onRegisteredSuccess, registrationWindow,
[registrationWindow, &app]() {
auto *mainWindow = new MainWindow();
mainWindow->show();
registrationWindow->close();
startNetworkService(app);
startDeviceScreener(app);
startEventHandler(app);
// startBlackPayByCardTest();
}, Qt::QueuedConnection);
};
if (apiKey.isEmpty()) {
showRegistration();
} else {
auto *loader = new LoaderWindow();
loader->setAttribute(Qt::WA_DeleteOnClose);
loader->show();
QObject::connect(loader, &LoaderWindow::loadingFinished, loader, [loader, &app, showRegistration](const int result) {
if (result == -1) {
auto *errorWindow = new ErrorWindow(
"Сервер не отвечает.\n\nПроверьте подключение к интернету и попробуйте снова.",
true
);
errorWindow->setAttribute(Qt::WA_DeleteOnClose);
errorWindow->show();
QObject::connect(errorWindow, &ErrorWindow::retryRequested, errorWindow, [errorWindow, &app, showRegistration]() {
errorWindow->close();
auto *newLoader = new LoaderWindow();
newLoader->setAttribute(Qt::WA_DeleteOnClose);
newLoader->show();
QObject::connect(newLoader, &LoaderWindow::loadingFinished, newLoader, [newLoader, &app, showRegistration](const int r) {
if (r == 1) {
auto *mainWindow = new MainWindow();
mainWindow->show();
startNetworkService(app);
startDeviceScreener(app);
startEventHandler(app);
// startBlackPayByCardTest();
} else if (r == 0) {
SettingsDAO::remove("api_key");
showRegistration();
} else {
auto *err2 = new ErrorWindow(
"Сервер не отвечает.\n\nПроверьте подключение к интернету и попробуйте снова.",
false
);
err2->setAttribute(Qt::WA_DeleteOnClose);
err2->show();
}
newLoader->close();
}, Qt::QueuedConnection);
});
} else if (result == 1) {
auto *mainWindow = new MainWindow();
mainWindow->show();
startNetworkService(app);
startDeviceScreener(app);
startEventHandler(app);
// startBlackPayByCardTest();
} else if (result == 0) {
SettingsDAO::remove("api_key");
showRegistration();
}
loader->close();
}, Qt::QueuedConnection);
}
return QApplication::exec();
}