Skip to content

Commit 01c17fa

Browse files
committed
Merge branch 'fix/room-exception-update' into dev
2 parents 179855b + 6dc3be1 commit 01c17fa

30 files changed

Lines changed: 546 additions & 409 deletions

Dockerfile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,7 @@
11
FROM ruby:3.2
22

3-
WORKDIR /rails
4-
COPY . /rails
3+
WORKDIR /studyroom_service
4+
COPY . /studyroom_service
55

66
RUN apt-get update -qq && apt-get install -y build-essential libpq-dev nodejs default-mysql-client wget
77
RUN gem install bundler rails

Gemfile

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@ source "https://rubygems.org"
55
# ======================================
66
gem "rails", "~> 8.0.3"
77
gem "bootsnap", require: false
8+
gem "puma", "~> 6.4"
89

910
# ======================================
1011
# [Database]
@@ -21,8 +22,8 @@ gem "grpc-tools", require: false
2122
# ======================================
2223
# [Utility]
2324
# ======================================
24-
gem "tzinfo-data" # (윈도우 개발 시만 필요)
25-
gem "dotenv-rails" # 환경변수 관리용
25+
gem "tzinfo-data"
26+
gem "dotenv-rails"
2627

2728
# ======================================
2829
# [Development & Test]

Gemfile.lock

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -147,6 +147,8 @@ GEM
147147
psych (5.2.6)
148148
date
149149
stringio
150+
puma (6.6.1)
151+
nio4r (~> 2.0)
150152
racc (1.8.1)
151153
rack (3.2.2)
152154
rack-session (2.1.1)
@@ -222,6 +224,7 @@ DEPENDENCIES
222224
grpc (~> 1.57)
223225
grpc-tools
224226
mysql2 (~> 0.5)
227+
puma (~> 6.4)
225228
rails (~> 8.0.3)
226229
tzinfo-data
227230

app/grpc/room/room_pb.rb

Lines changed: 1 addition & 1 deletion
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

app/interceptors/auth_interceptor.rb

Lines changed: 4 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -12,9 +12,6 @@ class AuthInterceptor < GRPC::ServerInterceptor
1212
"Unauthenticated: x-user-code metadata is missing"
1313
)
1414

15-
# ------------------------------------------------------------
16-
# gRPC 요청 처리 (interceptor entry point)
17-
# ------------------------------------------------------------
1815
def request_response(request:, call:, method:)
1916
# ============================================================
2017
# 1. HealthCheck 계열 요청은 인증 예외 처리
@@ -25,10 +22,10 @@ def request_response(request:, call:, method:)
2522
end
2623

2724
# ============================================================
28-
# 2. 일반 요청은 메타데이터 기반 인증 수행
25+
# 2. 인증 수행
2926
# ============================================================
3027
user_code = call.metadata['x-user-code']
31-
user_role = call.metadata['x-user-role']
28+
user_role = call.metadata['x-user-role'] || "student" # 기본 student
3229

3330
if user_code.nil? || user_code.empty?
3431
raise UNAUTHENTICATED
@@ -40,8 +37,9 @@ def request_response(request:, call:, method:)
4037

4138
puts "[AuthInterceptor] user_code=#{user_code}, user_role=#{user_role}"
4239

43-
# 실제 gRPC 서비스 로직 실행
40+
# 실제 서비스 로직 실행
4441
yield
42+
4543
ensure
4644
# ============================================================
4745
# 3. 요청 완료 후 Context 정리

app/models/concerns/current.rb

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,11 @@
1-
# frozen_string_literal: true
2-
# 유저 서비스 측으로 MetaData 수정 완료
1+
# frozen_string_literal: true
32

43
module Current
54
thread_mattr_accessor :user_code
65
thread_mattr_accessor :user_role
76

87
def self.reset
98
self.user_code = nil
10-
self.user_role = nil
9+
self.user_role = "student" # 기본 권한
1110
end
1211
end

app/models/department.rb

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
class Department < ApplicationRecord
2+
has_many :rooms
3+
end

app/models/reservation.rb

Lines changed: 7 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -18,6 +18,7 @@ class Reservation < ApplicationRecord
1818
validates :priority, presence: true
1919
validates :start_time, presence: true
2020
validates :end_time, presence: true
21+
2122
validate :start_time_before_end_time
2223

2324
# ========================================
@@ -29,7 +30,10 @@ class Reservation < ApplicationRecord
2930
# Soft delete method
3031
# ========================================
3132
def soft_delete(deleted_by: nil)
32-
update(deleted_at: Time.current)
33+
update(
34+
deleted_at: Time.current,
35+
deleted_by: deleted_by
36+
)
3337
end
3438

3539
def self.with_deleted
@@ -49,7 +53,8 @@ def set_defaults
4953
# Auto-generate code BEFORE validation
5054
# ========================================
5155
def generate_code
52-
self.code ||= SecureRandom.uuid[0..7]
56+
# UUID 앞 12자리 사용 (충돌 위험 감소)
57+
self.code ||= SecureRandom.uuid.delete('-')[0..11]
5358
end
5459

5560
# ========================================

app/models/room.rb

Lines changed: 29 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,47 +1,50 @@
11
class Room < ApplicationRecord
2-
# 연관 관계
2+
# --------------------------------------
3+
# 연관 관계 (department_id 제거)
4+
# --------------------------------------
5+
# belongs_to :department ← 삭제해야 함 (이제 department 테이블이 없기 때문)
6+
# 현재 구조에서는 department_id 컬럼 자체가 없으므로 belongs_to 제거
7+
# 필요하면 department_code 로 UserService 연동만 사용
8+
39
has_many :room_operating_hours, dependent: :destroy
410
has_many :room_exceptions, dependent: :destroy
511
has_many :reservations
612

7-
# -------------------------------
13+
# --------------------------------------
814
# 필수값 검증
9-
# -------------------------------
15+
# --------------------------------------
1016
validates :name, presence: true, length: { maximum: 100 }
11-
validates :maximum_member, presence: true,
12-
numericality: { only_integer: true, greater_than_or_equal_to: 1 }
1317

14-
# room_type이 enum이라면 자동 처리
15-
# enum room_type: { study_room: 0, lecture_room: 1, lab: 2 }, _prefix: :type
16-
#
17-
# enum 타입이라면 다음 validation은 안 넣어도 되는데
18-
# enum이 아니라면 아래처럼 강제해야 함:
19-
# validates :room_type, inclusion: { in: ["study", "lecture", "lab"] }
18+
validates :maximum_member,
19+
presence: true,
20+
numericality: { only_integer: true, greater_than_or_equal_to: 1 }
2021

21-
# -------------------------------
22-
# 이름 중복 검증
23-
# 1) department_id가 같은 방끼리는 같은 name 금지
24-
# 2) department_id가 nil(공용 방)일 경우, 전체에서 name 중복 금지
25-
# -------------------------------
26-
validate :unique_name_within_department
22+
# --------------------------------------
23+
# 이름 중복 검증 (department_code 기반)
24+
# --------------------------------------
25+
validate :unique_name_within_department_code
2726

28-
def unique_name_within_department
29-
if department_id.present?
30-
# 같은 학과 내 중복 name 금지
31-
if Room.where(department_id: department_id, name: name).where.not(id: id).exists?
27+
def unique_name_within_department_code
28+
if department_code.present?
29+
# 같은 학과(department_code) 내 동일 이름 금지
30+
if Room.where(department_code: department_code, name: name)
31+
.where.not(id: id)
32+
.exists?
3233
errors.add(:name, "해당 학과에 동일한 이름의 방이 이미 존재합니다.")
3334
end
3435
else
35-
# 공용 방은 전체에서 name 중복 금지
36-
if Room.where(department_id: nil, name: name).where.not(id: id).exists?
36+
# 공용방(nil) → 전체에서 중복 금지
37+
if Room.where(department_code: nil, name: name)
38+
.where.not(id: id)
39+
.exists?
3740
errors.add(:name, "공용 방 이름은 전체에서 중복될 수 없습니다.")
3841
end
3942
end
4043
end
4144

42-
# -------------------------------
43-
# 삭제된 수정 금지
44-
# -------------------------------
45+
# --------------------------------------
46+
# 삭제된 방이면 수정 금지
47+
# --------------------------------------
4548
before_update :prevent_update_if_deleted
4649

4750
def prevent_update_if_deleted

app/models/room_exception.rb

Lines changed: 27 additions & 22 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,6 @@
11
class RoomException < ApplicationRecord
2-
# Room 모델과 다대일 관계를 가집니다.
32
belongs_to :room
43

5-
# Callbacks
64
after_save :cancel_conflicting_reservations
75

86
# 기본 유효성 검사
@@ -15,7 +13,7 @@ class RoomException < ApplicationRecord
1513
validate :validate_time_order
1614
validate :validate_duplicate_date
1715

18-
# 소프트 삭제 (Soft Delete)
16+
# Soft Delete
1917
default_scope { where(deleted_at: nil) }
2018

2119
def soft_delete
@@ -32,68 +30,75 @@ def self.with_deleted
3230
# 1) 전체 휴일 vs 부분 휴일 규칙
3331
# ----------------------------------------
3432
def validate_time_rule
35-
# 전체 휴일 (both nil) → OK
3633
return if opening_time.nil? && closing_time.nil?
3734

38-
# 부분 휴일 → 둘 다 있어야 함
3935
if opening_time.present? ^ closing_time.present?
4036
errors.add(:base, "Both opening_time and closing_time must be present for partial holiday")
4137
end
4238
end
4339

4440
# ----------------------------------------
45-
# 2) 시간 순서 검증
41+
# 2) 시간 순서 검증 (Time.parse 적용)
4642
# ----------------------------------------
4743
def validate_time_order
4844
return if opening_time.blank? || closing_time.blank?
4945

50-
if opening_time >= closing_time
51-
errors.add(:opening_time, "must be earlier than closing_time")
46+
begin
47+
ot = Time.parse(opening_time)
48+
ct = Time.parse(closing_time)
49+
50+
errors.add(:opening_time, "must be earlier than closing_time") if ot >= ct
51+
rescue ArgumentError
52+
errors.add(:base, "Invalid time format (expected HH:MM)")
5253
end
5354
end
5455

5556
# ----------------------------------------
56-
# 3) 동일 날짜 중복 방지 (soft-delete 제외)
57+
# 3) 동일 날짜 중복 방지
5758
# ----------------------------------------
5859
def validate_duplicate_date
5960
return if room_id.blank? || holiday_date.blank?
6061

61-
duplicate = RoomException.with_deleted
62-
.where(room_id: room_id, holiday_date: holiday_date, deleted_at: nil)
63-
.where.not(id: id)
64-
.exists?
62+
duplicate = RoomException
63+
.where(room_id: room_id, holiday_date: holiday_date, deleted_at: nil)
64+
.where.not(id: id)
65+
.exists?
6566

6667
if duplicate
6768
errors.add(:holiday_date, "exception already exists for this date")
6869
end
6970
end
7071

7172
# ----------------------------------------
72-
# 기존 예약 자동 취소 로직 (그대로 유지)
73+
# 기존 예약 자동 취소 로직 (시간 비교 개선)
7374
# ----------------------------------------
7475
def cancel_conflicting_reservations
7576
reservations_on_date = Reservation.where(
7677
room_id: room_id,
7778
start_time: holiday_date.all_day
7879
)
7980

80-
# Case 1: 완전 휴무일
81+
# Case 1: 완전 휴무일 → 모두 삭제
8182
if opening_time.nil? && closing_time.nil?
8283
reservations_on_date.find_each { |reservation| reservation.soft_delete(deleted_by: 0) }
8384
return
8485
end
8586

86-
# Case 2: 부분 휴무
87+
# Case 2: 부분 휴무일
8788
if opening_time.present? && closing_time.present?
88-
exception_opening_time_str = opening_time.strftime('%H:%M')
89-
exception_closing_time_str = closing_time.strftime('%H:%M')
89+
begin
90+
ex_start = Time.parse(opening_time)
91+
ex_end = Time.parse(closing_time)
92+
rescue ArgumentError
93+
return
94+
end
9095

9196
reservations_on_date.find_each do |reservation|
92-
start_time_str = reservation.start_time.strftime('%H:%M')
93-
end_time_str = reservation.end_time.strftime('%H:%M')
97+
r_start = reservation.start_time
98+
r_end = reservation.end_time
9499

95-
unless start_time_str >= exception_opening_time_str &&
96-
end_time_str <= exception_closing_time_str
100+
# 예외 시간 외에 걸치면 삭제
101+
unless (r_start >= ex_start && r_end <= ex_end)
97102
reservation.soft_delete(deleted_by: 0)
98103
end
99104
end

0 commit comments

Comments
 (0)