#include "EventDAO.h" #include #include #include #include "DatabaseManager.h" #include "time/DateUtils.h" EventInfo parseEvent(const QSqlQuery &query) { EventInfo event; event.id = query.value("id").toInt(); event.status = eventStatusFromString(query.value("status").toString()); event.type = eventTypeFromString(query.value("type").toString()); event.externalId = query.value("external_id").toString(); event.transactionId = query.value("transaction_id").toString(); event.accountId = query.value("account_id").toInt(); event.deviceId = query.value("device_id").toString(); event.amount = query.value("amount").toDouble(); event.phone = query.value("phone").toString(); event.bankName = query.value("bank_name").toString(); event.comment = query.value("comment").toString(); event.json = query.value("json").toString(); event.sentToServer = query.value("sent_to_server").toBool(); event.timestamp = DateUtils::fromUtcString(query.value("timestamp").toString()); event.updateTime = DateUtils::fromUtcString(query.value("update_time").toString()); event.completeTime = DateUtils::fromUtcString(query.value("complete_time").toString()); const int hasScreenshotIdx = query.record().indexOf("has_screenshot"); if (hasScreenshotIdx >= 0) { event.hasScreenshot = query.value(hasScreenshotIdx).toBool(); } return event; } int EventDAO::insertEvent(const EventInfo &event) { QSqlQuery query(DatabaseManager::instance().database()); query.prepare(R"( INSERT INTO events ( status, external_id, transaction_id, account_id, amount, phone, bank_name, comment, json, timestamp, device_id, type ) VALUES ( :status, :external_id, :transaction_id, :account_id, :amount, :phone, :bank_name, :comment, :json, :timestamp, :device_id, :type ) )"); query.bindValue(":status", eventStatusToString(event.status)); query.bindValue(":type", eventTypeToString(event.type)); query.bindValue(":external_id", event.externalId); query.bindValue(":transaction_id", event.transactionId); query.bindValue(":account_id", event.accountId); query.bindValue(":device_id", event.deviceId); query.bindValue(":amount", event.amount); query.bindValue(":phone", event.phone); query.bindValue(":bank_name", event.bankName); query.bindValue(":comment", event.comment); query.bindValue(":json", event.json); query.bindValue(":timestamp", DateUtils::toUtcString(event.timestamp)); if (!query.exec()) { qCritical() << "Ошибка при вставке event:" << query.lastError().text(); return -1; } return query.lastInsertId().toInt(); } bool EventDAO::updateEvent( const int id, const EventStatus &status, const QString &comment ) { QSqlQuery query(DatabaseManager::instance().database()); if (status == EventStatus::InProgress) { query.prepare(R"( UPDATE events SET status = :status, update_time = :update_time WHERE id = :id )"); query.bindValue(":update_time", DateUtils::toUtcString(DateUtils::getUtcNow())); } if (status == EventStatus::Complete) { query.prepare(R"( UPDATE events SET status = :status, complete_time = :complete_time WHERE id = :id )"); query.bindValue(":complete_time", DateUtils::toUtcString(DateUtils::getUtcNow())); } if (status == EventStatus::Error) { query.prepare(R"( UPDATE events SET status = :status, update_time = :update_time, comment = :comment WHERE id = :id )"); query.bindValue(":update_time", DateUtils::toUtcString(DateUtils::getUtcNow())); query.bindValue(":comment", comment); } query.bindValue(":id", id); query.bindValue(":status", eventStatusToString(status)); if (!query.exec()) { qCritical() << "Ошибка при обновлении event:" << query.lastError().text(); return false; } return true; } bool EventDAO::updateEventAmount(int id, double amount) { QSqlQuery query(DatabaseManager::instance().database()); query.prepare("UPDATE events SET amount = :amount WHERE id = :id"); query.bindValue(":amount", amount); query.bindValue(":id", id); if (!query.exec()) { qCritical() << "Ошибка при обновлении amount event:" << query.lastError().text(); return false; } return true; } bool EventDAO::markSentToServer(int id) { QSqlQuery query(DatabaseManager::instance().database()); query.prepare("UPDATE events SET sent_to_server = 1 WHERE id = :id"); query.bindValue(":id", id); if (!query.exec()) { qWarning() << "Ошибка markSentToServer:" << query.lastError().text(); return false; } return true; } QList EventDAO::getAllEvents(const EventStatus &status) { QList events; QSqlQuery query(DatabaseManager::instance().database()); query.prepare(R"( SELECT id, account_id, device_id, external_id, transaction_id, amount, phone, bank_name, status, type, comment, json, sent_to_server, update_time, complete_time, timestamp FROM events WHERE status = :status )"); query.bindValue(":status", eventStatusToString(status)); if (!query.exec()) { qWarning() << "Ошибка при получении getAllEvents:" << query.lastError().text(); return events; } while (query.next()) { events.append(parseEvent(query)); } return events; } EventInfo EventDAO::getFirstWaitEventByDeviceId(const QString &key) { QSqlQuery query(DatabaseManager::instance().database()); query.prepare(R"( SELECT id, account_id, device_id, external_id, transaction_id, amount, phone, bank_name, status, type, comment, json, sent_to_server, update_time, complete_time, timestamp FROM events WHERE status = :status AND device_id = :device_id ORDER BY CASE type WHEN 'CREATE_TRANSACTION' THEN 0 WHEN 'FETCH_TRANSACTIONS' THEN 1 WHEN 'FETCH_PROFILE' THEN 2 ELSE 3 END ASC, timestamp ASC LIMIT 1 )"); query.bindValue(":status", eventStatusToString(EventStatus::Wait)); query.bindValue(":device_id", key); if (!query.exec()) { qCritical() << "Ошибка при получении getFirstWaitEventByDeviceId:" << query.lastError().text(); return {}; } while (query.next()) { return parseEvent(query); } return {}; } QList EventDAO::getEvents(int page, int pageSize) { QList events; QSqlQuery query(DatabaseManager::instance().database()); query.prepare(R"( SELECT id, account_id, device_id, external_id, transaction_id, amount, phone, bank_name, status, type, comment, json, sent_to_server, update_time, complete_time, timestamp, (LENGTH(screenshot) > 0) as has_screenshot FROM events ORDER BY id DESC LIMIT :limit OFFSET :offset )"); query.bindValue(":limit", pageSize); query.bindValue(":offset", page * pageSize); if (!query.exec()) { qWarning() << "Ошибка при получении getEvents:" << query.lastError().text(); return events; } while (query.next()) { events.append(parseEvent(query)); } return events; } int EventDAO::getTotalCount() { QSqlQuery query(DatabaseManager::instance().database()); if (!query.exec("SELECT COUNT(*) FROM events")) { qWarning() << "Ошибка при получении getTotalCount:" << query.lastError().text(); return 0; } if (query.next()) return query.value(0).toInt(); return 0; } bool EventDAO::hasPendingEvents() { QSqlQuery query(DatabaseManager::instance().database()); query.prepare(R"( SELECT 1 FROM events WHERE status IN ('wait', 'iPprogress') LIMIT 1 )"); if (!query.exec()) { qCritical() << "Ошибка при hasPendingEvents:" << query.lastError().text(); return false; } return query.next(); } QSet EventDAO::getDevicesWithPendingEvents() { QSet devices; QSqlQuery query(DatabaseManager::instance().database()); query.prepare(R"( SELECT DISTINCT device_id FROM events WHERE status IN ('wait', 'iPprogress') )"); if (!query.exec()) { qCritical() << "Ошибка при getDevicesWithPendingEvents:" << query.lastError().text(); return devices; } while (query.next()) { devices.insert(query.value(0).toString()); } return devices; } bool EventDAO::existsActiveByExternalId(const QString &externalId) { QSqlQuery query(DatabaseManager::instance().database()); query.prepare(R"( SELECT 1 FROM events WHERE external_id = :external_id AND status IN ('wait', 'iPprogress') LIMIT 1 )"); query.bindValue(":external_id", externalId); if (!query.exec()) { qCritical() << "Ошибка при existsActiveByExternalId:" << query.lastError().text(); return false; } return query.next(); } QList EventDAO::cancelWaitingByMaterial(const QString &materialId, EventType type) { QList cancelled; QSqlQuery selectQuery(DatabaseManager::instance().database()); selectQuery.prepare(R"( SELECT id, external_id, transaction_id, bank_name, type FROM events WHERE external_id = :material_id AND type = :type AND status = :wait )"); selectQuery.bindValue(":material_id", materialId); selectQuery.bindValue(":type", eventTypeToString(type)); selectQuery.bindValue(":wait", eventStatusToString(EventStatus::Wait)); if (selectQuery.exec()) { while (selectQuery.next()) { EventInfo e; e.id = selectQuery.value(0).toInt(); e.externalId = selectQuery.value(1).toString(); e.transactionId = selectQuery.value(2).toString(); e.bankName = selectQuery.value(3).toString(); e.type = eventTypeFromString(selectQuery.value(4).toString()); cancelled.append(e); } } QSqlQuery query(DatabaseManager::instance().database()); query.prepare(R"( UPDATE events SET status = :cancelled, comment = 'Cancelled: replaced by new task' WHERE external_id = :material_id AND type = :type AND status = :wait )"); query.bindValue(":cancelled", eventStatusToString(EventStatus::Error)); query.bindValue(":material_id", materialId); query.bindValue(":type", eventTypeToString(type)); query.bindValue(":wait", eventStatusToString(EventStatus::Wait)); if (!query.exec()) { qWarning() << "Ошибка при cancelWaitingByMaterial:" << query.lastError().text(); } return cancelled; } QList EventDAO::cancelWaitingByType(const QString &deviceId, EventType type, const QString &bankName) { QList cancelled; QSqlQuery selectQuery(DatabaseManager::instance().database()); if (bankName.isEmpty()) { selectQuery.prepare(R"( SELECT id, external_id, transaction_id, bank_name, type FROM events WHERE device_id = :device_id AND type = :type AND status = :wait )"); } else { selectQuery.prepare(R"( SELECT id, external_id, transaction_id, bank_name, type FROM events WHERE device_id = :device_id AND type = :type AND status = :wait AND bank_name = :bank_name )"); selectQuery.bindValue(":bank_name", bankName); } selectQuery.bindValue(":device_id", deviceId); selectQuery.bindValue(":type", eventTypeToString(type)); selectQuery.bindValue(":wait", eventStatusToString(EventStatus::Wait)); if (selectQuery.exec()) { while (selectQuery.next()) { EventInfo e; e.id = selectQuery.value(0).toInt(); e.externalId = selectQuery.value(1).toString(); e.transactionId = selectQuery.value(2).toString(); e.bankName = selectQuery.value(3).toString(); e.type = eventTypeFromString(selectQuery.value(4).toString()); cancelled.append(e); } } QSqlQuery query(DatabaseManager::instance().database()); if (bankName.isEmpty()) { query.prepare(R"( UPDATE events SET status = :cancelled, comment = 'Cancelled: replaced by new task' WHERE device_id = :device_id AND type = :type AND status = :wait )"); } else { query.prepare(R"( UPDATE events SET status = :cancelled, comment = 'Cancelled: replaced by new task' WHERE device_id = :device_id AND type = :type AND status = :wait AND bank_name = :bank_name )"); query.bindValue(":bank_name", bankName); } query.bindValue(":cancelled", eventStatusToString(EventStatus::Error)); query.bindValue(":device_id", deviceId); query.bindValue(":type", eventTypeToString(type)); query.bindValue(":wait", eventStatusToString(EventStatus::Wait)); if (!query.exec()) { qWarning() << "Ошибка при cancelWaitingByType:" << query.lastError().text(); } return cancelled; } QList EventDAO::cancelWaitingByBank(const QString &deviceId, const QString &bankName) { // Сначала получаем задачи которые будем отменять (для отправки на сервер) QList cancelled; QSqlQuery query(DatabaseManager::instance().database()); query.prepare(R"( SELECT id, account_id, device_id, external_id, transaction_id, amount, phone, bank_name, status, type, comment, json, sent_to_server, update_time, complete_time, timestamp FROM events WHERE device_id = :device_id AND bank_name = :bank_name AND status = :wait )"); query.bindValue(":device_id", deviceId); query.bindValue(":bank_name", bankName); query.bindValue(":wait", eventStatusToString(EventStatus::Wait)); if (query.exec()) { while (query.next()) { cancelled.append(parseEvent(query)); } } // Отменяем query.prepare(R"( UPDATE events SET status = :error, comment = 'Bank profile disabled' WHERE device_id = :device_id AND bank_name = :bank_name AND status = :wait )"); query.bindValue(":error", eventStatusToString(EventStatus::Error)); query.bindValue(":device_id", deviceId); query.bindValue(":bank_name", bankName); query.bindValue(":wait", eventStatusToString(EventStatus::Wait)); if (!query.exec()) { qWarning() << "Ошибка при cancelWaitingByBank:" << query.lastError().text(); } return cancelled; } bool EventDAO::updateScreenshot(int id, const QByteArray &screenshot) { QSqlQuery query(DatabaseManager::instance().database()); query.prepare("UPDATE events SET screenshot = :screenshot WHERE id = :id"); query.bindValue(":screenshot", screenshot.isEmpty() ? QVariant(QMetaType(QMetaType::QByteArray)) : QVariant(screenshot)); query.bindValue(":id", id); if (!query.exec()) { qWarning() << "Ошибка при updateScreenshot:" << query.lastError().text(); return false; } return true; } QByteArray EventDAO::getScreenshot(int id) { QSqlQuery query(DatabaseManager::instance().database()); query.prepare("SELECT screenshot FROM events WHERE id = :id"); query.bindValue(":id", id); if (!query.exec() || !query.next()) return {}; return query.value(0).toByteArray(); } QList EventDAO::getRecentEvents(int hours) { QList events; QSqlQuery query(DatabaseManager::instance().database()); query.prepare(R"( SELECT id, account_id, device_id, external_id, transaction_id, amount, phone, bank_name, status, type, comment, json, sent_to_server, update_time, complete_time, timestamp FROM events WHERE timestamp >= datetime('now', :hours) ORDER BY id DESC )"); query.bindValue(":hours", QString("-%1 hours").arg(hours)); if (!query.exec()) { qWarning() << "Ошибка при getRecentEvents:" << query.lastError().text(); return events; } while (query.next()) { events.append(parseEvent(query)); } return events; }