Skip to content

Commit

Permalink
Api-v0.2.3-5
Browse files Browse the repository at this point in the history
Api-v0.2.3-5
  • Loading branch information
ImNM authored Feb 27, 2023
2 parents 31bc469 + c804fdc commit 4087ee2
Show file tree
Hide file tree
Showing 20 changed files with 214 additions and 22 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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));
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -14,7 +13,6 @@
@UseCase
@RequiredArgsConstructor
public class ReadInviteUsersUseCase {
private final UserUtils userUtils;
private final HostMapper hostMapper;

@Transactional(readOnly = true)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -43,7 +43,7 @@ private Expression<BigDecimal> 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");
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand All @@ -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;
Expand All @@ -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);
Expand Down Expand Up @@ -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;
}
}

/** 결제 방식의 주문을 승인할수있는지 확인합니다. */
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down Expand Up @@ -73,6 +74,8 @@ public Page<Order> findEventOrders(FindEventOrdersCondition condition, Pageable
.join(user)
.on(user.id.eq(order.userId))
.where(
// 주문 승인 요청 보여질 때만 지워진 유저를 보이게 하지 않습니다.
condition.showDeleteUserExpression(),
eqEventId(condition.getEventId()),
condition.getOrderStatusFilter(),
condition.getSearchStringFilter())
Expand All @@ -88,6 +91,7 @@ public Page<Order> findEventOrders(FindEventOrdersCondition condition, Pageable
.join(user)
.on(user.id.eq(order.userId))
.where(
condition.showDeleteUserExpression(),
eqEventId(condition.getEventId()),
condition.getOrderStatusFilter(),
condition.getSearchStringFilter());
Expand Down Expand Up @@ -131,4 +135,8 @@ private BooleanExpression openingState(Boolean isShowing) {
? eventEndAtTemplate.after(now)
: eventEndAtTemplate.before(now);
}

private BooleanExpression notDeletedUser() {
return user.accountState.ne(AccountState.DELETED);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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.*;
Expand Down Expand Up @@ -83,6 +84,9 @@ public Boolean hasApplication(List<TicketItem> 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) {
Expand Down
Original file line number Diff line number Diff line change
@@ -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);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -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;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -126,4 +126,8 @@ public void toggleReceiveEmail() {
public void toggleMarketingAgree() {
marketingAgree = !marketingAgree;
}

public Boolean isDeletedUser() {
return accountState == AccountState.DELETED;
}
}
Original file line number Diff line number Diff line change
@@ -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);
}
}
Loading

0 comments on commit 4087ee2

Please sign in to comment.