From f604069204378d7accd8a180ad29cb2027f8dc93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EB=85=B8=EA=B2=BD=EB=AF=BC?= Date: Mon, 27 Feb 2023 16:31:04 +0900 Subject: [PATCH 1/4] =?UTF-8?q?fix=20:=20=ED=98=B8=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=9C=A0=EC=A0=80=20=EC=B4=88=EB=8C=80=20=EB=8F=99=EC=8B=9C?= =?UTF-8?q?=EC=84=B1=20=EC=9D=B4=EC=8A=88=20=EB=8C=80=EC=9D=91=20(#471)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix : 호스트 초대 분산락 적용 * test : 동시성 이슈 테스트 코드 작성 --- .../api/host/model/mapper/HostMapper.java | 5 +- .../api/host/service/InviteHostUseCase.java | 9 +-- .../host/service/ReadInviteUsersUseCase.java | 2 - .../domains/host/service/HostService.java | 2 + .../HostServiceConcurrencyFailureTest.java | 59 +++++++++++++++++++ .../service/HostServiceConcurrencyTest.java | 56 ++++++++++++++++++ 6 files changed, 123 insertions(+), 10 deletions(-) create mode 100644 DuDoong-Domain/src/test/java/band/gosrock/domain/domains/host/service/HostServiceConcurrencyFailureTest.java create mode 100644 DuDoong-Domain/src/test/java/band/gosrock/domain/domains/host/service/HostServiceConcurrencyTest.java diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/host/model/mapper/HostMapper.java b/DuDoong-Api/src/main/java/band/gosrock/api/host/model/mapper/HostMapper.java index 8ffcbaaa..6df9227b 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/host/model/mapper/HostMapper.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/host/model/mapper/HostMapper.java @@ -16,7 +16,9 @@ import band.gosrock.domain.domains.host.exception.AlreadyJoinedHostException; import band.gosrock.domain.domains.user.adaptor.UserAdaptor; import band.gosrock.domain.domains.user.domain.User; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.Map; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -66,7 +68,6 @@ public HostUser toMasterHostUser(Long hostId, Long userId) { public UserProfileVo toHostInviteUserList(Long hostId, String email) { final Host host = hostAdaptor.findById(hostId); - final User inviteUser = userAdaptor.queryUserByEmail(email); if (host.hasHostUserId(inviteUser.getId())) { throw AlreadyJoinedHostException.EXCEPTION; diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/host/service/InviteHostUseCase.java b/DuDoong-Api/src/main/java/band/gosrock/api/host/service/InviteHostUseCase.java index 30c1cec6..1c6b612d 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/host/service/InviteHostUseCase.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/host/service/InviteHostUseCase.java @@ -16,7 +16,6 @@ import band.gosrock.domain.domains.user.adaptor.UserAdaptor; import band.gosrock.domain.domains.user.domain.User; import lombok.RequiredArgsConstructor; -import org.springframework.transaction.annotation.Transactional; @UseCase @RequiredArgsConstructor @@ -27,15 +26,13 @@ public class InviteHostUseCase { private final HostAdaptor hostAdaptor; private final HostMapper hostMapper; - @Transactional @HostRolesAllowed(role = MANAGER, findHostFrom = HOST_ID) public HostDetailResponse execute(Long hostId, InviteHostRequest inviteHostRequest) { - // 초대할 유저 - final User user = userAdaptor.queryUserByEmail(inviteHostRequest.getEmail()); - final Long inviteUserId = user.getId(); final Host host = hostAdaptor.findById(hostId); + final User invitedUser = userAdaptor.queryUserByEmail(inviteHostRequest.getEmail()); + final Long invitedUserId = invitedUser.getId(); final HostRole role = inviteHostRequest.getRole(); - final HostUser hostUser = hostMapper.toHostUser(hostId, inviteUserId, role); + final HostUser hostUser = hostMapper.toHostUser(hostId, invitedUserId, role); return hostMapper.toHostDetailResponse(hostService.inviteHostUser(host, hostUser)); } diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/host/service/ReadInviteUsersUseCase.java b/DuDoong-Api/src/main/java/band/gosrock/api/host/service/ReadInviteUsersUseCase.java index fdf75808..342ccf7c 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/host/service/ReadInviteUsersUseCase.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/host/service/ReadInviteUsersUseCase.java @@ -3,7 +3,6 @@ import static band.gosrock.api.common.aop.hostRole.FindHostFrom.HOST_ID; import static band.gosrock.api.common.aop.hostRole.HostQualification.GUEST; -import band.gosrock.api.common.UserUtils; import band.gosrock.api.common.aop.hostRole.HostRolesAllowed; import band.gosrock.api.host.model.mapper.HostMapper; import band.gosrock.common.annotation.UseCase; @@ -14,7 +13,6 @@ @UseCase @RequiredArgsConstructor public class ReadInviteUsersUseCase { - private final UserUtils userUtils; private final HostMapper hostMapper; @Transactional(readOnly = true) diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/host/service/HostService.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/host/service/HostService.java index 916d7db7..34cd22de 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/host/service/HostService.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/host/service/HostService.java @@ -2,6 +2,7 @@ import band.gosrock.common.annotation.DomainService; +import band.gosrock.domain.common.aop.redissonLock.RedissonLock; import band.gosrock.domain.domains.host.adaptor.HostAdaptor; import band.gosrock.domain.domains.host.domain.Host; import band.gosrock.domain.domains.host.domain.HostProfile; @@ -30,6 +31,7 @@ public Host addHostUser(Host host, HostUser hostUser) { return hostRepository.save(host); } + @RedissonLock(LockName = "호스트유저초대", identifier = "id", paramClassType = Host.class) public Host inviteHostUser(Host host, HostUser hostUser) { host.inviteHostUsers(Set.of(hostUser)); return hostRepository.save(host); diff --git a/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/host/service/HostServiceConcurrencyFailureTest.java b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/host/service/HostServiceConcurrencyFailureTest.java new file mode 100644 index 00000000..dc4762f1 --- /dev/null +++ b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/host/service/HostServiceConcurrencyFailureTest.java @@ -0,0 +1,59 @@ +package band.gosrock.domain.domains.host.service; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; + +import band.gosrock.domain.CunCurrencyExecutorService; +import band.gosrock.domain.DisableDomainEvent; +import band.gosrock.domain.DisableRedissonLock; +import band.gosrock.domain.DomainIntegrateSpringBootTest; +import band.gosrock.domain.domains.host.domain.Host; +import band.gosrock.domain.domains.host.domain.HostUser; +import band.gosrock.domain.domains.host.repository.HostRepository; +import java.util.concurrent.atomic.AtomicLong; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.util.ReflectionTestUtils; + +@DomainIntegrateSpringBootTest +@DisableDomainEvent +@DisableRedissonLock +@Slf4j +public class HostServiceConcurrencyFailureTest { + @Autowired HostService hostService; + + @Autowired RedissonClient redissonClient; + + Host host; + + @Mock HostUser hostUser; + @MockBean HostRepository hostRepository; + + @BeforeEach + void setup() { + host = Host.builder().build(); + ReflectionTestUtils.setField(host, "id", 1L); + given(hostUser.getUserId()).willReturn(9L); + given(hostRepository.save(any(Host.class))).willReturn(host); + } + + @Test + @DisplayName("락 적용을 안하면 여러개의 초대가 승인될 수도 있다.") + public void 호스트유저_초대요청_동시성_실패() throws InterruptedException { + // given + // when + AtomicLong successCount = new AtomicLong(); + CunCurrencyExecutorService.execute( + () -> hostService.inviteHostUser(host, hostUser), successCount); + // then + log.info(String.valueOf(successCount.get())); + assertThat(successCount.get()).isGreaterThanOrEqualTo(1L); + } +} diff --git a/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/host/service/HostServiceConcurrencyTest.java b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/host/service/HostServiceConcurrencyTest.java new file mode 100644 index 00000000..c95d6e9a --- /dev/null +++ b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/host/service/HostServiceConcurrencyTest.java @@ -0,0 +1,56 @@ +package band.gosrock.domain.domains.host.service; + +import static org.assertj.core.api.AssertionsForClassTypes.assertThat; +import static org.mockito.ArgumentMatchers.any; +import static org.mockito.BDDMockito.given; + +import band.gosrock.domain.CunCurrencyExecutorService; +import band.gosrock.domain.DisableDomainEvent; +import band.gosrock.domain.DomainIntegrateSpringBootTest; +import band.gosrock.domain.domains.host.domain.Host; +import band.gosrock.domain.domains.host.domain.HostUser; +import band.gosrock.domain.domains.host.repository.HostRepository; +import java.util.concurrent.atomic.AtomicLong; +import lombok.extern.slf4j.Slf4j; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.mockito.Mock; +import org.redisson.api.RedissonClient; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.mock.mockito.MockBean; +import org.springframework.test.util.ReflectionTestUtils; + +@DomainIntegrateSpringBootTest +@DisableDomainEvent +@Slf4j +public class HostServiceConcurrencyTest { + @Autowired HostService hostService; + + @Autowired RedissonClient redissonClient; + + Host host; + + @Mock HostUser hostUser; + @MockBean HostRepository hostRepository; + + @BeforeEach + void setup() { + host = Host.builder().build(); + ReflectionTestUtils.setField(host, "id", 1L); + given(hostUser.getUserId()).willReturn(9L); + given(hostRepository.save(any(Host.class))).willReturn(host); + } + + @Test + @DisplayName("동시에 초대 요청을 보내도 하나의 요청만 성공해야한다.") + public void 호스트유저_초대요청_동시성테스트() throws InterruptedException { + // given + // when + AtomicLong successCount = new AtomicLong(); + CunCurrencyExecutorService.execute( + () -> hostService.inviteHostUser(host, hostUser), successCount); + // then + assertThat(successCount.get()).isEqualTo(1L); + } +} From 29d824a9b29084ef523249d7d7071ccc2ddf0e57 Mon Sep 17 00:00:00 2001 From: ONE ZIN <80401705+kim-wonjin@users.noreply.github.com> Date: Mon, 27 Feb 2023 21:56:53 +0900 Subject: [PATCH 2/4] =?UTF-8?q?fix=20:=20=EC=98=B5=EC=85=98=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1=EC=8B=9C,=20=EC=B6=94=EA=B0=80=20=EA=B0=80=EA=B2=A9?= =?UTF-8?q?=20=EC=9D=8C=EC=88=98=20=EB=B6=88=EA=B0=80=20=EC=B2=98=EB=A6=AC?= =?UTF-8?q?=20(#473)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor(ticketItem) : 두둥티켓 계좌정보 분리 * fix(ticketOption) : 옵션 추가 가격 음수 불가 처리 --- .../domains/ticket_item/domain/OptionGroup.java | 4 ++++ .../exception/InvalidOptionPriceException.java | 13 +++++++++++++ .../ticket_item/exception/TicketItemErrorCode.java | 4 +++- 3 files changed, 20 insertions(+), 1 deletion(-) create mode 100644 DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/exception/InvalidOptionPriceException.java diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/domain/OptionGroup.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/domain/OptionGroup.java index 4ae1b230..7ca6af6c 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/domain/OptionGroup.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/domain/OptionGroup.java @@ -8,6 +8,7 @@ import band.gosrock.domain.common.vo.Money; import band.gosrock.domain.domains.ticket_item.exception.ForbiddenOptionGroupDeleteException; import band.gosrock.domain.domains.ticket_item.exception.InvalidOptionGroupException; +import band.gosrock.domain.domains.ticket_item.exception.InvalidOptionPriceException; import java.util.ArrayList; import java.util.List; import javax.persistence.*; @@ -83,6 +84,9 @@ public Boolean hasApplication(List ticketItems) { public OptionGroup createTicketOption(Money additionalPrice) { OptionGroupType type = this.getType(); if (type == TRUE_FALSE) { + if (additionalPrice.isLessThan(ZERO)) { + throw InvalidOptionPriceException.EXCEPTION; + } this.options.add(Option.create(KR_YES, additionalPrice, this)); this.options.add(Option.create(KR_NO, ZERO, this)); } else if (type == SUBJECTIVE) { diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/exception/InvalidOptionPriceException.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/exception/InvalidOptionPriceException.java new file mode 100644 index 00000000..a99a9979 --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/exception/InvalidOptionPriceException.java @@ -0,0 +1,13 @@ +package band.gosrock.domain.domains.ticket_item.exception; + + +import band.gosrock.common.exception.DuDoongCodeException; + +public class InvalidOptionPriceException extends DuDoongCodeException { + + public static final DuDoongCodeException EXCEPTION = new InvalidOptionPriceException(); + + private InvalidOptionPriceException() { + super(TicketItemErrorCode.INVALID_OPTION_PRICE); + } +} diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/exception/TicketItemErrorCode.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/exception/TicketItemErrorCode.java index 38fd01c1..c34c0c5c 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/exception/TicketItemErrorCode.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/ticket_item/exception/TicketItemErrorCode.java @@ -53,7 +53,9 @@ public enum TicketItemErrorCode implements BaseErrorCode { @ExplainError("해당 티켓상품에 적용되지 않은 옵션을 취소 시도할 경우 발생하는 오류입니다.") NOT_APPLIED_ITEM_OPTION_GROUP(BAD_REQUEST, "Item_Option_Group_400_3", "적용되지 않은 옵션입니다."), @ExplainError("무료 티켓에 유료 옵션을 적용하려고 했을 때 발생하는 오류입니다.") - FORBIDDEN_OPTION_PRICE(BAD_REQUEST, "Item_Option_Group_400_4", "유료 옵션을 적용할 수 없습니다."); + FORBIDDEN_OPTION_PRICE(BAD_REQUEST, "Item_Option_Group_400_4", "유료 옵션을 적용할 수 없습니다."), + @ExplainError("옵션 추가가격이 음수일 때 발생하는 오류입니다.") + INVALID_OPTION_PRICE(BAD_REQUEST, "Option_Group_400_3", "설정할 수 없는 추가 가격입니다."); private Integer status; private String code; From c3e76530b7f456ae866f5677f4d1eb2a008e118f Mon Sep 17 00:00:00 2001 From: Chan Jin Date: Mon, 27 Feb 2023 22:05:39 +0900 Subject: [PATCH 3/4] =?UTF-8?q?fix=20:=20=EC=8A=B9=EC=9D=B8=20=EC=A3=BC?= =?UTF-8?q?=EB=AC=B8=EB=8F=84=20=ED=86=B5=EA=B3=84=EC=97=90=20=ED=95=A9?= =?UTF-8?q?=ED=95=98=EA=B8=B0=20(#475)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../band/gosrock/api/statistic/query/OrderQueryRepository.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/DuDoong-Api/src/main/java/band/gosrock/api/statistic/query/OrderQueryRepository.java b/DuDoong-Api/src/main/java/band/gosrock/api/statistic/query/OrderQueryRepository.java index f1f6dbb2..d054eb32 100644 --- a/DuDoong-Api/src/main/java/band/gosrock/api/statistic/query/OrderQueryRepository.java +++ b/DuDoong-Api/src/main/java/band/gosrock/api/statistic/query/OrderQueryRepository.java @@ -43,7 +43,7 @@ private Expression sellAmount(Long eventId) { return ExpressionUtils.as( JPAExpressions.select(order.totalPaymentInfo.paymentAmount.amount.sum()) .from(order) - .where(eventIdEq(eventId), order.orderStatus.eq(CONFIRM)), + .where(eventIdEq(eventId), order.orderStatus.in(CONFIRM, APPROVED)), "sellAmount"); } From c804fdca255f705632044f6a36e3d06eb614905f Mon Sep 17 00:00:00 2001 From: Chan Jin Date: Mon, 27 Feb 2023 22:08:29 +0900 Subject: [PATCH 4/4] fix : order approve not delete user (#477) MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * fix : 주문 승인 조회시에만 지워진 유저 안보이게 * fix : 주문 승인간 유저 탈퇴여부 검증 * style : spotless * test : 테스트 돌아가게 수정 --- .../domain/validator/OrderValidator.java | 20 ++++++++++++++----- ...anNotApproveDeletedUserOrderException.java | 14 +++++++++++++ .../order/exception/OrderErrorCode.java | 3 ++- .../repository/OrderCustomRepositoryImpl.java | 8 ++++++++ .../condition/AdminTableOrderFilterType.java | 17 +++++++++++++--- .../condition/FindEventOrdersCondition.java | 4 ++++ .../domain/domains/user/domain/User.java | 4 ++++ .../domain/validator/OrderValidatorTest.java | 8 +++++++- ...rderApproveServiceConcurrencyFailTest.java | 1 + .../OrderApproveServiceConcurrencyTest.java | 1 + 10 files changed, 70 insertions(+), 10 deletions(-) create mode 100644 DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/exception/CanNotApproveDeletedUserOrderException.java 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 65e8dd7b..423d9af2 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 @@ -9,6 +9,7 @@ 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.CanNotApproveDeletedUserOrderException; import band.gosrock.domain.domains.order.exception.CanNotCancelOrderException; import band.gosrock.domain.domains.order.exception.CanNotRefundOrderException; import band.gosrock.domain.domains.order.exception.InvalidOrderException; @@ -24,6 +25,8 @@ import band.gosrock.domain.domains.ticket_item.adaptor.TicketItemAdaptor; import band.gosrock.domain.domains.ticket_item.domain.Option; import band.gosrock.domain.domains.ticket_item.domain.TicketItem; +import band.gosrock.domain.domains.user.adaptor.UserAdaptor; +import band.gosrock.domain.domains.user.domain.User; import java.util.List; import java.util.Objects; import lombok.RequiredArgsConstructor; @@ -42,6 +45,8 @@ public class OrderValidator { private final IssuedTicketAdaptor issuedTicketAdaptor; private final OptionAdaptor optionAdaptor; + private final UserAdaptor userAdaptor; + /** 주문을 생성할 수 있는지에 대한검증 */ public void validCanCreate(Order order) { TicketItem item = getItem(order); @@ -74,16 +79,21 @@ public void validOptionNotChange(Order order, TicketItem item) { }); } - public void validOptionNotChangeAfterDoneOrderEvent(Order order) { - TicketItem item = getItem(order); - validOptionNotChange(order, item); - } - /** 승인 가능한 주문인지 검증합니다. */ public void validCanApproveOrder(Order order) { validMethodIsCanApprove(order); validStatusCanApprove(getOrderStatus(order)); validCanDone(order); + // 유저가 탈퇴를 안했는지 확인. + validUserNotDeleted(order); + } + + /** 주문 승인 간에 유저가 탈퇴를 했는지 조회합니다. */ + public void validUserNotDeleted(Order order) { + User user = userAdaptor.queryUser(order.getUserId()); + if (user.isDeletedUser()) { + throw CanNotApproveDeletedUserOrderException.EXCEPTION; + } } /** 결제 방식의 주문을 승인할수있는지 확인합니다. */ diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/exception/CanNotApproveDeletedUserOrderException.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/exception/CanNotApproveDeletedUserOrderException.java new file mode 100644 index 00000000..9ac8214f --- /dev/null +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/exception/CanNotApproveDeletedUserOrderException.java @@ -0,0 +1,14 @@ +package band.gosrock.domain.domains.order.exception; + + +import band.gosrock.common.exception.DuDoongCodeException; + +public class CanNotApproveDeletedUserOrderException extends DuDoongCodeException { + + public static final DuDoongCodeException EXCEPTION = + new CanNotApproveDeletedUserOrderException(); + + private CanNotApproveDeletedUserOrderException() { + super(OrderErrorCode.CAN_NOT_DELETED_USER_APPROVE); + } +} 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 1a4a9597..453d57d8 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 @@ -34,7 +34,8 @@ public enum OrderErrorCode implements BaseErrorCode { ORDER_LESS_THAN_MINIMUM(BAD_REQUEST, "Order_400_11", "최소 결제금액인 1000원보다 낮은 주문입니다."), @ExplainError("한 장바구니엔 관련된 한 아이템만 올수 있음") ORDER_INVALID_ITEM_KIND_POLICY(BAD_REQUEST, "Order_400_12", "장바구니에 아이템을 담는 정책을 위반하였습니다."), - ORDER_OPTION_CHANGED(BAD_REQUEST, "Order_400_13", "주문 과정중 아이템의 옵션이 변화했습니다."); + ORDER_OPTION_CHANGED(BAD_REQUEST, "Order_400_13", "주문 과정중 아이템의 옵션이 변화했습니다."), + CAN_NOT_DELETED_USER_APPROVE(BAD_REQUEST, "Order_400_14", "유저가 탈퇴를 했습니다."); private Integer status; private String code; diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/OrderCustomRepositoryImpl.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/OrderCustomRepositoryImpl.java index 7f31bc64..f2beb9cf 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/OrderCustomRepositoryImpl.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/OrderCustomRepositoryImpl.java @@ -10,6 +10,7 @@ import band.gosrock.domain.domains.order.domain.OrderStatus; import band.gosrock.domain.domains.order.repository.condition.FindEventOrdersCondition; import band.gosrock.domain.domains.order.repository.condition.FindMyPageOrderCondition; +import band.gosrock.domain.domains.user.domain.AccountState; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.core.types.dsl.DateTemplate; import com.querydsl.core.types.dsl.Expressions; @@ -73,6 +74,8 @@ public Page findEventOrders(FindEventOrdersCondition condition, Pageable .join(user) .on(user.id.eq(order.userId)) .where( + // 주문 승인 요청 보여질 때만 지워진 유저를 보이게 하지 않습니다. + condition.showDeleteUserExpression(), eqEventId(condition.getEventId()), condition.getOrderStatusFilter(), condition.getSearchStringFilter()) @@ -88,6 +91,7 @@ public Page findEventOrders(FindEventOrdersCondition condition, Pageable .join(user) .on(user.id.eq(order.userId)) .where( + condition.showDeleteUserExpression(), eqEventId(condition.getEventId()), condition.getOrderStatusFilter(), condition.getSearchStringFilter()); @@ -131,4 +135,8 @@ private BooleanExpression openingState(Boolean isShowing) { ? eventEndAtTemplate.after(now) : eventEndAtTemplate.before(now); } + + private BooleanExpression notDeletedUser() { + return user.accountState.ne(AccountState.DELETED); + } } diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/condition/AdminTableOrderFilterType.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/condition/AdminTableOrderFilterType.java index 9cab0c62..bb7504d7 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/condition/AdminTableOrderFilterType.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/condition/AdminTableOrderFilterType.java @@ -6,21 +6,32 @@ import static band.gosrock.domain.domains.order.domain.OrderStatus.PENDING_APPROVE; import static band.gosrock.domain.domains.order.domain.OrderStatus.REFUND; import static band.gosrock.domain.domains.order.domain.QOrder.order; +import static band.gosrock.domain.domains.user.domain.QUser.user; +import band.gosrock.domain.domains.user.domain.AccountState; import com.querydsl.core.types.dsl.BooleanExpression; /** 어드민 테이블의 주문 상태 검색 조건 지정을 위함. */ public enum AdminTableOrderFilterType { - APPROVE_WAITING(order.orderStatus.eq(PENDING_APPROVE)), - CONFIRMED(order.orderStatus.in(CONFIRM, APPROVED, CANCELED, REFUND)); + APPROVE_WAITING( + order.orderStatus.eq(PENDING_APPROVE), user.accountState.ne(AccountState.DELETED)), + // 완료된 주문을 가져오는건 지워진 유저도 불러와야한다. + CONFIRMED(order.orderStatus.in(CONFIRM, APPROVED, CANCELED, REFUND), null); private final BooleanExpression expression; + private final BooleanExpression showDeleteUserExpression; - AdminTableOrderFilterType(BooleanExpression expression) { + AdminTableOrderFilterType( + BooleanExpression expression, BooleanExpression showDeleteUserExpression) { this.expression = expression; + this.showDeleteUserExpression = showDeleteUserExpression; } public BooleanExpression getFilter() { return expression; } + + public BooleanExpression showDeleteUserExpression() { + return showDeleteUserExpression; + } } diff --git a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/condition/FindEventOrdersCondition.java b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/condition/FindEventOrdersCondition.java index 31563a77..9b50843c 100644 --- a/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/condition/FindEventOrdersCondition.java +++ b/DuDoong-Domain/src/main/java/band/gosrock/domain/domains/order/repository/condition/FindEventOrdersCondition.java @@ -31,6 +31,10 @@ public BooleanExpression getOrderStatusFilter() { return filterType.getFilter(); } + public BooleanExpression showDeleteUserExpression() { + return filterType.showDeleteUserExpression(); + } + public BooleanExpression getSearchStringFilter() { if (searchType == null) return null; return searchType.getContains(searchString); 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 591a5786..bf7e82fc 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 @@ -126,4 +126,8 @@ public void toggleReceiveEmail() { public void toggleMarketingAgree() { marketingAgree = !marketingAgree; } + + public Boolean isDeletedUser() { + return accountState == AccountState.DELETED; + } } 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 12c7ec67..fa4616eb 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 @@ -34,6 +34,7 @@ import band.gosrock.domain.domains.ticket_item.domain.TicketItem; import band.gosrock.domain.domains.ticket_item.exception.TicketItemQuantityLackException; import band.gosrock.domain.domains.ticket_item.exception.TicketPurchaseLimitException; +import band.gosrock.domain.domains.user.adaptor.UserAdaptor; import java.util.Arrays; import java.util.List; import org.junit.jupiter.api.BeforeEach; @@ -55,6 +56,7 @@ class OrderValidatorTest { @Mock TicketItemAdaptor ticketItemAdaptor; @Mock IssuedTicketAdaptor issuedTicketAdaptor; @Mock OptionAdaptor optionAdaptor; + @Mock UserAdaptor userAdaptor; @Mock Option optionOfGroup1; @Mock Option optionOfGroup2; @Mock OrderLineItem orderLineItem; @@ -67,7 +69,11 @@ class OrderValidatorTest { void setUp() { orderValidator = new OrderValidator( - eventAdaptor, ticketItemAdaptor, issuedTicketAdaptor, optionAdaptor); + eventAdaptor, + ticketItemAdaptor, + issuedTicketAdaptor, + optionAdaptor, + userAdaptor); } @Test diff --git a/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/service/OrderApproveServiceConcurrencyFailTest.java b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/service/OrderApproveServiceConcurrencyFailTest.java index b3f31db5..905b7099 100644 --- a/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/service/OrderApproveServiceConcurrencyFailTest.java +++ b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/service/OrderApproveServiceConcurrencyFailTest.java @@ -52,6 +52,7 @@ void setUp() { .build(); order.addUUID(); willDoNothing().given(orderValidator).validCanDone(any()); + willDoNothing().given(orderValidator).validUserNotDeleted(any()); willCallRealMethod().given(orderValidator).validCanApproveOrder(any()); willCallRealMethod().given(orderValidator).validStatusCanApprove(any()); given(orderAdaptor.findByOrderUuid(any())).willReturn(order); diff --git a/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/service/OrderApproveServiceConcurrencyTest.java b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/service/OrderApproveServiceConcurrencyTest.java index 989b6dab..94f94b4e 100644 --- a/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/service/OrderApproveServiceConcurrencyTest.java +++ b/DuDoong-Domain/src/test/java/band/gosrock/domain/domains/order/service/OrderApproveServiceConcurrencyTest.java @@ -53,6 +53,7 @@ void setUp() { .build(); order.addUUID(); willDoNothing().given(orderValidator).validCanDone(any()); + willDoNothing().given(orderValidator).validUserNotDeleted(any()); willCallRealMethod().given(orderValidator).validCanApproveOrder(any()); willCallRealMethod().given(orderValidator).validStatusCanApprove(any()); given(orderAdaptor.findByOrderUuid(any())).willReturn(order);