169 lines
5.8 KiB
C++
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);
|
||
|
|
}
|