Skip to content

Commit 0e77c31

Browse files
committed
feat : Implement APIs for schedules resource
최소 요구사항을 충족시키는 1차적인 API Controller를 구현했습니다.
1 parent 969552b commit 0e77c31

File tree

1 file changed

+143
-0
lines changed

1 file changed

+143
-0
lines changed
Lines changed: 143 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,143 @@
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

Comments
 (0)