diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/order/service/CreateOrderUseCase.java b/DuDoong-Api/src/main/java/band/gosrock/api/order/service/CreateOrderUseCase.java index 1b51b70e..d0c9c1ea 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/order/service/CreateOrderUseCase.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/order/service/CreateOrderUseCase.java @@ -23,7 +23,7 @@ public CreateOrderResponse execute(CreateOrderRequest createOrderRequest) { Long cartId = createOrderRequest.getCartId(); if (couponId == null) { return CreateOrderResponse.from( - createOrderService.WithOutCoupon(cartId, user.getId()), user.getProfile()); + createOrderService.withOutCoupon(cartId, user.getId()), user.getProfile()); } return CreateOrderResponse.from( createOrderService.withCoupon(cartId, user.getId(), couponId), user.getProfile()); diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/WithDrawOrderEvent.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/WithDrawOrderEvent.java index 5cf7ded2..9d6bebbc 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/WithDrawOrderEvent.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/common/events/order/WithDrawOrderEvent.java @@ -21,6 +21,9 @@ public class WithDrawOrderEvent extends DomainEvent { @Nullable private final String paymentKey; private final Long itemId; + private final Boolean isUsingCoupon; + @Nullable private final Long issuedCouponId; + public static WithDrawOrderEvent from(Order order) { return WithDrawOrderEvent.builder() .orderMethod(order.getOrderMethod()) @@ -29,6 +32,8 @@ public static WithDrawOrderEvent from(Order order) { .orderUuid(order.getUuid()) .orderStatus(order.getOrderStatus()) .itemId(order.getItemId()) + .isUsingCoupon(order.hasCoupon()) + .issuedCouponId(order.getOrderCouponVo().getCouponId()) .build(); } } diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/domain/IssuedCoupon.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/domain/IssuedCoupon.java index 761fe49d..9a5bebbb 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/domain/IssuedCoupon.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/domain/IssuedCoupon.java @@ -3,7 +3,9 @@ import band.gosrock.domain.common.model.BaseTimeEntity; import band.gosrock.domain.common.vo.Money; +import band.gosrock.domain.domains.coupon.exception.AlreadyRecoveredCouponException; import band.gosrock.domain.domains.coupon.exception.AlreadyUsedCouponException; +import band.gosrock.domain.domains.coupon.exception.NotApplicableCouponException; import band.gosrock.domain.domains.coupon.exception.NotMyCouponException; import java.util.Objects; import javax.persistence.*; @@ -47,10 +49,10 @@ public Money getDiscountAmount(Money supplyAmount) { } public Money checkSupplyIsGreaterThenDiscount(Money supply, Long discount) { - if (supply.isGreaterThanOrEqual(Money.wons(discount))) { - return Money.wons(discount); + if (supply.isLessThan(Money.wons(discount))) { + throw NotApplicableCouponException.EXCEPTION; } - return Money.ZERO; + return Money.wons(discount); } @Builder @@ -76,4 +78,11 @@ public void use() { } usageStatus = true; } + + public void recovery() { + if (!usageStatus) { // 동시성 이슈 가능 + throw AlreadyRecoveredCouponException.EXCEPTION; + } + usageStatus = false; + } } diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/exception/AlreadyRecoveredCouponException.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/exception/AlreadyRecoveredCouponException.java new file mode 100644 index 00000000..b5218162 --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/exception/AlreadyRecoveredCouponException.java @@ -0,0 +1,12 @@ +package band.gosrock.domain.domains.coupon.exception; + + +import band.gosrock.common.exception.DuDoongCodeException; + +public class AlreadyRecoveredCouponException extends DuDoongCodeException { + public static final DuDoongCodeException EXCEPTION = new AlreadyRecoveredCouponException(); + + private AlreadyRecoveredCouponException() { + super(CouponErrorCode.ALREADY_RECOVERED_COUPON); + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/exception/CouponErrorCode.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/exception/CouponErrorCode.java index 31f92b2a..75f1b434 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/exception/CouponErrorCode.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/exception/CouponErrorCode.java @@ -21,9 +21,10 @@ public enum CouponErrorCode implements BaseErrorCode { NO_COUPON_STOCK_LEFT(BAD_REQUEST, "Coupon_400_5", "쿠폰이 모두 소진됐습니다."), NOT_COUPON_ISSUING_PERIOD(BAD_REQUEST, "Coupon_400_6", "쿠폰 발급 가능 시각이 아닙니다."), NOT_FOUND_COUPON(NOT_FOUND, "Coupon_404_2", "존재하지 않는 쿠폰 입니다."), - NOT_MY_COUPON(BAD_REQUEST, "Coupon_400_7", "내 쿠폰이 아닙니다."), - ALREADY_USED_COUPON(BAD_REQUEST, "Coupon_400_8", "이미 사용한 쿠폰입니다."); + ALREADY_USED_COUPON(BAD_REQUEST, "Coupon_400_8", "이미 사용한 쿠폰입니다."), + NOT_APPLICABLE_COUPON(BAD_REQUEST, "Coupon_400_9", "적용 불가 쿠폰입니다. 할인 금액이 결제 금액보다 큽니다."), + ALREADY_RECOVERED_COUPON(BAD_REQUEST, "Coupon_400_10", "이미 복구한 쿠폰입니다."); private final Integer status; private final String code; private final String reason; diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/exception/NotApplicableCouponException.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/exception/NotApplicableCouponException.java new file mode 100644 index 00000000..c164c4d7 --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/exception/NotApplicableCouponException.java @@ -0,0 +1,12 @@ +package band.gosrock.domain.domains.coupon.exception; + + +import band.gosrock.common.exception.DuDoongCodeException; + +public class NotApplicableCouponException extends DuDoongCodeException { + public static final DuDoongCodeException EXCEPTION = new NotApplicableCouponException(); + + private NotApplicableCouponException() { + super(CouponErrorCode.NOT_APPLICABLE_COUPON); + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/service/RecoveryCouponService.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/service/RecoveryCouponService.java new file mode 100644 index 00000000..5db3fdff --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/service/RecoveryCouponService.java @@ -0,0 +1,22 @@ +package band.gosrock.domain.domains.coupon.service; + + +import band.gosrock.common.annotation.DomainService; +import band.gosrock.domain.common.aop.redissonLock.RedissonLock; +import band.gosrock.domain.domains.coupon.adaptor.IssuedCouponAdaptor; +import band.gosrock.domain.domains.coupon.domain.IssuedCoupon; +import lombok.RequiredArgsConstructor; + +@DomainService +@RequiredArgsConstructor +public class RecoveryCouponService { + private final IssuedCouponAdaptor issuedCouponAdaptor; + + @RedissonLock(LockName = "쿠폰", identifier = "couponId") + public Long execute(Long userId, Long couponId) { + IssuedCoupon coupon = issuedCouponAdaptor.query(couponId); + coupon.validMine(userId); + coupon.recovery(); + return couponId; + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/service/handler/CreateOrderHandler.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/service/handler/CreateOrderCouponHandler.java similarity index 95% rename from DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/service/handler/CreateOrderHandler.java rename to DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/service/handler/CreateOrderCouponHandler.java index 57f5cef2..99c76a13 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/service/handler/CreateOrderHandler.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/service/handler/CreateOrderCouponHandler.java @@ -12,7 +12,7 @@ @Component @RequiredArgsConstructor @Slf4j -public class CreateOrderHandler { +public class CreateOrderCouponHandler { private final UseCouponService useCouponService; @@ -26,7 +26,7 @@ public void handleDoneOrderFailEvent(CreateOrderEvent createOrderEvent) { log.info(createOrderEvent.getOrderUuid() + "주문 생성 이벤트 쿠폰 사용 리스너 : 쿠폰 사용 도메인 서비스 호출"); useCouponService.execute( createOrderEvent.getUserId(), createOrderEvent.getIssuedCouponId()); - log.info(createOrderEvent.getOrderUuid() + "주문 생성 이벤트 쿠폰 사용 리스너 : 쿠폰 사용 도메인 서비스 호출종료"); + log.info(createOrderEvent.getOrderUuid() + "주문 생성 이벤트 쿠폰 사용 리스너 : 쿠폰 사용 도메인 서비스 호출 종료"); } } } diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/service/handler/WithDrawOrderCouponHandler.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/service/handler/WithDrawOrderCouponHandler.java new file mode 100644 index 00000000..edfa4c03 --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/coupon/service/handler/WithDrawOrderCouponHandler.java @@ -0,0 +1,32 @@ +package band.gosrock.domain.domains.coupon.service.handler; + + +import band.gosrock.domain.common.events.order.WithDrawOrderEvent; +import band.gosrock.domain.domains.coupon.service.RecoveryCouponService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +@Component +@RequiredArgsConstructor +@Slf4j +public class WithDrawOrderCouponHandler { + private final RecoveryCouponService recoveryCouponService; + + @TransactionalEventListener( + classes = WithDrawOrderEvent.class, + phase = TransactionPhase.BEFORE_COMMIT) + public void handleWithDrawOrderEvent(WithDrawOrderEvent withDrawOrderEvent) { + log.info(withDrawOrderEvent.getOrderUuid() + "주문 철회 이벤트 쿠폰 회복 리스너"); + if (withDrawOrderEvent.getIsUsingCoupon()) { + log.info(withDrawOrderEvent.getOrderUuid() + "주문 철회 이벤트 쿠폰 회복 리스너 : 쿠폰 회복 도메인 서비스 호출"); + recoveryCouponService.execute( + withDrawOrderEvent.getUserId(), withDrawOrderEvent.getIssuedCouponId()); + log.info( + withDrawOrderEvent.getOrderUuid() + + "주문 철회 이벤트 쿠폰 사용 리스너 : 쿠폰 회복 도메인 서비스 호출 종료"); + } + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/issuedTicket/service/handlers/WithDrawOrderEventHandler.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/issuedTicket/service/handlers/WithDrawOrderEventHandler.java index 621fc3a4..09b75337 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/issuedTicket/service/handlers/WithDrawOrderEventHandler.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/issuedTicket/service/handlers/WithDrawOrderEventHandler.java @@ -30,6 +30,5 @@ public void handleWithDrawOrderEvent(WithDrawOrderEvent withDrawOrderEvent) { issuedTicketAdaptor.findAllByOrderUuid(withDrawOrderEvent.getOrderUuid()); issuedTicketDomainService.withDrawIssuedTicket( withDrawOrderEvent.getItemId(), issuedTickets); - log.info(withDrawOrderEvent.getOrderUuid() + "주문 상태 완료, 티켓 철회 완료"); } }