-
Notifications
You must be signed in to change notification settings - Fork 0
예약하기 기능을 구현했습니다. #12
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/\uC608\uC57D\uD558\uAE30"
예약하기 기능을 구현했습니다. #12
Changes from all commits
13e73ef
ca48d3f
dc82912
8c19e82
60158c2
5415a6c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -1,30 +1,28 @@ | ||
| 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(), | ||
| redisTemplate.opsForZSet().add(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)); | ||
| public boolean isValidWaitingUser(StoreQueueDto storeQueueDto) { | ||
| Double score = redisTemplate.opsForZSet().score(storeQueueDto.key(), storeQueueDto.value()); | ||
| long now = System.currentTimeMillis(); | ||
| return now - score <= 7 * 60 * 1000; | ||
| } | ||
|
|
||
| public void delete(StoreQueueDto storeQueueDto) { | ||
| redisTemplate.opsForZSet().remove(storeQueueDto.key(), storeQueueDto.value()); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,38 @@ | ||
| package com.yscp.catchtable.application.redis; | ||
|
|
||
| import com.yscp.catchtable.exception.BadRequestError; | ||
| import com.yscp.catchtable.exception.CatchTableException; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.redisson.api.RLock; | ||
| import org.redisson.api.RedissonClient; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| import java.util.concurrent.TimeUnit; | ||
|
|
||
| @RequiredArgsConstructor | ||
| @Service | ||
| public class RedisLockService { | ||
| private final RedissonClient redissonClient; | ||
|
|
||
| public void lock(Runnable runnable, | ||
| String key, | ||
| long waitTime, | ||
| long leaseTime, | ||
| TimeUnit unit) { | ||
| RLock lock = redissonClient.getLock(key); | ||
|
|
||
| try { | ||
| if (lock.tryLock(waitTime, leaseTime, unit)) { | ||
| try { | ||
| runnable.run(); | ||
| } finally { | ||
| lock.unlock(); | ||
| } | ||
| } else { | ||
| throw new CatchTableException(BadRequestError.ALREADY_RESERVE); | ||
| } | ||
| } catch (InterruptedException e) { | ||
| Thread.currentThread().interrupt(); | ||
| } | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,33 @@ | ||
| package com.yscp.catchtable.application.reserve; | ||
|
|
||
| import com.yscp.catchtable.application.queue.dto.StoreQueueDto; | ||
| import com.yscp.catchtable.application.redis.RedisLockService; | ||
| import com.yscp.catchtable.application.reserve.mapper.StoreReserveMapper; | ||
| import com.yscp.catchtable.presentation.reserve.dto.StoreReserveRequestDto; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
|
|
||
| import java.time.LocalDateTime; | ||
| import java.util.concurrent.TimeUnit; | ||
|
|
||
| @RequiredArgsConstructor | ||
| @Service | ||
| public class UserReserveAggregateService { | ||
| private final UserReserveService userReserveService; | ||
| private final RedisLockService redisLockService; | ||
|
|
||
| public void reserve(StoreReserveRequestDto storeReserveRequestDto) { | ||
| StoreQueueDto storeQueueDto = new StoreQueueDto(storeReserveRequestDto.storeReserveIdx().toString(), | ||
| storeReserveRequestDto.userIdx().toString()); | ||
|
|
||
| redisLockService.lock( | ||
| () -> userReserveService.reserve(StoreReserveMapper.toDto(storeReserveRequestDto, LocalDateTime.now())), | ||
| storeQueueDto.reserveKey(), | ||
| 2L, | ||
| 3L, | ||
| TimeUnit.SECONDS | ||
| ); | ||
|
|
||
|
|
||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,68 @@ | ||
| package com.yscp.catchtable.application.reserve; | ||
|
|
||
| import com.yscp.catchtable.application.queue.StoreQueueService; | ||
| import com.yscp.catchtable.application.queue.dto.StoreQueueDto; | ||
| import com.yscp.catchtable.application.reserve.dto.StoreReserveRegisterDto; | ||
| import com.yscp.catchtable.application.reserve.mapper.UserReserveDataMapper; | ||
| import com.yscp.catchtable.domain.reserve.entity.StoreReserve; | ||
| import com.yscp.catchtable.domain.reserve.entity.UserReserve; | ||
| import com.yscp.catchtable.domain.reserve.repository.UserReserveRepository; | ||
| import com.yscp.catchtable.exception.BadRequestError; | ||
| import com.yscp.catchtable.exception.CatchTableException; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.stereotype.Service; | ||
| import org.springframework.transaction.annotation.Transactional; | ||
|
|
||
| import java.util.Optional; | ||
|
|
||
| @Transactional | ||
| @RequiredArgsConstructor | ||
| @Service | ||
| public class UserReserveService { | ||
| private final StoreQueueService storeQueueService; | ||
| private final ReserveService reserveService; | ||
| private final UserReserveRepository userReserveDataRepository; | ||
|
|
||
| public void reserve(StoreReserveRegisterDto storeReserveRegisterDto) { | ||
|
|
||
| StoreQueueDto storeQueueDto = new StoreQueueDto(storeReserveRegisterDto.storeReserveIdx().toString(), | ||
| storeReserveRegisterDto.userIdx().toString()); | ||
|
|
||
| if (!isValidWaitingUser(storeQueueDto)) { | ||
| throw new CatchTableException(BadRequestError.EXPIRED_TICKET); | ||
| } | ||
|
|
||
| Optional<StoreReserve> reserveDataOptional = reserveService.findWithStoreByIdx(storeReserveRegisterDto.storeReserveIdx()); | ||
|
|
||
| reserveDataOptional.ifPresentOrElse( | ||
| reserveData -> { | ||
| saveUserReserveData( | ||
| storeReserveRegisterDto, | ||
| reserveData, | ||
| storeQueueDto); | ||
| }, | ||
| () -> { | ||
| throw new CatchTableException(BadRequestError.NULL_EXCEPTION); | ||
| } | ||
| ); | ||
| } | ||
|
|
||
| private void saveUserReserveData(StoreReserveRegisterDto dto, | ||
| StoreReserve reserveData, | ||
| StoreQueueDto storeQueueDto) { | ||
|
|
||
| reserveData.userReserve(dto.requestDatetime(), dto.userIdx()); | ||
|
|
||
| UserReserve userReserveData = UserReserveDataMapper.toEntity(reserveData, dto); | ||
|
|
||
| if (userReserveData != null) { | ||
| userReserveDataRepository.save(userReserveData); | ||
| } | ||
|
|
||
| storeQueueService.delete(storeQueueDto); | ||
| } | ||
|
|
||
| private boolean isValidWaitingUser(StoreQueueDto queueDto) { | ||
| return storeQueueService.isValidWaitingUser(queueDto); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,14 @@ | ||
| package com.yscp.catchtable.application.reserve.dto; | ||
|
|
||
| import java.time.LocalDateTime; | ||
|
|
||
| public record StoreReserveRegisterDto( | ||
| Long userIdx, | ||
| Long storeReserveIdx, | ||
| String reservePayType, | ||
| String transactionNo, | ||
| String purpose, | ||
| Integer reservationNumberOfPeople, | ||
| LocalDateTime requestDatetime | ||
| ) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,23 @@ | ||
| package com.yscp.catchtable.application.reserve.mapper; | ||
|
|
||
| import com.yscp.catchtable.application.reserve.dto.StoreReserveRegisterDto; | ||
| import com.yscp.catchtable.presentation.reserve.dto.StoreReserveRequestDto; | ||
| import lombok.experimental.UtilityClass; | ||
|
|
||
| import java.time.LocalDateTime; | ||
|
|
||
| @UtilityClass | ||
| public class StoreReserveMapper { | ||
| public static StoreReserveRegisterDto toDto(StoreReserveRequestDto storeReserveRequestDto, | ||
|
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. UtilityClass를 사용하면 static 함수로 만들어 준다고 하는데 static을 붙일 필요가 있을까요? 그리고 궁금한게 UtilityClass를 꼭 사용해야 하는 이유도 궁금합니다.
Collaborator
Author
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. RequestDto에서 생성할까 고민하다가. 보통 레이어간의 변환을 어떻게 하시는지 궁금합니다! |
||
| LocalDateTime reserveDatetime) { | ||
| return new StoreReserveRegisterDto( | ||
| storeReserveRequestDto.userIdx(), | ||
| storeReserveRequestDto.storeReserveIdx(), | ||
| storeReserveRequestDto.reserveType(), | ||
| storeReserveRequestDto.transactionNo(), | ||
| storeReserveRequestDto.purpose(), | ||
| storeReserveRequestDto.reservationNumberOfPeople(), | ||
| reserveDatetime | ||
| ); | ||
| } | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,25 @@ | ||
| package com.yscp.catchtable.application.reserve.mapper; | ||
|
|
||
| import com.yscp.catchtable.application.reserve.dto.StoreReserveRegisterDto; | ||
| import com.yscp.catchtable.domain.reserve.entity.StoreReserve; | ||
| import com.yscp.catchtable.domain.reserve.entity.UserReserve; | ||
| import com.yscp.catchtable.domain.reserve.entity.value.ReservePayType; | ||
| import com.yscp.catchtable.domain.reserve.entity.value.ReserveStatus; | ||
| import com.yscp.catchtable.domain.user.entity.User; | ||
|
|
||
| public class UserReserveDataMapper { | ||
| public static UserReserve toEntity(StoreReserve reserveData, StoreReserveRegisterDto dto) { | ||
| User requestUser = User.builder() | ||
| .idx(dto.userIdx()) | ||
| .build(); | ||
|
|
||
| return UserReserve.builder() | ||
| .user(requestUser) | ||
| .storeReserve(reserveData) | ||
| .reserveStatus(ReserveStatus.RESERVE) | ||
| .reservePayType(ReservePayType.from(dto.reservePayType())) | ||
| .regIdx(dto.userIdx()) | ||
| .regDatetime(dto.requestDatetime()) | ||
| .build(); | ||
| } | ||
| } |
This file was deleted.
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.
이게 null이 될 수 있는걸까요??
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.
함수를 통해 호출되는 부분을 사용한다고 생각하여
null Check를 진행했습니다.
로직상 절대 Null이 안될 것 같아요..
이부분은 고민이 되는데 응답값이 항상 null을 안 줄 경우
null 체크를 무시해도 괜찮을까요!?