Files
2026-07-12 14:34:52 +04:00

169 lines
5.8 KiB
C++

#include "refund_generator.h"
#include "create_refund_request.h"
#include "refunds_repository.h"
#include "repository_manager.h"
#include "repository_result.h"
#include "schema_metadata.h"
#include "session_dto.h"
#include "sessions_repository.h"
#include "ticket_dto.h"
#include "tickets_repository.h"
#include <QRandomGenerator>
RefundGenerator::RefundGenerator(const QPair<double, double> &refundRatioRange,
const QPair<qint64, qint64> &dayRange,
const QPair<QTime, QTime> &timeRange)
: BaseGenerator("Refund"), DateTimeGenerator(dayRange, timeRange),
refundRatioRange_(refundRatioRange) {
auto &[lowest, highest] = refundRatioRange_;
if (lowest > highest) {
qCritical() << "Lowest refund ratio is greater than the highest refund, "
"values will be swapped";
std::swap(lowest, highest);
}
initializeRepositories(); // First
initializeTicketIdToCacheValueMap(); // After initializing
// repositories
initializeRemainingCapacity(); // After initializing ticketIdToCacheValue
}
void RefundGenerator::initializeRepositories() {
RepositoryManager &repositoryManager = RepositoryManager::instance();
SchemaMetadata metadata = SchemaMetadata::defaultSchema();
ticketsRepository_ = repositoryManager.getRepositoryAs<TicketsRepository>(
metadata.repositoryTableName("tickets"));
refundsRepository_ = repositoryManager.getRepositoryAs<RefundsRepository>(
metadata.repositoryTableName("refunds"));
sessionsRepository_ = repositoryManager.getRepositoryAs<SessionsRepository>(
metadata.repositoryTableName("sessions"));
if (!bool(ticketsRepository_ && refundsRepository_ && sessionsRepository_)) {
qFatal() << "Failed to initialize repositories";
}
}
void RefundGenerator::initializeTicketIdToCacheValueMap() {
QVector<SessionDTO> sessions;
RepositoryResult resultGetAllSessions =
sessionsRepository_->getAllSessions(sessions);
if (!resultGetAllSessions.success) {
qFatal() << "Failed to retrieve sessions";
}
QMap<qint32 /* sessionId */,
QPair<QDateTime /* beginAt */, double /* ticketPrice */>>
sessionIdToBeginAtAndTicketPrice;
for (const SessionDTO &session : sessions) {
sessionIdToBeginAtAndTicketPrice[session.sessionId()] =
qMakePair(session.beginAt(), session.ticketPrice());
}
QVector<TicketDTO> tickets;
RepositoryResult resultGetAllTickets =
ticketsRepository_->getAllTickets(tickets);
if (!resultGetAllTickets.success) {
qFatal() << "Failed to retrieve tickets";
}
for (const auto &ticket : tickets) {
const auto &[beginAt, ticketPrice] =
sessionIdToBeginAtAndTicketPrice[ticket.sessionId()];
CacheValue cacheValue{beginAt, ticket.soldAt(), ticketPrice};
if (calculateDurationMs(cacheValue) < 2) {
continue;
}
ticketIdToCacheValue_[ticket.ticketId()] = cacheValue;
}
}
QString
RefundGenerator::createFailureMessage(const QString &errorMessage) const {
const QString dateTimeFormat = "yyyy-MM-dd hh:mm:ss.zzz";
QString additionalInfo =
QString("Additional info: Ticket id: %1 Refund amount: %2 Sold at: %3 "
"Refund at: %4 Begin at: %5")
.arg(reservationContext_.ticketId)
.arg(reservationContext_.refundAmount)
.arg(reservationContext_.soldAt.toString(dateTimeFormat))
.arg(reservationContext_.refundAt.toString(dateTimeFormat))
.arg(reservationContext_.beginAt.toString(dateTimeFormat));
QString failureMessage = "Failure:\n\t%1\n\tError message: %2";
return failureMessage.arg(additionalInfo).arg(errorMessage);
}
QString RefundGenerator::createSuccessMessage() const {
return QString("Success: EId: %1 RAm: %2 RAt: %3")
.arg(reservationContext_.ticketId)
.arg(reservationContext_.refundAmount)
.arg(reservationContext_.refundAt.toString("yyyy-MM-dd hh:mm:ss"));
}
qsizetype RefundGenerator::calculateRemainingCapacity() const {
return remainingCapacity_;
}
RepositoryResult RefundGenerator::createRecord() {
CreateRefundRequest request;
request.setRefundAmount(reservationContext_.refundAmount);
request.setRefundAt(reservationContext_.refundAt);
request.setTicketId(reservationContext_.ticketId);
RepositoryResult result = refundsRepository_->createRefund(request);
if (result.success) {
--remainingCapacity_;
}
return result;
}
bool RefundGenerator::prepareData() {
if (ticketIdToCacheValue_.isEmpty()) {
qInfo() << "No tickets to refund";
return false;
}
qsizetype ticketIdIndex = bounded<qsizetype>(0, ticketIdToCacheValue_.size());
auto ticketIdIt = ticketIdToCacheValue_.cbegin();
std::advance(ticketIdIt, ticketIdIndex);
qint32 ticketId = ticketIdIt.key();
CacheValue cacheValue = ticketIdIt.value();
constexpr qint32 fiveMinutesMs = 5 * 1000 * 60;
qint64 deltaBoundMs = calculateDurationMs(cacheValue) / 2;
if (deltaBoundMs > fiveMinutesMs) {
deltaBoundMs = fiveMinutesMs;
}
const QDateTime refundAt =
// SoldAt < RefundAt < BeginAt
// SoldAt + n minute <= RefundAt <= BeginAt - n minute
generate(cacheValue.soldAt.addMSecs(+deltaBoundMs),
cacheValue.beginAt.addMSecs(-deltaBoundMs));
const auto [refundRatioLowest, refundRatioHighest] = refundRatioRange_;
int lowest = refundRatioLowest * 100;
int highest = refundRatioHighest * 100;
int numerator = bounded(lowest, highest);
double refundAmount = std::floor(numerator * cacheValue.ticketPrice) / 100.0;
ReservationContext context{cacheValue.beginAt, refundAt, cacheValue.soldAt,
refundAmount, ticketId};
reservationContext_ = std::move(context);
return true;
}
void RefundGenerator::clearStaleData() {
qint32 ticketId = reservationContext_.ticketId;
ticketIdToCacheValue_.remove(ticketId);
}