|
| 1 | +require_relative "./dto/response/schedule_detail" |
| 2 | +require_relative "./dto/response/schedule_simple" |
| 3 | +require_relative "./dto/response/schedule_available" |
| 4 | + |
| 5 | +class SchedulesController < ApplicationController |
| 6 | + before_action :authenticate_user |
| 7 | + before_action :find_schedule, only: [ :show, :update, :destroy, :confirm ] |
| 8 | + before_action :find_all_schedules, only: [ :index ] |
| 9 | + |
| 10 | + # 예약 상세 조회 |
| 11 | + def show |
| 12 | + return render json: BaseResponse.of_failure(Failure::NO_PERMISSION_SHOW).to_json, |
| 13 | + status: Failure::NO_PERMISSION_SHOW.status_code unless admin? || @current_user.eql?(@schedule.user) |
| 14 | + render json: BaseResponse.of_success(Success::GET_RESERVATION_DETAIL, ScheduleDetail.new(@schedule)).to_json, |
| 15 | + status: Success::GET_RESERVATION_DETAIL.status_code |
| 16 | + end |
| 17 | + |
| 18 | + # 예약 내역 조회 |
| 19 | + def index |
| 20 | + result = @schedules.map do |schedule| ScheduleSimple.new(schedule) end |
| 21 | + render json: BaseResponse.of_success(Success::GET_RESERVATION_ALL, result).to_json, |
| 22 | + status: Success::GET_RESERVATION_DETAIL.status_code |
| 23 | + end |
| 24 | + |
| 25 | + # 예약 신청 가능 시간 및 인원 조회 |
| 26 | + def available |
| 27 | + start_date = target_month_start_date |
| 28 | + end_date = target_month_start_date.end_of_month |
| 29 | + |
| 30 | + confirmed_schedules = Schedule.confirmed.where(start_datetime: start_date..end_date) |
| 31 | + |
| 32 | + available_times = calculate_available_times(confirmed_schedules, start_date, end_date) |
| 33 | + |
| 34 | + render json: BaseResponse.of_success(Success::GET_RESERVATION_AVAILABILITY_TIME, available_times.map { |datetime, personnel| ScheduleAvailable.new(datetime, personnel) }).to_json, |
| 35 | + status: Success::GET_RESERVATION_AVAILABILITY_TIME.status_code |
| 36 | + rescue ArgumentError |
| 37 | + render json: BaseResponse.of_failure(Failure::INVALID_PARAMETER).to_json, |
| 38 | + status: Failure::INVALID_PARAMETER.status_code |
| 39 | + end |
| 40 | + |
| 41 | + # 예약 신청 |
| 42 | + def create |
| 43 | + return render json: BaseResponse.of_failure(Failure::IMPOSSIBLE_CREATE_TOO_LATE).to_json, |
| 44 | + status: Failure::IMPOSSIBLE_CREATE_TOO_LATE.status_code if too_late? |
| 45 | + return render json: BaseResponse.of_failure(Failure::OVER_PERSONNEL_TO_RESERVE).to_json, |
| 46 | + status: Failure::OVER_PERSONNEL_TO_RESERVE.status_code if exceeds_capacity? |
| 47 | + |
| 48 | + schedule = @current_user.schedules.create!(create_schedule_body) |
| 49 | + render json: BaseResponse.of_success(Success::CREATE_RESERVATION, schedule).to_json, |
| 50 | + status: Success::CREATE_RESERVATION.status_code |
| 51 | + end |
| 52 | + |
| 53 | + # 예약 확정 (Admin only) |
| 54 | + def confirm |
| 55 | + return render json: BaseResponse.of_failure(Failure::OVER_PERSONNEL_TO_CONFIRM).to_json, |
| 56 | + status: Failure::OVER_PERSONNEL_TO_CONFIRM.status_code if exceeds_capacity?(true) |
| 57 | + return render json: BaseResponse.of_failure(Failure::NO_PERMISSION_CONFIRM).to_json, |
| 58 | + status: Failure::NO_PERMISSION_CONFIRM.status_code unless admin? || @schedule.user(@current_user) |
| 59 | + |
| 60 | + @schedule.update!(is_confirm: true) |
| 61 | + render status: Success::CONFIRM_RESERVATION.status_code |
| 62 | + end |
| 63 | + |
| 64 | + # 예약 수정 |
| 65 | + def update |
| 66 | + return render json: BaseResponse.of_failure(Failure::IMPOSSIBLE_UPDATE_ALREADY_CONFIRM).to_json, |
| 67 | + status: Failure::IMPOSSIBLE_UPDATE_ALREADY_CONFIRM.status_code if !admin? && @schedule.is_confirm |
| 68 | + return render json: BaseResponse.of_failure(Failure::OVER_PERSONNEL_TO_CONFIRM).to_json, |
| 69 | + status: Failure::OVER_PERSONNEL_TO_CONFIRM.status_code if exceeds_capacity? |
| 70 | + @schedule.update!(create_schedule_body) |
| 71 | + render status: Success::MODIFY_RESERVATION.status_code |
| 72 | + end |
| 73 | + |
| 74 | + # 예약 삭제 |
| 75 | + def destroy |
| 76 | + return render json: BaseResponse.of_failure(Failure::IMPOSSIBLE_DELETE_ALREADY_CONFIRM).to_json, |
| 77 | + status: Failure::IMPOSSIBLE_DELETE_ALREADY_CONFIRM.status_code if !admin? && @schedule.is_confirm |
| 78 | + @schedule.destroy |
| 79 | + render status: Success::DELETE_RESERVATION.status_code |
| 80 | + end |
| 81 | + |
| 82 | + private |
| 83 | + def authenticate_user |
| 84 | + @current_user = User.find_by(id: request.env["user_id"]) |
| 85 | + render json: BaseResponse.of_failure(Failure::NOT_FOUND_USER).to_json, |
| 86 | + status: Failure::NOT_FOUND_USER.status_code unless @current_user |
| 87 | + end |
| 88 | + |
| 89 | + def admin? |
| 90 | + @current_user&.admin? |
| 91 | + end |
| 92 | + |
| 93 | + def target_month_start_date |
| 94 | + params[:target_month].present? ? Date.strptime(params[:target_month].to_s, "%Y-%m") : Date.today |
| 95 | + end |
| 96 | + |
| 97 | + def find_schedule |
| 98 | + @schedule = admin? ? Schedule.find(params[:id]) : @current_user.schedules.find(params[:id]) |
| 99 | + render json: BaseResponse.of_failure(Failure::NOT_FOUND_SCHEDULE).to_json, |
| 100 | + status: Failure::NOT_FOUND_SCHEDULE.status_code unless @schedule |
| 101 | + end |
| 102 | + |
| 103 | + def find_all_schedules |
| 104 | + @schedules = admin? ? Schedule.all : Schedule.where(user_id: request.env["user_id"]) |
| 105 | + end |
| 106 | + |
| 107 | + def create_schedule_body |
| 108 | + params.require(:schedule).permit(:start_datetime, :end_datetime, :personnel) |
| 109 | + end |
| 110 | + |
| 111 | + def too_late? |
| 112 | + params[:start_datetime].to_date <= 3.days.from_now.to_date |
| 113 | + end |
| 114 | + |
| 115 | + def exceeds_capacity?(confirming = false) |
| 116 | + current_count = Schedule.confirmed.where(start_datetime: params[:start_datetime]).sum(:personnel) |
| 117 | + new_count = confirming ? @schedule.personnel : params[:personnel].to_i |
| 118 | + (current_count + new_count) > 50_000 |
| 119 | + end |
| 120 | + |
| 121 | + def calculate_available_times(schedules, start_date, end_date) |
| 122 | + available_slots = {} |
| 123 | + # 해당 월의 각 날짜를 순회하며 기본 값(50,000명 가능) 설정 |
| 124 | + (start_date..end_date).each do |date| |
| 125 | + datetime = date.to_datetime |
| 126 | + if datetime >= Date.today + 3.days |
| 127 | + (0..23).each do |hour| |
| 128 | + available_slots[datetime.change(hour: hour)] = 50_000 |
| 129 | + end |
| 130 | + end |
| 131 | + end |
| 132 | + |
| 133 | + # 예약된 일정 반영하여 예약 가능 인원 조정 |
| 134 | + schedules.each do |schedule| |
| 135 | + (schedule.start_datetime.hour..schedule.end_datetime.hour - 1).each do |hour| |
| 136 | + datetime = schedule.start_datetime.to_datetime.change(hour: hour) |
| 137 | + available_slots[datetime] -= schedule.personnel if available_slots[datetime] |
| 138 | + end |
| 139 | + end |
| 140 | + |
| 141 | + available_slots.select { |_, personnel| personnel > 0 } |
| 142 | + end |
| 143 | +end |
0 commit comments