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

[BSVR-232] 믹스패널 이벤트 추가 #165

Merged
merged 12 commits into from
Sep 1, 2024
1 change: 1 addition & 0 deletions .gitignore
Original file line number Diff line number Diff line change
Expand Up @@ -390,6 +390,7 @@ application-jwt.yml
application-oauth.yml
application-sentry.yml
application-aws.yaml
application-mixpanel.yaml

# 민성 레디스 바이너리 파일
redis-server-7.2.3-mac-arm64
3 changes: 3 additions & 0 deletions application/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -32,6 +32,9 @@ dependencies {
// aop
implementation("org.springframework.boot:spring-boot-starter-aop")

// Mixpanel
implementation("com.mixpanel:mixpanel-java:_")

// test container
testImplementation("org.testcontainers:testcontainers:_")
testImplementation("org.testcontainers:junit-jupiter:_")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,7 +33,8 @@ public class SecurityConfig {
"/api/v1/members/**",
"/actuator/**",
"/login/oauth2/code/google/**",
"/google/**"
"/google/**",
"/trackEvent"
};

@Bean
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,8 @@ public class JwtAuthenticationFilter extends OncePerRequestFilter {
"/api/v1/levels/info",
"/kakao",
"/api/v1/jwts",
"/google/callback"
"/google/callback",
"/trackEvent",
};

private static final Map<String, Set<String>> AUTH_METHOD_WHITELIST =
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -131,7 +131,7 @@ public BaseReviewResponse findReviewByReviewId(
@Parameter(hidden = true) Long memberId,
@PathVariable("reviewId") @NotNull @Parameter(description = "리뷰 PK", required = true)
Long reviewId) {
ReadReviewResult readReviewResult = readReviewUsecase.findReviewById(reviewId);
ReadReviewResult readReviewResult = readReviewUsecase.findReviewById(reviewId, memberId);
return BaseReviewResponse.from(readReviewResult.review());
}
}
5 changes: 4 additions & 1 deletion application/src/main/resources/application.yaml
Original file line number Diff line number Diff line change
Expand Up @@ -5,26 +5,29 @@ server:
spring:
# 서브모듈 profile
profiles:
active: local
active: dev
group:
local:
- jpa
- aws
- jwt
- oauth
- mixpanel
dev:
- jpa
- aws
- jwt
- oauth
- monitoring
- mixpanel
prod:
- jpa
- aws
- jwt
- oauth
- sentry
- monitoring
- mixpanel
servlet:
multipart:
max-file-size: 10MB
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
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;
}
}
3 changes: 3 additions & 0 deletions infrastructure/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -39,6 +39,9 @@ dependencies {

// caffeine cache
implementation("com.github.ben-manes.caffeine:caffeine:_")

// Mixpanel
implementation("com.mixpanel:mixpanel-java:_")
}

tasks.bootJar { enabled = false }
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package org.depromeet.spot.infrastructure.mixpanel.property;

import org.springframework.boot.context.properties.ConfigurationProperties;

@ConfigurationProperties(prefix = "mixpanel")
public record MixpanelProperties(String token) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.depromeet.spot.infrastructure.mixpanel.repository;

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.json.JSONObject;
import org.springframework.stereotype.Component;

import com.mixpanel.mixpanelapi.ClientDelivery;
import com.mixpanel.mixpanelapi.MessageBuilder;
import com.mixpanel.mixpanelapi.MixpanelAPI;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Component
@RequiredArgsConstructor
public class MixpanelRepositoryImpl implements MixpanelRepository {

private final MixpanelProperties mixpanelProperties;

// mixpanelEvent는 eventName(이 단위로 이벤트가 묶임)
// distinctId는 사용자를 구분하는 데 사용됨.
@Override
public void eventTrack(MixpanelEvent mixpanelEvent, String distinctId) {
try {

// 믹스패널 이벤트 메시지 생성
MessageBuilder messageBuilder = new MessageBuilder(mixpanelProperties.token());

// 이벤트 생성
JSONObject sentEvent = messageBuilder.event(distinctId, mixpanelEvent.getValue(), null);

// 만든 여러 이벤트를 delivery
ClientDelivery delivery = new ClientDelivery();
delivery.addMessage(sentEvent);

// Mixpanel로 데이터 전송
MixpanelAPI mixpanel = new MixpanelAPI();
mixpanel.deliver(delivery);
} catch (IOException e) {
e.printStackTrace();
}
}
}
4 changes: 4 additions & 0 deletions usecase/build.gradle.kts
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,10 @@ dependencies {
implementation("org.springframework.boot:spring-boot-starter-data-jpa:_") {
because("@Transactional을 위해 추가")
}

// Mixpanel
implementation("com.mixpanel:mixpanel-java:_")

}

tasks.bootJar { enabled = false }
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@ MyReviewListResult findMyReviewsByUserId(

MyRecentReviewResult findLastReviewByMemberId(Long memberId);

ReadReviewResult findReviewById(Long reviewId);
ReadReviewResult findReviewById(Long reviewId, Long memberId);

long countByIdByMemberId(Long memberId);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package org.depromeet.spot.usecase.port.out.mixpanel;

import org.depromeet.spot.domain.mixpanel.MixpanelEvent;

public interface MixpanelRepository {
void eventTrack(MixpanelEvent mixpanelEvent, String distinctId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,12 @@
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;
Expand All @@ -30,6 +32,7 @@ public class CreateReviewService implements CreateReviewUsecase {
private final ReviewImageProcessor reviewImageProcessor;
private final ReviewKeywordProcessor reviewKeywordProcessor;
private final MemberLevelProcessor memberLevelProcessor;
private final MixpanelRepository mixpanelRepository;

@Override
@Transactional
Expand All @@ -46,6 +49,9 @@ 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());
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
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;
Expand All @@ -15,6 +16,7 @@
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;
Expand Down Expand Up @@ -46,6 +48,7 @@ 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;
Expand Down Expand Up @@ -185,10 +188,13 @@ public List<ReviewYearMonth> findReviewMonths(Long memberId, ReviewType reviewTy
}

@Override
public ReadReviewResult findReviewById(Long reviewId) {
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();
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,9 @@ 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) {
Expand All @@ -34,6 +37,11 @@ public void toggleLike(final Long memberId, final long reviewId) {
}

addLike(memberId, reviewId, review);

// TODO : 테스트 시에도 이벤트 발생함.
// 믹스패널 이벤트(좋아요 수) 발생
// mixpanelRepository.eventTrack(MixpanelEvent.REVIEW_LIKE_COUNT,
// String.valueOf(memberId));
}

public void cancelLike(final long memberId, final long reviewId, Review review) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,12 +2,14 @@

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;
Expand All @@ -29,6 +31,8 @@ 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) {
Expand Down Expand Up @@ -86,6 +90,10 @@ public boolean toggleScrap(final long memberId, final long reviewId) {
}

addScrap(memberId, reviewId, review);

// 믹스패널 이벤트(스크랩 수) 발생
mixpanelRepository.eventTrack(MixpanelEvent.REVIEW_SCRAP_COUNT, String.valueOf(memberId));

return true;
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -18,6 +18,7 @@
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;
Expand All @@ -37,6 +38,7 @@ class ReviewScrapServiceTest {
@Mock private ReadReviewService readReviewService;
@Mock private ReadReviewProcessor readReviewProcessor;
@Mock private PaginationProcessor paginationProcessor;
@Mock private MixpanelRepository mixpanelRepository;

@BeforeEach
void init() {
Expand All @@ -49,7 +51,8 @@ void init() {
fakeReviewScrapRepository,
readReviewService,
readReviewProcessor,
paginationProcessor);
paginationProcessor,
mixpanelRepository);
}

@Test
Expand Down
2 changes: 2 additions & 0 deletions versions.properties
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ plugin.io.spring.dependency-management=1.0.11.RELEASE

plugin.com.diffplug.spotless=6.21.0

version.com.mixpanel..mixpanel-java=1.5.3

version.io.jsonwebtoken..jjwt=0.12.6

version.junit=5.9.1
Expand Down
Loading