Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feature : 옵션 적용 취소 #381

Merged
merged 3 commits into from
Feb 20, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@

import band.gosrock.api.ticketItem.dto.request.ApplyTicketOptionRequest;
import band.gosrock.api.ticketItem.dto.request.CreateTicketItemRequest;
import band.gosrock.api.ticketItem.dto.request.UnapplyTicketOptionRequest;
import band.gosrock.api.ticketItem.dto.response.GetAppliedOptionGroupsResponse;
import band.gosrock.api.ticketItem.dto.response.GetEventTicketItemsResponse;
import band.gosrock.api.ticketItem.dto.response.GetTicketItemOptionsResponse;
Expand All @@ -29,6 +30,7 @@ public class TicketItemController {
public final GetEventTicketItemsUseCase getEventTicketItemsUseCase;
public final DeleteTicketItemUseCase deleteTicketItemUseCase;
public final GetAppliedOptionGroupsUseCase getAppliedOptionGroupsUseCase;
public final UnapplyTicketOptionUseCase unapplyTicketOptionUseCase;

@Operation(
summary = "특정 이벤트에 속하는 티켓 상품을 생성합니다.",
Expand All @@ -49,6 +51,16 @@ public GetTicketItemOptionsResponse applyTicketOption(
return applyTicketOptionUseCase.execute(applyTicketOptionRequest, eventId, ticketItemId);
}

@Operation(summary = "옵션을 티켓상품에 적용 취소합니다.")
@PatchMapping("/{ticketItemId}/option/cancel")
public GetTicketItemOptionsResponse unapplyTicketOption(
@RequestBody @Valid UnapplyTicketOptionRequest unapplyTicketOptionRequest,
@PathVariable Long eventId,
@PathVariable Long ticketItemId) {
return unapplyTicketOptionUseCase.execute(
unapplyTicketOptionRequest, eventId, ticketItemId);
}

@Operation(summary = "해당 이벤트의 티켓상품을 모두 조회합니다.")
@DisableSwaggerSecurity
@GetMapping
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package band.gosrock.api.ticketItem.dto.request;


import io.swagger.v3.oas.annotations.media.Schema;
import javax.validation.constraints.Positive;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public class UnapplyTicketOptionRequest {

@Schema(nullable = false, example = "1")
@Positive(message = "올바른 옵션그룹 고유 아이디를 입력해주세요")
private Long optionGroupId;
}
Original file line number Diff line number Diff line change
@@ -1,39 +1,27 @@
package band.gosrock.api.ticketItem.service;

import static band.gosrock.api.common.aop.hostRole.FindHostFrom.EVENT_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.ticketItem.dto.request.ApplyTicketOptionRequest;
import band.gosrock.api.ticketItem.dto.response.GetTicketItemOptionsResponse;
import band.gosrock.api.ticketItem.mapper.TicketOptionMapper;
import band.gosrock.common.annotation.UseCase;
import band.gosrock.domain.domains.event.adaptor.EventAdaptor;
import band.gosrock.domain.domains.event.domain.Event;
import band.gosrock.domain.domains.host.adaptor.HostAdaptor;
import band.gosrock.domain.domains.host.domain.Host;
import band.gosrock.domain.domains.host.service.HostService;
import band.gosrock.domain.domains.ticket_item.domain.TicketItem;
import band.gosrock.domain.domains.ticket_item.service.ItemOptionGroupService;
import band.gosrock.domain.domains.user.domain.User;
import lombok.RequiredArgsConstructor;

@UseCase
@RequiredArgsConstructor
public class ApplyTicketOptionUseCase {

private final UserUtils userUtils;
private final EventAdaptor eventAdaptor;
private final HostAdaptor hostAdaptor;
private final ItemOptionGroupService itemOptionGroupService;
private final HostService hostService;
private final TicketOptionMapper ticketOptionMapper;

@HostRolesAllowed(role = GUEST, findHostFrom = EVENT_ID, applyTransaction = false)
public GetTicketItemOptionsResponse execute(
ApplyTicketOptionRequest applyTicketOptionRequest, Long eventId, Long ticketItemId) {
User user = userUtils.getCurrentUser();
Event event = eventAdaptor.findById(eventId);
Host host = hostAdaptor.findById(event.getHostId());
// 권한 체크 ( 해당 이벤트의 호스트인지 )
hostService.validateHostUser(host, user.getId());

Long optionGroupId = applyTicketOptionRequest.getOptionGroupId();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,9 @@
package band.gosrock.api.ticketItem.service;

import static band.gosrock.api.common.aop.hostRole.FindHostFrom.EVENT_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.ticketItem.dto.request.CreateTicketItemRequest;
import band.gosrock.api.ticketItem.dto.response.TicketItemResponse;
import band.gosrock.api.ticketItem.mapper.TicketItemMapper;
Expand All @@ -10,32 +12,26 @@
import band.gosrock.domain.domains.event.domain.Event;
import band.gosrock.domain.domains.host.adaptor.HostAdaptor;
import band.gosrock.domain.domains.host.domain.Host;
import band.gosrock.domain.domains.host.service.HostService;
import band.gosrock.domain.domains.ticket_item.domain.TicketItem;
import band.gosrock.domain.domains.ticket_item.service.TicketItemService;
import band.gosrock.domain.domains.user.domain.User;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;

@UseCase
@RequiredArgsConstructor
public class CreateTicketItemUseCase {

private final EventAdaptor eventAdaptor;
private final HostAdaptor hostAdaptor;
private final UserUtils userUtils;
private final HostService hostService;
private final TicketItemService ticketItemService;
private final TicketItemMapper ticketItemMapper;

@Transactional
@HostRolesAllowed(role = GUEST, findHostFrom = EVENT_ID, applyTransaction = false)
public TicketItemResponse execute(
CreateTicketItemRequest createTicketItemRequest, Long eventId) {
User user = userUtils.getCurrentUser();

Event event = eventAdaptor.findById(eventId);
Host host = hostAdaptor.findById(event.getHostId());
// 권한 체크 ( 해당 이벤트의 호스트인지 )
hostService.validateHostUser(host, user.getId());

// 호스트 제휴 여부
Boolean isPartner = host.getPartner();
TicketItem ticketItem =
Expand Down
Original file line number Diff line number Diff line change
@@ -1,43 +1,34 @@
package band.gosrock.api.ticketItem.service;

import static band.gosrock.api.common.aop.hostRole.FindHostFrom.EVENT_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.ticketItem.dto.request.CreateTicketOptionRequest;
import band.gosrock.api.ticketItem.dto.response.OptionGroupResponse;
import band.gosrock.api.ticketItem.mapper.TicketOptionMapper;
import band.gosrock.common.annotation.UseCase;
import band.gosrock.domain.common.vo.Money;
import band.gosrock.domain.domains.event.adaptor.EventAdaptor;
import band.gosrock.domain.domains.event.domain.Event;
import band.gosrock.domain.domains.host.adaptor.HostAdaptor;
import band.gosrock.domain.domains.host.domain.Host;
import band.gosrock.domain.domains.host.service.HostService;
import band.gosrock.domain.domains.ticket_item.domain.OptionGroup;
import band.gosrock.domain.domains.ticket_item.service.TicketOptionService;
import band.gosrock.domain.domains.user.domain.User;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;

@UseCase
@RequiredArgsConstructor
public class CreateTicketOptionUseCase {

private final UserUtils userUtils;
private final EventAdaptor eventAdaptor;
private final HostAdaptor hostAdaptor;
private final TicketOptionMapper ticketOptionMapper;
private final TicketOptionService ticketOptionService;
private final HostService hostService;

@Transactional
@HostRolesAllowed(role = GUEST, findHostFrom = EVENT_ID, applyTransaction = false)
public OptionGroupResponse execute(
CreateTicketOptionRequest createTicketOptionRequest, Long eventId) {
User user = userUtils.getCurrentUser();

Event event = eventAdaptor.findById(eventId);

Host host = hostAdaptor.findById(event.getHostId());
// 권한 체크 ( 해당 이벤트의 호스트인지 )
hostService.validateHostUser(host, user.getId());
OptionGroup ticketOption =
ticketOptionMapper
.toOptionGroup(createTicketOptionRequest, event)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,7 +9,6 @@
import band.gosrock.common.annotation.UseCase;
import band.gosrock.domain.domains.ticket_item.service.TicketOptionService;
import lombok.RequiredArgsConstructor;
import org.springframework.transaction.annotation.Transactional;

@UseCase
@RequiredArgsConstructor
Expand All @@ -18,8 +17,7 @@ public class DeleteOptionGroupUseCase {
private final TicketOptionMapper ticketOptionMapper;
private final TicketOptionService ticketOptionService;

@Transactional
@HostRolesAllowed(role = GUEST, findHostFrom = EVENT_ID)
@HostRolesAllowed(role = GUEST, findHostFrom = EVENT_ID, applyTransaction = false)
public GetEventOptionsResponse execute(Long eventId, Long optionGroupId) {

ticketOptionService.softDeleteOptionGroup(eventId, optionGroupId);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,36 +1,24 @@
package band.gosrock.api.ticketItem.service;

import static band.gosrock.api.common.aop.hostRole.FindHostFrom.EVENT_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.ticketItem.dto.response.GetEventTicketItemsResponse;
import band.gosrock.api.ticketItem.mapper.TicketItemMapper;
import band.gosrock.common.annotation.UseCase;
import band.gosrock.domain.domains.event.adaptor.EventAdaptor;
import band.gosrock.domain.domains.event.domain.Event;
import band.gosrock.domain.domains.host.adaptor.HostAdaptor;
import band.gosrock.domain.domains.host.domain.Host;
import band.gosrock.domain.domains.host.service.HostService;
import band.gosrock.domain.domains.ticket_item.service.TicketItemService;
import band.gosrock.domain.domains.user.domain.User;
import lombok.RequiredArgsConstructor;

@UseCase
@RequiredArgsConstructor
public class DeleteTicketItemUseCase {

private final TicketItemMapper ticketItemMapper;
private final UserUtils userUtils;
private final EventAdaptor eventAdaptor;
private final HostAdaptor hostAdaptor;
private final HostService hostService;
private final TicketItemService ticketItemService;

@HostRolesAllowed(role = GUEST, findHostFrom = EVENT_ID, applyTransaction = false)
public GetEventTicketItemsResponse execute(Long eventId, Long ticketItemId) {
// 권한체크 aop 변경 예정
User user = userUtils.getCurrentUser();
Event event = eventAdaptor.findById(eventId);
Host host = hostAdaptor.findById(event.getHostId());
hostService.validateHostUser(host, user.getId());

ticketItemService.softDeleteTicketItem(eventId, ticketItemId);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,35 @@
package band.gosrock.api.ticketItem.service;

import static band.gosrock.api.common.aop.hostRole.FindHostFrom.EVENT_ID;
import static band.gosrock.api.common.aop.hostRole.HostQualification.GUEST;

import band.gosrock.api.common.aop.hostRole.HostRolesAllowed;
import band.gosrock.api.ticketItem.dto.request.UnapplyTicketOptionRequest;
import band.gosrock.api.ticketItem.dto.response.GetTicketItemOptionsResponse;
import band.gosrock.api.ticketItem.mapper.TicketOptionMapper;
import band.gosrock.common.annotation.UseCase;
import band.gosrock.domain.domains.ticket_item.domain.TicketItem;
import band.gosrock.domain.domains.ticket_item.service.ItemOptionGroupService;
import lombok.RequiredArgsConstructor;

@UseCase
@RequiredArgsConstructor
public class UnapplyTicketOptionUseCase {

private final ItemOptionGroupService itemOptionGroupService;
private final TicketOptionMapper ticketOptionMapper;

@HostRolesAllowed(role = GUEST, findHostFrom = EVENT_ID, applyTransaction = false)
public GetTicketItemOptionsResponse execute(
UnapplyTicketOptionRequest unapplyTicketOptionRequest,
Long eventId,
Long ticketItemId) {

Long optionGroupId = unapplyTicketOptionRequest.getOptionGroupId();

TicketItem ticketItem =
itemOptionGroupService.removeItemOptionGroup(ticketItemId, optionGroupId, eventId);

return ticketOptionMapper.toGetTicketItemOptionResponse(eventId, ticketItem.getId());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -77,7 +77,7 @@ public class TicketItem extends BaseTimeEntity {
@JoinColumn(name = "event_id", nullable = false)
private Event event;

@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER)
@OneToMany(cascade = CascadeType.ALL, fetch = FetchType.EAGER, orphanRemoval = true)
private final List<ItemOptionGroup> itemOptionGroups = new ArrayList<>();

@Builder
Expand Down Expand Up @@ -129,6 +129,22 @@ public void addItemOptionGroup(OptionGroup optionGroup) {
this.itemOptionGroups.add(itemOptionGroup);
}

public void removeItemOptionGroup(OptionGroup optionGroup) {
// 재고 감소된 티켓상품은 옵션적용 변경 불가
if (this.isQuantityReduced()) {
throw ForbiddenOptionChangeException.EXCEPTION;
}
ItemOptionGroup itemOptionGroup = this.findItemOptionGroup(optionGroup);
this.itemOptionGroups.remove(itemOptionGroup);
}

public ItemOptionGroup findItemOptionGroup(OptionGroup optionGroup) {
return this.itemOptionGroups.stream()
.filter(itemOptionGroup -> itemOptionGroup.getOptionGroup().equals(optionGroup))
.findAny()
.orElseThrow(() -> NotAppliedItemOptionGroupException.EXCEPTION);
}

public Boolean hasItemOptionGroup(Long optionGroupId) {
return this.itemOptionGroups.stream()
.anyMatch(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,6 @@ public class EmptyAccountInfoException extends DuDoongCodeException {
public static final DuDoongCodeException EXCEPTION = new EmptyAccountInfoException();

private EmptyAccountInfoException() {
super(TicketItemErrorCode.EMPTY_ACCOUT_INFO);
super(TicketItemErrorCode.EMPTY_ACCOUNT_INFO);
}
}
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 NotAppliedItemOptionGroupException extends DuDoongCodeException {

public static final DuDoongCodeException EXCEPTION = new NotAppliedItemOptionGroupException();

private NotAppliedItemOptionGroupException() {
super(TicketItemErrorCode.NOT_APPLIED_ITEM_OPTION_GROUP);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -45,11 +45,13 @@ public enum TicketItemErrorCode implements BaseErrorCode {
@ExplainError("이미 적용되어 옵션그룹 삭제가 불가능할 경우 발생하는 오류입니다.")
FORBIDDEN_OPTION_GROUP_DELETE(BAD_REQUEST, "Option_Group_400_2", "옵션그룹 삭제가 불가능한 상태입니다."),
@ExplainError("두둥티켓 타입에 계좌번호가 입력되지 않았을 경우 발생하는 오류입니다.")
EMPTY_ACCOUT_INFO(BAD_REQUEST, "Ticket_Item_400_8", "계좌정보가 필요합니다."),
EMPTY_ACCOUNT_INFO(BAD_REQUEST, "Ticket_Item_400_8", "계좌정보가 필요합니다."),
@ExplainError("티켓 지불방식과 승인방식이 불가능한 조합일때 발생하는 오류입니다.")
INVALID_TICKET_TYPE(BAD_REQUEST, "Ticket_Item_400_9", "잘못된 티켓 승인타입입니다."),
@ExplainError("제휴되지 않은 호스트가 유료티켓 생성을 요청했을때 발생하는 오류입니다.")
INVALID_PARTNER(BAD_REQUEST, "Ticket_Item_400_3", "제휴된 호스트가 아닙니다.");
INVALID_PARTNER(BAD_REQUEST, "Ticket_Item_400_3", "제휴된 호스트가 아닙니다."),
@ExplainError("해당 티켓상품에 적용되지 않은 옵션을 취소 시도할 경우 발생하는 오류입니다.")
NOT_APPLIED_ITEM_OPTION_GROUP(BAD_REQUEST, "Item_Option_Group_400_3", "적용되지 않은 옵션입니다.");

private Integer status;
private String code;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -21,7 +21,6 @@ public class ItemOptionGroupService {
private final TicketItemRepository ticketItemRepository;

@RedissonLock(LockName = "티켓관리", identifier = "ticketItemId")
@Transactional
public TicketItem addItemOptionGroup(Long ticketItemId, Long optionGroupId, Long eventId) {

TicketItem ticketItem = ticketItemAdaptor.queryTicketItem(ticketItemId);
Expand All @@ -34,4 +33,19 @@ public TicketItem addItemOptionGroup(Long ticketItemId, Long optionGroupId, Long
ticketItem.addItemOptionGroup(optionGroup);
return ticketItemRepository.save(ticketItem);
}

@RedissonLock(LockName = "티켓관리", identifier = "ticketItemId")
public TicketItem removeItemOptionGroup(Long ticketItemId, Long optionGroupId, Long eventId) {

TicketItem ticketItem = ticketItemAdaptor.queryTicketItem(ticketItemId);
OptionGroup optionGroup = optionGroupAdaptor.queryOptionGroup(optionGroupId);

// 해당 eventId에 속해 있는 티켓 아이템, 옵션그룹이 맞는지 확인
ticketItem.validateEventId(eventId);
optionGroup.validateEventId(eventId);

ticketItem.removeItemOptionGroup(optionGroup);

return ticketItemRepository.save(ticketItem);
}
}