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
4 changes: 3 additions & 1 deletion build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -32,8 +32,10 @@ dependencies {

implementation 'org.hibernate.orm:hibernate-spatial:'

implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
// REDIS CACHE
implementation 'org.springframework.boot:spring-boot-starter-data-redis'
implementation 'org.springframework.boot:spring-boot-starter-cache'
implementation 'org.springframework.boot:spring-boot-starter-data-jpa'
implementation 'org.springframework.boot:spring-boot-starter-web'
compileOnly 'org.projectlombok:lombok'
runtimeOnly 'com.mysql:mysql-connector-j'
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.yscp.catchtable.application.queue;

import com.yscp.catchtable.application.queue.dto.StoreQueueDto;
import com.yscp.catchtable.exception.BadRequestError;
import com.yscp.catchtable.exception.CatchTableException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.redis.core.RedisTemplate;
import org.springframework.stereotype.Service;

import java.time.Duration;

@RequiredArgsConstructor
@Service
public class StoreQueueService {
private final RedisTemplate<String, String> redisTemplate;

public void registerWaiting(StoreQueueDto storeQueueDto) {

Boolean result = redisTemplate.opsForZSet().addIfAbsent(storeQueueDto.key(),
storeQueueDto.value(),
storeQueueDto.score());

if (Boolean.FALSE.equals(result)) {
throw new CatchTableException(BadRequestError.ALREADY_REGISTER_WAITING);
}

redisTemplate.expire(storeQueueDto.key(), Duration.ofMinutes(7L));

}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.yscp.catchtable.application.queue.dto;

import java.time.Instant;

public record StoreQueueDto(
String storeReserveIdx,
String userIdx
) {
private static final String WAITING_KEY_FORMAT = "store:%s:waiting:v1:%s";

public String key() {
return String.format(WAITING_KEY_FORMAT, storeReserveIdx, userIdx);
}

public String value() {
return userIdx;
}

public double score() {
return Instant.now().toEpochMilli();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.yscp.catchtable.application.reserve.dto.ReserveDto;
import com.yscp.catchtable.application.reserve.dto.StoreReserveDto;
import com.yscp.catchtable.domain.reserve.entity.ReserveData;
import com.yscp.catchtable.domain.reserve.repository.ReserveRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -30,4 +31,9 @@ public List<ReserveDto> findReserveDtos(Long idx, LocalDate localDate) {
.map(ReserveDto::from)
.toList();
}

public ReservesInDayDto getReservesInDay(Long storeIdx, LocalDate date) {
List<ReserveData> reserveDates = repository.findByStore_IdxAndReserveDate(storeIdx, date);
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

"_" 이것은 무엇인가요ㅎㅎ
의도한건가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

메소드네이밍으로 함수 지을 때 _을 사용할 경우 연관관계 객체의 필드를 사용할 수 있는 것 같더라구요!
Property Expressions 관련 내용을 참고했습니다!
(https://docs.spring.io/spring-data/commons/reference/repositories/query-methods-details.html)

return ReservesInDayDto.from(reserveDates);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.yscp.catchtable.application.reserve;

import com.yscp.catchtable.application.reserve.dto.ReserveInDayDto;
import com.yscp.catchtable.domain.reserve.entity.ReserveData;
import org.springframework.util.CollectionUtils;

import java.util.ArrayList;
import java.util.List;

public record ReservesInDayDto(
List<ReserveInDayDto> reserves
) {
public static ReservesInDayDto from(List<ReserveData> reserveDates) {
if (CollectionUtils.isEmpty(reserveDates)) {
return new ReservesInDayDto(new ArrayList<>());
}

return new ReservesInDayDto(convert(reserveDates));
}

private static List<ReserveInDayDto> convert(List<ReserveData> reserveDates) {
return reserveDates.stream()
.map(reserveData -> new ReserveInDayDto(
reserveData.getIdx(),
reserveData.getReserveTime(),
reserveData.getMaxUserCount()
)).toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.yscp.catchtable.application.reserve.dto;

public record ReserveInDayDto(
Long idx,
String reserveTime,
Integer maxUserCount
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.yscp.catchtable.application.store.mapper;

import com.yscp.catchtable.application.queue.dto.StoreQueueDto;
import com.yscp.catchtable.presentation.store.dto.StoreQueueRequestDto;
import lombok.experimental.UtilityClass;

@UtilityClass
public class StoreQueueMapper {
public static StoreQueueDto toQueueDto(StoreQueueRequestDto storeQueueRequestDto) {
return new StoreQueueDto(
storeQueueRequestDto.storeReserveIdx().toString(),
storeQueueRequestDto.userIdx()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,9 +27,9 @@ public class ReserveData {
private Store store;

@Comment("예약 일자")
private LocalDate reserveData;
private LocalDate reserveDate;
@Comment("예약 시간")
private Integer reserveTime;
private String reserveTime;
@Comment("최소 인원")
private Integer minUserCount;
@Comment("최대 인원")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -38,4 +38,6 @@ public interface ReserveRepository extends JpaRepository<ReserveData, Long> {
"""
, nativeQuery = true)
List<StoreReserveDto> getStoreReserveDtoBeforeMaxDate(@Param("idx") Long idx, @Param("date") LocalDate date);

List<ReserveData> findByStore_IdxAndReserveDate(Long idx, LocalDate reserveDate);
}
Original file line number Diff line number Diff line change
Expand Up @@ -11,11 +11,16 @@ public enum BadRequestError implements CustomError {
/**
* 0 ~ 100 Common
*/
NULL_EXCEPTION("%s", 1, true);
NULL_EXCEPTION("%s", "1", true),

/**
* 200 ~ 300 Waiting
*/
ALREADY_REGISTER_WAITING("이미 예약을 진행하고 있습니다.", "200" , false);

private final HttpStatus httpStatus = HttpStatus.BAD_REQUEST;
private final String message;
private final Integer errorCode;
private final String errorCode;
private final Boolean isCustomMessage;

@Override
Expand Down
11 changes: 4 additions & 7 deletions src/main/java/com/yscp/catchtable/exception/CustomError.java
Original file line number Diff line number Diff line change
Expand Up @@ -3,13 +3,10 @@
import org.springframework.http.HttpStatus;

public interface CustomError {
HttpStatus getHttpStatus();

String getErrorCode();

String getMessage();

default Boolean isCustomMessage() {
HttpStatus getHttpStatus();
String getErrorCode();
String getMessage();
default Boolean isCustomMessage() {
return false;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,15 +4,15 @@
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;

@RequiredArgsConstructor
@Getter
@RequiredArgsConstructor
public enum NotFoundError implements CustomError {
NOT_FOUND_STORE("상점을 찾을 수 없습니다.", 100, false),
NOT_FOUND_BUSINESS_HOUR("영업 시간을 조회할 수 없습니다.", 101, false);
NOT_FOUND_STORE("상점을 찾을 수 없습니다.", "100", false),
NOT_FOUND_BUSINESS_HOUR("영업 시간을 조회할 수 없습니다.", "101", false);

private final HttpStatus httpStatus = HttpStatus.NOT_FOUND;
private final String message;
private final Integer errorCode;
private final String errorCode;
private final Boolean isCustomMessage;

@Override
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package com.yscp.catchtable.presentation.reserve;

import com.yscp.catchtable.application.reserve.ReserveService;
import com.yscp.catchtable.application.reserve.ReservesInDayDto;
import com.yscp.catchtable.presentation.reserve.dto.response.ReserveInDayResponseDtos;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

import java.time.LocalDate;

@RequiredArgsConstructor
@RestController
public class ReserveController {

private final ReserveService reserveService;

@GetMapping("/store/reserves/{storeIdx}")
public ResponseEntity<ReserveInDayResponseDtos> getReservesInDay(@PathVariable Long storeIdx,
@RequestParam LocalDate date) {

ReservesInDayDto reservesInDay = reserveService.getReservesInDay(storeIdx, date);

return ResponseEntity.ok(ReserveInDayResponseDtos.from(reservesInDay));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package com.yscp.catchtable.presentation.reserve.dto.response;

public record ReserveInDayResponseDto(
Long idx,
String reserveTime,
Integer maxUserCount
) {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package com.yscp.catchtable.presentation.reserve.dto.response;

import com.yscp.catchtable.application.reserve.ReservesInDayDto;
import com.yscp.catchtable.application.reserve.dto.ReserveInDayDto;

import java.util.ArrayList;
import java.util.List;

public record ReserveInDayResponseDtos(
List<ReserveInDayResponseDto> reserves
) {
public static ReserveInDayResponseDtos from(ReservesInDayDto reservesInDay) {
if (reservesInDay == null) {
return new ReserveInDayResponseDtos(new ArrayList<>());
}

return new ReserveInDayResponseDtos(convert(reservesInDay.reserves()));
}

private static List<ReserveInDayResponseDto> convert(List<ReserveInDayDto> reservesInDay) {
return reservesInDay.stream()
.map(reserveInDayDto -> new ReserveInDayResponseDto(
reserveInDayDto.idx(),
reserveInDayDto.reserveTime(),
reserveInDayDto.maxUserCount()
)).toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.yscp.catchtable.presentation.store;

import com.yscp.catchtable.application.queue.StoreQueueService;
import com.yscp.catchtable.application.store.mapper.StoreQueueMapper;
import com.yscp.catchtable.presentation.store.dto.StoreQueueRequestDto;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RestController;

@RequiredArgsConstructor
@RestController
public class StoreQueueController {
private final StoreQueueService storeQueueService;

@PostMapping("/store/reservation/enter")
public ResponseEntity<Void> enter(@RequestBody StoreQueueRequestDto storeQueueRequestDto) {
storeQueueService.registerWaiting(StoreQueueMapper.toQueueDto(storeQueueRequestDto));
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package com.yscp.catchtable.presentation.store.dto;

public record StoreQueueRequestDto(
Long storeReserveIdx,
String userIdx
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,14 @@ void findStoreReserveDtoListBeforeMaxDate() {
void getStoreReserveDtoBeforeMaxDate() {
Assertions.assertThatCode(() -> reserveRepository.getStoreReserveDtoBeforeMaxDate(1L, LocalDate.of(2025, 6, 20)))
.doesNotThrowAnyException();
}

@DisplayName("findByStore_IdxAndReserveDate")
@Test
void findByStore_IdxAndReserveDate() {
Assertions.assertThatCode(() -> reserveRepository.findByStore_IdxAndReserveDate(1L, LocalDate.of(2025, 6, 20)))
.doesNotThrowAnyException();

}
}