Added handling for event types (Balance, Phone) and respective processing logic in EventHandler. Introduced support for tracking and updating event statuses both locally and through network APIs. Enhanced data integrity and clarity by expanding the EventInfo model and associated database management.
281 lines
10 KiB
C++
281 lines
10 KiB
C++
#include "NetworkService.h"
|
||
|
||
#include <QUrlQuery>
|
||
#include <QHttpPart>
|
||
#include <QNetworkReply>
|
||
#include <QJsonObject>
|
||
#include <QJsonArray>
|
||
#include <QThread>
|
||
#include <QSettings>
|
||
#include <QTimer>
|
||
#include <QTimeZone>
|
||
|
||
#include "dao/AccountDAO.h"
|
||
#include "dao/ApplicationDAO.h"
|
||
#include "dao/DeviceDAO.h"
|
||
#include "dao/EventDAO.h"
|
||
#include "dao/TransactionDAO.h"
|
||
#include "db/EventInfo.h"
|
||
|
||
NetworkService::NetworkService(QObject *parent) : QObject(parent) {
|
||
const QSettings settings("config.ini", QSettings::IniFormat);
|
||
m_urlAccountUpdate = settings.value("net/accountUpdate").toString();
|
||
m_urlPaymentsGet = settings.value("net/paymentsGet").toString();
|
||
m_urlTransactionUpdate = settings.value("net/transactionUpdate").toString();
|
||
m_urlAppStatusUpdate = settings.value("net/appStatusUpdate").toString();
|
||
m_urlTransactionInsert = settings.value("net/transactionInsert").toString();
|
||
m_urlEventStatusUpdate = settings.value("net/eventStatusUpdate").toString();
|
||
m_manager = new QNetworkAccessManager(this);
|
||
m_desktopId = "DT88bokcwQ";
|
||
}
|
||
|
||
NetworkService::~NetworkService() = default;
|
||
|
||
// FIXME доделать работу с экстра
|
||
void NetworkService::addExtra(const QString &key, const QVariant &value) {
|
||
m_extraData[key] = value;
|
||
}
|
||
|
||
void addFormField(QHttpMultiPart *multiPart, const QString &fieldName, const QString &fieldValue) {
|
||
QHttpPart formField;
|
||
formField.setHeader(
|
||
QNetworkRequest::ContentDispositionHeader,QStringLiteral("form-data; name=\"%1\"").arg(fieldName)
|
||
);
|
||
formField.setBody(fieldValue.toUtf8());
|
||
multiPart->append(formField);
|
||
}
|
||
|
||
void addFormMetadata(QHttpMultiPart *multiPart, const QByteArray &fieldValue) {
|
||
QHttpPart jsonPart;
|
||
jsonPart.setHeader(QNetworkRequest::ContentTypeHeader, "application/json; charset=UTF-8");
|
||
jsonPart.setHeader(QNetworkRequest::ContentDispositionHeader, R"(form-data; name="metadata")");
|
||
jsonPart.setBody(fieldValue);
|
||
multiPart->append(jsonPart);
|
||
}
|
||
|
||
QJsonValue NetworkService::post(QHttpMultiPart *multiPart, const QString &path) {
|
||
const QUrl url(path);
|
||
const QNetworkRequest request(url);
|
||
|
||
QNetworkReply *reply = m_manager->post(request, multiPart);
|
||
multiPart->setParent(reply);
|
||
|
||
QEventLoop loop;
|
||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) {
|
||
loop.quit();
|
||
});
|
||
loop.exec(); // <- здесь и запускается event-loop потока
|
||
|
||
QJsonValue result;
|
||
if (reply->error() == QNetworkReply::NoError) {
|
||
const QByteArray raw = reply->readAll();
|
||
QJsonParseError err;
|
||
const QJsonDocument doc = QJsonDocument::fromJson(raw, &err);
|
||
if (err.error == QJsonParseError::NoError && doc.isObject()) {
|
||
const QJsonObject root = doc.object();
|
||
if (root.value("success").toBool()) {
|
||
result = root.value("data");
|
||
} else {
|
||
qCritical() << "Ошибка:" << root;
|
||
}
|
||
}
|
||
} else {
|
||
qCritical() << "Ошибка:" << reply->errorString() << path;
|
||
}
|
||
reply->deleteLater();
|
||
return result;
|
||
}
|
||
|
||
QJsonArray NetworkService::get(const QString &path, const QMap<QString, QString> ¶ms) {
|
||
QUrl url(path);
|
||
QUrlQuery query;
|
||
for (auto it = params.constBegin(); it != params.constEnd(); ++it) {
|
||
query.addQueryItem(it.key(), it.value());
|
||
}
|
||
url.setQuery(query);
|
||
|
||
const QNetworkRequest request(url);
|
||
QNetworkReply *reply = m_manager->get(request);
|
||
QScopedPointer<QNetworkReply, QScopedPointerDeleteLater> guard(reply);
|
||
|
||
QEventLoop loop;
|
||
connect(reply, &QNetworkReply::finished, &loop, &QEventLoop::quit);
|
||
connect(reply, &QNetworkReply::errorOccurred, [&loop](QNetworkReply::NetworkError) {
|
||
loop.quit();
|
||
});
|
||
loop.exec(); // <- здесь и запускается event-loop потока
|
||
|
||
QJsonArray result;
|
||
if (reply->error() == QNetworkReply::NoError) {
|
||
const QByteArray raw = reply->readAll();
|
||
QJsonParseError err;
|
||
const QJsonDocument doc = QJsonDocument::fromJson(raw, &err);
|
||
if (err.error == QJsonParseError::NoError && doc.isObject()) {
|
||
const QJsonObject root = doc.object();
|
||
if (root.value("success").toBool()) {
|
||
result = root.value("data").toArray();
|
||
} else {
|
||
qWarning() << "Ошибка:" << root;
|
||
}
|
||
}
|
||
} else {
|
||
qWarning() << "Ошибка:" << reply->errorString();
|
||
}
|
||
reply->deleteLater();
|
||
return result;
|
||
}
|
||
|
||
void NetworkService::postAccountsData() {
|
||
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||
|
||
addFormMetadata(multiPart, m_json);
|
||
addFormField(multiPart, "desktopUniqueName", m_desktopId);
|
||
addFormField(multiPart, "androidUniqueName", m_deviceId);
|
||
const bool isSuccess = post(multiPart, m_urlAccountUpdate).toBool();
|
||
if (!isSuccess) {
|
||
qCritical() << "Cant post accounts data";
|
||
}
|
||
|
||
emit finished();
|
||
}
|
||
|
||
void NetworkService::getPayments() {
|
||
QMap<QString, QString> params;
|
||
params["desktopUniqueName"] = m_desktopId;
|
||
const QJsonArray data = get(m_urlPaymentsGet, params);
|
||
|
||
for (QJsonValue item: data) {
|
||
QJsonObject obj = item.toObject();
|
||
EventInfo eventInfo;
|
||
eventInfo.status = EventStatus::Wait;
|
||
eventInfo.type = eventTypeFromString(obj["type"].toString());
|
||
eventInfo.externalId = obj["id"].toInt();
|
||
eventInfo.timestamp = QDateTime::fromMSecsSinceEpoch(obj["timestamp"].toDouble(), QTimeZone::utc());
|
||
eventInfo.amount = obj["amount"].toDouble();
|
||
eventInfo.bankName = obj["bankName"].toString();
|
||
eventInfo.phone = obj["phone"].toString();
|
||
|
||
QString deviceId = obj["deviceId"].toString();
|
||
QString appName = obj["appName"].toString();
|
||
QString lastNumbers = obj["lastNumbers"].toString();
|
||
AccountInfo account = AccountDAO::findAppAccount(deviceId, appName, lastNumbers);
|
||
|
||
eventInfo.accountId = account.id;
|
||
eventInfo.deviceId = account.deviceId;
|
||
|
||
if (int id = EventDAO::insertEvent(eventInfo); id == -1) {
|
||
qCritical() << "Cant insert event: " << eventInfo.externalId;
|
||
}
|
||
}
|
||
|
||
if (!m_running) {
|
||
emit finished();
|
||
}
|
||
}
|
||
|
||
void NetworkService::postAppStatus() {
|
||
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||
|
||
addFormMetadata(multiPart, m_json);
|
||
addFormField(multiPart, "desktopUniqueName", m_desktopId);
|
||
addFormField(multiPart, "androidUniqueName", m_deviceId);
|
||
|
||
const bool isSuccess = post(multiPart, m_urlAppStatusUpdate).toBool();
|
||
if (!isSuccess) {
|
||
qCritical() << "Cant post data: postAppStatus";
|
||
}
|
||
|
||
emit finished();
|
||
}
|
||
|
||
void NetworkService::postEventStatus() {
|
||
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||
|
||
addFormMetadata(multiPart, m_json);
|
||
|
||
const bool isSuccess = post(multiPart, m_urlEventStatusUpdate).toBool();
|
||
if (!isSuccess) {
|
||
qCritical() << "Cant post data: postEventStatus";
|
||
}
|
||
|
||
emit finished();
|
||
}
|
||
|
||
void NetworkService::updateTransactionData() {
|
||
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||
|
||
addFormMetadata(multiPart, m_json);
|
||
addFormField(multiPart, "desktopUniqueName", m_desktopId);
|
||
addFormField(multiPart, "androidUniqueName", m_deviceId);
|
||
addFormField(multiPart, "lastNumbers", m_extraData["lastNumbers"].toString());
|
||
|
||
const bool isSuccess = post(multiPart, m_urlTransactionUpdate).toBool();
|
||
if (!isSuccess) {
|
||
qCritical() << "Cant update transaction: updateTransactionData";
|
||
}
|
||
|
||
emit finished();
|
||
}
|
||
|
||
void NetworkService::insertTransactionData() {
|
||
QHttpMultiPart *multiPart = new QHttpMultiPart(QHttpMultiPart::FormDataType);
|
||
|
||
addFormMetadata(multiPart, m_json);
|
||
addFormField(multiPart, "desktopUniqueName", m_desktopId);
|
||
addFormField(multiPart, "androidUniqueName", m_deviceId);
|
||
addFormField(multiPart, "lastNumbers", m_extraData["lastNumbers"].toString());
|
||
|
||
const int id = post(multiPart, m_urlTransactionInsert).toInt();
|
||
if (id > 0) {
|
||
const int transactionId = m_extraData["transactionId"].toInt();
|
||
if (!TransactionDAO::updateExternalTransactionId(transactionId, id)) {
|
||
qCritical() << "Cant update transaction id: " << id;
|
||
}
|
||
}
|
||
emit finished();
|
||
}
|
||
|
||
void NetworkService::start() {
|
||
QList<DeviceInfo> devices = DeviceDAO::getAllDevices(true);
|
||
for (DeviceInfo &device: devices) {
|
||
QList<ApplicationInfo> applications = ApplicationDAO::getApplicationsByDeviceId(device.id);
|
||
for (ApplicationInfo &app: applications) {
|
||
if (app.pinCodeChecked) {
|
||
QList<AccountInfo> accounts = AccountDAO::getAccounts(device.id, app.code);
|
||
QJsonArray list;
|
||
|
||
for (const AccountInfo &account: accounts) {
|
||
QJsonObject obj;
|
||
obj["appName"] = account.appCode;
|
||
obj["lastNumbers"] = account.lastNumbers;
|
||
obj["amount"] = account.amount;
|
||
obj["description"] = account.description;
|
||
obj["status"] = accountStatusToString(account.status);
|
||
list.append(obj);
|
||
}
|
||
|
||
auto *paymentThread = new QThread;
|
||
auto *networkService = new NetworkService;
|
||
networkService->setDeviceId(device.id);
|
||
networkService->setPayload(QJsonDocument(list).toJson());
|
||
networkService->moveToThread(paymentThread);
|
||
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();
|
||
}
|
||
}
|
||
}
|
||
|
||
QTimer *poller = new QTimer(this);
|
||
poller->setInterval(1000);
|
||
connect(poller, &QTimer::timeout, this, &NetworkService::getPayments);
|
||
poller->start();
|
||
}
|
||
|
||
void NetworkService::stop() {
|
||
m_running = false;
|
||
}
|