diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/dto/EntryStatusResponseDto.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/dto/EntryStatusResponseDto.java index 4322e197..1b6ad2cc 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/dto/EntryStatusResponseDto.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/dto/EntryStatusResponseDto.java @@ -69,14 +69,5 @@ public static EntryStatusResponseDto fromEntity(Reservation r) { }) .build(); } - - private String buildMessage(ReservationStatus status, String nickname) { - return switch (status) { - case CALLING -> nickname + "님을 호출하였습니다."; - case CONFIRMED -> nickname + "님의 입장이 완료되었습니다."; - case CANCELLED -> nickname + "님의 예약이 취소되었습니다."; - default -> ""; - }; - } } diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/dto/WaitingUserResponse.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/dto/WaitingUserResponse.java index 881b68ec..95fbf33a 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/dto/WaitingUserResponse.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/dto/WaitingUserResponse.java @@ -17,7 +17,7 @@ public class WaitingUserResponse { @Schema(description = "예약 ID", example = "16-20240201-0002") - private String reservationId; + private String reservationNumber; @Schema(description = "유저 ID", example = "16") private String userId; @@ -39,7 +39,7 @@ public class WaitingUserResponse { public static WaitingUserResponse fromEntity(Reservation reservation) { return WaitingUserResponse.builder() - .reservationId(reservation.getId().toString()) + .reservationNumber(reservation.getReservationNumber()) .userId(reservation.getUser().getId().toString()) .partySize(reservation.getPartySize()) .userName(reservation.getUser().getNickname()) @@ -50,7 +50,7 @@ public static WaitingUserResponse fromEntity(Reservation reservation) { public static WaitingUserResponse fromRedis(String reservationId, String userId, Integer partySize, String userName, LocalDateTime createdAt, String status, Double score) { return WaitingUserResponse.builder() - .reservationId(reservationId) + .reservationNumber(reservationId) .userId(userId) .partySize(partySize) .userName(userName) diff --git a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/repository/WaitingRedisRepository.java b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/repository/WaitingRedisRepository.java index e21233a2..02e29c82 100644 --- a/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/repository/WaitingRedisRepository.java +++ b/nowait-app-admin-api/src/main/java/com/nowait/applicationadmin/reservation/repository/WaitingRedisRepository.java @@ -16,8 +16,8 @@ @Repository @RequiredArgsConstructor public class WaitingRedisRepository { + private final StringRedisTemplate redisTemplate; - private final Date expireAt = RedisKeyUtils.expireAtNext03(); // 대기열 전체 인원수 조회 public List> getAllWaitingWithScore(Long storeId) { @@ -90,7 +90,7 @@ public void setWaitingCalledAt(Long storeId, String userId, long timestamp) { String key = RedisKeyUtils.buildWaitingCalledAtKeyPrefix() + storeId; redisTemplate.opsForHash().put(key, userId, String.valueOf(timestamp)); - redisTemplate.expireAt(key, expireAt); + redisTemplate.expireAt(key, RedisKeyUtils.expireAtNext03()); } public Long getWaitingCalledAt(Long storeId, String userId) { 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 c95a6248..bbb163da 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 @@ -247,7 +247,7 @@ public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, Me throw new IllegalStateException("WAITING 상태에서만 CALLING 가능합니다."); } waitingRedisRepository.setWaitingStatus(storeId, userId, ReservationStatus.CALLING.name()); - waitingRedisRepository.setWaitingCalledAt(storeId, userId, now.toInstant(ZoneOffset.ofHours(9)).toEpochMilli()); + waitingRedisRepository.setWaitingCalledAt(storeId, userId, now.atZone(ZoneId.of("Asia/Seoul")).toInstant().toEpochMilli()); return EntryStatusResponseDto.builder() @@ -287,28 +287,26 @@ public EntryStatusResponseDto processEntryStatus(Long storeId, String userId, Me Reservation saved = reservationRepository.save(r); return EntryStatusResponseDto.fromEntity(saved); + } else { + // 2) 이미 취소(CANCELLED)된 경우: DB 레코드 찾아 바로 CONFIRMED 로 전환 + // TODO 메서드로 분리 + LocalDateTime start = LocalDate.now().atStartOfDay(); + LocalDateTime end = LocalDate.now().atTime(LocalTime.MAX); + Reservation existing = reservationRepository + .findFirstByStore_StoreIdAndUserIdAndStatusInAndRequestedAtBetweenOrderByRequestedAtDesc( + storeId, + Long.valueOf(userId), + List.of(ReservationStatus.CANCELLED), + start, + end + ).orElseThrow(() -> new IllegalStateException("취소된 예약이 없습니다.")); + + existing.markConfirmed(now); + existing.updateStatus(ReservationStatus.CONFIRMED); + Reservation saved = reservationRepository.save(existing); + return EntryStatusResponseDto.fromEntity(saved); } - // 2) 이미 취소(CANCELLED)된 경우: DB 레코드 찾아 바로 CONFIRMED 로 전환 - // (Redis에는 상태가 남아있지 않으므로 currStatus==null) - { - LocalDateTime start = LocalDate.now().atStartOfDay(); - LocalDateTime end = LocalDate.now().atTime(LocalTime.MAX); - Reservation existing = reservationRepository - .findFirstByStore_StoreIdAndUserIdAndStatusInAndRequestedAtBetweenOrderByRequestedAtDesc( - storeId, - Long.valueOf(userId), - List.of(ReservationStatus.CANCELLED), - start, - end - ).orElseThrow(() -> new IllegalStateException("취소된 예약이 없습니다.")); - - existing.markConfirmed(now); - existing.updateStatus(ReservationStatus.CONFIRMED); - Reservation saved = reservationRepository.save(existing); - return EntryStatusResponseDto.fromEntity(saved); - } - case CANCELLED: if (!(ReservationStatus.WAITING.name().equals(currStatus) || ReservationStatus.CALLING.name().equals(currStatus))) { diff --git a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/repository/WaitingUserRedisRepository.java b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/repository/WaitingUserRedisRepository.java index 08fb899b..ccf73412 100644 --- a/nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/repository/WaitingUserRedisRepository.java +++ b/nowait-app-user-api/src/main/java/com/nowait/applicationuser/reservation/repository/WaitingUserRedisRepository.java @@ -36,8 +36,8 @@ public String addToWaitingQueue(Long storeId, String userId, Integer partySize, String partyKey = RedisKeyUtils.buildWaitingPartySizeKeyPrefix() + storeId; String statusKey = RedisKeyUtils.buildWaitingStatusKeyPrefix() + storeId; - String seqKey = RedisKeyUtils.buildReservationSeqKey(storeId); - String numberMapKey = RedisKeyUtils.buildReservationNumberKey(storeId); + String seqKey = RedisKeyUtils.buildReservationSeqKey(storeId); + String numberMapKey = RedisKeyUtils.buildReservationNumberKey(storeId); Boolean added = redisTemplate.opsForZSet().addIfAbsent(queueKey, userId, timestamp); String reservationId; @@ -64,7 +64,7 @@ public String addToWaitingQueue(Long storeId, String userId, Integer partySize, redisTemplate.opsForHash().put(partyKey, userId, partySize.toString()); redisTemplate.opsForHash().put(statusKey, userId, "WAITING"); - Duration ttl = setTTL(partyKey, statusKey, userId, partySize); + Duration ttl = calculateTTLUntilNext03AM(); redisTemplate.expire(queueKey, ttl); redisTemplate.expire(partyKey, ttl); @@ -148,7 +148,7 @@ public List getUserWaitingStoreIds(String userId) { // 3) 파이프라인으로 zRank 한 번에 조회 List pipelineResults = redisTemplate.executePipelined( - (RedisCallback) conn -> { + (RedisCallback)conn -> { byte[] uid = redisTemplate.getStringSerializer().serialize(userId); for (String key : zsetKeys) { byte[] rawKey = redisTemplate.getStringSerializer().serialize(key); @@ -191,15 +191,12 @@ public String getReservationId(Long storeId, String userId) { return val != null ? val.toString() : null; } - public Duration setTTL(String partyKey, String statusKey, String userId, Integer partySize) { - // 6) 기존 partySize, status, TTL 설정 - redisTemplate.opsForHash().put(partyKey, userId, partySize.toString()); - redisTemplate.opsForHash().put(statusKey, userId, "WAITING"); - + public Duration calculateTTLUntilNext03AM() { // 6-1) Asia/Seoul 기준으로 오늘 자정(내일 00:00) 구하기 ZoneId zone = ZoneId.of("Asia/Seoul"); - LocalDateTime now = LocalDateTime.now(zone); + LocalDateTime now = LocalDateTime.now(zone); LocalDateTime midnight = now.toLocalDate().plusDays(1).atTime(3, 0); + // 6-2) TTL 남은 초 계산 long secondsUntilMidnight = now.until(midnight, ChronoUnit.SECONDS); diff --git a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/entity/Reservation.java b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/entity/Reservation.java index 6857a7a8..2f64fb34 100644 --- a/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/entity/Reservation.java +++ b/nowait-domain/domain-core-rdb/src/main/java/com/nowait/domaincorerdb/reservation/entity/Reservation.java @@ -35,7 +35,7 @@ public class Reservation { @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - @Column(name = "reservation_number", nullable = false, length = 50) + @Column(name = "reservation_number", nullable = true, length = 50) private String reservationNumber; @ManyToOne(fetch = FetchType.LAZY, optional = false)