diff --git a/application/src/main/java/org/depromeet/spot/application/review/CreateReviewController.java b/application/src/main/java/org/depromeet/spot/application/review/CreateReviewController.java index ab386cd3..1005d089 100644 --- a/application/src/main/java/org/depromeet/spot/application/review/CreateReviewController.java +++ b/application/src/main/java/org/depromeet/spot/application/review/CreateReviewController.java @@ -13,6 +13,9 @@ import org.depromeet.spot.application.review.dto.response.BaseReviewResponse; import org.depromeet.spot.usecase.port.in.review.CreateReviewUsecase; import org.depromeet.spot.usecase.port.in.review.CreateReviewUsecase.CreateReviewResult; +import org.depromeet.spot.usecase.service.event.MixpanelEvent; +import org.depromeet.spot.usecase.service.event.MixpanelEvent.MixpanelEventName; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -36,6 +39,8 @@ @RequestMapping("/api/v1") public class CreateReviewController { + private final ApplicationEventPublisher applicationEventPublisher; + private final CreateReviewUsecase createReviewUsecase; @CurrentMember @@ -49,6 +54,11 @@ public BaseReviewResponse create( CreateReviewResult result = createReviewUsecase.create(blockId, memberId, request.toCommand()); + + // 믹스패널 이벤트(후기 등록 완료) 호출 + applicationEventPublisher.publishEvent( + new MixpanelEvent(MixpanelEventName.REVIEW_REGISTER, String.valueOf(memberId))); + return BaseReviewResponse.from(result); } diff --git a/application/src/main/java/org/depromeet/spot/application/review/ReadReviewController.java b/application/src/main/java/org/depromeet/spot/application/review/ReadReviewController.java index 59b5007d..02a29cc0 100644 --- a/application/src/main/java/org/depromeet/spot/application/review/ReadReviewController.java +++ b/application/src/main/java/org/depromeet/spot/application/review/ReadReviewController.java @@ -22,6 +22,9 @@ import org.depromeet.spot.usecase.port.in.review.ReadReviewUsecase.MyRecentReviewResult; import org.depromeet.spot.usecase.port.in.review.ReadReviewUsecase.MyReviewListResult; import org.depromeet.spot.usecase.port.in.review.ReadReviewUsecase.ReadReviewResult; +import org.depromeet.spot.usecase.service.event.MixpanelEvent; +import org.depromeet.spot.usecase.service.event.MixpanelEvent.MixpanelEventName; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.*; @@ -36,6 +39,8 @@ @RequestMapping("/api/v1") public class ReadReviewController { + private final ApplicationEventPublisher applicationEventPublisher; + private final ReadReviewUsecase readReviewUsecase; @CurrentMember @@ -132,6 +137,11 @@ public BaseReviewResponse findReviewByReviewId( @PathVariable("reviewId") @NotNull @Parameter(description = "리뷰 PK", required = true) Long reviewId) { ReadReviewResult readReviewResult = readReviewUsecase.findReviewById(reviewId, memberId); + + // 믹스패널 이벤트(조회수) 발생 + applicationEventPublisher.publishEvent( + new MixpanelEvent(MixpanelEventName.REVIEW_OPEN_COUNT, String.valueOf(memberId))); + return BaseReviewResponse.from(readReviewResult.review()); } } diff --git a/application/src/main/java/org/depromeet/spot/application/review/like/ReviewLikeController.java b/application/src/main/java/org/depromeet/spot/application/review/like/ReviewLikeController.java index c503f72a..8959d3a2 100644 --- a/application/src/main/java/org/depromeet/spot/application/review/like/ReviewLikeController.java +++ b/application/src/main/java/org/depromeet/spot/application/review/like/ReviewLikeController.java @@ -5,6 +5,9 @@ import org.depromeet.spot.application.common.annotation.CurrentMember; import org.depromeet.spot.usecase.port.in.review.like.ReviewLikeUsecase; +import org.depromeet.spot.usecase.service.event.MixpanelEvent; +import org.depromeet.spot.usecase.service.event.MixpanelEvent.MixpanelEventName; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; @@ -23,6 +26,8 @@ @RequestMapping("/api/v1/reviews") public class ReviewLikeController { + private final ApplicationEventPublisher applicationEventPublisher; + private final ReviewLikeUsecase reviewLikeUsecase; @CurrentMember @@ -32,6 +37,12 @@ public class ReviewLikeController { public void toggleLike( @PathVariable @Positive @NotNull final Long reviewId, @Parameter(hidden = true) Long memberId) { - reviewLikeUsecase.toggleLike(memberId, reviewId); + boolean result = reviewLikeUsecase.toggleLike(memberId, reviewId); + if (result) { + // 리뷰 공감 추이 이벤트 발생 + applicationEventPublisher.publishEvent( + new MixpanelEvent( + MixpanelEventName.REVIEW_LIKE_COUNT, String.valueOf(memberId))); + } } } diff --git a/application/src/main/java/org/depromeet/spot/application/review/scrap/ReviewScrapController.java b/application/src/main/java/org/depromeet/spot/application/review/scrap/ReviewScrapController.java index 1d7af9ff..02a1cde3 100644 --- a/application/src/main/java/org/depromeet/spot/application/review/scrap/ReviewScrapController.java +++ b/application/src/main/java/org/depromeet/spot/application/review/scrap/ReviewScrapController.java @@ -10,6 +10,9 @@ import org.depromeet.spot.application.review.dto.response.scrap.MyScrapListResponse; import org.depromeet.spot.usecase.port.in.review.scrap.ReviewScrapUsecase; import org.depromeet.spot.usecase.port.in.review.scrap.ReviewScrapUsecase.MyScrapListResult; +import org.depromeet.spot.usecase.service.event.MixpanelEvent; +import org.depromeet.spot.usecase.service.event.MixpanelEvent.MixpanelEventName; +import org.springframework.context.ApplicationEventPublisher; import org.springframework.http.HttpStatus; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -29,6 +32,8 @@ @RequestMapping("/api/v1/reviews") public class ReviewScrapController { + private final ApplicationEventPublisher applicationEventPublisher; + private final ReviewScrapUsecase reviewScrapUsecase; @CurrentMember @@ -38,7 +43,13 @@ public class ReviewScrapController { public boolean toggleScrap( @PathVariable @Positive @NotNull final Long reviewId, @Parameter(hidden = true) Long memberId) { - return reviewScrapUsecase.toggleScrap(memberId, reviewId); + boolean result = reviewScrapUsecase.toggleScrap(memberId, reviewId); + + // 믹스패널 이벤트(스크랩 수) 발생 + applicationEventPublisher.publishEvent( + new MixpanelEvent(MixpanelEventName.REVIEW_SCRAP_COUNT, String.valueOf(memberId))); + + return result; } @CurrentMember diff --git a/domain/src/main/java/org/depromeet/spot/domain/mixpanel/MixpanelEvent.java b/domain/src/main/java/org/depromeet/spot/domain/mixpanel/MixpanelEvent.java deleted file mode 100644 index ad4758f3..00000000 --- a/domain/src/main/java/org/depromeet/spot/domain/mixpanel/MixpanelEvent.java +++ /dev/null @@ -1,19 +0,0 @@ -package org.depromeet.spot.domain.mixpanel; - -import lombok.Getter; - -@Getter -public enum MixpanelEvent { - REVIEW_REGISTER("review_register"), - REVIEW_REGISTER_MAX("review_register"), - REVIEW_OPEN_COUNT("review_open_count"), - REVIEW_LIKE_COUNT("review_like_count"), - REVIEW_SCRAP_COUNT("review_scrap_count"), - ; - - String value; - - MixpanelEvent(String value) { - this.value = value; - } -} diff --git a/infrastructure/src/main/java/org/depromeet/spot/infrastructure/mixpanel/repository/MixpanelRepositoryImpl.java b/infrastructure/src/main/java/org/depromeet/spot/infrastructure/mixpanel/repository/MixpanelRepositoryImpl.java index 3d6980b1..3cf333c8 100644 --- a/infrastructure/src/main/java/org/depromeet/spot/infrastructure/mixpanel/repository/MixpanelRepositoryImpl.java +++ b/infrastructure/src/main/java/org/depromeet/spot/infrastructure/mixpanel/repository/MixpanelRepositoryImpl.java @@ -2,9 +2,9 @@ import java.io.IOException; -import org.depromeet.spot.domain.mixpanel.MixpanelEvent; import org.depromeet.spot.infrastructure.mixpanel.property.MixpanelProperties; import org.depromeet.spot.usecase.port.out.mixpanel.MixpanelRepository; +import org.depromeet.spot.usecase.service.event.MixpanelEvent; import org.json.JSONObject; import org.springframework.stereotype.Component; @@ -25,14 +25,18 @@ public class MixpanelRepositoryImpl implements MixpanelRepository { // mixpanelEvent는 eventName(이 단위로 이벤트가 묶임) // distinctId는 사용자를 구분하는 데 사용됨. @Override - public void eventTrack(MixpanelEvent mixpanelEvent, String distinctId) { + public void eventTrack(MixpanelEvent mixpanelEvent) { try { // 믹스패널 이벤트 메시지 생성 MessageBuilder messageBuilder = new MessageBuilder(mixpanelProperties.token()); // 이벤트 생성 - JSONObject sentEvent = messageBuilder.event(distinctId, mixpanelEvent.getValue(), null); + JSONObject sentEvent = + messageBuilder.event( + mixpanelEvent.getDistinctId(), + mixpanelEvent.getMixpanelEventName().getValue(), + null); // 만든 여러 이벤트를 delivery ClientDelivery delivery = new ClientDelivery(); diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/in/review/like/ReviewLikeUsecase.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/in/review/like/ReviewLikeUsecase.java index 105dc1fb..50b76365 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/port/in/review/like/ReviewLikeUsecase.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/port/in/review/like/ReviewLikeUsecase.java @@ -1,5 +1,5 @@ package org.depromeet.spot.usecase.port.in.review.like; public interface ReviewLikeUsecase { - void toggleLike(Long memberId, long reviewId); + boolean toggleLike(Long memberId, long reviewId); } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/mixpanel/MixpanelRepository.java b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/mixpanel/MixpanelRepository.java index 09ca9d30..b58a3a3e 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/port/out/mixpanel/MixpanelRepository.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/port/out/mixpanel/MixpanelRepository.java @@ -1,7 +1,7 @@ package org.depromeet.spot.usecase.port.out.mixpanel; -import org.depromeet.spot.domain.mixpanel.MixpanelEvent; +import org.depromeet.spot.usecase.service.event.MixpanelEvent; public interface MixpanelRepository { - void eventTrack(MixpanelEvent mixpanelEvent, String distinctId); + void eventTrack(MixpanelEvent mixpanelEvent); } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/event/MixpanelEvent.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/event/MixpanelEvent.java new file mode 100644 index 00000000..16ef96c1 --- /dev/null +++ b/usecase/src/main/java/org/depromeet/spot/usecase/service/event/MixpanelEvent.java @@ -0,0 +1,28 @@ +package org.depromeet.spot.usecase.service.event; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public class MixpanelEvent { + + private final MixpanelEventName mixpanelEventName; + + private final String distinctId; + + @Getter + public enum MixpanelEventName { + REVIEW_REGISTER("review_register"), + REVIEW_OPEN_COUNT("review_open_count"), + REVIEW_LIKE_COUNT("review_like_count"), + REVIEW_SCRAP_COUNT("review_scrap_count"), + ; + + String value; + + MixpanelEventName(String value) { + this.value = value; + } + } +} diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/event/MixpanelEventListener.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/event/MixpanelEventListener.java new file mode 100644 index 00000000..972d4c47 --- /dev/null +++ b/usecase/src/main/java/org/depromeet/spot/usecase/service/event/MixpanelEventListener.java @@ -0,0 +1,24 @@ +package org.depromeet.spot.usecase.service.event; + +import org.depromeet.spot.usecase.port.out.mixpanel.MixpanelRepository; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.context.event.EventListener; +import org.springframework.scheduling.annotation.Async; +import org.springframework.stereotype.Component; + +import lombok.RequiredArgsConstructor; + +@Component +@RequiredArgsConstructor +public class MixpanelEventListener { + + private static final Logger log = LoggerFactory.getLogger(MixpanelEventListener.class); + private final MixpanelRepository mixpanelRepository; + + @Async + @EventListener + public void eventTrack(MixpanelEvent mixpanelEvent) { + mixpanelRepository.eventTrack(mixpanelEvent); + } +} diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/review/CreateReviewService.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/review/CreateReviewService.java index e3a782c8..5071aa0c 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/service/review/CreateReviewService.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/service/review/CreateReviewService.java @@ -3,12 +3,10 @@ import java.util.Map; import org.depromeet.spot.domain.member.Member; -import org.depromeet.spot.domain.mixpanel.MixpanelEvent; import org.depromeet.spot.domain.review.Review; import org.depromeet.spot.domain.review.keyword.Keyword; import org.depromeet.spot.usecase.port.in.review.CreateReviewUsecase; import org.depromeet.spot.usecase.port.out.member.MemberRepository; -import org.depromeet.spot.usecase.port.out.mixpanel.MixpanelRepository; import org.depromeet.spot.usecase.port.out.review.ReviewRepository; import org.depromeet.spot.usecase.service.member.processor.MemberLevelProcessor; import org.depromeet.spot.usecase.service.review.processor.ReviewCreationProcessor; @@ -31,7 +29,6 @@ public class CreateReviewService implements CreateReviewUsecase { private final ReviewImageProcessor reviewImageProcessor; private final ReviewKeywordProcessor reviewKeywordProcessor; private final MemberLevelProcessor memberLevelProcessor; - private final MixpanelRepository mixpanelRepository; @Override @Transactional @@ -49,9 +46,6 @@ public CreateReviewResult create(Long blockId, Long memberId, CreateReviewComman Member levelUpdateMember = memberLevelProcessor.calculateAndUpdateMemberLevel(member); - // 믹스패널 이벤트(후기 등록 완료) 호출 - mixpanelRepository.eventTrack(MixpanelEvent.REVIEW_REGISTER, String.valueOf(memberId)); - return new CreateReviewResult(savedReview, levelUpdateMember, review.getSeat()); } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/review/ReadReviewService.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/review/ReadReviewService.java index 49ecdb3a..bb263e77 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/service/review/ReadReviewService.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/service/review/ReadReviewService.java @@ -5,7 +5,6 @@ import java.util.stream.Collectors; import org.depromeet.spot.domain.member.Member; -import org.depromeet.spot.domain.mixpanel.MixpanelEvent; import org.depromeet.spot.domain.review.Review; import org.depromeet.spot.domain.review.Review.ReviewType; import org.depromeet.spot.domain.review.Review.SortCriteria; @@ -16,7 +15,6 @@ import org.depromeet.spot.domain.team.BaseballTeam; import org.depromeet.spot.usecase.port.in.review.ReadReviewUsecase; import org.depromeet.spot.usecase.port.out.member.MemberRepository; -import org.depromeet.spot.usecase.port.out.mixpanel.MixpanelRepository; import org.depromeet.spot.usecase.port.out.review.BlockTopKeywordRepository; import org.depromeet.spot.usecase.port.out.review.KeywordRepository; import org.depromeet.spot.usecase.port.out.review.ReviewImageRepository; @@ -48,7 +46,6 @@ public class ReadReviewService implements ReadReviewUsecase { private final ReviewScrapRepository reviewScrapRepository; private final ReadReviewProcessor readReviewProcessor; private final PaginationProcessor paginationProcessor; - private final MixpanelRepository mixpanelRepository; private static final int TOP_KEYWORDS_LIMIT = 5; private static final int TOP_IMAGES_LIMIT = 5; @@ -192,9 +189,6 @@ public ReadReviewResult findReviewById(Long reviewId, Long memberId) { Review review = reviewRepository.findById(reviewId); Review reviewWithKeywords = mapKeywordsToSingleReview(review); - // 믹스패널 이벤트(조회수) 발생 - mixpanelRepository.eventTrack(MixpanelEvent.REVIEW_OPEN_COUNT, String.valueOf(memberId)); - return ReadReviewResult.builder().review(reviewWithKeywords).build(); } diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/review/like/ReviewLikeService.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/review/like/ReviewLikeService.java index e0eb93c4..6ecbf536 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/service/review/like/ReviewLikeService.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/service/review/like/ReviewLikeService.java @@ -23,25 +23,18 @@ public class ReviewLikeService implements ReviewLikeUsecase { private final UpdateReviewUsecase updateReviewUsecase; private final ReviewLikeRepository reviewLikeRepository; - // TODO : Service 코드와 분리하기 - // private final MixpanelRepository mixpanelRepository; - @Override @DistributedLock(key = "#reviewId") - public void toggleLike(final Long memberId, final long reviewId) { + public boolean toggleLike(final Long memberId, final long reviewId) { Review review = readReviewUsecase.findById(reviewId); if (reviewLikeRepository.existsBy(memberId, reviewId)) { cancelLike(memberId, reviewId, review); - return; + return false; } addLike(memberId, reviewId, review); - - // TODO : 테스트 시에도 이벤트 발생함. - // 믹스패널 이벤트(좋아요 수) 발생 - // mixpanelRepository.eventTrack(MixpanelEvent.REVIEW_LIKE_COUNT, - // String.valueOf(memberId)); + return true; } public void cancelLike(final long memberId, final long reviewId, Review review) { diff --git a/usecase/src/main/java/org/depromeet/spot/usecase/service/review/scrap/ReviewScrapService.java b/usecase/src/main/java/org/depromeet/spot/usecase/service/review/scrap/ReviewScrapService.java index 5c372400..539e0d09 100644 --- a/usecase/src/main/java/org/depromeet/spot/usecase/service/review/scrap/ReviewScrapService.java +++ b/usecase/src/main/java/org/depromeet/spot/usecase/service/review/scrap/ReviewScrapService.java @@ -2,14 +2,12 @@ import java.util.List; -import org.depromeet.spot.domain.mixpanel.MixpanelEvent; import org.depromeet.spot.domain.review.Review; import org.depromeet.spot.domain.review.scrap.ReviewScrap; import org.depromeet.spot.usecase.port.in.review.ReadReviewUsecase; import org.depromeet.spot.usecase.port.in.review.UpdateReviewUsecase; import org.depromeet.spot.usecase.port.in.review.page.PageCommand; import org.depromeet.spot.usecase.port.in.review.scrap.ReviewScrapUsecase; -import org.depromeet.spot.usecase.port.out.mixpanel.MixpanelRepository; import org.depromeet.spot.usecase.port.out.review.ReviewScrapRepository; import org.depromeet.spot.usecase.service.review.ReadReviewService; import org.depromeet.spot.usecase.service.review.processor.PaginationProcessor; @@ -31,8 +29,6 @@ public class ReviewScrapService implements ReviewScrapUsecase { private final ReadReviewProcessor readReviewProcessor; private final PaginationProcessor paginationProcessor; - private final MixpanelRepository mixpanelRepository; - @Override public MyScrapListResult findMyScrappedReviews( Long memberId, MyScrapCommand command, PageCommand pageCommand) { @@ -91,9 +87,6 @@ public boolean toggleScrap(final long memberId, final long reviewId) { addScrap(memberId, reviewId, review); - // 믹스패널 이벤트(스크랩 수) 발생 - mixpanelRepository.eventTrack(MixpanelEvent.REVIEW_SCRAP_COUNT, String.valueOf(memberId)); - return true; } diff --git a/usecase/src/test/java/org/depromeet/spot/usecase/service/review/ReviewScrapServiceTest.java b/usecase/src/test/java/org/depromeet/spot/usecase/service/review/ReviewScrapServiceTest.java index a88718bc..458de70f 100644 --- a/usecase/src/test/java/org/depromeet/spot/usecase/service/review/ReviewScrapServiceTest.java +++ b/usecase/src/test/java/org/depromeet/spot/usecase/service/review/ReviewScrapServiceTest.java @@ -18,7 +18,6 @@ import org.depromeet.spot.usecase.port.in.review.page.PageCommand; import org.depromeet.spot.usecase.port.in.review.scrap.ReviewScrapUsecase.MyScrapCommand; import org.depromeet.spot.usecase.port.in.review.scrap.ReviewScrapUsecase.MyScrapListResult; -import org.depromeet.spot.usecase.port.out.mixpanel.MixpanelRepository; import org.depromeet.spot.usecase.service.fake.FakeReviewScrapRepository; import org.depromeet.spot.usecase.service.review.processor.PaginationProcessor; import org.depromeet.spot.usecase.service.review.processor.ReadReviewProcessor; @@ -38,7 +37,6 @@ class ReviewScrapServiceTest { @Mock private ReadReviewService readReviewService; @Mock private ReadReviewProcessor readReviewProcessor; @Mock private PaginationProcessor paginationProcessor; - @Mock private MixpanelRepository mixpanelRepository; @BeforeEach void init() { @@ -51,8 +49,7 @@ void init() { fakeReviewScrapRepository, readReviewService, readReviewProcessor, - paginationProcessor, - mixpanelRepository); + paginationProcessor); } @Test