-
Notifications
You must be signed in to change notification settings - Fork 0
feat(Reservation): 관리자 예약 대기열 호출 #131
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
The head ref may contain hidden characters: "feature/#130_\uAD00\uB9AC\uC790\uC608\uC57D\uD638\uCD9C\uB85C\uC9C1"
Conversation
- redis에 예약 상태 key 추가 - 특정 userId 호출 로직 구현 - 예약 상태 CALLING으로 변경 시, TTL 10분으로 변경
Walkthrough관리자 API에 대기 호출 기능이 추가되었습니다. 새로운 PATCH 엔드포인트가 도입되어, 관리자가 특정 매장과 사용자의 대기 상태를 10분간 "호출중"으로 변경할 수 있습니다. 이를 위해 Redis 저장소와 DTO, 서비스, 키 유틸리티, 사용자 API의 관련 클래스에 메서드 및 필드가 추가·수정되었습니다. Changes
Sequence Diagram(s)sequenceDiagram
participant Admin as 관리자(요청자)
participant Controller as ReservationController
participant Service as ReservationService
participant RedisRepo as WaitingRedisRepository
participant DTO as CallingWaitingResponseDto
Admin->>Controller: PATCH /admin/calling/redis/{storeId}?userId={userId}
Controller->>Service: callWaiting(storeId, userId, memberDetails)
Service->>RedisRepo: getWaitingStatus(storeId, userId)
RedisRepo-->>Service: "WAITING" or other status
Service-->>Service: 상태 검증/예외처리
Service->>RedisRepo: setWaitingStatus(storeId, userId, "CALLING")
Service-->>DTO: CallingWaitingResponseDto 생성
Service-->>Controller: CallingWaitingResponseDto 반환
Controller-->>Admin: API 성공 응답 반환
Estimated code review effort3 (30–60분) Suggested reviewers
📜 Recent review detailsConfiguration used: CodeRabbit UI 📒 Files selected for processing (3)
🚧 Files skipped from review as they are similar to previous changes (3)
✨ Finishing Touches
Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. 🪧 TipsChatThere are 3 ways to chat with CodeRabbit:
SupportNeed help? Create a ticket on our support page for assistance with any issues or questions. Note: Be mindful of the bot's finite context window. It's strongly recommended to break down tasks such as reading entire modules into smaller chunks. For a focused discussion, use review comments to chat about specific files and their changes, instead of using the PR comments. CodeRabbit Commands (Invoked using PR comments)
Other keywords and placeholders
CodeRabbit Configuration File (
|
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Actionable comments posted: 6
🧹 Nitpick comments (1)
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java (1)
100-100: TODO 주석에 대한 구현 계획을 명확히 해주세요.CALLING에서 입장완료로의 상태 전환 로직 구현이 필요합니다. Redis 삭제와 RDB 저장 전략에 대한 결정이 필요합니다.
해당 기능 구현에 도움이 필요하시면 구체적인 구현 방안을 제안해드릴 수 있습니다.
📜 Review details
Configuration used: CodeRabbit UI
Review profile: CHILL
Plan: Pro
📒 Files selected for processing (7)
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/controller/ReservationController.java(2 hunks)nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/dto/CallingWaitingResponseDto.java(1 hunks)nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/repository/WaitingRedisRepository.java(2 hunks)nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java(3 hunks)nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/repository/WaitingUserRedisRepository.java(2 hunks)nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/service/ReservationService.java(8 hunks)nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/common/util/RedisKeyUtils.java(2 hunks)
🧰 Additional context used
🧬 Code Graph Analysis (3)
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/repository/WaitingRedisRepository.java (1)
nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/common/util/RedisKeyUtils.java (1)
RedisKeyUtils(5-45)
nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/repository/WaitingUserRedisRepository.java (1)
nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/common/util/RedisKeyUtils.java (1)
RedisKeyUtils(5-45)
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java (1)
nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/service/ReservationService.java (1)
Service(39-192)
🔇 Additional comments (7)
nowait-domain/domain-redis/src/main/java/com/nowait/domaincoreredis/common/util/RedisKeyUtils.java (1)
19-19: 변경사항이 기존 코드 패턴과 일치합니다.새로운 Redis 키 접두사와 getter 메서드가 기존 코드 스타일과 일관성 있게 구현되었습니다.
Also applies to: 44-44
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/controller/ReservationController.java (2)
11-11: 필요한 import가 올바르게 추가되었습니다.새로운 기능을 위한 import 구문들이 적절히 추가되었습니다.
Also applies to: 15-15
66-80: REST 호출 엔드포인트 검토 완료: userId 타입 일관성 확인됨모든 예약 대기 호출 흐름(Controller → Service → Redis Repository)에서
userId를String으로 일관되게 사용하고 있습니다. 별도의 타입 변경이나 추가 조치가 필요하지 않습니다.nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/service/ReservationService.java (2)
20-20: 리포지토리 네이밍 변경이 적절히 적용되었습니다.
WaitingUserRedisRepository로의 네이밍 변경이 일관성 있게 적용되었습니다.Also applies to: 46-46
68-68: 모든 리포지토리 메서드 호출이 올바르게 업데이트되었습니다.새로운 리포지토리 인터페이스를 사용하도록 모든 메서드 호출이 일관성 있게 변경되었으며,
getWaitingStatus메서드 추가로 사용자에게 실시간 상태를 제공할 수 있게 되었습니다.Also applies to: 73-73, 86-87, 100-100, 109-109, 128-130, 143-143
nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/repository/WaitingUserRedisRepository.java (1)
84-89: getWaitingStatus 메서드 구현이 올바릅니다상태값 조회 메서드가 적절하게 구현되었습니다. null 체크와 String 변환 로직이 올바르게 처리되어 있습니다.
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/repository/WaitingRedisRepository.java (1)
38-43: getWaitingStatus 메서드가 적절히 구현되었습니다상태값 조회 로직이 올바르게 구현되어 있으며, null 처리도 적절합니다.
| package com.nowait.applicationadmin.reservation.dto; | ||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
| import lombok.AllArgsConstructor; | ||
| import lombok.Builder; | ||
| import lombok.Getter; | ||
| import lombok.NoArgsConstructor; | ||
|
|
||
| import java.time.LocalDateTime; | ||
|
|
||
| @Getter | ||
| @Builder | ||
| @NoArgsConstructor | ||
| @AllArgsConstructor | ||
| @Schema(description = "관리자 호출(호출 상태 변경) 응답 DTO") | ||
| public class CallingWaitingResponseDto { | ||
| @Schema(description = "매장 ID", example = "7") | ||
| private Long storeId; | ||
|
|
||
| @Schema(description = "유저 ID", example = "123") | ||
| private String userId; | ||
|
|
||
| @Schema(description = "대기 상태", example = "CALLING") | ||
| private String status; | ||
|
|
||
| @Schema(description = "호출 시각", example = "2025-07-21T17:01:00") | ||
| private LocalDateTime calledAt; | ||
|
|
||
| @Schema(description = "대기 순번", example = "3") | ||
| private Integer rank; | ||
|
|
||
| @Schema(description = "파티 인원", example = "4") | ||
| private Integer partySize; | ||
|
|
||
|
|
||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
DTO 구조가 올바르게 설계되었습니다.
Lombok 어노테이션과 Swagger 문서화가 적절히 적용되었습니다. 하지만 서비스 레이어에서 rank와 partySize 필드가 채워지지 않고 있습니다.
서비스 메서드에서 누락된 필드들이 올바르게 설정되는지 확인해보세요:
🏁 Script executed:
#!/bin/bash
# CallingWaitingResponseDto 빌더에서 설정되는 필드 확인
rg -A 10 "CallingWaitingResponseDto\.builder"Length of output: 1441
rank 및 partySize 필드 서비스 레이어 설정 필요
서비스 메서드의 CallingWaitingResponseDto.builder() 호출부에서 rank와 partySize 값이 누락되어 있습니다. 아래 위치를 확인 후, 적절한 값으로 설정해 주세요.
- 파일:
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java - 영역:
CallingWaitingResponseDto.builder()호출 직후 - 예시 수정
return CallingWaitingResponseDto.builder() .storeId(storeId) .userId(userId) .status("CALLING") .calledAt(calledAt) .rank(reservation.getRank()) // 추가 .partySize(reservation.getPartySize()) // 추가 .build();
🤖 Prompt for AI Agents
In
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java
around the CallingWaitingResponseDto.builder() call, the fields rank and
partySize are missing from the builder. Fix this by adding
.rank(reservation.getRank()) and .partySize(reservation.getPartySize()) to the
builder chain before calling build(), ensuring these values are properly set in
the response DTO.
| // 상태값 저장 및 변경 | ||
| public void setWaitingStatus(Long storeId, String userId, String status) { | ||
| String queueKey = RedisKeyUtils.buildWaitingKeyPrefix() + storeId; | ||
| String partyKey = RedisKeyUtils.buildWaitingPartySizeKeyPrefix() + storeId; | ||
| String statusKey = RedisKeyUtils.buildWaitingStatusKeyPrefix() + storeId; | ||
| redisTemplate.opsForHash().put(statusKey, userId, status); | ||
| // WAITING -> CALLING 으로 변경 시 TTL 12h에서 10m로 변경 | ||
| if (status.equals("CALLING")) { | ||
| redisTemplate.expire(queueKey, Duration.ofMinutes(10)); | ||
| redisTemplate.expire(partyKey, Duration.ofMinutes(10)); | ||
| redisTemplate.expire(statusKey, Duration.ofMinutes(10)); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
💡 Verification agent
🧩 Analysis chain
CALLING 상태 변경 시 TTL 조정 로직 검토 필요
"CALLING" 상태로 변경할 때 모든 관련 키의 TTL을 10분으로 단축하는 로직은 좋지만, 몇 가지 고려사항이 있습니다.
다음 사항들을 확인해주세요:
- 하드코딩된 문자열 "CALLING" 대신 상수 사용을 고려하세요
- TTL 변경이 기존 대기열의 다른 사용자들에게 영향을 주지 않는지 확인이 필요합니다
개선 제안:
+ private static final String STATUS_CALLING = "CALLING";
+ private static final Duration CALLING_TTL = Duration.ofMinutes(10);
// 상태값 저장 및 변경
public void setWaitingStatus(Long storeId, String userId, String status) {
String queueKey = RedisKeyUtils.buildWaitingKeyPrefix() + storeId;
String partyKey = RedisKeyUtils.buildWaitingPartySizeKeyPrefix() + storeId;
String statusKey = RedisKeyUtils.buildWaitingStatusKeyPrefix() + storeId;
redisTemplate.opsForHash().put(statusKey, userId, status);
// WAITING -> CALLING 으로 변경 시 TTL 12h에서 10m로 변경
- if (status.equals("CALLING")) {
+ if (STATUS_CALLING.equals(status)) {
- redisTemplate.expire(queueKey, Duration.ofMinutes(10));
- redisTemplate.expire(partyKey, Duration.ofMinutes(10));
- redisTemplate.expire(statusKey, Duration.ofMinutes(10));
+ redisTemplate.expire(queueKey, CALLING_TTL);
+ redisTemplate.expire(partyKey, CALLING_TTL);
+ redisTemplate.expire(statusKey, CALLING_TTL);
}
}🏁 Script executed:
#!/bin/bash
# CALLING 상태 관련 상수나 enum이 정의되어 있는지 확인
rg -A 3 -B 3 "CALLING|WAITING" --type javaLength of output: 19839
CALLING 상태 변경 시 TTL 조정 및 상수화 적용 필요
다음 사항을 반영해주세요:
- ReservationStatus enum 활용
• 하드코딩된"CALLING"대신ReservationStatus.CALLING.name()혹은 메서드 시그니처를ReservationStatus status로 변경 고려 - TTL 값 상수화
•Duration.ofMinutes(10)을private static final Duration CALLING_TTL = Duration.ofMinutes(10);로 분리 - Redis 키 전체에 대한 expire 적용 주의
•queueKey,partyKey,statusKey모두 같은 store 단위 키에 적용되어, 한 사용자 호출 시 같은 스토어의 모든 대기 데이터 TTL이 10분으로 변경됩니다.
• 의도치 않은 대기열 만료를 방지하기 위해 per-user TTL 전략(개별 키 분리, Sorted Set 활용 등) 검토 필요
예시 변경안:
+ private static final Duration CALLING_TTL = Duration.ofMinutes(10);
// 상태값 저장 및 변경
public void setWaitingStatus(Long storeId, String userId, String status) {
String queueKey = RedisKeyUtils.buildWaitingKeyPrefix() + storeId;
String partyKey = RedisKeyUtils.buildWaitingPartySizeKeyPrefix() + storeId;
String statusKey = RedisKeyUtils.buildWaitingStatusKeyPrefix() + storeId;
redisTemplate.opsForHash().put(statusKey, userId, status);
// WAITING -> CALLING 으로 변경 시 TTL 12h에서 10m로 변경
- if (status.equals("CALLING")) {
+ if (ReservationStatus.CALLING.name().equals(status)) {
redisTemplate.expire(queueKey, CALLING_TTL);
redisTemplate.expire(partyKey, CALLING_TTL);
redisTemplate.expire(statusKey, CALLING_TTL);
}
}🤖 Prompt for AI Agents
In
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/repository/WaitingRedisRepository.java
around lines 24 to 36, replace the hardcoded "CALLING" string with the
ReservationStatus enum by changing the method signature to accept
ReservationStatus status or using ReservationStatus.CALLING.name(). Extract the
TTL value Duration.ofMinutes(10) into a private static final constant like
CALLING_TTL. Additionally, revise the TTL expiration logic to avoid setting the
expire on the entire store-level keys (queueKey, partyKey, statusKey) since this
affects all users in the store; instead, implement a per-user TTL strategy by
separating keys per user or using a Sorted Set to manage expirations
individually.
...src/main/java/com/nowait/applicationadmin/reservation/repository/WaitingRedisRepository.java
Outdated
Show resolved
Hide resolved
| @Transactional | ||
| public CallingWaitingResponseDto callWaiting(Long storeId, String userId, MemberDetails memberDetails) { | ||
| User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); | ||
| if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { | ||
| throw new ReservationViewUnauthorizedException(); | ||
| } | ||
| String status = waitingRedisRepository.getWaitingStatus(storeId, userId); | ||
| System.out.println(status); | ||
| if (!"WAITING".equals(status)) { | ||
| throw new IllegalStateException("이미 호출되었거나 없는 예약입니다."); | ||
| } | ||
| waitingRedisRepository.setWaitingStatus(storeId, userId, "CALLING"); | ||
| LocalDateTime calledAt = LocalDateTime.now(); | ||
| return CallingWaitingResponseDto.builder() | ||
| .storeId(storeId) | ||
| .userId(userId) | ||
| .status("CALLING") | ||
| .calledAt(calledAt) | ||
| .build(); | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
🛠️ Refactor suggestion
메서드 로직은 올바르지만 몇 가지 개선이 필요합니다.
전반적인 구현 로직은 적절하나 다음 사항들을 개선해주세요:
System.out.println디버그 출력을 제거하고 적절한 로깅으로 교체- 응답 DTO에서
rank와partySize필드가 설정되지 않음 - Redis 전용 작업에
@Transactional이 필요한지 검토 필요
- System.out.println(status);
+ // Redis에서 rank와 partySize 정보도 가져와서 응답에 포함
+ Long rank = waitingRedisRepository.getRank(storeId, userId);
+ Integer partySize = waitingRedisRepository.getPartySize(storeId, userId);
+
if (!"WAITING".equals(status)) {
throw new IllegalStateException("이미 호출되었거나 없는 예약입니다.");
}
@@ -96,6 +99,8 @@
return CallingWaitingResponseDto.builder()
.storeId(storeId)
.userId(userId)
.status("CALLING")
.calledAt(calledAt)
+ .rank(rank != null ? rank.intValue() + 1 : null)
+ .partySize(partySize)
.build();📝 Committable suggestion
‼️ IMPORTANT
Carefully review the code before committing. Ensure that it accurately replaces the highlighted code, contains no missing lines, and has no issues with indentation. Thoroughly test & benchmark the code to ensure it meets the requirements.
| @Transactional | |
| public CallingWaitingResponseDto callWaiting(Long storeId, String userId, MemberDetails memberDetails) { | |
| User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); | |
| if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { | |
| throw new ReservationViewUnauthorizedException(); | |
| } | |
| String status = waitingRedisRepository.getWaitingStatus(storeId, userId); | |
| System.out.println(status); | |
| if (!"WAITING".equals(status)) { | |
| throw new IllegalStateException("이미 호출되었거나 없는 예약입니다."); | |
| } | |
| waitingRedisRepository.setWaitingStatus(storeId, userId, "CALLING"); | |
| LocalDateTime calledAt = LocalDateTime.now(); | |
| return CallingWaitingResponseDto.builder() | |
| .storeId(storeId) | |
| .userId(userId) | |
| .status("CALLING") | |
| .calledAt(calledAt) | |
| .build(); | |
| } | |
| @Transactional | |
| public CallingWaitingResponseDto callWaiting(Long storeId, String userId, MemberDetails memberDetails) { | |
| User user = userRepository.findById(memberDetails.getId()).orElseThrow(UserNotFoundException::new); | |
| if (!Role.SUPER_ADMIN.equals(user.getRole()) && !user.getStoreId().equals(storeId)) { | |
| throw new ReservationViewUnauthorizedException(); | |
| } | |
| String status = waitingRedisRepository.getWaitingStatus(storeId, userId); | |
| - System.out.println(status); | |
| + // Redis에서 rank와 partySize 정보도 가져와서 응답에 포함 | |
| + Long rank = waitingRedisRepository.getRank(storeId, userId); | |
| + Integer partySize = waitingRedisRepository.getPartySize(storeId, userId); | |
| + | |
| if (!"WAITING".equals(status)) { | |
| throw new IllegalStateException("이미 호출되었거나 없는 예약입니다."); | |
| } | |
| waitingRedisRepository.setWaitingStatus(storeId, userId, "CALLING"); | |
| LocalDateTime calledAt = LocalDateTime.now(); | |
| return CallingWaitingResponseDto.builder() | |
| .storeId(storeId) | |
| .userId(userId) | |
| .status("CALLING") | |
| .calledAt(calledAt) | |
| + .rank(rank != null ? rank.intValue() + 1 : null) | |
| + .partySize(partySize) | |
| .build(); | |
| } |
🤖 Prompt for AI Agents
In
nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/service/ReservationService.java
around lines 80 to 99, replace the System.out.println debug output with a proper
logger call to follow best practices. Add the missing rank and partySize fields
to the CallingWaitingResponseDto before returning it, ensuring these values are
retrieved appropriately from the reservation or waiting data. Finally, review
whether the @Transactional annotation is necessary for this Redis-related method
and remove it if it does not manage any database transactions to avoid
unnecessary overhead.
.../main/java/com/nowait/applicationuser/reservation/repository/WaitingUserRedisRepository.java
Show resolved
Hide resolved
.../main/java/com/nowait/applicationuser/reservation/repository/WaitingUserRedisRepository.java
Show resolved
Hide resolved
- redis에 예약 상태 key 추가 - 특정 userId 호출 로직 구현 - 예약 상태 CALLING으로 변경 시, TTL 10분으로 변경
작업 요약
Issue Link
문제점 및 어려움
해결 방안
Reference
Summary by CodeRabbit
신규 기능
버그 수정
리팩터
문서화