Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -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 -> "";
};
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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())
Expand All @@ -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)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -16,8 +16,8 @@
@Repository
@RequiredArgsConstructor
public class WaitingRedisRepository {

private final StringRedisTemplate redisTemplate;
private final Date expireAt = RedisKeyUtils.expireAtNext03();

// 대기열 전체 인원수 조회
public List<ZSetOperations.TypedTuple<String>> getAllWaitingWithScore(Long storeId) {
Expand Down Expand Up @@ -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) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()
Expand Down Expand Up @@ -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))) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -148,7 +148,7 @@ public List<Long> getUserWaitingStoreIds(String userId) {

// 3) 파이프라인으로 zRank 한 번에 조회
List<Object> pipelineResults = redisTemplate.executePipelined(
(RedisCallback<Object>) conn -> {
(RedisCallback<Object>)conn -> {
byte[] uid = redisTemplate.getStringSerializer().serialize(userId);
for (String key : zsetKeys) {
byte[] rawKey = redisTemplate.getStringSerializer().serialize(key);
Expand Down Expand Up @@ -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);

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down