Implemented database migration for adding new fields (`json`, `sent_to_server`) in `events` table. Updated DAO methods to support the new fields and introduced `markSentToServer`. Enhanced Ozon transaction workflows with OCR-based extraction, event error handling improvements, and result reporting. Refactored `EventHandler` for streamlined task processing and stale event cleanup.
212 lines
9.1 KiB
C++
212 lines
9.1 KiB
C++
#include "EventWindow.h"
|
||
|
||
#include <QDialog>
|
||
#include <QHBoxLayout>
|
||
#include <QHeaderView>
|
||
#include <QJsonDocument>
|
||
#include <QJsonObject>
|
||
#include <QPlainTextEdit>
|
||
#include <QThread>
|
||
#include <QVBoxLayout>
|
||
|
||
#include "dao/EventDAO.h"
|
||
#include "net/NetworkService.h"
|
||
|
||
static constexpr int COL_ID = 0;
|
||
static constexpr int COL_TIME = 1;
|
||
static constexpr int COL_TYPE = 2;
|
||
static constexpr int COL_BANK = 3;
|
||
static constexpr int COL_DEVICE = 4;
|
||
static constexpr int COL_STATUS = 5;
|
||
static constexpr int COL_AMOUNT = 6;
|
||
static constexpr int COL_COMMENT = 7;
|
||
static constexpr int COL_JSON = 8;
|
||
static constexpr int COL_SENT = 9;
|
||
static constexpr int COL_ACTION = 10;
|
||
|
||
static void sendErrorToServer(const QString &type, const QString &materialId, const QString &bankName) {
|
||
QJsonObject errData;
|
||
errData["status"] = "error";
|
||
errData["description"] = "Closed manually";
|
||
|
||
QJsonObject obj;
|
||
obj["type"] = type;
|
||
obj["material_id"] = materialId;
|
||
obj["bank_name"] = bankName;
|
||
obj["data"] = errData;
|
||
|
||
auto *thread = new QThread;
|
||
auto *networkService = new NetworkService;
|
||
networkService->moveToThread(thread);
|
||
networkService->setPayload(QJsonDocument(obj).toJson());
|
||
QObject::connect(thread, &QThread::started, networkService, &NetworkService::postTaskResult);
|
||
QObject::connect(networkService, &NetworkService::finished, thread, &QThread::quit);
|
||
QObject::connect(networkService, &NetworkService::finished, networkService, &QObject::deleteLater);
|
||
QObject::connect(thread, &QThread::finished, thread, &QThread::deleteLater);
|
||
thread->start();
|
||
}
|
||
|
||
static QString statusDisplayName(EventStatus s) {
|
||
switch (s) {
|
||
case EventStatus::Wait: return "Ожидание";
|
||
case EventStatus::InProgress: return "В работе";
|
||
case EventStatus::Complete: return "Завершено";
|
||
case EventStatus::Error: return "Ошибка";
|
||
default: return "—";
|
||
}
|
||
}
|
||
|
||
static QString typeDisplayName(EventType t) {
|
||
switch (t) {
|
||
case EventType::FetchProfile: return "Профиль";
|
||
case EventType::FetchTransactions: return "Транзакции";
|
||
case EventType::CreateTransaction: return "Платёж";
|
||
default: return "—";
|
||
}
|
||
}
|
||
|
||
EventWindow::EventWindow(QWidget *parent) : QDialog(parent) {
|
||
setWindowTitle("События");
|
||
resize(1400, 600);
|
||
|
||
// ── Таблица ───────────────────────────────────────────────────────────────
|
||
m_table = new QTableWidget(this);
|
||
m_table->setColumnCount(11);
|
||
m_table->setHorizontalHeaderLabels({
|
||
"#", "Время", "Тип", "Банк", "Устройство", "Статус", "Сумма", "Комментарий", "JSON", "Отпр.", ""
|
||
});
|
||
m_table->setEditTriggers(QAbstractItemView::NoEditTriggers);
|
||
m_table->setSelectionBehavior(QAbstractItemView::SelectRows);
|
||
m_table->setSelectionMode(QAbstractItemView::SingleSelection);
|
||
m_table->verticalHeader()->hide();
|
||
m_table->horizontalHeader()->setStretchLastSection(true);
|
||
m_table->setColumnWidth(COL_ID, 45);
|
||
m_table->setColumnWidth(COL_TIME, 145);
|
||
m_table->setColumnWidth(COL_TYPE, 100);
|
||
m_table->setColumnWidth(COL_BANK, 90);
|
||
m_table->setColumnWidth(COL_DEVICE, 120);
|
||
m_table->setColumnWidth(COL_STATUS, 100);
|
||
m_table->setColumnWidth(COL_AMOUNT, 80);
|
||
m_table->setColumnWidth(COL_JSON, 200);
|
||
m_table->setColumnWidth(COL_SENT, 45);
|
||
m_table->setColumnWidth(COL_ACTION, 80);
|
||
|
||
// ── Пагинация ─────────────────────────────────────────────────────────────
|
||
m_prevBtn = new QPushButton("←", this);
|
||
m_nextBtn = new QPushButton("→", this);
|
||
m_pageLabel = new QLabel(this);
|
||
m_pageLabel->setAlignment(Qt::AlignCenter);
|
||
m_pageLabel->setMinimumWidth(140);
|
||
|
||
auto *refreshBtn = new QPushButton("Обновить", this);
|
||
|
||
auto *paginationLayout = new QHBoxLayout;
|
||
paginationLayout->addWidget(m_prevBtn);
|
||
paginationLayout->addWidget(m_pageLabel);
|
||
paginationLayout->addWidget(m_nextBtn);
|
||
paginationLayout->addStretch();
|
||
paginationLayout->addWidget(refreshBtn);
|
||
|
||
auto *mainLayout = new QVBoxLayout(this);
|
||
mainLayout->addWidget(m_table, 1);
|
||
mainLayout->addLayout(paginationLayout);
|
||
|
||
// ── Сигналы ───────────────────────────────────────────────────────────────
|
||
connect(m_prevBtn, &QPushButton::clicked, this, [this]() {
|
||
if (m_page > 0) { --m_page; loadPage(); }
|
||
});
|
||
connect(m_nextBtn, &QPushButton::clicked, this, [this]() {
|
||
if (m_page < m_totalPages - 1) { ++m_page; loadPage(); }
|
||
});
|
||
connect(refreshBtn, &QPushButton::clicked, this, [this]() {
|
||
m_page = 0;
|
||
loadPage();
|
||
});
|
||
connect(m_table, &QTableWidget::cellDoubleClicked, this, [this](int row, int col) {
|
||
if (col != COL_JSON) return;
|
||
auto *item = m_table->item(row, col);
|
||
if (!item) return;
|
||
const QString json = item->toolTip();
|
||
if (json.isEmpty()) return;
|
||
|
||
auto *dlg = new QDialog(this);
|
||
dlg->setWindowTitle("JSON");
|
||
dlg->resize(600, 400);
|
||
auto *edit = new QPlainTextEdit(dlg);
|
||
edit->setReadOnly(true);
|
||
edit->setPlainText(QJsonDocument::fromJson(json.toUtf8()).toJson(QJsonDocument::Indented));
|
||
auto *layout = new QVBoxLayout(dlg);
|
||
layout->addWidget(edit);
|
||
dlg->setAttribute(Qt::WA_DeleteOnClose);
|
||
dlg->show();
|
||
});
|
||
|
||
loadPage();
|
||
}
|
||
|
||
void EventWindow::loadPage() {
|
||
const int total = EventDAO::getTotalCount();
|
||
m_totalPages = qMax(1, (total + m_pageSize - 1) / m_pageSize);
|
||
m_page = qBound(0, m_page, m_totalPages - 1);
|
||
|
||
const QList<EventInfo> events = EventDAO::getEvents(m_page, m_pageSize);
|
||
|
||
m_table->setRowCount(static_cast<int>(events.size()));
|
||
for (int i = 0; i < events.size(); ++i) {
|
||
const auto &e = events[i];
|
||
m_table->setItem(i, COL_ID, new QTableWidgetItem(QString::number(e.id)));
|
||
m_table->setItem(i, COL_TIME, new QTableWidgetItem(
|
||
e.timestamp.toLocalTime().toString("dd.MM.yy HH:mm:ss")));
|
||
m_table->setItem(i, COL_TYPE, new QTableWidgetItem(typeDisplayName(e.type)));
|
||
m_table->setItem(i, COL_BANK, new QTableWidgetItem(e.bankName));
|
||
m_table->setItem(i, COL_DEVICE, new QTableWidgetItem(e.deviceId));
|
||
m_table->setItem(i, COL_STATUS, new QTableWidgetItem(statusDisplayName(e.status)));
|
||
m_table->setItem(i, COL_AMOUNT, new QTableWidgetItem(
|
||
e.amount > 0 ? QString::number(e.amount, 'f', 2) : "—"));
|
||
m_table->setItem(i, COL_COMMENT, new QTableWidgetItem(e.comment));
|
||
|
||
auto *jsonItem = new QTableWidgetItem(e.json.left(80));
|
||
jsonItem->setToolTip(e.json);
|
||
m_table->setItem(i, COL_JSON, jsonItem);
|
||
|
||
m_table->setItem(i, COL_SENT, new QTableWidgetItem(e.sentToServer ? "+" : ""));
|
||
|
||
// Подсветка строки по статусу
|
||
QColor bg;
|
||
switch (e.status) {
|
||
case EventStatus::Error: bg = QColor(255, 230, 230); break;
|
||
case EventStatus::Complete: bg = QColor(230, 255, 230); break;
|
||
case EventStatus::InProgress: bg = QColor(230, 240, 255); break;
|
||
default: bg = QColor(255, 255, 230); break;
|
||
}
|
||
for (int col = 0; col < m_table->columnCount(); ++col) {
|
||
if (auto *item = m_table->item(i, col))
|
||
item->setBackground(bg);
|
||
}
|
||
|
||
// Кнопка "Закрыть" только для активных задач
|
||
if (e.status == EventStatus::Wait || e.status == EventStatus::InProgress) {
|
||
auto *closeBtn = new QPushButton("Закрыть");
|
||
closeBtn->setStyleSheet("background: #e74c3c; color: white; border: none; padding: 3px 8px; border-radius: 3px;");
|
||
const int eventId = e.id;
|
||
const QString externalId = e.externalId;
|
||
const QString bankName = e.bankName;
|
||
const QString typeStr = eventTypeToString(e.type);
|
||
connect(closeBtn, &QPushButton::clicked, this, [this, eventId, externalId, bankName, typeStr]() {
|
||
EventDAO::updateEvent(eventId, EventStatus::Error, "Closed manually");
|
||
sendErrorToServer(typeStr, externalId, bankName);
|
||
loadPage();
|
||
});
|
||
m_table->setCellWidget(i, COL_ACTION, closeBtn);
|
||
}
|
||
}
|
||
|
||
updatePagination();
|
||
}
|
||
|
||
void EventWindow::updatePagination() {
|
||
m_pageLabel->setText(QString("Страница %1 из %2").arg(m_page + 1).arg(m_totalPages));
|
||
m_prevBtn->setEnabled(m_page > 0);
|
||
m_nextBtn->setEnabled(m_page < m_totalPages - 1);
|
||
}
|