318 lines
12 KiB
C++
318 lines
12 KiB
C++
#include <algorithm>
|
|
#include <cstring>
|
|
#include <sstream>
|
|
#include <stdexcept>
|
|
#include <string>
|
|
#include <utility>
|
|
#include "basic_defs.h"
|
|
#include "data.h"
|
|
#include "engine.h"
|
|
#include "utils.h"
|
|
|
|
std::string TicketSystemEngine::QueryTicket(const std::string &command) {
|
|
command_id_t command_id;
|
|
sscanf(command.c_str(), "[%llu]", &command_id);
|
|
LOG->debug("command id: {}", command_id);
|
|
std::stringstream command_stream(command), response_stream;
|
|
std::string token;
|
|
int date;
|
|
std::string from, to;
|
|
std::string order_by = "time";
|
|
command_stream >> token >> token;
|
|
while (command_stream >> token) {
|
|
switch (token[1]) {
|
|
case 'd': {
|
|
std::string date_raw;
|
|
command_stream >> date_raw;
|
|
int mm, dd;
|
|
sscanf(date_raw.c_str(), "%d-%d", &mm, &dd);
|
|
date = GetCompactDate(mm, dd);
|
|
break;
|
|
}
|
|
case 's': {
|
|
command_stream >> from;
|
|
break;
|
|
}
|
|
case 't': {
|
|
command_stream >> to;
|
|
break;
|
|
}
|
|
case 'p': {
|
|
command_stream >> order_by;
|
|
break;
|
|
}
|
|
default: {
|
|
throw std::invalid_argument("Invalid argument");
|
|
}
|
|
}
|
|
}
|
|
LOG->debug("date {}={}-{}, from {}, to {}, order by {}", date, RetrieveReadableDate(date).first,
|
|
RetrieveReadableDate(date).second, from, to, order_by);
|
|
hash_t from_hash = SplitMix64Hash(from), to_hash = SplitMix64Hash(to);
|
|
std::vector<StopRegister::DirectTrainInfo> valid_trains;
|
|
stop_register.QueryDirectTrains(date, from_hash, to_hash, valid_trains);
|
|
size_t len = valid_trains.size();
|
|
std::vector<std::pair<StopRegister::DirectTrainInfo, AdditionalTrainInfo>> valid_trains_full(len);
|
|
LOG->debug("retrieving full data");
|
|
for (size_t i = 0; i < len; i++) {
|
|
valid_trains_full[i].first = valid_trains[i];
|
|
TicketPriceData ticket_price_data;
|
|
SeatsData seats_data;
|
|
int from_station_id = valid_trains[i].from_stop_id;
|
|
int to_station_id = valid_trains[i].to_stop_id;
|
|
ticket_price_data_storage.Get(valid_trains[i].train_ID_hash, ticket_price_data);
|
|
LOG->debug("analyzing train {} from {} to {}", ticket_price_data.trainID, from_station_id, to_station_id);
|
|
seats_data_storage.Get(
|
|
{valid_trains[i].train_ID_hash, valid_trains[i].actual_start_date - valid_trains[i].saleDate_beg}, seats_data);
|
|
strcpy(valid_trains_full[i].second.trainID, ticket_price_data.trainID);
|
|
int total_price = 0;
|
|
for (int j = from_station_id; j < to_station_id; j++) {
|
|
total_price += ticket_price_data.price[j];
|
|
}
|
|
valid_trains_full[i].second.price = total_price;
|
|
int seats = seats_data.seat[from_station_id];
|
|
for (int j = from_station_id + 1; j < to_station_id; j++) {
|
|
seats = std::min(seats, (int)seats_data.seat[j]);
|
|
}
|
|
valid_trains_full[i].second.seats = seats;
|
|
}
|
|
LOG->debug("successfully retrieved full data");
|
|
std::vector<int> valid_trains_full_index(len);
|
|
for (size_t i = 0; i < len; i++) {
|
|
valid_trains_full_index[i] = i;
|
|
}
|
|
if (order_by == "time") {
|
|
auto cmp = [&valid_trains_full](int a, int b) {
|
|
int time_cost_a = valid_trains_full[a].first.arrive_time_stamp - valid_trains_full[a].first.leave_time_stamp;
|
|
int time_cost_b = valid_trains_full[b].first.arrive_time_stamp - valid_trains_full[b].first.leave_time_stamp;
|
|
if (time_cost_a != time_cost_b) return time_cost_a < time_cost_b;
|
|
return strcmp(valid_trains_full[a].second.trainID, valid_trains_full[b].second.trainID) < 0;
|
|
};
|
|
std::sort(valid_trains_full_index.begin(), valid_trains_full_index.end(), cmp);
|
|
} else {
|
|
// order by price
|
|
auto cmp = [&valid_trains_full](int a, int b) {
|
|
if (valid_trains_full[a].second.price != valid_trains_full[b].second.price)
|
|
return valid_trains_full[a].second.price < valid_trains_full[b].second.price;
|
|
return strcmp(valid_trains_full[a].second.trainID, valid_trains_full[b].second.trainID) < 0;
|
|
};
|
|
std::sort(valid_trains_full_index.begin(), valid_trains_full_index.end(), cmp);
|
|
}
|
|
response_stream << "[" << command_id << "] " << len;
|
|
for (int i = 0; i < len; i++) {
|
|
response_stream << '\n';
|
|
response_stream << valid_trains_full[valid_trains_full_index[i]].second.trainID << ' ' << from << ' ';
|
|
int leave_time_stamp = valid_trains_full[valid_trains_full_index[i]].first.leave_time_stamp;
|
|
int leave_time_month, leave_time_day, leave_time_hour, leave_time_minute;
|
|
RetrieveReadableTimeStamp(leave_time_stamp, leave_time_month, leave_time_day, leave_time_hour, leave_time_minute);
|
|
response_stream << std::setw(2) << std::setfill('0') << leave_time_month << '-' << std::setw(2) << std::setfill('0')
|
|
<< leave_time_day << ' ' << std::setw(2) << std::setfill('0') << leave_time_hour << ':'
|
|
<< std::setw(2) << std::setfill('0') << leave_time_minute;
|
|
response_stream << " -> " << to << ' ';
|
|
int arrive_time_stamp = valid_trains_full[valid_trains_full_index[i]].first.arrive_time_stamp;
|
|
int arrive_time_month, arrive_time_day, arrive_time_hour, arrive_time_minute;
|
|
RetrieveReadableTimeStamp(arrive_time_stamp, arrive_time_month, arrive_time_day, arrive_time_hour,
|
|
arrive_time_minute);
|
|
response_stream << std::setw(2) << std::setfill('0') << arrive_time_month << '-' << std::setw(2)
|
|
<< std::setfill('0') << arrive_time_day << ' ' << std::setw(2) << std::setfill('0')
|
|
<< arrive_time_hour << ':' << std::setw(2) << std::setfill('0') << arrive_time_minute;
|
|
response_stream << ' ' << valid_trains_full[valid_trains_full_index[i]].second.price << ' '
|
|
<< valid_trains_full[valid_trains_full_index[i]].second.seats;
|
|
}
|
|
return response_stream.str();
|
|
}
|
|
|
|
std::string TicketSystemEngine::QueryTransfer(const std::string &command) {
|
|
command_id_t command_id;
|
|
sscanf(command.c_str(), "[%llu]", &command_id);
|
|
LOG->debug("command id: {}", command_id);
|
|
std::stringstream command_stream(command), response_stream;
|
|
std::string token;
|
|
int date;
|
|
std::string from, to;
|
|
std::string order_by = "time";
|
|
command_stream >> token >> token;
|
|
while (command_stream >> token) {
|
|
switch (token[1]) {
|
|
case 'd': {
|
|
std::string date_raw;
|
|
command_stream >> date_raw;
|
|
int mm, dd;
|
|
sscanf(date_raw.c_str(), "%d-%d", &mm, &dd);
|
|
date = GetCompactDate(mm, dd);
|
|
break;
|
|
}
|
|
case 's': {
|
|
command_stream >> from;
|
|
break;
|
|
}
|
|
case 't': {
|
|
command_stream >> to;
|
|
break;
|
|
}
|
|
case 'p': {
|
|
command_stream >> order_by;
|
|
break;
|
|
}
|
|
default: {
|
|
throw std::invalid_argument("Invalid argument");
|
|
}
|
|
}
|
|
}
|
|
LOG->debug("date {}={}-{}, from {}, to {}, order by {}", date, RetrieveReadableDate(date).first,
|
|
RetrieveReadableDate(date).second, from, to, order_by);
|
|
// TODO
|
|
response_stream << "[" << command_id << "] QueryTransfer";
|
|
return response_stream.str();
|
|
}
|
|
|
|
std::string TicketSystemEngine::BuyTicket(const std::string &command) {
|
|
command_id_t command_id;
|
|
sscanf(command.c_str(), "[%llu]", &command_id);
|
|
LOG->debug("command id: {}", command_id);
|
|
std::stringstream command_stream(command), response_stream;
|
|
std::string token;
|
|
std::string user_name;
|
|
std::string train_id;
|
|
int date;
|
|
std::string from, to;
|
|
int ticket_num;
|
|
std::string accept_queue = "false";
|
|
command_stream >> token >> token;
|
|
while (command_stream >> token) {
|
|
switch (token[1]) {
|
|
case 'u': {
|
|
command_stream >> user_name;
|
|
break;
|
|
}
|
|
case 'i': {
|
|
command_stream >> train_id;
|
|
break;
|
|
}
|
|
case 'd': {
|
|
std::string date_raw;
|
|
command_stream >> date_raw;
|
|
int mm, dd;
|
|
sscanf(date_raw.c_str(), "%d-%d", &mm, &dd);
|
|
date = GetCompactDate(mm, dd);
|
|
break;
|
|
}
|
|
case 'f': {
|
|
command_stream >> from;
|
|
break;
|
|
}
|
|
case 't': {
|
|
command_stream >> to;
|
|
break;
|
|
}
|
|
case 'n': {
|
|
command_stream >> ticket_num;
|
|
break;
|
|
}
|
|
case 'q': {
|
|
command_stream >> accept_queue;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
LOG->debug("user {}, train {}, date {}={}-{}, from {}, to {}, ticket num {}, accept queue {}", user_name, train_id,
|
|
date, RetrieveReadableDate(date).first, RetrieveReadableDate(date).second, from, to, ticket_num,
|
|
accept_queue);
|
|
hash_t user_ID_hash = SplitMix64Hash(user_name);
|
|
hash_t train_ID_hash = SplitMix64Hash(train_id);
|
|
if (online_users.find(user_ID_hash) == online_users.end()) {
|
|
LOG->debug("user {} not online", user_name);
|
|
response_stream << "[" << command_id << "] -1";
|
|
return response_stream.str();
|
|
}
|
|
bool success = false;
|
|
StopRegister::DirectTrainInfo info;
|
|
hash_t from_station_hash = SplitMix64Hash(from), to_station_hash = SplitMix64Hash(to);
|
|
stop_register.RequestSingleTrain(train_ID_hash, date, from_station_hash, to_station_hash, success, info);
|
|
if (!success) {
|
|
LOG->debug("no train available");
|
|
response_stream << "[" << command_id << "] -1";
|
|
return response_stream.str();
|
|
}
|
|
TicketPriceData ticket_price_data;
|
|
SeatsData seats_data;
|
|
int from_station_id = info.from_stop_id;
|
|
int to_station_id = info.to_stop_id;
|
|
int total_price = 0;
|
|
int available_seats = 0;
|
|
ticket_price_data_storage.Get(train_ID_hash, ticket_price_data);
|
|
seats_data_storage.Get({train_ID_hash, info.actual_start_date - info.saleDate_beg}, seats_data);
|
|
for (int j = from_station_id; j < to_station_id; j++) {
|
|
total_price += ticket_price_data.price[j];
|
|
}
|
|
available_seats = seats_data.seat[from_station_id];
|
|
for (int j = from_station_id + 1; j < to_station_id; j++) {
|
|
available_seats = std::min(available_seats, (int)seats_data.seat[j]);
|
|
}
|
|
if (ticket_num > available_seats) {
|
|
if (accept_queue == "false") {
|
|
LOG->debug("no enough seats");
|
|
response_stream << "[" << command_id << "] -1";
|
|
return response_stream.str();
|
|
}
|
|
transaction_manager.AddOrder(train_id, from, to, 0, info.leave_time_stamp, info.arrive_time_stamp, ticket_num,
|
|
total_price * (unsigned long long)ticket_num,
|
|
info.actual_start_date - info.saleDate_beg);
|
|
response_stream << "[" << command_id << "] queue";
|
|
return response_stream.str();
|
|
}
|
|
transaction_manager.AddOrder(train_id, from, to, 1, info.leave_time_stamp, info.arrive_time_stamp, ticket_num,
|
|
total_price * (unsigned long long)ticket_num,
|
|
info.actual_start_date - info.saleDate_beg);
|
|
for (int j = from_station_id; j < to_station_id; j++) {
|
|
seats_data.seat[j] -= ticket_num;
|
|
}
|
|
seats_data_storage.Put({train_ID_hash, info.actual_start_date - info.saleDate_beg}, seats_data);
|
|
response_stream << "[" << command_id << "] " << total_price * (unsigned long long)ticket_num;
|
|
return response_stream.str();
|
|
}
|
|
|
|
std::string TicketSystemEngine::QueryOrder(const std::string &command) {
|
|
command_id_t command_id;
|
|
sscanf(command.c_str(), "[%llu]", &command_id);
|
|
LOG->debug("command id: {}", command_id);
|
|
std::stringstream command_stream(command), response_stream;
|
|
std::string token;
|
|
std::string user_name;
|
|
command_stream >> token >> token;
|
|
while (command_stream >> token) {
|
|
switch (token[1]) {
|
|
case 'u': {
|
|
command_stream >> user_name;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
response_stream << "[" << command_id << "] QueryOrder";
|
|
return response_stream.str();
|
|
}
|
|
|
|
std::string TicketSystemEngine::RefundTicket(const std::string &command) {
|
|
command_id_t command_id;
|
|
sscanf(command.c_str(), "[%llu]", &command_id);
|
|
LOG->debug("command id: {}", command_id);
|
|
std::stringstream command_stream(command), response_stream;
|
|
std::string token;
|
|
std::string user_name;
|
|
int order = 1;
|
|
command_stream >> token >> token;
|
|
while (command_stream >> token) {
|
|
switch (token[1]) {
|
|
case 'u': {
|
|
command_stream >> user_name;
|
|
break;
|
|
}
|
|
case 'n': {
|
|
command_stream >> order;
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
response_stream << "[" << command_id << "] RefundTicket";
|
|
return response_stream.str();
|
|
} |