-
Notifications
You must be signed in to change notification settings - Fork 8
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
- Loading branch information
1 parent
29f42c5
commit c5ec08d
Showing
40 changed files
with
1,429 additions
and
0 deletions.
There are no files selected for viewing
23 changes: 23 additions & 0 deletions
23
.../main/java/com/festago/ticket/application/command/TicketMaxReserveCountChangeService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package com.festago.ticket.application.command; | ||
|
||
import com.festago.common.exception.ErrorCode; | ||
import com.festago.common.exception.NotFoundException; | ||
import com.festago.ticket.domain.NewTicket; | ||
import com.festago.ticket.repository.NewTicketRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Service | ||
@Transactional | ||
@RequiredArgsConstructor | ||
public class TicketMaxReserveCountChangeService { | ||
|
||
private final NewTicketRepository ticketRepository; | ||
|
||
public void changeMaxReserveAmount(Long ticketId, int maxReserveAmount) { | ||
NewTicket ticket = ticketRepository.findById(ticketId) | ||
.orElseThrow(() -> new NotFoundException(ErrorCode.TICKET_NOT_FOUND)); | ||
ticket.changeMaxReserveAmount(maxReserveAmount); | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
...main/java/com/festago/ticket/domain/validator/stage/ExistsTicketStageDeleteValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package com.festago.ticket.domain.validator.stage; | ||
|
||
import com.festago.common.exception.BadRequestException; | ||
import com.festago.common.exception.ErrorCode; | ||
import com.festago.stage.domain.Stage; | ||
import com.festago.stage.domain.validator.StageDeleteValidator; | ||
import com.festago.ticket.repository.StageTicketRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class ExistsTicketStageDeleteValidator implements StageDeleteValidator { | ||
|
||
private final StageTicketRepository stageTicketRepository; | ||
|
||
@Override | ||
public void validate(Stage stage) { | ||
if (stageTicketRepository.existsByStageId(stage.getId())) { | ||
throw new BadRequestException(ErrorCode.STAGE_DELETE_CONSTRAINT_EXISTS_TICKET); | ||
} | ||
} | ||
} |
23 changes: 23 additions & 0 deletions
23
...main/java/com/festago/ticket/domain/validator/stage/ExistsTicketStageUpdateValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,23 @@ | ||
package com.festago.ticket.domain.validator.stage; | ||
|
||
import com.festago.common.exception.BadRequestException; | ||
import com.festago.common.exception.ErrorCode; | ||
import com.festago.stage.domain.Stage; | ||
import com.festago.stage.domain.validator.StageUpdateValidator; | ||
import com.festago.ticket.repository.StageTicketRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class ExistsTicketStageUpdateValidator implements StageUpdateValidator { | ||
|
||
private final StageTicketRepository stageTicketRepository; | ||
|
||
@Override | ||
public void validate(Stage stage) { | ||
if (stageTicketRepository.existsByStageId(stage.getId())) { | ||
throw new BadRequestException(ErrorCode.STAGE_UPDATE_CONSTRAINT_EXISTS_TICKET); | ||
} | ||
} | ||
} |
24 changes: 24 additions & 0 deletions
24
backend/src/main/java/com/festago/ticket/repository/NewTicketDao.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,24 @@ | ||
package com.festago.ticket.repository; | ||
|
||
import com.festago.common.exception.ErrorCode; | ||
import com.festago.common.exception.NotFoundException; | ||
import com.festago.ticket.domain.NewTicket; | ||
import com.festago.ticket.domain.NewTicketType; | ||
import java.util.Optional; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Repository; | ||
|
||
// TODO NewTicket -> Ticket 이름 변경할 것 | ||
@Repository | ||
@RequiredArgsConstructor | ||
public class NewTicketDao { | ||
|
||
private final StageTicketRepository stageTicketRepository; | ||
|
||
public NewTicket findByIdWithTicketTypeAndFetch(Long id, NewTicketType ticketType) { | ||
Optional<? extends NewTicket> ticket = switch (ticketType) { | ||
case STAGE -> stageTicketRepository.findByIdWithFetch(id); | ||
}; | ||
return ticket.orElseThrow(() -> new NotFoundException(ErrorCode.TICKET_NOT_FOUND)); | ||
} | ||
} |
13 changes: 13 additions & 0 deletions
13
backend/src/main/java/com/festago/ticket/repository/NewTicketRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,13 @@ | ||
package com.festago.ticket.repository; | ||
|
||
import com.festago.ticket.domain.NewTicket; | ||
import java.util.Optional; | ||
import org.springframework.data.repository.Repository; | ||
|
||
// TODO NewTicket -> Ticket 이름 변경할 것 | ||
public interface NewTicketRepository extends Repository<NewTicket, Long> { | ||
|
||
NewTicket save(NewTicket ticket); | ||
|
||
Optional<NewTicket> findById(Long id); | ||
} |
32 changes: 32 additions & 0 deletions
32
backend/src/main/java/com/festago/ticketing/application/TicketQuantityEventListener.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.festago.ticketing.application; | ||
|
||
import com.festago.ticket.domain.NewTicket; | ||
import com.festago.ticket.dto.event.TicketCreatedEvent; | ||
import com.festago.ticket.dto.event.TicketDeletedEvent; | ||
import com.festago.ticketing.application.command.TicketQuantityUpdateService; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.scheduling.annotation.Async; | ||
import org.springframework.stereotype.Component; | ||
import org.springframework.transaction.event.TransactionPhase; | ||
import org.springframework.transaction.event.TransactionalEventListener; | ||
|
||
@Component | ||
@RequiredArgsConstructor | ||
public class TicketQuantityEventListener { | ||
|
||
private final TicketQuantityUpdateService ticketQuantityUpdateService; | ||
|
||
@Async | ||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) | ||
public void ticketCreatedEventHandler(TicketCreatedEvent event) { | ||
NewTicket ticket = event.ticket(); | ||
ticketQuantityUpdateService.putOrDeleteTicketQuantity(ticket); | ||
} | ||
|
||
@Async | ||
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) | ||
public void ticketDeletedEventHandler(TicketDeletedEvent event) { | ||
NewTicket ticket = event.ticket(); | ||
ticketQuantityUpdateService.putOrDeleteTicketQuantity(ticket); | ||
} | ||
} |
51 changes: 51 additions & 0 deletions
51
...end/src/main/java/com/festago/ticketing/application/command/QuantityTicketingService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,51 @@ | ||
package com.festago.ticketing.application.command; | ||
|
||
import com.festago.common.exception.BadRequestException; | ||
import com.festago.common.exception.ErrorCode; | ||
import com.festago.common.exception.NotFoundException; | ||
import com.festago.ticketing.domain.Booker; | ||
import com.festago.ticketing.domain.TicketQuantity; | ||
import com.festago.ticketing.domain.TicketingRateLimiter; | ||
import com.festago.ticketing.dto.TicketingResult; | ||
import com.festago.ticketing.dto.command.TicketingCommand; | ||
import com.festago.ticketing.repository.TicketQuantityRepository; | ||
import java.util.concurrent.TimeUnit; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
//@Transactional 명시적으로 Transactional 사용하지 않음 | ||
public class QuantityTicketingService { | ||
|
||
private final TicketQuantityRepository ticketQuantityRepository; | ||
private final TicketingCommandService ticketingCommandService; | ||
private final TicketingRateLimiter ticketingRateLimiter; | ||
|
||
public TicketingResult ticketing(TicketingCommand command) { | ||
TicketQuantity ticketQuantity = getTicketQuantity(command.ticketId()); | ||
validateFrequentTicketing(command.booker()); | ||
try { | ||
ticketQuantity.decreaseQuantity(); | ||
return ticketingCommandService.reserveTicket(command); | ||
} catch (Exception e) { | ||
ticketQuantity.increaseQuantity(); | ||
throw e; | ||
} | ||
} | ||
|
||
private TicketQuantity getTicketQuantity(Long ticketId) { | ||
TicketQuantity ticketQuantity = ticketQuantityRepository.findByTicketId(ticketId) | ||
.orElseThrow(() -> new NotFoundException(ErrorCode.TICKET_NOT_FOUND)); | ||
if (ticketQuantity.isSoldOut()) { | ||
throw new BadRequestException(ErrorCode.TICKET_SOLD_OUT); | ||
} | ||
return ticketQuantity; | ||
} | ||
|
||
private void validateFrequentTicketing(Booker booker) { | ||
if (ticketingRateLimiter.isFrequentTicketing(booker, 5, TimeUnit.SECONDS)) { | ||
throw new BadRequestException(ErrorCode.TOO_FREQUENT_REQUESTS); | ||
} | ||
} | ||
} |
21 changes: 21 additions & 0 deletions
21
.../src/main/java/com/festago/ticketing/application/command/TicketQuantityUpdateService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,21 @@ | ||
package com.festago.ticketing.application.command; | ||
|
||
import com.festago.ticket.domain.NewTicket; | ||
import com.festago.ticketing.repository.TicketQuantityRepository; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
|
||
@Service | ||
@RequiredArgsConstructor | ||
public class TicketQuantityUpdateService { | ||
|
||
private final TicketQuantityRepository ticketQuantityRepository; | ||
|
||
public void putOrDeleteTicketQuantity(NewTicket ticket) { | ||
if (ticket.isEmptyAmount()) { | ||
ticketQuantityRepository.delete(ticket); | ||
} else { | ||
ticketQuantityRepository.put(ticket); | ||
} | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
backend/src/main/java/com/festago/ticketing/application/command/TicketingCommandService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.festago.ticketing.application.command; | ||
|
||
import com.festago.ticketing.dto.TicketingResult; | ||
import com.festago.ticketing.dto.command.TicketingCommand; | ||
|
||
public interface TicketingCommandService { | ||
|
||
TicketingResult reserveTicket(TicketingCommand command); | ||
} |
54 changes: 54 additions & 0 deletions
54
.../src/main/java/com/festago/ticketing/application/command/TicketingCommandServiceImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,54 @@ | ||
package com.festago.ticketing.application.command; | ||
|
||
import com.festago.common.exception.BadRequestException; | ||
import com.festago.common.exception.ErrorCode; | ||
import com.festago.ticket.domain.NewTicket; | ||
import com.festago.ticket.domain.NewTicketType; | ||
import com.festago.ticket.repository.NewTicketDao; | ||
import com.festago.ticketing.domain.Booker; | ||
import com.festago.ticketing.domain.ReserveTicket; | ||
import com.festago.ticketing.domain.TicketingSequenceGenerator; | ||
import com.festago.ticketing.domain.validator.TicketingValidator; | ||
import com.festago.ticketing.dto.TicketingResult; | ||
import com.festago.ticketing.dto.command.TicketingCommand; | ||
import com.festago.ticketing.repository.ReserveTicketRepository; | ||
import java.time.Clock; | ||
import java.time.LocalDateTime; | ||
import java.util.List; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Service | ||
@Transactional | ||
@RequiredArgsConstructor | ||
public class TicketingCommandServiceImpl implements TicketingCommandService { | ||
|
||
private final NewTicketDao ticketDao; | ||
private final ReserveTicketRepository reserveTicketRepository; | ||
private final TicketingSequenceGenerator sequenceGenerator; | ||
private final List<TicketingValidator> validators; | ||
private final Clock clock; | ||
|
||
@Override | ||
public TicketingResult reserveTicket(TicketingCommand command) { | ||
Long ticketId = command.ticketId(); | ||
NewTicketType ticketType = command.ticketType(); | ||
NewTicket ticket = ticketDao.findByIdWithTicketTypeAndFetch(ticketId, ticketType); | ||
Booker booker = command.booker(); | ||
ticket.validateReserve(booker, LocalDateTime.now(clock)); | ||
validators.forEach(validator -> validator.validate(ticket, booker)); | ||
validate(ticket, booker); | ||
int sequence = sequenceGenerator.generate(ticketId); | ||
ReserveTicket reserveTicket = ticket.reserve(booker, sequence); | ||
reserveTicketRepository.save(ticket.reserve(booker, sequence)); | ||
return new TicketingResult(reserveTicket.getTicketId()); | ||
} | ||
|
||
private void validate(NewTicket ticket, Booker booker) { | ||
long reserveCount = reserveTicketRepository.countByMemberIdAndTicketId(booker.getMemberId(), ticket.getId()); | ||
if (reserveCount >= ticket.getMaxReserveAmount()) { | ||
throw new BadRequestException(ErrorCode.RESERVE_TICKET_OVER_AMOUNT); | ||
} | ||
} | ||
} |
36 changes: 36 additions & 0 deletions
36
backend/src/main/java/com/festago/ticketing/domain/TicketQuantity.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,36 @@ | ||
package com.festago.ticketing.domain; | ||
|
||
import com.festago.common.exception.BadRequestException; | ||
|
||
/** | ||
* 티켓의 재고를 관리하는 도메인 <br/> 해당 도메인을 구현하는 구현체는 반드시 원자적인 연산을 사용해야 한다. <br/> | ||
*/ | ||
public interface TicketQuantity { | ||
|
||
/** | ||
* 티켓의 매진 여부를 반환한다. | ||
* | ||
* @return 티켓이 매진이면 true, 매진이 아니면 false | ||
*/ | ||
boolean isSoldOut(); | ||
|
||
/** | ||
* 티켓의 재고를 하나 감소시킨다. <br/> 해당 메서드의 연산은 atomic 해야 한다. <br/> 감소 시킨 뒤 값이 음수인 경우에는 매진이 된 상태에 요청이 들어온 것이므로, 예외를 던져야 한다. | ||
* <br/> | ||
* | ||
* @throws BadRequestException 감소 시킨 뒤 값이 음수이면 | ||
*/ | ||
void decreaseQuantity() throws BadRequestException; | ||
|
||
/** | ||
* 티켓의 재고를 하나 증가시킨다. <br/> 해당 메서드의 연산은 atomic 해야 한다. <br/> | ||
*/ | ||
void increaseQuantity(); | ||
|
||
/** | ||
* 티켓의 남은 재고를 반환한다. <br/> | ||
* | ||
* @return 티켓의 남은 재고 | ||
*/ | ||
int getQuantity(); | ||
} |
8 changes: 8 additions & 0 deletions
8
backend/src/main/java/com/festago/ticketing/domain/TicketingRateLimiter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,8 @@ | ||
package com.festago.ticketing.domain; | ||
|
||
import java.util.concurrent.TimeUnit; | ||
|
||
public interface TicketingRateLimiter { | ||
|
||
boolean isFrequentTicketing(Booker booker, long timeout, TimeUnit unit); | ||
} |
6 changes: 6 additions & 0 deletions
6
backend/src/main/java/com/festago/ticketing/domain/TicketingSequenceGenerator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,6 @@ | ||
package com.festago.ticketing.domain; | ||
|
||
public interface TicketingSequenceGenerator { | ||
|
||
int generate(Long ticketId); | ||
} |
17 changes: 17 additions & 0 deletions
17
...d/src/main/java/com/festago/ticketing/domain/validator/StageReservedTicketIdResolver.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,17 @@ | ||
package com.festago.ticketing.domain.validator; | ||
|
||
import jakarta.annotation.Nullable; | ||
|
||
public interface StageReservedTicketIdResolver { | ||
|
||
/** | ||
* 사용자가 StageTicket에 대해 예매한 티켓의 식별자를 반환합니다. <br/> 예매한 이력이 없으면 null이 반환되고, 예매한 이력이 있으면 사용자가 예매했던 티켓의 식별자를 반환합니다. | ||
* <br/> | ||
* | ||
* @param memberId 티켓을 예매한 사용자의 식별자 | ||
* @param stageId StageTicket의 Stage 식별자 | ||
* @return 예매한 이력이 없으면 null, 예매한 이력이 있으면 티켓 식별자 반환 | ||
*/ | ||
@Nullable | ||
Long resolve(Long memberId, Long stageId); | ||
} |
34 changes: 34 additions & 0 deletions
34
.../java/com/festago/ticketing/domain/validator/StageSingleTicketTypeTicketingValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,34 @@ | ||
package com.festago.ticketing.domain.validator; | ||
|
||
import com.festago.common.exception.BadRequestException; | ||
import com.festago.common.exception.ErrorCode; | ||
import com.festago.ticket.domain.NewTicket; | ||
import com.festago.ticket.domain.StageTicket; | ||
import com.festago.ticketing.domain.Booker; | ||
import java.util.Objects; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.stereotype.Component; | ||
|
||
/** | ||
* 공연의 티켓에 대해 하나의 유형에 대해서만 예매가 가능하도록 검증하는 클래스 <br/> ex) 사용자가 하나의 공연에 대해 학생 전용, 외부인 전용을 모두 예매하는 상황을 방지 <br/> | ||
*/ | ||
@Component | ||
@RequiredArgsConstructor | ||
public class StageSingleTicketTypeTicketingValidator implements TicketingValidator { | ||
|
||
private final StageReservedTicketIdResolver stageReservedTicketIdResolver; | ||
|
||
@Override | ||
public void validate(NewTicket ticket, Booker booker) { | ||
if (!(ticket instanceof StageTicket stageTicket)) { | ||
return; | ||
} | ||
Long memberId = booker.getMemberId(); | ||
Long stageId = stageTicket.getStage().getId(); | ||
Long ticketId = stageReservedTicketIdResolver.resolve(memberId, stageId); | ||
if (ticketId == null || Objects.equals(ticketId, ticket.getId())) { | ||
return; | ||
} | ||
throw new BadRequestException(ErrorCode.ONLY_STAGE_TICKETING_SINGLE_TYPE); | ||
} | ||
} |
9 changes: 9 additions & 0 deletions
9
backend/src/main/java/com/festago/ticketing/domain/validator/TicketingValidator.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,9 @@ | ||
package com.festago.ticketing.domain.validator; | ||
|
||
import com.festago.ticket.domain.NewTicket; | ||
import com.festago.ticketing.domain.Booker; | ||
|
||
public interface TicketingValidator { | ||
|
||
void validate(NewTicket ticket, Booker booker); | ||
} |
Oops, something went wrong.