Skip to content

Commit 33bbfe9

Browse files
authored
Refactor: 예약 요청 저장 로직 수정 (#33)
* feat: redis decrement 설정 * feat: 중복 예약 방지를 위한 필드 추가 (유니크 속성에 사용) * refact: 중복 예약 방지를 위한 필드 추가로 인한 JPA 네이밍 메서드 수정 * refact: 예약 중복 저장시, 예외 발생 * 예약 존재 여부 조회 로직 삭제 * 예외 발생 시, 예약 반호 decr * refact: 시간대 예약 락 범위 축소 (가게 + 예약 날짜 + 시간대) * docs: 불필요 주석 삭제
1 parent 8c9c167 commit 33bbfe9

13 files changed

Lines changed: 64 additions & 38 deletions

src/main/java/com/project/tableforyou/domain/reservationrefactor/entity/QueueReservation.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ public class QueueReservation extends BaseTimeEntity {
4747
@Column(name = "is_entered", nullable = false)
4848
private boolean isEntered = false;
4949

50+
// 중복 예약 방지 (유니크 설정에 사용)
51+
// 예약 취소 또는 입장 완료 시, null로 변경하여 재예약 가능하도록 처리
52+
@Column(name = "active_flag")
53+
private Boolean activeFlag = true;
54+
5055
// 취소 시각
5156
@Column(name = "canceled_at")
5257
private LocalDateTime canceledAt;
@@ -74,6 +79,7 @@ public void cancelReservation() {
7479
}
7580

7681
this.isCanceled = true;
82+
this.activeFlag = null;
7783
this.canceledAt = LocalDateTime.now();
7884
}
7985

@@ -87,6 +93,7 @@ public void enterRestaurant() {
8793
}
8894

8995
this.isEntered = true;
96+
this.activeFlag = null;
9097
this.enteredAt = LocalDateTime.now();
9198
}
9299
}

src/main/java/com/project/tableforyou/domain/reservationrefactor/entity/TimeSlotReservation.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -48,6 +48,11 @@ public class TimeSlotReservation extends BaseTimeEntity {
4848
@Column(name = "is_entered", nullable = false)
4949
private boolean isEntered = false;
5050

51+
// 중복 예약 방지 (유니크 설정에 사용)
52+
// 예약 취소 또는 입장 완료 시, null로 변경하여 재예약 가능하도록 처리
53+
@Column(name = "active_flag")
54+
private Boolean activeFlag = true;
55+
5156
// 취소 시각
5257
@Column(name = "canceled_at")
5358
private LocalDateTime canceledAt;
@@ -75,6 +80,7 @@ public void cancelReservation() {
7580
}
7681

7782
this.isCanceled = true;
83+
this.activeFlag = null;
7884
this.canceledAt = LocalDateTime.now();
7985
}
8086

@@ -88,6 +94,7 @@ public void enterRestaurant() {
8894
}
8995

9096
this.isEntered = true;
97+
this.activeFlag = null;
9198
this.enteredAt = LocalDateTime.now();
9299
}
93100
}

src/main/java/com/project/tableforyou/domain/reservationrefactor/redis/RedisRepository.java

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -112,4 +112,8 @@ public boolean hashExisted(String key, Object hashKey) {
112112
public Long increase(String key) {
113113
return stringRedisTemplate.opsForValue().increment(key);
114114
}
115+
116+
public Long decrease(String key) {
117+
return stringRedisTemplate.opsForValue().decrement(key);
118+
}
115119
}

src/main/java/com/project/tableforyou/domain/reservationrefactor/redis/queue/QueueReservationRedisService.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -82,6 +82,11 @@ public int generateNextReservationNumber(Long restaurantId) {
8282
return number != null ? number.intValue() : 1;
8383
}
8484

85+
public void compensateReservationNumber(Long restaurantId) {
86+
String key = ReservationConstants.getQueueReservationNumberKey(restaurantId);
87+
redisRepository.decrease(key);
88+
}
89+
8590
public void markAsEntered(Long userId, Long restaurantId) {
8691
String key = ReservationConstants.getQueueReservationKey(restaurantId);
8792
String enteredKey = ReservationConstants.getQueueEnteredCountKey(restaurantId);

src/main/java/com/project/tableforyou/domain/reservationrefactor/redis/timeslot/TimeSlotReservationRedisService.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,6 +47,11 @@ public int generateNextReservationNumber(Long restaurantId, String date, TimeSlo
4747
return number != null ? number.intValue() : 1;
4848
}
4949

50+
public void compensateReservationNumber(Long restaurantId, String date, TimeSlot timeSlot) {
51+
String key = ReservationConstants.getTimeSlotReservationNumberKey(restaurantId, date, timeSlot);
52+
redisRepository.decrease(key);
53+
}
54+
5055
public void cancelReservation(Long userId, Long restaurantId, String date, TimeSlot timeSlot) {
5156
String key = ReservationConstants.getTimeSlotReservationKey(restaurantId, date, timeSlot);
5257
redisRepository.hashDel(key, userId);

src/main/java/com/project/tableforyou/domain/reservationrefactor/repository/QueueReservationRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@
99
public interface QueueReservationRepository extends JpaRepository<QueueReservation, Long> {
1010
int countByRestaurantIdAndDate(Long restaurantId, LocalDate date);
1111

12-
Optional<QueueReservation> findByUserIdAndRestaurantIdAndDateAndIsCanceledFalse(
12+
Optional<QueueReservation> findByUserIdAndRestaurantIdAndDateAndIsCanceledFalseAndActiveFlagTrue(
1313
Long userId, Long restaurantId, LocalDate date
1414
);
1515

src/main/java/com/project/tableforyou/domain/reservationrefactor/repository/TimeSlotReservationRepository.java

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@
1010
public interface TimeSlotReservationRepository extends JpaRepository<TimeSlotReservation, Long> {
1111
int countByRestaurantIdAndDateAndTimeSlotAndIsCanceledFalse(Long restaurantId, LocalDate date, TimeSlot timeSlot);
1212

13-
Optional<TimeSlotReservation> findByUserIdAndRestaurantIdAndDateAndTimeSlotAndIsCanceledFalse(
13+
Optional<TimeSlotReservation> findByUserIdAndRestaurantIdAndDateAndTimeSlotAndIsCanceledFalseAndActiveFlagTrue(
1414
Long userId, Long restaurantId, LocalDate date, TimeSlot timeSlot
1515
);
1616
}

src/main/java/com/project/tableforyou/domain/reservationrefactor/service/ReservationLockManager.java

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ public class ReservationLockManager {
3737
* @return 예약 번호
3838
*/
3939
public int saveQueueReservation(Long userId, String username, Long restaurantId) {
40+
// Lock 범위 : 가게
4041
String key = ReservationConstants.RESERVATION_KEY_PREFIX + QUEUE + restaurantId;
4142
RLock lock = redissonClient.getFairLock(LOCK + key);
4243
try {
@@ -67,7 +68,12 @@ public int saveQueueReservation(Long userId, String username, Long restaurantId)
6768
* @param createReq 예약 요청 정보 (날짜, 시간, 좌석 수 포함)
6869
*/
6970
public void saveTimeSlotReservation(Long userId, String username, Long restaurantId, TimeSlotReservationDto.Create createReq) {
70-
String key = ReservationConstants.RESERVATION_KEY_PREFIX + TIME_SLOT + restaurantId;
71+
// Lock 범위 : 가게 + 예약 날짜 + 시간대
72+
String key = ReservationConstants.RESERVATION_KEY_PREFIX
73+
+ TIME_SLOT
74+
+ restaurantId + ":"
75+
+ createReq.date() + ":"
76+
+ createReq.timeSlot();
7177
RLock lock = redissonClient.getFairLock(LOCK + key);
7278

7379
try {

src/main/java/com/project/tableforyou/domain/reservationrefactor/service/queue/QueueReservationCacheSyncService.java

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -87,7 +87,7 @@ public int getOrLoadCanceledCount(Long restaurantId) {
8787
*/
8888
public void restoreUserReservationToCache(Long userId, Long restaurantId) {
8989
Optional<QueueReservation> queueReservation =
90-
queueReservationRepository.findByUserIdAndRestaurantIdAndDateAndIsCanceledFalse(
90+
queueReservationRepository.findByUserIdAndRestaurantIdAndDateAndIsCanceledFalseAndActiveFlagTrue(
9191
userId, restaurantId, LocalDate.now()
9292
);
9393

@@ -117,7 +117,7 @@ public void restoreUserReservationToCache(Long userId, Long restaurantId) {
117117
@Transactional
118118
public int syncUserReservationToCacheIfAbsent(Long userId, Long restaurantId) {
119119
QueueReservation queueReservation =
120-
queueReservationRepository.findByUserIdAndRestaurantIdAndDateAndIsCanceledFalse(
120+
queueReservationRepository.findByUserIdAndRestaurantIdAndDateAndIsCanceledFalseAndActiveFlagTrue(
121121
userId, restaurantId, LocalDate.now()
122122
).orElseThrow(() -> new CustomException(ErrorCode.RESERVATION_NOT_FOUND));
123123

src/main/java/com/project/tableforyou/domain/reservationrefactor/service/queue/QueueReservationCommandService.java

Lines changed: 11 additions & 12 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import com.project.tableforyou.domain.reservationrefactor.redis.queue.QueueReservationRedisService;
88
import com.project.tableforyou.domain.reservationrefactor.repository.QueueReservationRepository;
99
import lombok.RequiredArgsConstructor;
10+
import org.springframework.dao.DataIntegrityViolationException;
1011
import org.springframework.stereotype.Service;
1112
import org.springframework.transaction.annotation.Transactional;
1213

@@ -31,14 +32,6 @@ public class QueueReservationCommandService {
3132
*/
3233
@Transactional
3334
public int create(Long userId, String username, Long restaurantId) {
34-
// Redis에 이미 예약된 사용자라면 예외 발생
35-
if (!queueReservationRedisService.isAlreadyReserved(userId, restaurantId)) {
36-
// 캐시가 없을 경우 DB에서 확인.
37-
queueReservationCacheSyncService.restoreUserReservationToCache(userId, restaurantId);
38-
} else {
39-
throw new CustomException(ErrorCode.ALREADY_USER_RESERVATION);
40-
}
41-
4235
// 예약 번호 계산
4336
int reservationNumber = getReservationCount(restaurantId);
4437

@@ -50,8 +43,14 @@ public int create(Long userId, String username, Long restaurantId) {
5043
.date(LocalDate.now())
5144
.build();
5245

53-
// DB에 예약 저장
54-
queueReservationRepository.save(queueReservation);
46+
try {
47+
// DB에 예약 저장
48+
queueReservationRepository.save(queueReservation);
49+
} catch (DataIntegrityViolationException ex) {
50+
// DB 유니크 제약 위반 (중복 예약)
51+
queueReservationRedisService.compensateReservationNumber(restaurantId);
52+
throw new CustomException(ErrorCode.ALREADY_USER_RESERVATION);
53+
}
5554

5655
// Redis에 캐싱
5756
queueReservationRedisService.saveReservation(
@@ -86,7 +85,7 @@ private int getReservationCount(Long restaurantId) {
8685
@Transactional
8786
public void cancel(Long userId, Long restaurantId) {
8887
QueueReservation queueReservation =
89-
queueReservationRepository.findByUserIdAndRestaurantIdAndDateAndIsCanceledFalse(
88+
queueReservationRepository.findByUserIdAndRestaurantIdAndDateAndIsCanceledFalseAndActiveFlagTrue(
9089
userId, restaurantId, LocalDate.now()
9190
).orElseThrow(() -> new CustomException(ErrorCode.RESERVATION_NOT_FOUND));
9291

@@ -102,7 +101,7 @@ public void cancel(Long userId, Long restaurantId) {
102101
@Transactional
103102
public void markAsEntered(Long userId, Long restaurantId) {
104103
QueueReservation queueReservation =
105-
queueReservationRepository.findByUserIdAndRestaurantIdAndDateAndIsCanceledFalse(
104+
queueReservationRepository.findByUserIdAndRestaurantIdAndDateAndIsCanceledFalseAndActiveFlagTrue(
106105
userId, restaurantId, LocalDate.now()
107106
).orElseThrow(() -> new CustomException(ErrorCode.RESERVATION_NOT_FOUND));
108107

0 commit comments

Comments
 (0)