diff --git a/src/main/generated/com/nexters/dailyphrase/prize/domain/QPrizeTicketPopupCheck.java b/src/main/generated/com/nexters/dailyphrase/prize/domain/QPrizeTicketPopupCheck.java new file mode 100644 index 0000000..ef71e1a --- /dev/null +++ b/src/main/generated/com/nexters/dailyphrase/prize/domain/QPrizeTicketPopupCheck.java @@ -0,0 +1,41 @@ +package com.nexters.dailyphrase.prize.domain; + +import static com.querydsl.core.types.PathMetadataFactory.*; + +import com.querydsl.core.types.dsl.*; + +import com.querydsl.core.types.PathMetadata; +import javax.annotation.processing.Generated; +import com.querydsl.core.types.Path; + + +/** + * QPrizeTicketPopupCheck is a Querydsl query type for PrizeTicketPopupCheck + */ +@Generated("com.querydsl.codegen.DefaultEntitySerializer") +public class QPrizeTicketPopupCheck extends EntityPathBase { + + private static final long serialVersionUID = -2068721456L; + + public static final QPrizeTicketPopupCheck prizeTicketPopupCheck = new QPrizeTicketPopupCheck("prizeTicketPopupCheck"); + + public final NumberPath id = createNumber("id", Long.class); + + public final NumberPath memberId = createNumber("memberId", Long.class); + + public final EnumPath type = createEnum("type", com.nexters.dailyphrase.common.enums.PrizeTicketPopupType.class); + + public QPrizeTicketPopupCheck(String variable) { + super(PrizeTicketPopupCheck.class, forVariable(variable)); + } + + public QPrizeTicketPopupCheck(Path path) { + super(path.getType(), path.getMetadata()); + } + + public QPrizeTicketPopupCheck(PathMetadata metadata) { + super(PrizeTicketPopupCheck.class, metadata); + } + +} + diff --git a/src/main/java/com/nexters/dailyphrase/common/enums/PrizeTicketPopupType.java b/src/main/java/com/nexters/dailyphrase/common/enums/PrizeTicketPopupType.java new file mode 100644 index 0000000..f5430eb --- /dev/null +++ b/src/main/java/com/nexters/dailyphrase/common/enums/PrizeTicketPopupType.java @@ -0,0 +1,5 @@ +package com.nexters.dailyphrase.common.enums; + +public enum PrizeTicketPopupType { + GET_BY_SIGNUP +} 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 6c2205e..709c33a 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/business/PrizeEventMapper.java +++ b/src/main/java/com/nexters/dailyphrase/prize/business/PrizeEventMapper.java @@ -6,6 +6,7 @@ import com.nexters.dailyphrase.common.annotation.Mapper; import com.nexters.dailyphrase.common.consts.DailyPhraseStatic; import com.nexters.dailyphrase.common.enums.PrizeEntryStatus; +import com.nexters.dailyphrase.common.enums.PrizeTicketPopupType; import com.nexters.dailyphrase.common.enums.PrizeTicketSource; import com.nexters.dailyphrase.common.enums.PrizeTicketStatus; import com.nexters.dailyphrase.prize.domain.*; @@ -103,4 +104,15 @@ public PrizeEventResponseDTO.CheckPrizeEntryResult toCheckPrizeEntryResult( .memberId(prizeEntryCheck.getMemberId()) .build(); } + + public PrizeTicketPopupCheck toPrizeTicketPopupCheck(Long memberId, PrizeTicketPopupType type) { + return PrizeTicketPopupCheck.builder().memberId(memberId).type(type).build(); + } + + public PrizeEventResponseDTO.MyInfo toMyInfo(Long memberId, Boolean showGetTicketPopup) { + return PrizeEventResponseDTO.MyInfo.builder() + .memberId(memberId) + .showGetTicketPopup(showGetTicketPopup) + .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 42f7f9b..38e0387 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/business/PrizeEventService.java +++ b/src/main/java/com/nexters/dailyphrase/prize/business/PrizeEventService.java @@ -10,10 +10,7 @@ import org.springframework.transaction.annotation.Transactional; import com.nexters.dailyphrase.common.consts.DailyPhraseStatic; -import com.nexters.dailyphrase.common.enums.PrizeEntryStatus; -import com.nexters.dailyphrase.common.enums.PrizeEventStatus; -import com.nexters.dailyphrase.common.enums.PrizeTicketSource; -import com.nexters.dailyphrase.common.enums.PrizeTicketStatus; +import com.nexters.dailyphrase.common.enums.*; import com.nexters.dailyphrase.common.jwt.JwtTokenService; import com.nexters.dailyphrase.common.jwt.dto.AccessTokenInfo; import com.nexters.dailyphrase.common.utils.MemberUtils; @@ -37,6 +34,8 @@ public class PrizeEventService { private final PrizeEntryQueryAdapter prizeEntryQueryAdapter; private final PrizeEntryCommandAdapter prizeEntryCommandAdapter; private final PrizeEntryCheckCommandAdapter prizeEntryCheckCommandAdapter; + private final PrizeTicketPopupCheckQueryAdapter prizeTicketPopupCheckQueryAdapter; + private final PrizeTicketPopupCheckCommandAdapter prizeTicketPopupCheckCommandAdapter; private final JwtTokenService jwtTokenService; private final PrizeEventMapper prizeEventMapper; private final MemberUtils memberUtils; @@ -135,4 +134,27 @@ public PrizeEventResponseDTO.CheckPrizeEntryResult checkPrizeEntryResult( return prizeEventMapper.toCheckPrizeEntryResult( prizeEntryCheckCommandAdapter.add(prizeEntryCheck)); } + + @Transactional(readOnly = true) + public PrizeEventResponseDTO.MyInfo myInfo() { + Long memberId = memberUtils.getCurrentMemberId(); + Boolean showGetTicketPopup = Boolean.FALSE; + // 회원가입 응모권을 획득한 기록이 있는 경우 + + boolean hasSignupTicket = + prizeTicketQueryAdapter.existsByMemberIdAndSource( + memberId, PrizeTicketSource.SIGNUP); + boolean isFirstCheck = + !prizeTicketPopupCheckQueryAdapter.existsByMemberIdAndType( + memberId, PrizeTicketPopupType.GET_BY_SIGNUP); + + if (hasSignupTicket && isFirstCheck) { + showGetTicketPopup = Boolean.TRUE; + prizeTicketPopupCheckCommandAdapter.add( + prizeEventMapper.toPrizeTicketPopupCheck( + memberId, PrizeTicketPopupType.GET_BY_SIGNUP)); + } + + return prizeEventMapper.toMyInfo(memberId, showGetTicketPopup); + } } diff --git a/src/main/java/com/nexters/dailyphrase/prize/domain/PrizeTicketPopupCheck.java b/src/main/java/com/nexters/dailyphrase/prize/domain/PrizeTicketPopupCheck.java new file mode 100644 index 0000000..343d466 --- /dev/null +++ b/src/main/java/com/nexters/dailyphrase/prize/domain/PrizeTicketPopupCheck.java @@ -0,0 +1,23 @@ +package com.nexters.dailyphrase.prize.domain; + +import jakarta.persistence.*; + +import com.nexters.dailyphrase.common.enums.PrizeTicketPopupType; + +import lombok.*; + +@Entity +@Getter +@Builder +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +public class PrizeTicketPopupCheck { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Long memberId; + + @Enumerated(EnumType.STRING) + private PrizeTicketPopupType type; +} diff --git a/src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketPopupCheckRepository.java b/src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketPopupCheckRepository.java new file mode 100644 index 0000000..cfe52a8 --- /dev/null +++ b/src/main/java/com/nexters/dailyphrase/prize/domain/repository/PrizeTicketPopupCheckRepository.java @@ -0,0 +1,11 @@ +package com.nexters.dailyphrase.prize.domain.repository; + +import org.springframework.data.jpa.repository.JpaRepository; + +import com.nexters.dailyphrase.common.enums.PrizeTicketPopupType; +import com.nexters.dailyphrase.prize.domain.PrizeTicketPopupCheck; + +public interface PrizeTicketPopupCheckRepository + extends JpaRepository { + boolean existsByMemberIdAndType(Long memberId, PrizeTicketPopupType type); +} diff --git a/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeTicketPopupCheckCommandAdapter.java b/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeTicketPopupCheckCommandAdapter.java new file mode 100644 index 0000000..49cbcd8 --- /dev/null +++ b/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeTicketPopupCheckCommandAdapter.java @@ -0,0 +1,17 @@ +package com.nexters.dailyphrase.prize.implement; + +import com.nexters.dailyphrase.common.annotation.Adapter; +import com.nexters.dailyphrase.prize.domain.PrizeTicketPopupCheck; +import com.nexters.dailyphrase.prize.domain.repository.PrizeTicketPopupCheckRepository; + +import lombok.RequiredArgsConstructor; + +@Adapter +@RequiredArgsConstructor +public class PrizeTicketPopupCheckCommandAdapter { + private final PrizeTicketPopupCheckRepository prizeTicketPopupCheckRepository; + + public PrizeTicketPopupCheck add(PrizeTicketPopupCheck prizeTicketPopupCheck) { + return prizeTicketPopupCheckRepository.save(prizeTicketPopupCheck); + } +} diff --git a/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeTicketPopupCheckQueryAdapter.java b/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeTicketPopupCheckQueryAdapter.java new file mode 100644 index 0000000..1196867 --- /dev/null +++ b/src/main/java/com/nexters/dailyphrase/prize/implement/PrizeTicketPopupCheckQueryAdapter.java @@ -0,0 +1,17 @@ +package com.nexters.dailyphrase.prize.implement; + +import com.nexters.dailyphrase.common.annotation.Adapter; +import com.nexters.dailyphrase.common.enums.PrizeTicketPopupType; +import com.nexters.dailyphrase.prize.domain.repository.PrizeTicketPopupCheckRepository; + +import lombok.RequiredArgsConstructor; + +@Adapter +@RequiredArgsConstructor +public class PrizeTicketPopupCheckQueryAdapter { + private final PrizeTicketPopupCheckRepository prizeTicketPopupCheckRepository; + + public boolean existsByMemberIdAndType(Long memberId, PrizeTicketPopupType type) { + return prizeTicketPopupCheckRepository.existsByMemberIdAndType(memberId, type); + } +} 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 228d145..1b83cf2 100644 --- a/src/main/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApi.java +++ b/src/main/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApi.java @@ -78,7 +78,7 @@ public CommonResponse enterPhoneNumber( } @Operation( - summary = "07-05 Event 🎁 경품 응모 이벤트의 경품 응모 결과 확인 처리 Made By 성훈", + summary = "07-06 Event 🎁 경품 응모 이벤트의 경품 응모 결과 확인 처리 Made By 성훈", description = "경품 응모 이벤트의 경품 응모 결과 확인 처리 API입니다.") @PostMapping("/prizes/entry-result/check") public CommonResponse checkPrizeEntryResult( @@ -87,7 +87,15 @@ public CommonResponse checkPrizeEnt } @Operation( - summary = "07-07 Event 🎁 경품 응모 이벤트의 응모권 발급용 카카오 콜백 Made By 성훈", + summary = "07-07 Event 🎁 경품 응모 이벤트의 응모권 팝업 플래그 확인 Made By 성훈", + description = "경품 응모 이벤트의 응모권 팝업 플래그 확인 API입니다.") + @GetMapping("/tickets/me") + public CommonResponse myInfo() { + return CommonResponse.onSuccess(prizeEventService.myInfo()); + } + + @Operation( + summary = "07-08 Event 🎁 경품 응모 이벤트의 응모권 발급용 카카오 콜백 Made By 성훈", description = "경품 응모 이벤트의 응모권 발급용 카카오 콜백입니다. (직접 호출 X)") @PostMapping("/kakaolink/callback") public ResponseEntity handleKakaoLinkCallback( 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 84881f1..bb9fcc1 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 @@ -109,4 +109,13 @@ public static class CheckPrizeEntryResult { private Long prizeId; private Long memberId; } + + @Builder + @Getter + @NoArgsConstructor + @AllArgsConstructor + public static class MyInfo { + private Long memberId; + @Builder.Default private Boolean showGetTicketPopup = Boolean.FALSE; + } } diff --git a/src/main/resources/application.yml b/src/main/resources/application.yml index 304d8aa..c1864f8 100644 --- a/src/main/resources/application.yml +++ b/src/main/resources/application.yml @@ -100,7 +100,7 @@ fcm: scope: https://www.googleapis.com/auth/cloud-platform api: url: https://fcm.googleapis.com/v1/projects/${fcm-project-id}/messages:send - topic: "test" + topic: "deprecated_topic" --- spring: config: @@ -151,3 +151,10 @@ spring: # format_sql: true # use_sql_comments: true default_batch_fetch_size: 1000 +fcm: + key: + path: firebase/firebase-adminsdk.json + scope: https://www.googleapis.com/auth/cloud-platform + api: + url: https://fcm.googleapis.com/v1/projects/${fcm-project-id}/messages:send + topic: "test_topic" \ No newline at end of file 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 b17b25b..0f3aa65 100644 --- a/src/test/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApiTest.java +++ b/src/test/java/com/nexters/dailyphrase/prize/presentation/PrizeEventApiTest.java @@ -31,6 +31,7 @@ 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.PrizeTicketSource; import com.nexters.dailyphrase.common.enums.PrizeTicketStatus; import com.nexters.dailyphrase.member.domain.Member; import com.nexters.dailyphrase.member.domain.repository.MemberRepository; @@ -277,4 +278,48 @@ public void setup() { .andExpect(jsonPath("$.result.memberId").value(1L)) .andExpect(jsonPath("$.result.status").value(PrizeEntryStatus.ENTERED.toString())); } + + @Test + @DisplayName("회원가입 티켓 획득 후 팝업 렌더링 플래그 확인 테스트입니다.") + @WithMockUser(username = "1") + void 회원가입_티켓_획득_후_팝업() throws Exception { + // given + prizeTicketRepository.save( + PrizeTicket.builder() + .eventId(1L) + .memberId(1L) + .status(PrizeTicketStatus.AVAILABLE) + .source(PrizeTicketSource.SIGNUP) + .build()); + + // when & then + MockHttpServletRequestBuilder request = + MockMvcRequestBuilders.get("/api/v1/events/tickets/me"); + + mockMvc.perform(request) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.result.showGetTicketPopup").value(Boolean.TRUE)); + + mockMvc.perform(request) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.result.showGetTicketPopup").value(Boolean.FALSE)); + } + + @Test + @DisplayName("회원가입 미획득시 팝업 렌더링 플래그 확인 테스트입니다.") + @WithMockUser(username = "1") + void 회원가입_미획득시_팝업() throws Exception { + // given + + // when & then + MockHttpServletRequestBuilder request = + MockMvcRequestBuilders.get("/api/v1/events/tickets/me"); + + mockMvc.perform(request) + .andDo(print()) + .andExpect(status().isOk()) + .andExpect(jsonPath("$.result.showGetTicketPopup").value(Boolean.FALSE)); + } }