diff --git a/src/main/java/com/readyvery/readyverydemo/config/JwtConfig.java b/src/main/java/com/readyvery/readyverydemo/config/JwtConfig.java index 955afb0..0033740 100644 --- a/src/main/java/com/readyvery/readyverydemo/config/JwtConfig.java +++ b/src/main/java/com/readyvery/readyverydemo/config/JwtConfig.java @@ -18,7 +18,6 @@ public class JwtConfig { private final String refreshTokenName; private final String userFrontendUrl; private final String guestFrontendUrl; - private final String cookieDomain; private final Algorithm algorithm; public static final String ACCESS_TOKEN_SUBJECT = "AccessToken"; @@ -35,8 +34,8 @@ public JwtConfig( @Value("${jwt.access.cookie}") String accessTokenName, @Value("${jwt.refresh.cookie}") String refreshTokenName, @Value("${jwt.redirect-uri-user}") String userFrontendUrl, - @Value("${jwt.redirect-uri-guest}") String guestFrontendUrl, - @Value("${jwt.cookie.domain}") String cookieDomain + @Value("${jwt.redirect-uri-guest}") String guestFrontendUrl + ) { this.secretKey = secretKey; this.accessTokenExpirationPeriod = accessTokenExpirationPeriod; @@ -45,7 +44,6 @@ public JwtConfig( this.refreshTokenName = refreshTokenName; this.userFrontendUrl = userFrontendUrl; this.guestFrontendUrl = guestFrontendUrl; - this.cookieDomain = cookieDomain; this.algorithm = initializeAlgorithm(secretKey); } diff --git a/src/main/java/com/readyvery/readyverydemo/domain/Point.java b/src/main/java/com/readyvery/readyverydemo/domain/Point.java index e5904a8..49feb53 100644 --- a/src/main/java/com/readyvery/readyverydemo/domain/Point.java +++ b/src/main/java/com/readyvery/readyverydemo/domain/Point.java @@ -15,8 +15,10 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; @Getter +@Setter @Entity @Builder @AllArgsConstructor diff --git a/src/main/java/com/readyvery/readyverydemo/domain/repository/PointRepository.java b/src/main/java/com/readyvery/readyverydemo/domain/repository/PointRepository.java index 4a99c2a..d44f65b 100644 --- a/src/main/java/com/readyvery/readyverydemo/domain/repository/PointRepository.java +++ b/src/main/java/com/readyvery/readyverydemo/domain/repository/PointRepository.java @@ -1,12 +1,16 @@ package com.readyvery.readyverydemo.domain.repository; import java.util.List; +import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import com.readyvery.readyverydemo.domain.Order; import com.readyvery.readyverydemo.domain.Point; import com.readyvery.readyverydemo.domain.UserInfo; public interface PointRepository extends JpaRepository { List findAllByUserInfo(UserInfo userInfo); + + Optional findByOrder(Order order); } diff --git a/src/main/java/com/readyvery/readyverydemo/domain/repository/UserRepository.java b/src/main/java/com/readyvery/readyverydemo/domain/repository/UserRepository.java index 60f5241..2dd5423 100644 --- a/src/main/java/com/readyvery/readyverydemo/domain/repository/UserRepository.java +++ b/src/main/java/com/readyvery/readyverydemo/domain/repository/UserRepository.java @@ -3,11 +3,17 @@ import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Lock; import com.readyvery.readyverydemo.domain.SocialType; import com.readyvery.readyverydemo.domain.UserInfo; +import jakarta.persistence.LockModeType; + public interface UserRepository extends JpaRepository { + @Lock(LockModeType.PESSIMISTIC_WRITE) + Optional findUserInfoById(Long id); + Optional findByEmail(String email); Optional findByRefreshToken(String refreshToken); diff --git a/src/main/java/com/readyvery/readyverydemo/global/exception/ExceptionCode.java b/src/main/java/com/readyvery/readyverydemo/global/exception/ExceptionCode.java index ec11657..e99b629 100644 --- a/src/main/java/com/readyvery/readyverydemo/global/exception/ExceptionCode.java +++ b/src/main/java/com/readyvery/readyverydemo/global/exception/ExceptionCode.java @@ -35,7 +35,8 @@ public enum ExceptionCode { POINT_NOT_ENOUGH(400, "Point is not enough."), INVALID_INPUT(400, "Invalid input."), UNAUTHORIZED(400, "Already User"), - AUTH_ERROR(403, "Auth Error"); + AUTH_ERROR(403, "Auth Error"), + POINT_NOT_FOUND(403, "Point not found"); private int status; private String message; diff --git a/src/main/java/com/readyvery/readyverydemo/security/jwt/service/sendmanger/TokenSendManager.java b/src/main/java/com/readyvery/readyverydemo/security/jwt/service/sendmanger/TokenSendManager.java index 7f4a932..d42483f 100644 --- a/src/main/java/com/readyvery/readyverydemo/security/jwt/service/sendmanger/TokenSendManager.java +++ b/src/main/java/com/readyvery/readyverydemo/security/jwt/service/sendmanger/TokenSendManager.java @@ -20,7 +20,6 @@ public void addTokenCookie(HttpServletResponse response, String name, String val Cookie cookie = new Cookie(name, value); cookie.setHttpOnly(httpOnly); cookie.setPath(path); - //cookie.setDomain(jwtConfig.getCookieDomain()); cookie.setMaxAge(maxAge); response.addCookie(cookie); diff --git a/src/main/java/com/readyvery/readyverydemo/src/board/dto/BoardMapper.java b/src/main/java/com/readyvery/readyverydemo/src/board/dto/BoardMapper.java index 41a7aac..30ae441 100644 --- a/src/main/java/com/readyvery/readyverydemo/src/board/dto/BoardMapper.java +++ b/src/main/java/com/readyvery/readyverydemo/src/board/dto/BoardMapper.java @@ -20,7 +20,7 @@ public BoardRes toBoardRes(List stores) { throw new BusinessLogicException(ExceptionCode.STORE_NOT_FOUND); } return BoardRes.builder() - .stores(stores.stream().map(this::toStoreDto).toList()) + .stores(stores.stream().filter(store -> !store.isDeleted()).map(this::toStoreDto).toList()) .build(); } diff --git a/src/main/java/com/readyvery/readyverydemo/src/order/OrderServiceImpl.java b/src/main/java/com/readyvery/readyverydemo/src/order/OrderServiceImpl.java index 5efbcff..e5c14e8 100644 --- a/src/main/java/com/readyvery/readyverydemo/src/order/OrderServiceImpl.java +++ b/src/main/java/com/readyvery/readyverydemo/src/order/OrderServiceImpl.java @@ -67,6 +67,8 @@ import com.readyvery.readyverydemo.src.order.dto.TossCancelReq; import com.readyvery.readyverydemo.src.order.dto.TosspaymentDto; import com.readyvery.readyverydemo.src.order.dto.TosspaymentMakeRes; +import com.readyvery.readyverydemo.src.point.PointServiceFacade; +import com.readyvery.readyverydemo.src.user.UserServiceFacade; import jakarta.transaction.Transactional; import lombok.RequiredArgsConstructor; @@ -82,6 +84,7 @@ public class OrderServiceImpl implements OrderService { private final FoodieRepository foodieRepository; private final FoodieOptionRepository foodieOptionRepository; private final UserRepository userRepository; + private final UserServiceFacade userServiceFacade; private final StoreRepository storeRepository; private final OrderMapper orderMapper; private final TossPaymentConfig tosspaymentConfig; @@ -89,6 +92,7 @@ public class OrderServiceImpl implements OrderService { private final ReceiptRepository receiptRepository; private final CouponRepository couponRepository; private final PointRepository pointRepository; + private final PointServiceFacade pointServiceFacade; @Override public FoodyDetailRes getFoody(Long storeId, Long foodyId, Long inout) { @@ -307,7 +311,9 @@ private void cartOrder(Cart cart) { @Transactional public PaySuccess tossPaymentSuccess(String paymentKey, String orderId, Long amount) { Order order = getOrder(orderId); + UserInfo user = userServiceFacade.getUserInfoWithPessimisticLock(order.getUserInfo().getId()); verifyOrder(order, amount); + verifyUsePoint(order, user); TosspaymentDto tosspaymentDto; if (amount > 0) { @@ -328,10 +334,23 @@ public PaySuccess tossPaymentSuccess(String paymentKey, String orderId, Long amo if (order.getPoint() < 0L) { Point point = orderMapper.orderToPoint(order); pointRepository.save(point); + userServiceFacade.saveUserPoint(user, user.getPoint() + order.getPoint()); } return orderMapper.tosspaymentDtoToPaySuccess(TOSSPAYMENT_SUCCESS_MESSAGE); } + private void verifyUsePoint(Order order, UserInfo user) { + // 포인트 사용 X + if (order.getPoint() >= 0L) { + return; + } + // 유저 포인트 충분 + if (user.getPoint() + order.getPoint() >= 0L) { + return; + } + throw new BusinessLogicException(ExceptionCode.POINT_NOT_ENOUGH); + } + private TosspaymentDto makeZeroPaymentDto(String paymentKey) { return TosspaymentDto.builder() .paymentKey(paymentKey) @@ -365,18 +384,33 @@ public CurrentRes getCurrent(String orderId) { @Override @Transactional public Object cancelTossPayment(CustomUserDetails userDetails, TossCancelReq tossCancelReq) { - UserInfo user = getUserInfo(userDetails); + UserInfo user = userServiceFacade.getUserInfoWithPessimisticLock(userDetails.getId()); Order order = getOrder(tossCancelReq.getOrderId()); - verifyCancel(order, user); + Point point = pointServiceFacade.getPointByOrder(order); - TosspaymentDto tosspaymentDto = requestTossPaymentCancel(order.getPaymentKey()); + verifyCancel(order, user); + TosspaymentDto tosspaymentDto = null; + if (order.getAmount() > 0L) { + tosspaymentDto = requestTossPaymentCancel(order.getPaymentKey()); + } else { + tosspaymentDto = makeZeroPaymentCancelDto(); + } applyCancelTosspaymentDto(order, tosspaymentDto); + userServiceFacade.saveUserPoint(user, user.getPoint() - order.getPoint()); + pointServiceFacade.cancelPoint(point); ordersRepository.save(order); return orderMapper.tosspaymentDtoToCancelRes(); } + private TosspaymentDto makeZeroPaymentCancelDto() { + return TosspaymentDto.builder() + .cancels(",=단순 변심") + .status("CANCELED") + .build(); + } + @Override @Transactional public HistoryDetailRes getReceipt(CustomUserDetails userDetails, String orderId) { @@ -430,7 +464,6 @@ private void applyCancelTosspaymentDto(Order order, TosspaymentDto tosspaymentDt order.setPayStatus(false); order.getReceipt().setCancels(tosspaymentDto.getCancels().toString()); order.getReceipt().setStatus(tosspaymentDto.getStatus()); - order.getUserInfo().setPoint(order.getUserInfo().getPoint() - order.getPoint()); if (order.getCoupon() != null) { order.getCoupon().setUseCount(order.getCoupon().getUseCount() - 1); } @@ -508,7 +541,6 @@ private void applyTosspaymentDto(Order order, TosspaymentDto tosspaymentDto) { order.setPayStatus(true); order.getCart().setIsOrdered(true); order.setMessage(TOSSPAYMENT_SUCCESS_MESSAGE); - order.getUserInfo().setPoint(order.getUserInfo().getPoint() + order.getPoint()); if (order.getCoupon() != null) { order.getCoupon().setUseCount(order.getCoupon().getUseCount() + 1); } diff --git a/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderMapper.java b/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderMapper.java index 287aceb..d165554 100644 --- a/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderMapper.java +++ b/src/main/java/com/readyvery/readyverydemo/src/order/dto/OrderMapper.java @@ -236,7 +236,9 @@ public HistoryRes ordersToHistoryRes(List orders) { .receipts( orders .stream() + .filter(order -> !order.getStore().isDeleted()) .filter(order -> order.getProgress() == PICKUP + || order.getProgress() == COMPLETE || order.getProgress() == FAIL || order.getProgress() == CANCEL) .map(this::orderToReceiptHistoryDto) @@ -249,6 +251,7 @@ public HistoryRes ordersToFastOrderRes(List orders) { .receipts( orders .stream() + .filter(order -> !order.getStore().isDeleted()) .filter(order -> order.getCart().getCreatedAt() // 데이터 변경 시점 이후의 데이터만 가져옴 .isAfter(LocalDateTime.of(2023, 12, 6, 0, 0, 0))) @@ -264,9 +267,9 @@ public HistoryRes ordersToNewHistoryRes(List orders) { .receipts( orders .stream() + .filter(order -> !order.getStore().isDeleted()) .filter(order -> order.getProgress() == ORDER - || order.getProgress() == MAKE - || order.getProgress() == COMPLETE) + || order.getProgress() == MAKE) .map(this::orderToReceiptHistoryDto) .toList()) .build(); diff --git a/src/main/java/com/readyvery/readyverydemo/src/point/PointServiceFacade.java b/src/main/java/com/readyvery/readyverydemo/src/point/PointServiceFacade.java new file mode 100644 index 0000000..d447082 --- /dev/null +++ b/src/main/java/com/readyvery/readyverydemo/src/point/PointServiceFacade.java @@ -0,0 +1,34 @@ +package com.readyvery.readyverydemo.src.point; + +import java.util.List; + +import org.springframework.stereotype.Service; + +import com.readyvery.readyverydemo.domain.Order; +import com.readyvery.readyverydemo.domain.Point; +import com.readyvery.readyverydemo.domain.UserInfo; +import com.readyvery.readyverydemo.domain.repository.PointRepository; +import com.readyvery.readyverydemo.global.exception.BusinessLogicException; +import com.readyvery.readyverydemo.global.exception.ExceptionCode; + +import lombok.RequiredArgsConstructor; + +@Service +@RequiredArgsConstructor +public class PointServiceFacade { + private final PointRepository pointRepository; + + public List findAllByUserInfo(UserInfo userInfo) { + return pointRepository.findAllByUserInfo(userInfo); + } + + public Point getPointByOrder(Order order) { + return pointRepository.findByOrder(order) + .orElseThrow(() -> new BusinessLogicException(ExceptionCode.POINT_NOT_FOUND)); + } + + public void cancelPoint(Point point) { + point.setIsDeleted(true); + pointRepository.save(point); + } +} diff --git a/src/main/java/com/readyvery/readyverydemo/src/point/PointServiceImpl.java b/src/main/java/com/readyvery/readyverydemo/src/point/PointServiceImpl.java index 1c48e0d..f214604 100644 --- a/src/main/java/com/readyvery/readyverydemo/src/point/PointServiceImpl.java +++ b/src/main/java/com/readyvery/readyverydemo/src/point/PointServiceImpl.java @@ -3,39 +3,30 @@ import org.springframework.stereotype.Service; import com.readyvery.readyverydemo.domain.UserInfo; -import com.readyvery.readyverydemo.domain.repository.PointRepository; -import com.readyvery.readyverydemo.domain.repository.UserRepository; -import com.readyvery.readyverydemo.global.exception.BusinessLogicException; -import com.readyvery.readyverydemo.global.exception.ExceptionCode; import com.readyvery.readyverydemo.security.jwt.dto.CustomUserDetails; import com.readyvery.readyverydemo.src.point.dto.GetPointHistoryRes; import com.readyvery.readyverydemo.src.point.dto.GetPointRes; import com.readyvery.readyverydemo.src.point.dto.PointMapper; +import com.readyvery.readyverydemo.src.user.UserServiceFacade; import lombok.RequiredArgsConstructor; @Service @RequiredArgsConstructor public class PointServiceImpl implements PointService { - private final UserRepository userRepository; - private final PointRepository pointRepository; + private final UserServiceFacade userServiceFacade; + private final PointServiceFacade pointServiceFacade; private final PointMapper pointMapper; @Override public GetPointRes getPoint(CustomUserDetails userDetails) { - UserInfo userInfo = getUserInfo(userDetails.getId()); + UserInfo userInfo = userServiceFacade.getUserInfo(userDetails.getId()); return pointMapper.toGetPointRes(userInfo.getPoint()); } @Override public GetPointHistoryRes getPointHistory(CustomUserDetails userDetails) { - UserInfo userInfo = getUserInfo(userDetails.getId()); - return pointMapper.toGetPointHistoryRes(pointRepository.findAllByUserInfo(userInfo)); - } - - private UserInfo getUserInfo(Long id) { - return userRepository.findById(id).orElseThrow( - () -> new BusinessLogicException(ExceptionCode.USER_NOT_FOUND) - ); + UserInfo userInfo = userServiceFacade.getUserInfo(userDetails.getId()); + return pointMapper.toGetPointHistoryRes(pointServiceFacade.findAllByUserInfo(userInfo)); } } diff --git a/src/main/java/com/readyvery/readyverydemo/src/point/dto/PointMapper.java b/src/main/java/com/readyvery/readyverydemo/src/point/dto/PointMapper.java index ee00552..32b7c4f 100644 --- a/src/main/java/com/readyvery/readyverydemo/src/point/dto/PointMapper.java +++ b/src/main/java/com/readyvery/readyverydemo/src/point/dto/PointMapper.java @@ -21,13 +21,13 @@ public GetPointRes toGetPointRes(Long point) { public GetPointHistoryRes toGetPointHistoryRes(List points) { return GetPointHistoryRes .builder() - .history(points.stream().map(this::pointToPointDto).toList()) + .history(points.stream().filter(point -> !point.getIsDeleted()).map(this::pointToPointDto).toList()) .build(); } private PointDto pointToPointDto(Point point) { return PointDto.builder() - .point((point.getPoint() > 0 ? "+" : "") + point.getPoint()) // +1 or -1 + .point((point.getPoint() >= 0 ? "+" : "") + point.getPoint()) // +1 or -1 .date(point.getCreatedAt().format(DateTimeFormatter.ofPattern(DATE_FORMAT))) .status(point.getIsDeleted()) .store(point.getOrder().getStore().getName()) diff --git a/src/main/java/com/readyvery/readyverydemo/src/user/UserServiceFacade.java b/src/main/java/com/readyvery/readyverydemo/src/user/UserServiceFacade.java index 8efbec3..02365f9 100644 --- a/src/main/java/com/readyvery/readyverydemo/src/user/UserServiceFacade.java +++ b/src/main/java/com/readyvery/readyverydemo/src/user/UserServiceFacade.java @@ -21,6 +21,12 @@ public UserInfo getUserInfo(Long id) { ); } + public UserInfo getUserInfoWithPessimisticLock(Long id) { + return userRepository.findUserInfoById(id).orElseThrow( + () -> new BusinessLogicException(ExceptionCode.USER_NOT_FOUND) + ); + } + public UserInfo getUserInfoByEmail(String email) { return userRepository.findByEmail(email).orElseThrow( () -> new BusinessLogicException(ExceptionCode.USER_NOT_FOUND) @@ -32,4 +38,9 @@ public void updateUserPhone(UserInfo userInfo, String phoneNumber) { userInfo.updatePhone(phoneNumber); userRepository.save(userInfo); } + + public void saveUserPoint(UserInfo user, long point) { + user.setPoint(point); + userRepository.save(user); + } }