From c320e9237df66a4ba09b64d72f6035e0a0608e07 Mon Sep 17 00:00:00 2001 From: Jihun Kim Date: Tue, 12 Aug 2025 21:02:00 +0900 Subject: [PATCH] =?UTF-8?q?bug(Reservation):=20=EC=9C=A0=EB=A0=B9=20?= =?UTF-8?q?=ED=95=AD=EB=AA=A9=20=EC=82=AD=EC=A0=9C=20=EB=A1=9C=EC=A7=81=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/ReservationService.java | 22 +++++++++++++++- .../reservation/ReservationServiceTest.java | 1 - .../WaitingRedisRepositoryTest.java | 3 --- .../service/ReservationService.java | 25 +++++++++++++++++-- .../ReservationServiceConcurrencyTest.java | 2 +- .../service/ReservationServiceTest.java | 1 - .../WaitingPermitLuaRepositoryTest.java | 1 - .../WaitingPermitLuaRepository.java | 2 +- .../repository/WaitingRedisRepository.java | 3 +-- 9 files changed, 47 insertions(+), 13 deletions(-) rename {nowait-app-user-api/src/main/java/com/nowait/applicationuser => nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis}/reservation/repository/WaitingPermitLuaRepository.java (98%) rename {nowait-app-admin-api/src/main/java/com/nowait/applicationadmin => nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis}/reservation/repository/WaitingRedisRepository.java (97%) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java index b2714127..63139bbf 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java @@ -24,7 +24,6 @@ import com.nowait.applicationadmin.reservation.dto.ReservationStatusSummaryDto; import com.nowait.applicationadmin.reservation.dto.ReservationStatusUpdateRequestDto; import com.nowait.applicationadmin.reservation.dto.WaitingUserResponse; -import com.nowait.applicationadmin.reservation.repository.WaitingRedisRepository; import com.nowait.common.enums.ReservationStatus; import com.nowait.common.enums.Role; import com.nowait.domaincorerdb.reservation.entity.Reservation; @@ -38,6 +37,8 @@ import com.nowait.domaincorerdb.user.exception.UserNotFoundException; import com.nowait.domaincorerdb.user.repository.UserRepository; import com.nowait.domaincoreredis.common.util.RedisKeyUtils; +import com.nowait.domaincoreredis.reservation.repository.WaitingPermitLuaRepository; +import com.nowait.domaincoreredis.reservation.repository.WaitingRedisRepository; import lombok.RequiredArgsConstructor; @@ -49,6 +50,7 @@ public class ReservationService { private final UserRepository userRepository; private final WaitingRedisRepository waitingRedisRepository; private final StoreRepository storeRepository; + private final WaitingPermitLuaRepository waitingPermitLuaRepository; private final RedisTemplate redisTemplate; //TODO 성능 비교를 위해 남겨둔 로직 @@ -242,6 +244,12 @@ public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, Me if (ReservationStatus.WAITING.name().equals(currStatus) || ReservationStatus.CALLING.name() .equals(currStatus)) { + if (reservationNumber != null) { + waitingPermitLuaRepository.removeActiveMember( + userId, String.valueOf(storeId), reservationNumber + ); + } + // 새 Reservation 생성 & 저장 Reservation r = Reservation.builder() .reservationNumber(reservationNumber) @@ -259,6 +267,12 @@ public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, Me waitingRedisRepository.deleteWaiting(storeId, userId); return EntryStatusResponseDto.fromEntity(saved); } else { + if (reservationNumber != null) { + try { + waitingPermitLuaRepository.removeActiveMember(userId, String.valueOf(storeId), reservationNumber); + } catch (Exception ignore) {} + } + // 2) 이미 취소(CANCELLED)된 경우: DB 레코드 찾아 바로 CONFIRMED 로 전환 // TODO 메서드로 분리 LocalDateTime start = LocalDate.now().atStartOfDay(); @@ -283,6 +297,12 @@ public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, Me throw new IllegalStateException("WAITING/CALLING 상태에서만 취소 가능합니다."); } + if (reservationNumber != null) { + waitingPermitLuaRepository.removeActiveMember( + userId, String.valueOf(storeId), reservationNumber + ); + } + Reservation r = Reservation.builder() .reservationNumber(reservationNumber) .store(storeRepository.getReferenceById(storeId)) diff --git a/nowait-app-admin-api/src/test/java/com/nowait/applicationadmin/reservation/ReservationServiceTest.java b/nowait-app-admin-api/src/test/java/com/nowait/applicationadmin/reservation/ReservationServiceTest.java index c3757ca5..2290d86c 100644 --- a/nowait-app-admin-api/src/test/java/com/nowait/applicationadmin/reservation/ReservationServiceTest.java +++ b/nowait-app-admin-api/src/test/java/com/nowait/applicationadmin/reservation/ReservationServiceTest.java @@ -26,7 +26,6 @@ import com.nowait.applicationadmin.reservation.dto.EntryStatusResponseDto; import com.nowait.applicationadmin.reservation.dto.WaitingUserResponse; -import com.nowait.applicationadmin.reservation.repository.WaitingRedisRepository; import com.nowait.applicationadmin.reservation.service.ReservationService; import com.nowait.common.enums.ReservationStatus; import com.nowait.common.enums.Role; diff --git a/nowait-app-admin-api/src/test/java/com/nowait/applicationadmin/reservation/WaitingRedisRepositoryTest.java b/nowait-app-admin-api/src/test/java/com/nowait/applicationadmin/reservation/WaitingRedisRepositoryTest.java index 841c1ea7..0b63cad2 100644 --- a/nowait-app-admin-api/src/test/java/com/nowait/applicationadmin/reservation/WaitingRedisRepositoryTest.java +++ b/nowait-app-admin-api/src/test/java/com/nowait/applicationadmin/reservation/WaitingRedisRepositoryTest.java @@ -1,6 +1,5 @@ package com.nowait.applicationadmin.reservation; -import static org.assertj.core.api.Assertions.*; import static org.mockito.BDDMockito.*; import org.junit.jupiter.api.BeforeEach; @@ -13,8 +12,6 @@ import org.springframework.data.redis.core.StringRedisTemplate; import org.springframework.data.redis.core.ZSetOperations; -import com.nowait.applicationadmin.reservation.repository.WaitingRedisRepository; - public class WaitingRedisRepositoryTest { @Mock private StringRedisTemplate template; diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/service/ReservationService.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/service/ReservationService.java index 69c5956a..82e66e3c 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/service/ReservationService.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/service/ReservationService.java @@ -22,7 +22,6 @@ import com.nowait.applicationuser.reservation.dto.ReservationCreateRequestDto; import com.nowait.applicationuser.reservation.dto.ReservationCreateResponseDto; import com.nowait.applicationuser.reservation.dto.WaitingResponseDto; -import com.nowait.applicationuser.reservation.repository.WaitingPermitLuaRepository; import com.nowait.applicationuser.reservation.repository.WaitingUserRedisRepository; import com.nowait.common.enums.ReservationStatus; import com.nowait.common.enums.Role; @@ -45,6 +44,7 @@ import com.nowait.domaincorerdb.user.exception.UserNotFoundException; import com.nowait.domaincorerdb.user.repository.UserRepository; import com.nowait.domaincoreredis.common.util.RedisKeyUtils; +import com.nowait.domaincoreredis.reservation.repository.WaitingPermitLuaRepository; import com.nowait.domainuserrdb.oauth.dto.CustomOAuth2User; import lombok.RequiredArgsConstructor; @@ -218,7 +218,6 @@ public boolean cancelWaiting(Long storeId, CustomOAuth2User customOAuth2User) { .build(); reservationRepository.save(reservation); - waitingPermitLuaRepository.removeActiveMember(userId, String.valueOf(storeId), reservationNumber); return true; // return removed; @@ -232,6 +231,11 @@ public List getAllMyWaitings(CustomOAuth2User customOAuth2Use if (members.isEmpty()) return Collections.emptyList(); + Map activeResIdByStore = members.stream() + .map(m -> m.split(":", 2)) + .filter(a -> a.length == 2) + .collect(Collectors.toMap(a -> Long.parseLong(a[0]), a -> a[1], (a, b) -> a)); + // 1) 현재 SCAN 기반으로 얻어온 storeId 리스트 // List storeIds = waitingUserRedisRepository.getUserWaitingStoreIds(userId); // if (storeIds.isEmpty()) @@ -298,6 +302,23 @@ public List getAllMyWaitings(CustomOAuth2User customOAuth2Use String status = (String)it.next(); String reservationId = (String)it.next(); + + String activeReservationId = activeResIdByStore.get(storeId); + // 유령 감지: 큐에 없음/번호 불일치/번호 null + boolean zombie = (rankObj == null) || (reservationId == null) || + (activeReservationId != null && !activeReservationId.equals(reservationId)); + if (zombie) { + try { + String toRemove = (activeReservationId != null) ? activeReservationId + : (reservationId != null ? reservationId : null); + if (toRemove != null) { + waitingPermitLuaRepository.removeActiveMember(userId, String.valueOf(storeId), toRemove); + } + } catch (Exception ignore) {} + continue; // 목록에서 제외 + } + + int rank = (rankObj != null ? rankObj.intValue() + 1 : 0); int teamsAhead = (rankObj != null ? rankObj.intValue() : 0); int partySize = (partyStr != null ? Integer.parseInt(partyStr) : 0); diff --git a/nowait-app-user-api/src/test/java/com/nowait/applicationuser/reservation/service/ReservationServiceConcurrencyTest.java b/nowait-app-user-api/src/test/java/com/nowait/applicationuser/reservation/service/ReservationServiceConcurrencyTest.java index 2ae1d641..bbba92f8 100644 --- a/nowait-app-user-api/src/test/java/com/nowait/applicationuser/reservation/service/ReservationServiceConcurrencyTest.java +++ b/nowait-app-user-api/src/test/java/com/nowait/applicationuser/reservation/service/ReservationServiceConcurrencyTest.java @@ -26,7 +26,6 @@ import com.nowait.applicationuser.reservation.dto.ReservationCreateRequestDto; import com.nowait.applicationuser.reservation.dto.WaitingResponseDto; -import com.nowait.applicationuser.reservation.repository.WaitingPermitLuaRepository; import com.nowait.applicationuser.reservation.repository.WaitingUserRedisRepository; import com.nowait.common.enums.Role; import com.nowait.domaincoreredis.common.util.RedisKeyUtils; @@ -37,6 +36,7 @@ import com.nowait.domaincorerdb.user.repository.UserRepository; import com.nowait.domaincorerdb.store.entity.Store; import com.nowait.domaincorerdb.user.entity.User; +import com.nowait.domaincoreredis.reservation.repository.WaitingPermitLuaRepository; import com.nowait.domainuserrdb.oauth.dto.CustomOAuth2User; import com.redis.testcontainers.RedisContainer; diff --git a/nowait-app-user-api/src/test/java/com/nowait/applicationuser/reservation/service/ReservationServiceTest.java b/nowait-app-user-api/src/test/java/com/nowait/applicationuser/reservation/service/ReservationServiceTest.java index 0389a72f..35342205 100644 --- a/nowait-app-user-api/src/test/java/com/nowait/applicationuser/reservation/service/ReservationServiceTest.java +++ b/nowait-app-user-api/src/test/java/com/nowait/applicationuser/reservation/service/ReservationServiceTest.java @@ -23,7 +23,6 @@ import com.nowait.applicationuser.reservation.dto.ReservationCreateRequestDto; import com.nowait.applicationuser.reservation.dto.ReservationCreateResponseDto; import com.nowait.applicationuser.reservation.dto.WaitingResponseDto; -import com.nowait.applicationuser.reservation.repository.WaitingPermitLuaRepository; import com.nowait.applicationuser.reservation.repository.WaitingUserRedisRepository; import com.nowait.common.enums.ReservationStatus; import com.nowait.common.enums.Role; diff --git a/nowait-app-user-api/src/test/java/com/nowait/applicationuser/reservation/service/WaitingPermitLuaRepositoryTest.java b/nowait-app-user-api/src/test/java/com/nowait/applicationuser/reservation/service/WaitingPermitLuaRepositoryTest.java index 6f22a617..a9ca63ab 100644 --- a/nowait-app-user-api/src/test/java/com/nowait/applicationuser/reservation/service/WaitingPermitLuaRepositoryTest.java +++ b/nowait-app-user-api/src/test/java/com/nowait/applicationuser/reservation/service/WaitingPermitLuaRepositoryTest.java @@ -14,7 +14,6 @@ import org.testcontainers.junit.jupiter.Container; import org.testcontainers.junit.jupiter.Testcontainers; -import com.nowait.applicationuser.reservation.repository.WaitingPermitLuaRepository; import com.nowait.domaincoreredis.common.util.RedisKeyUtils; @Testcontainers diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/repository/WaitingPermitLuaRepository.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/reservation/repository/WaitingPermitLuaRepository.java similarity index 98% rename from nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/repository/WaitingPermitLuaRepository.java rename to nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/reservation/repository/WaitingPermitLuaRepository.java index 8804919f..ffab7c60 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/repository/WaitingPermitLuaRepository.java +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/reservation/repository/WaitingPermitLuaRepository.java @@ -1,4 +1,4 @@ -package com.nowait.applicationuser.reservation.repository; +package com.nowait.domaincoreredis.reservation.repository; import java.nio.charset.StandardCharsets; import java.time.Duration; diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/repository/WaitingRedisRepository.java b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/reservation/repository/WaitingRedisRepository.java similarity index 97% rename from nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/repository/WaitingRedisRepository.java rename to nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/reservation/repository/WaitingRedisRepository.java index 02e29c82..0799786e 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/repository/WaitingRedisRepository.java +++ b/nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/reservation/repository/WaitingRedisRepository.java @@ -1,7 +1,6 @@ -package com.nowait.applicationadmin.reservation.repository; +package com.nowait.domaincoreredis.reservation.repository; import java.util.ArrayList; -import java.util.Date; import java.util.List; import java.util.Set;