diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/adaptor/OrderAdaptor.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/adaptor/OrderAdaptor.java index 049a3090..2351b575 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/adaptor/OrderAdaptor.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/adaptor/OrderAdaptor.java @@ -3,6 +3,7 @@ import band.gosrock.common.annotation.Adaptor; import band.gosrock.domain.domains.order.domain.Order; +import band.gosrock.domain.domains.order.domain.OrderStatus; import band.gosrock.domain.domains.order.exception.OrderNotFoundException; import band.gosrock.domain.domains.order.repository.OrderRepository; import band.gosrock.domain.domains.order.repository.condition.FindEventOrdersCondition; @@ -34,6 +35,10 @@ public List findByEventId(Long eventId) { return orderRepository.findByEventId(eventId); } + public List findByEventIdAndOrderStatus(Long eventId, OrderStatus orderStatus) { + return orderRepository.findByEventIdAndOrderStatus(eventId, orderStatus); + } + public Order findByOrderUuid(String uuid) { return orderRepository .findByOrderUuid(uuid) diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/Order.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/Order.java index 151e575a..3604e3c0 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/Order.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/Order.java @@ -153,6 +153,7 @@ public static Order createApproveOrder( .eventId(item.getEventId()) .build(); orderValidator.validCanCreate(order); + orderValidator.validApproveStatePurchaseLimit(order); order.calculatePaymentInfo(); return order; } @@ -199,7 +200,7 @@ public void calculatePaymentInfo() { /** 결제 방식의 주문을 승인 합니다. */ public void confirmPayment( LocalDateTime approvedAt, PgPaymentInfo pgPaymentInfo, OrderValidator orderValidator) { - Events.raise(DoneOrderEvent.from(this)); + issueDoneOrderEvent(); orderValidator.validCanConfirmPayment(this); orderStatus = OrderStatus.CONFIRM; this.approvedAt = approvedAt; @@ -208,7 +209,7 @@ public void confirmPayment( /** 승인 방식의 주문을 승인합니다. */ public void approve(OrderValidator orderValidator) { - Events.raise(DoneOrderEvent.from(this)); + issueDoneOrderEvent(); orderValidator.validCanApproveOrder(this); this.approvedAt = LocalDateTime.now(); this.orderStatus = OrderStatus.APPROVED; @@ -217,12 +218,19 @@ public void approve(OrderValidator orderValidator) { /** 선착순 방식의 0원 결제입니다. */ public void freeConfirm(Long currentUserId, OrderValidator orderValidator) { orderValidator.validOwner(this, currentUserId); - Events.raise(DoneOrderEvent.from(this)); + issueDoneOrderEvent(); orderValidator.validCanFreeConfirm(this); this.approvedAt = LocalDateTime.now(); this.orderStatus = OrderStatus.APPROVED; } + /** 주문 상태가 완료될수 있는 상태일 때 돈 오더 이벤트를 발생시킵니다. */ + private void issueDoneOrderEvent() { + if (orderStatus.isCanDone()) { + Events.raise(DoneOrderEvent.from(this)); + } + } + /** 관리자가 주문을 취소 시킵니다 */ public void cancel(OrderValidator orderValidator) { orderValidator.validCanCancel(this); diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/OrderStatus.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/OrderStatus.java index 401d6650..40bf3ff5 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/OrderStatus.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/OrderStatus.java @@ -38,6 +38,10 @@ public Boolean isInEventOrderExcelStatus() { || this == OrderStatus.REFUND; } + public Boolean isCanDone() { + return this == OrderStatus.PENDING_PAYMENT || this == OrderStatus.PENDING_APPROVE; + } + public Boolean isCanWithDraw() { return this == OrderStatus.APPROVED || this == OrderStatus.CONFIRM; } diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/validator/OrderValidator.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/validator/OrderValidator.java index 423d9af2..0a11d2e8 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/validator/OrderValidator.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/domain/validator/OrderValidator.java @@ -6,9 +6,11 @@ import band.gosrock.domain.domains.event.adaptor.EventAdaptor; import band.gosrock.domain.domains.event.domain.Event; import band.gosrock.domain.domains.issuedTicket.adaptor.IssuedTicketAdaptor; +import band.gosrock.domain.domains.order.adaptor.OrderAdaptor; import band.gosrock.domain.domains.order.domain.Order; import band.gosrock.domain.domains.order.domain.OrderLineItem; import band.gosrock.domain.domains.order.domain.OrderStatus; +import band.gosrock.domain.domains.order.exception.ApproveWaitingOrderPurchaseLimitException; import band.gosrock.domain.domains.order.exception.CanNotApproveDeletedUserOrderException; import band.gosrock.domain.domains.order.exception.CanNotCancelOrderException; import band.gosrock.domain.domains.order.exception.CanNotRefundOrderException; @@ -47,6 +49,8 @@ public class OrderValidator { private final UserAdaptor userAdaptor; + private final OrderAdaptor orderAdaptor; + /** 주문을 생성할 수 있는지에 대한검증 */ public void validCanCreate(Order order) { TicketItem item = getItem(order); @@ -158,6 +162,30 @@ public void validItemPurchaseLimit(Order order, TicketItem item) { item.validPurchaseLimit(totalIssuedCount); } + /** 승인 주문 생성시에 이미 넣은 승인 주문 총합 계산 */ + public void validApproveStatePurchaseLimit(Order order) { + TicketItem item = getItem(order); + // 이미 발급된 티켓 개수 + Long paidTicketCount = issuedTicketAdaptor.countPaidTicket(order.getUserId(), item.getId()); + + List approveWaitingOrders = + orderAdaptor.findByEventIdAndOrderStatus( + order.getEventId(), OrderStatus.PENDING_APPROVE); + // 승인 대기중인 티켓 개수 + Long approveWaitingTicketCount = + approveWaitingOrders.stream() + .filter(o -> Objects.equals(item.getId(), o.getItemId())) + .map(Order::getTotalQuantity) + .reduce(0L, Long::sum); + // 주문승인 요청할 티켓 개수 + Long totalIssuedCount = + paidTicketCount + approveWaitingTicketCount + order.getTotalQuantity(); + // 아이템 갯수 리밋을 초과하면 + if (item.isPurchaseLimitExceed(totalIssuedCount)) { + throw ApproveWaitingOrderPurchaseLimitException.EXCEPTION; + } + } + /** 이벤트가 열려있는 상태인지 */ public void validEventIsOpen(Event event) { event.validateStatusOpen(); diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/exception/ApproveWaitingOrderPurchaseLimitException.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/exception/ApproveWaitingOrderPurchaseLimitException.java new file mode 100644 index 00000000..dca991d6 --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/exception/ApproveWaitingOrderPurchaseLimitException.java @@ -0,0 +1,14 @@ +package band.gosrock.domain.domains.order.exception; + + +import band.gosrock.common.exception.DuDoongCodeException; + +public class ApproveWaitingOrderPurchaseLimitException extends DuDoongCodeException { + + public static final DuDoongCodeException EXCEPTION = + new ApproveWaitingOrderPurchaseLimitException(); + + private ApproveWaitingOrderPurchaseLimitException() { + super(OrderErrorCode.APPROVE_WAITING_PURCHASE_LIMIT); + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/exception/OrderErrorCode.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/exception/OrderErrorCode.java index 453d57d8..320ec74c 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/exception/OrderErrorCode.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/exception/OrderErrorCode.java @@ -35,7 +35,11 @@ public enum OrderErrorCode implements BaseErrorCode { @ExplainError("한 장바구니엔 관련된 한 아이템만 올수 있음") ORDER_INVALID_ITEM_KIND_POLICY(BAD_REQUEST, "Order_400_12", "장바구니에 아이템을 담는 정책을 위반하였습니다."), ORDER_OPTION_CHANGED(BAD_REQUEST, "Order_400_13", "주문 과정중 아이템의 옵션이 변화했습니다."), - CAN_NOT_DELETED_USER_APPROVE(BAD_REQUEST, "Order_400_14", "유저가 탈퇴를 했습니다."); + CAN_NOT_DELETED_USER_APPROVE(BAD_REQUEST, "Order_400_14", "유저가 탈퇴를 했습니다."), + APPROVE_WAITING_PURCHASE_LIMIT( + BAD_REQUEST, + "Order_400_15", + "승인 대기중인 주문으로 인해 티켓 최대 구매 가능 회수를 넘겼습니다." + "이미 신청한 주문이 승인 될 때까지 기다려주세요."); private Integer status; private String code; diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/OrderRepository.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/OrderRepository.java index d3ab02bf..c273d86e 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/OrderRepository.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/OrderRepository.java @@ -2,6 +2,7 @@ import band.gosrock.domain.domains.order.domain.Order; +import band.gosrock.domain.domains.order.domain.OrderStatus; import java.util.List; import org.springframework.data.repository.CrudRepository; @@ -9,5 +10,7 @@ public interface OrderRepository extends CrudRepository, OrderCusto List findByEventId(Long eventId); + List findByEventIdAndOrderStatus(Long eventId, OrderStatus orderStatus); + List findByUuidIn(List uuids); } diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/service/CreateOrderService.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/service/CreateOrderService.java index 179a607d..ce7c4984 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/service/CreateOrderService.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/service/CreateOrderService.java @@ -2,6 +2,7 @@ import band.gosrock.common.annotation.DomainService; +import band.gosrock.domain.common.aop.redissonLock.RedissonLock; import band.gosrock.domain.domains.order.adaptor.OrderAdaptor; import band.gosrock.domain.domains.order.domain.Order; import lombok.RequiredArgsConstructor; @@ -15,13 +16,15 @@ public class CreateOrderService { private final OrderFactory orderFactory; private final OrderAdaptor orderAdaptor; - @Transactional + // 주문 생성간에 승인 주문일경우 이전 목록 조회해서 티켓 발급 한계 개산해야하므로 + // 동시성 이슈가 있음. + @RedissonLock(LockName = "주문생성", identifier = "userId") public String withOutCoupon(Long cartId, Long userId) { Order order = orderFactory.createNormalOrder(cartId, userId); return orderAdaptor.save(order).getUuid(); } - @Transactional + @RedissonLock(LockName = "주문생성", identifier = "userId") public String withCoupon(Long cartId, Long userId, Long couponId) { Order order = orderFactory.createCouponOrder(cartId, userId, couponId); return orderAdaptor.save(order).getUuid(); diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/service/handler/ConfirmOrderFailHandler.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/service/handler/ConfirmOrderFailHandler.java index 417e2740..13637b10 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/service/handler/ConfirmOrderFailHandler.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/service/handler/ConfirmOrderFailHandler.java @@ -3,11 +3,9 @@ import band.gosrock.domain.common.events.order.DoneOrderEvent; import band.gosrock.domain.domains.coupon.service.RecoveryCouponService; -import band.gosrock.domain.domains.issuedTicket.adaptor.IssuedTicketAdaptor; import band.gosrock.domain.domains.issuedTicket.service.IssuedTicketDomainService; import band.gosrock.domain.domains.order.adaptor.OrderAdaptor; import band.gosrock.domain.domains.order.domain.Order; -import band.gosrock.domain.domains.order.domain.OrderStatus; import band.gosrock.domain.domains.order.service.WithdrawPaymentService; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -27,7 +25,6 @@ public class ConfirmOrderFailHandler { private final RecoveryCouponService recoveryCouponService; private final OrderAdaptor orderAdaptor; - private final IssuedTicketAdaptor issuedTicketAdaptor; @Async @TransactionalEventListener( @@ -38,10 +35,6 @@ public void handleDoneOrderFailEvent(DoneOrderEvent doneOrderEvent) { log.info(doneOrderEvent.getOrderUuid() + "주문 실패 처리 핸들러"); Order order = orderAdaptor.findByOrderUuid(doneOrderEvent.getOrderUuid()); - if (order.getOrderStatus() == OrderStatus.FAILED) { - return; - } - order.fail(); if (order.hasCoupon()) { // 쿠폰 사용했을 시 쿠폰 복구 diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/domain/TicketItem.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/domain/TicketItem.java index cf746a50..daf3082e 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/domain/TicketItem.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/domain/TicketItem.java @@ -245,11 +245,15 @@ public void validEnoughQuantity(Long quantity) { } public void validPurchaseLimit(Long quantity) { - if (this.purchaseLimit < quantity) { + if (isPurchaseLimitExceed(quantity)) { throw TicketPurchaseLimitException.EXCEPTION; } } + public Boolean isPurchaseLimitExceed(Long quantity) { + return this.purchaseLimit < quantity; + } + public void increaseQuantity(Long quantity) { if (this.quantity + quantity > supplyCount) { throw TicketItemQuantityLargeException.EXCEPTION; diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/user/domain/User.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/user/domain/User.java index bf7e82fc..624a11d3 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/user/domain/User.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/user/domain/User.java @@ -82,6 +82,8 @@ public void withDrawUser() { accountState = AccountState.DELETED; profile.withdraw(); oauthInfo = oauthInfo.withDrawOauthInfo(); + marketingAgree = Boolean.FALSE; + receiveMail = Boolean.FALSE; } public void login() { diff --git a/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/domain/validator/OrderValidatorTest.java b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/domain/validator/OrderValidatorTest.java index fa4616eb..5c6292a8 100644 --- a/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/domain/validator/OrderValidatorTest.java +++ b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/domain/validator/OrderValidatorTest.java @@ -13,6 +13,7 @@ import band.gosrock.domain.domains.event.exception.EventNotOpenException; import band.gosrock.domain.domains.event.exception.EventTicketingTimeIsPassedException; import band.gosrock.domain.domains.issuedTicket.adaptor.IssuedTicketAdaptor; +import band.gosrock.domain.domains.order.adaptor.OrderAdaptor; import band.gosrock.domain.domains.order.domain.Order; import band.gosrock.domain.domains.order.domain.OrderLineItem; import band.gosrock.domain.domains.order.domain.OrderMethod; @@ -57,6 +58,7 @@ class OrderValidatorTest { @Mock IssuedTicketAdaptor issuedTicketAdaptor; @Mock OptionAdaptor optionAdaptor; @Mock UserAdaptor userAdaptor; + @Mock OrderAdaptor OrderAdaptor; @Mock Option optionOfGroup1; @Mock Option optionOfGroup2; @Mock OrderLineItem orderLineItem; @@ -73,7 +75,8 @@ void setUp() { ticketItemAdaptor, issuedTicketAdaptor, optionAdaptor, - userAdaptor); + userAdaptor, + OrderAdaptor); } @Test diff --git a/temp.xlsx b/temp.xlsx deleted file mode 100644 index 293e90c6..00000000 Binary files a/temp.xlsx and /dev/null differ