From 224de69d3c7c77f8b945511e332a4729b285b56e Mon Sep 17 00:00:00 2001 From: swa07016 Date: Sat, 20 Jul 2024 13:52:38 +0900 Subject: [PATCH 1/2] =?UTF-8?q?feat:=20=EA=B2=BD=ED=92=88=20=EC=9D=91?= =?UTF-8?q?=EB=AA=A8=20API?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../prize/business/PrizeEventMapper.java | 13 ++++++++ .../prize/business/PrizeEventService.java | 23 ++++++++++++++ .../dailyphrase/prize/domain/PrizeEntry.java | 3 +- .../dailyphrase/prize/domain/PrizeTicket.java | 4 +++ .../PrizeTicketCustomRepository.java | 10 +++++++ .../PrizeTicketCustomRepositoryImpl.java | 30 +++++++++++++++++++ .../repository/PrizeTicketRepository.java | 6 +++- .../InsufficientTicketsException.java | 11 +++++++ .../prize/exception/PrizeEventErrorCode.java | 5 +++- .../exception/PrizeNotFoundException.java | 11 +++++++ .../implement/PrizeEntryCommandAdapter.java | 17 +++++++++++ .../prize/implement/PrizeQueryAdapter.java | 8 +++++ .../implement/PrizeTicketQueryAdapter.java | 9 ++++++ .../prize/presentation/PrizeEventApi.java | 15 ++++++++-- .../dto/PrizeEventRequestDTO.java | 5 ++++ .../dto/PrizeEventResponseDTO.java | 10 +++++++ .../prize/presentation/PrizeEventApiTest.java | 18 ++++------- 17 files changed, 180 insertions(+), 18 deletions(-) create mode 100644 src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketCustomRepository.java create mode 100644 src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketCustomRepositoryImpl.java create mode 100644 src/main/java/com/nexters/dailyphrase/prize/exception/InsufficientTicketsException.java create mode 100644 src/main/java/com/nexters/dailyphrase/prize/exception/PrizeNotFoundException.java create mode 100644 src/main/java/com/nexters/dailyphrase/prize/implement/PrizeEntryCommandAdapter.java diff --git a/src/main/java/com/nexters/dailyphrase/prize/business/PrizeEventMapper.java b/src/main/java/com/nexters/dailyphrase/prize/business/PrizeEventMapper.java index 6ef748b..eea85fd 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/business/PrizeEventMapper.java +++ b/src/main/java/com/nexters/dailyphrase/prize/business/PrizeEventMapper.java @@ -5,6 +5,7 @@ import com.nexters.dailyphrase.common.annotation.Mapper; import com.nexters.dailyphrase.common.enums.PrizeEntryStatus; import com.nexters.dailyphrase.common.enums.PrizeTicketStatus; +import com.nexters.dailyphrase.prize.domain.Prize; import com.nexters.dailyphrase.prize.domain.PrizeEntry; import com.nexters.dailyphrase.prize.domain.PrizeEvent; import com.nexters.dailyphrase.prize.domain.PrizeTicket; @@ -60,4 +61,16 @@ public PrizeEventResponseDTO.PrizeEventInfo toPrizeEventInfo(PrizeEvent prizeEve .status(prizeEvent.getStatus().getDescription()) .build(); } + + public PrizeEntry toPrizeEntry(Long memberId, Prize prize) { + return PrizeEntry.builder().memberId(memberId).prize(prize).build(); + } + + public PrizeEventResponseDTO.EnterPrizeEvent toEnterPrizeEvent(final PrizeEntry prizeEntry) { + return PrizeEventResponseDTO.EnterPrizeEvent.builder() + .memberId(prizeEntry.getMemberId()) + .prizeId(prizeEntry.getPrize().getId()) + .status(prizeEntry.getStatus()) + .build(); + } } diff --git a/src/main/java/com/nexters/dailyphrase/prize/business/PrizeEventService.java b/src/main/java/com/nexters/dailyphrase/prize/business/PrizeEventService.java index 247b1f9..29b3247 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/business/PrizeEventService.java +++ b/src/main/java/com/nexters/dailyphrase/prize/business/PrizeEventService.java @@ -16,9 +16,11 @@ import com.nexters.dailyphrase.common.jwt.JwtTokenService; import com.nexters.dailyphrase.common.jwt.dto.AccessTokenInfo; import com.nexters.dailyphrase.common.utils.MemberUtils; +import com.nexters.dailyphrase.prize.domain.Prize; import com.nexters.dailyphrase.prize.domain.PrizeEntry; import com.nexters.dailyphrase.prize.domain.PrizeEvent; import com.nexters.dailyphrase.prize.domain.PrizeTicket; +import com.nexters.dailyphrase.prize.exception.InsufficientTicketsException; import com.nexters.dailyphrase.prize.implement.*; import com.nexters.dailyphrase.prize.presentation.dto.PrizeEventRequestDTO; import com.nexters.dailyphrase.prize.presentation.dto.PrizeEventResponseDTO; @@ -35,6 +37,7 @@ public class PrizeEventService { private final PrizeTicketQueryAdapter prizeTicketQueryAdapter; private final PrizeTicketCommandAdapter prizeTicketCommandAdapter; private final PrizeEntryQueryAdapter prizeEntryQueryAdapter; + private final PrizeEntryCommandAdapter prizeEntryCommandAdapter; private final JwtTokenService jwtTokenService; private final PrizeEventMapper prizeEventMapper; private final MemberUtils memberUtils; @@ -101,4 +104,24 @@ public PrizeEventResponseDTO.EnterPhoneNumber enterPhoneNumber( prizeEntry -> prizeEntry.setPhoneNumber(request.getPhoneNumber())); return prizeEventMapper.toEnterPhoneNumber(memberId, prizeId, request.getPhoneNumber()); } + + @Transactional + public PrizeEventResponseDTO.EnterPrizeEvent enterPrize( + final PrizeEventRequestDTO.EnterPrizeEvent request) { + Long memberId = memberUtils.getCurrentMemberId(); + Prize prize = prizeQueryAdapter.findById(request.getPrizeId()); + int requiredTicketCount = prize.getRequiredTicketCount(); + + List prizeTicketList = + prizeTicketQueryAdapter.findPrizeTicketByMemberIdAndStatus( + memberId, PrizeTicketStatus.AVAILABLE, requiredTicketCount); + if (prizeTicketList.size() < requiredTicketCount) + throw InsufficientTicketsException.EXCEPTION; + + prizeTicketList.forEach(prizeTicket -> prizeTicket.setStatus(PrizeTicketStatus.USED)); + PrizeEntry savedPrizeEntry = + prizeEntryCommandAdapter.add(prizeEventMapper.toPrizeEntry(memberId, prize)); + + return prizeEventMapper.toEnterPrizeEvent(savedPrizeEntry); + } } diff --git a/src/main/java/com/nexters/dailyphrase/prize/domain/PrizeEntry.java b/src/main/java/com/nexters/dailyphrase/prize/domain/PrizeEntry.java index a16f364..2b82994 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/domain/PrizeEntry.java +++ b/src/main/java/com/nexters/dailyphrase/prize/domain/PrizeEntry.java @@ -26,8 +26,9 @@ public class PrizeEntry extends BaseDateTimeEntity { @JoinColumn(name = "prize_id", nullable = false) private Prize prize; + @Builder.Default @Enumerated(EnumType.STRING) - private PrizeEntryStatus status; + private PrizeEntryStatus status = PrizeEntryStatus.ENTERED; public void setPhoneNumber(final String phoneNumber) { this.phoneNumber = phoneNumber; diff --git a/src/main/java/com/nexters/dailyphrase/prize/domain/PrizeTicket.java b/src/main/java/com/nexters/dailyphrase/prize/domain/PrizeTicket.java index c9592b7..1196a79 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/domain/PrizeTicket.java +++ b/src/main/java/com/nexters/dailyphrase/prize/domain/PrizeTicket.java @@ -23,4 +23,8 @@ public class PrizeTicket extends BaseDateTimeEntity { @Enumerated(EnumType.STRING) private PrizeTicketStatus status; + + public void setStatus(PrizeTicketStatus status) { + this.status = status; + } } diff --git a/src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketCustomRepository.java b/src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketCustomRepository.java new file mode 100644 index 0000000..134d15d --- /dev/null +++ b/src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketCustomRepository.java @@ -0,0 +1,10 @@ +package com.nexters.dailyphrase.prize.domain.repository; + +import java.util.List; + +import com.nexters.dailyphrase.common.enums.PrizeTicketStatus; +import com.nexters.dailyphrase.prize.domain.PrizeTicket; + +public interface PrizeTicketCustomRepository { + List findByMemberIdAndStatus(Long memberId, PrizeTicketStatus status, int size); +} diff --git a/src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketCustomRepositoryImpl.java b/src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketCustomRepositoryImpl.java new file mode 100644 index 0000000..085362f --- /dev/null +++ b/src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketCustomRepositoryImpl.java @@ -0,0 +1,30 @@ +package com.nexters.dailyphrase.prize.domain.repository; + +import java.util.List; + +import org.springframework.stereotype.Repository; + +import com.nexters.dailyphrase.common.enums.PrizeTicketStatus; +import com.nexters.dailyphrase.prize.domain.PrizeTicket; +import com.nexters.dailyphrase.prize.domain.QPrizeTicket; +import com.querydsl.jpa.impl.JPAQueryFactory; + +import lombok.RequiredArgsConstructor; + +@Repository +@RequiredArgsConstructor +public class PrizeTicketCustomRepositoryImpl implements PrizeTicketCustomRepository { + private final JPAQueryFactory queryFactory; + + @Override + public List findByMemberIdAndStatus( + Long memberId, PrizeTicketStatus status, int size) { + QPrizeTicket qPrizeTicket = QPrizeTicket.prizeTicket; + + return queryFactory + .selectFrom(qPrizeTicket) + .where(qPrizeTicket.memberId.eq(memberId).and(qPrizeTicket.status.eq(status))) + .limit(size) + .fetch(); + } +} diff --git a/src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketRepository.java b/src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketRepository.java index 9c3c43c..77e53c2 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketRepository.java +++ b/src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketRepository.java @@ -4,9 +4,13 @@ import org.springframework.data.jpa.repository.JpaRepository; +import com.nexters.dailyphrase.common.enums.PrizeTicketStatus; import com.nexters.dailyphrase.prize.domain.PrizeTicket; -public interface PrizeTicketRepository extends JpaRepository { +public interface PrizeTicketRepository + extends JpaRepository, PrizeTicketCustomRepository { int countByMemberIdAndCreatedAtBetween( Long memberId, LocalDateTime startDateTime, LocalDateTime endDateTime); + + int countByMemberIdAndStatus(Long memberId, PrizeTicketStatus status); } diff --git a/src/main/java/com/nexters/dailyphrase/prize/exception/InsufficientTicketsException.java b/src/main/java/com/nexters/dailyphrase/prize/exception/InsufficientTicketsException.java new file mode 100644 index 0000000..5f35469 --- /dev/null +++ b/src/main/java/com/nexters/dailyphrase/prize/exception/InsufficientTicketsException.java @@ -0,0 +1,11 @@ +package com.nexters.dailyphrase.prize.exception; + +import com.nexters.dailyphrase.common.exception.BaseCodeException; + +public class InsufficientTicketsException extends BaseCodeException { + public static BaseCodeException EXCEPTION = new InsufficientTicketsException(); + + private InsufficientTicketsException() { + super(PrizeEventErrorCode.INSUFFICIENT_TICKETS); + } +} diff --git a/src/main/java/com/nexters/dailyphrase/prize/exception/PrizeEventErrorCode.java b/src/main/java/com/nexters/dailyphrase/prize/exception/PrizeEventErrorCode.java index 8756cee..50843b4 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/exception/PrizeEventErrorCode.java +++ b/src/main/java/com/nexters/dailyphrase/prize/exception/PrizeEventErrorCode.java @@ -1,5 +1,6 @@ package com.nexters.dailyphrase.prize.exception; +import static com.nexters.dailyphrase.common.consts.DailyPhraseStatic.BAD_REQUEST; import static com.nexters.dailyphrase.common.consts.DailyPhraseStatic.NOT_FOUND; import java.lang.reflect.Field; @@ -15,7 +16,9 @@ @Getter @AllArgsConstructor public enum PrizeEventErrorCode implements BaseErrorCode { - PRIZE_EVENT_NOT_FOUND(NOT_FOUND, "PHRASE_EVENT_404_1", "존재하지 않는 이벤트입니다."); + PRIZE_EVENT_NOT_FOUND(NOT_FOUND, "PRIZE_EVENT_404_1", "존재하지 않는 이벤트입니다."), + PRIZE_NOT_FOUND(NOT_FOUND, "PRIZE_404_1", "존재하지 않는 경품입니다."), + INSUFFICIENT_TICKETS(BAD_REQUEST, "PRIZE_TICKET_400_1", "티켓이 부족합니다."); private final Integer status; private final String code; diff --git a/src/main/java/com/nexters/dailyphrase/prize/exception/PrizeNotFoundException.java b/src/main/java/com/nexters/dailyphrase/prize/exception/PrizeNotFoundException.java new file mode 100644 index 0000000..9cba104 --- /dev/null +++ b/src/main/java/com/nexters/dailyphrase/prize/exception/PrizeNotFoundException.java @@ -0,0 +1,11 @@ +package com.nexters.dailyphrase.prize.exception; + +import com.nexters.dailyphrase.common.exception.BaseCodeException; + +public class PrizeNotFoundException extends BaseCodeException { + public static BaseCodeException EXCEPTION = new PrizeNotFoundException(); + + private PrizeNotFoundException() { + super(PrizeEventErrorCode.PRIZE_NOT_FOUND); + } +} diff --git a/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeEntryCommandAdapter.java b/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeEntryCommandAdapter.java new file mode 100644 index 0000000..57c4176 --- /dev/null +++ b/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeEntryCommandAdapter.java @@ -0,0 +1,17 @@ +package com.nexters.dailyphrase.prize.implement; + +import com.nexters.dailyphrase.common.annotation.Adapter; +import com.nexters.dailyphrase.prize.domain.PrizeEntry; +import com.nexters.dailyphrase.prize.domain.repository.PrizeEntryRepository; + +import lombok.RequiredArgsConstructor; + +@Adapter +@RequiredArgsConstructor +public class PrizeEntryCommandAdapter { + private final PrizeEntryRepository prizeEntryRepository; + + public PrizeEntry add(PrizeEntry prizeEntry) { + return prizeEntryRepository.save(prizeEntry); + } +} diff --git a/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeQueryAdapter.java b/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeQueryAdapter.java index 368644d..7bd3a05 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeQueryAdapter.java +++ b/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeQueryAdapter.java @@ -1,7 +1,9 @@ package com.nexters.dailyphrase.prize.implement; import com.nexters.dailyphrase.common.annotation.Adapter; +import com.nexters.dailyphrase.prize.domain.Prize; import com.nexters.dailyphrase.prize.domain.repository.PrizeRepository; +import com.nexters.dailyphrase.prize.exception.PrizeNotFoundException; import com.nexters.dailyphrase.prize.presentation.dto.PrizeEventResponseDTO; import lombok.RequiredArgsConstructor; @@ -14,4 +16,10 @@ public class PrizeQueryAdapter { public PrizeEventResponseDTO.PrizeList findPrizeListDTO(final Long eventId) { return prizeRepository.findPrizeListDTO(eventId); } + + public Prize findById(Long prizeId) { + return prizeRepository + .findById(prizeId) + .orElseThrow(() -> PrizeNotFoundException.EXCEPTION); + } } diff --git a/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeTicketQueryAdapter.java b/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeTicketQueryAdapter.java index 3c6f69f..74fc154 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeTicketQueryAdapter.java +++ b/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeTicketQueryAdapter.java @@ -2,8 +2,11 @@ import java.time.LocalDate; import java.time.LocalDateTime; +import java.util.List; import com.nexters.dailyphrase.common.annotation.Adapter; +import com.nexters.dailyphrase.common.enums.PrizeTicketStatus; +import com.nexters.dailyphrase.prize.domain.PrizeTicket; import com.nexters.dailyphrase.prize.domain.repository.PrizeTicketRepository; import lombok.RequiredArgsConstructor; @@ -21,4 +24,10 @@ public Integer countPrizeTicketByMemberIdAndLocalDate( return prizeTicketRepository.countByMemberIdAndCreatedAtBetween( memberId, startOfDay, endOfDay); } + + public List findPrizeTicketByMemberIdAndStatus( + final Long memberId, final PrizeTicketStatus status, final int count) { + return prizeTicketRepository.findByMemberIdAndStatus( + memberId, status, count); + } } diff --git a/src/main/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApi.java b/src/main/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApi.java index e90604e..d2934fd 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApi.java +++ b/src/main/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApi.java @@ -46,7 +46,16 @@ public CommonResponse getPrizeList() { } @Operation( - summary = "07-03 Event 🎁 경품 응모 이벤트의 경품 응모 결과 확인 Made By 성훈", + summary = "07-03 Event 🎁 경품 응모 이벤트의 경품 응모 Made By 성훈", + description = "경품 응모 이벤트의 경품 응모 API입니다.") + @PostMapping("/enter") + public CommonResponse enterPrize( + @RequestBody PrizeEventRequestDTO.EnterPrizeEvent request) { + return CommonResponse.onSuccess(prizeEventService.enterPrize(request)); + } + + @Operation( + summary = "07-04 Event 🎁 경품 응모 이벤트의 경품 응모 결과 확인 Made By 성훈", description = "경품 응모 이벤트의 경품 응모 결과 확인 API입니다.") @GetMapping("/prizes/{prizeId}/entry-result") public CommonResponse getPrizeEntryResult( @@ -55,7 +64,7 @@ public CommonResponse getPrizeEntryResul } @Operation( - summary = "07-04 Event 🎁 경품 응모 이벤트의 당첨자 연락처 입력 Made By 성훈", + summary = "07-05 Event 🎁 경품 응모 이벤트의 당첨자 연락처 입력 Made By 성훈", description = "경품 응모 이벤트의 당첨자 연락처 입력 API입니다.") @PostMapping("/prizes/{prizeId}/phone-number") public CommonResponse enterPhoneNumber( @@ -65,7 +74,7 @@ public CommonResponse enterPhoneNumber( } @Operation( - summary = "07-05 Event 🎁 경품 응모 이벤트의 응모권 발급용 카카오 콜백 Made By 성훈", + summary = "07-06 Event 🎁 경품 응모 이벤트의 응모권 발급용 카카오 콜백 Made By 성훈", description = "경품 응모 이벤트의 응모권 발급용 카카오 콜백입니다. (직접 호출 X)") @PostMapping("/kakaolink/callback") public ResponseEntity handleKakaoLinkCallback( diff --git a/src/main/java/com/nexters/dailyphrase/prize/presentation/dto/PrizeEventRequestDTO.java b/src/main/java/com/nexters/dailyphrase/prize/presentation/dto/PrizeEventRequestDTO.java index 65f3ab2..2647128 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/presentation/dto/PrizeEventRequestDTO.java +++ b/src/main/java/com/nexters/dailyphrase/prize/presentation/dto/PrizeEventRequestDTO.java @@ -11,4 +11,9 @@ public class PrizeEventRequestDTO { public static class EnterPhoneNumber { private String phoneNumber; } + + @Getter + public static class EnterPrizeEvent { + private Long prizeId; + } } diff --git a/src/main/java/com/nexters/dailyphrase/prize/presentation/dto/PrizeEventResponseDTO.java b/src/main/java/com/nexters/dailyphrase/prize/presentation/dto/PrizeEventResponseDTO.java index c87b740..cb64c6e 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/presentation/dto/PrizeEventResponseDTO.java +++ b/src/main/java/com/nexters/dailyphrase/prize/presentation/dto/PrizeEventResponseDTO.java @@ -87,4 +87,14 @@ public static class PrizeEventInfo { private LocalDateTime eventWinnerAnnouncementDateTime; private String status; } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class EnterPrizeEvent { + private Long prizeId; + private Long memberId; + private PrizeEntryStatus status; + } } diff --git a/src/test/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApiTest.java b/src/test/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApiTest.java index 8068726..5141a2f 100644 --- a/src/test/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApiTest.java +++ b/src/test/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApiTest.java @@ -6,7 +6,6 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.time.LocalDateTime; -import java.util.Collections; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -18,10 +17,7 @@ import org.springframework.boot.test.autoconfigure.web.servlet.AutoConfigureMockMvc; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.http.MediaType; -import org.springframework.security.authentication.UsernamePasswordAuthenticationToken; -import org.springframework.security.core.Authentication; -import org.springframework.security.core.context.SecurityContext; -import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.web.servlet.MockMvc; import org.springframework.test.web.servlet.request.MockHttpServletRequestBuilder; @@ -54,7 +50,7 @@ class PrizeEventApiTest { private MockMvc mockMvc; - Long eventId = 1L; + Long eventId = 2L; int prizeCount = 5; private Member testMember; private List prizes; @@ -63,12 +59,6 @@ class PrizeEventApiTest { public void setup() { mockMvc = MockMvcBuilders.webAppContextSetup(webApplicationContext).build(); - SecurityContext context = SecurityContextHolder.createEmptyContext(); - Authentication authentication = - new UsernamePasswordAuthenticationToken("1", null, Collections.emptyList()); - context.setAuthentication(authentication); - SecurityContextHolder.setContext(context); - PrizeEvent prizeEvent = PrizeEvent.builder() .id(eventId) @@ -88,6 +78,7 @@ public void setup() { .mapToObj( i -> Prize.builder() + .id((long) i) .event(prizeEvent) .name("Prize " + i) .shortName("Short Prize" + i) @@ -103,6 +94,7 @@ public void setup() { @Test @DisplayName("경품 목록 조회 기능 테스트입니다.") + @WithMockUser(username = "1") void 경품_목록_조회_테스트() throws Exception { // 멤버가 일부 경품에 응모한 내역 생성 PrizeEntry prizeEntry = @@ -133,6 +125,7 @@ public void setup() { @Test @DisplayName("경품 응모 결과 확인 기능 테스트입니다. - 당첨") + @WithMockUser(username = "1") void 경품_응모_결과_확인_테스트_당첨() throws Exception { // given Prize prize = prizes.get(0); @@ -159,6 +152,7 @@ public void setup() { @Test @DisplayName("경품 응모 결과 확인 기능 테스트입니다. - 미당첨") + @WithMockUser(username = "1") void 경품_응모_결과_확인_테스트_미당첨() throws Exception { // given Prize prize = prizes.get(0); From 452c97d5e67a751b2f8451dee2274465c1712b85 Mon Sep 17 00:00:00 2001 From: swa07016 Date: Sun, 21 Jul 2024 16:15:34 +0900 Subject: [PATCH 2/2] =?UTF-8?q?feat:=20=EA=B2=BD=ED=92=88=20=EC=9D=91?= =?UTF-8?q?=EB=AA=A8=20API=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../implement/PrizeTicketQueryAdapter.java | 3 +- .../prize/presentation/PrizeEventApiTest.java | 74 ++++++++++++++++++- 2 files changed, 74 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeTicketQueryAdapter.java b/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeTicketQueryAdapter.java index 74fc154..8674984 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeTicketQueryAdapter.java +++ b/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeTicketQueryAdapter.java @@ -27,7 +27,6 @@ public Integer countPrizeTicketByMemberIdAndLocalDate( public List findPrizeTicketByMemberIdAndStatus( final Long memberId, final PrizeTicketStatus status, final int count) { - return prizeTicketRepository.findByMemberIdAndStatus( - memberId, status, count); + return prizeTicketRepository.findByMemberIdAndStatus(memberId, status, count); } } diff --git a/src/test/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApiTest.java b/src/test/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApiTest.java index 5141a2f..c461d55 100644 --- a/src/test/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApiTest.java +++ b/src/test/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApiTest.java @@ -6,6 +6,7 @@ import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status; import java.time.LocalDateTime; +import java.util.ArrayList; import java.util.List; import java.util.stream.Collectors; import java.util.stream.IntStream; @@ -26,16 +27,21 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.context.WebApplicationContext; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.fasterxml.jackson.databind.node.ObjectNode; import com.nexters.dailyphrase.common.enums.PrizeEntryStatus; import com.nexters.dailyphrase.common.enums.PrizeEventStatus; +import com.nexters.dailyphrase.common.enums.PrizeTicketStatus; import com.nexters.dailyphrase.member.domain.Member; import com.nexters.dailyphrase.member.domain.repository.MemberRepository; import com.nexters.dailyphrase.prize.domain.Prize; import com.nexters.dailyphrase.prize.domain.PrizeEntry; import com.nexters.dailyphrase.prize.domain.PrizeEvent; +import com.nexters.dailyphrase.prize.domain.PrizeTicket; import com.nexters.dailyphrase.prize.domain.repository.PrizeEntryRepository; import com.nexters.dailyphrase.prize.domain.repository.PrizeEventRepository; import com.nexters.dailyphrase.prize.domain.repository.PrizeRepository; +import com.nexters.dailyphrase.prize.domain.repository.PrizeTicketRepository; @SpringBootTest @AutoConfigureMockMvc @@ -54,6 +60,7 @@ class PrizeEventApiTest { int prizeCount = 5; private Member testMember; private List prizes; + @Autowired private PrizeTicketRepository prizeTicketRepository; @BeforeEach public void setup() { @@ -86,7 +93,7 @@ public void setup() { .bannerImageUrl("") .welcomeImageUrl("") .manufacturer("manufacturer" + i) - .requiredTicketCount(10 * i) + .requiredTicketCount(5) .build()) .collect(Collectors.toList()); prizeRepository.saveAll(prizes); // 경품들 저장 @@ -176,4 +183,69 @@ public void setup() { .andExpect(status().isOk()) .andExpect(jsonPath("$.result.status").value("MISSED")); } + + @Test + @DisplayName("경품 응모 기능 테스트입니다. - 응모권 부족 예외") + @WithMockUser(username = "1") + void 경품_응모_테스트_응모권_부족() throws Exception { + // given + Prize prize = prizes.get(0); + Long prizeId = prize.getId(); + + // when & then + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode jsonNode = objectMapper.createObjectNode(); + jsonNode.put("prizeId", prizeId); + String jsonRequest = objectMapper.writeValueAsString(jsonNode); + + MockHttpServletRequestBuilder request = + MockMvcRequestBuilders.post("/api/v1/events/enter") + .contentType(MediaType.APPLICATION_JSON) + .content(jsonRequest); + + mockMvc.perform(request) + .andDo(print()) + .andExpect(status().isBadRequest()) + .andExpect(jsonPath("$.code").value("PRIZE_TICKET_400_1")); + } + + @Test + @DisplayName("경품 응모 기능 테스트입니다. - 응모 성공") + @WithMockUser(username = "1") + void 경품_응모_테스트_응모_성공() throws Exception { + // given + Prize prize = prizes.get(0); + Long prizeId = prize.getId(); + + List prizeTicketList = new ArrayList<>(); + for (int i = 1; i <= 5; i++) { + PrizeTicket prizeTicket = + PrizeTicket.builder() + .id(Long.valueOf(i)) + .eventId(1L) + .status(PrizeTicketStatus.AVAILABLE) + .memberId(1L) + .build(); + prizeTicketList.add(prizeTicket); + } + prizeTicketRepository.saveAll(prizeTicketList); + + // when & then + ObjectMapper objectMapper = new ObjectMapper(); + ObjectNode jsonNode = objectMapper.createObjectNode(); + jsonNode.put("prizeId", prizeId); + String jsonRequest = objectMapper.writeValueAsString(jsonNode); + + MockHttpServletRequestBuilder request = + MockMvcRequestBuilders.post("/api/v1/events/enter") + .contentType(MediaType.APPLICATION_JSON) + .content(jsonRequest); + + mockMvc.perform(request) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.result.prizeId").value(prizeId)) + .andExpect(jsonPath("$.result.memberId").value(1L)) + .andExpect(jsonPath("$.result.status").value(PrizeEntryStatus.ENTERED.toString())); + } }