From 37516a00724aa6ea2990d9e0e31e141f4a4b86b4 Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 5 Mar 2025 21:49:33 +0900 Subject: [PATCH 1/2] =?UTF-8?q?=EB=A7=88=EC=BC=80=ED=8C=85=20-=20=EA=B4=91?= =?UTF-8?q?=EA=B3=A0=20=ED=86=B5=EA=B3=84=20=EC=A1=B0=ED=9A=8C=20API=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../statistics/AdminAdStatisticsController.kt | 21 +++ .../statistics/AdminAdStatisticsRepository.kt | 129 ++++++++++++++++++ .../statistics/AdminAdStatisticsService.kt | 18 +++ .../GetAdminAdStatisticsResponse.kt | 22 +++ 4 files changed, 190 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/AdminAdStatisticsController.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/AdminAdStatisticsRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/AdminAdStatisticsService.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/GetAdminAdStatisticsResponse.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/AdminAdStatisticsController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/AdminAdStatisticsController.kt new file mode 100644 index 0000000..1848592 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/AdminAdStatisticsController.kt @@ -0,0 +1,21 @@ +package kr.co.vividnext.sodalive.admin.marketing.statistics + +import kr.co.vividnext.sodalive.common.ApiResponse +import org.springframework.data.domain.Pageable +import org.springframework.security.access.prepost.PreAuthorize +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RestController + +@RestController +@PreAuthorize("hasRole('ADMIN')") +@RequestMapping("/admin/marketing/statistics") +class AdminAdStatisticsController(private val service: AdminAdStatisticsService) { + @GetMapping + fun getStatistics(pageable: Pageable) = ApiResponse.ok( + service.getStatistics( + offset = pageable.offset, + limit = pageable.pageSize.toLong() + ) + ) +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/AdminAdStatisticsRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/AdminAdStatisticsRepository.kt new file mode 100644 index 0000000..6cd8046 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/AdminAdStatisticsRepository.kt @@ -0,0 +1,129 @@ +package kr.co.vividnext.sodalive.admin.marketing.statistics + +import com.querydsl.core.types.dsl.CaseBuilder +import com.querydsl.core.types.dsl.DateTimePath +import com.querydsl.core.types.dsl.Expressions +import com.querydsl.core.types.dsl.NumberExpression +import com.querydsl.core.types.dsl.StringTemplate +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.marketing.AdTrackingHistoryType +import kr.co.vividnext.sodalive.marketing.QAdTrackingHistory.adTrackingHistory +import org.springframework.stereotype.Repository +import java.time.LocalDateTime + +@Repository +class AdminAdStatisticsRepository(private val queryFactory: JPAQueryFactory) { + fun getAdStatisticsDataTotalCount(): Int { + return queryFactory + .select(adTrackingHistory.id.pid) + .from(adTrackingHistory) + .groupBy( + getFormattedDate(adTrackingHistory.id.createdAt), + adTrackingHistory.mediaGroup, + adTrackingHistory.id.pid, + adTrackingHistory.pidName + ) + .fetch() + .size + } + + fun getAdStatisticsDataList(offset: Long, limit: Long): List { + val signUpCount = CaseBuilder() + .`when`(adTrackingHistory.id.type.eq(AdTrackingHistoryType.SIGNUP)) + .then(1) + .otherwise(0) + .sum() + + val firstPaymentCount = CaseBuilder() + .`when`(adTrackingHistory.id.type.eq(AdTrackingHistoryType.FIRST_PAYMENT)) + .then(1) + .otherwise(0) + .sum() + + val firstPaymentTotalAmount = CaseBuilder() + .`when`(adTrackingHistory.id.type.eq(AdTrackingHistoryType.FIRST_PAYMENT)) + .then(adTrackingHistory.price) + .otherwise(Expressions.constant(0.0)) + .sum() + + val repeatPaymentCount = CaseBuilder() + .`when`(adTrackingHistory.id.type.eq(AdTrackingHistoryType.REPEAT_PAYMENT)) + .then(1) + .otherwise(0) + .sum() + + val repeatPaymentTotalAmount = CaseBuilder() + .`when`(adTrackingHistory.id.type.eq(AdTrackingHistoryType.REPEAT_PAYMENT)) + .then(adTrackingHistory.price) + .otherwise(Expressions.constant(0.0)) + .sum() + + val allPaymentCount = CaseBuilder() + .`when`( + adTrackingHistory.id.type.eq(AdTrackingHistoryType.FIRST_PAYMENT) + .or(adTrackingHistory.id.type.eq(AdTrackingHistoryType.REPEAT_PAYMENT)) + ) + .then(1) + .otherwise(0) + .sum() + + val allPaymentTotalAmount = CaseBuilder() + .`when`( + adTrackingHistory.id.type.eq(AdTrackingHistoryType.FIRST_PAYMENT) + .or(adTrackingHistory.id.type.eq(AdTrackingHistoryType.REPEAT_PAYMENT)) + ) + .then(adTrackingHistory.price) + .otherwise(Expressions.constant(0.0)) + .sum() + + return queryFactory + .select( + QGetAdminAdStatisticsItem( + getFormattedDate(adTrackingHistory.id.createdAt), + adTrackingHistory.mediaGroup, + adTrackingHistory.id.pid, + adTrackingHistory.pidName, + signUpCount, + firstPaymentCount, + roundedValueDecimalPlaces2(firstPaymentTotalAmount), + repeatPaymentCount, + roundedValueDecimalPlaces2(repeatPaymentTotalAmount), + allPaymentCount, + roundedValueDecimalPlaces2(allPaymentTotalAmount) + ) + ) + .from(adTrackingHistory) + .groupBy( + getFormattedDate(adTrackingHistory.id.createdAt), + adTrackingHistory.mediaGroup, + adTrackingHistory.id.pid, + adTrackingHistory.pidName + ) + .offset(offset) + .limit(limit) + .fetch() + } + + private fun getFormattedDate(dateTimePath: DateTimePath): StringTemplate { + return Expressions.stringTemplate( + "DATE_FORMAT({0}, {1})", + Expressions.dateTimeTemplate( + LocalDateTime::class.java, + "CONVERT_TZ({0},{1},{2})", + dateTimePath, + "UTC", + "Asia/Seoul" + ), + "%Y-%m-%d" + ) + } + + private fun roundedValueDecimalPlaces2(valueExpression: NumberExpression): NumberExpression { + return Expressions.numberTemplate( + Double::class.java, + "ROUND({0}, {1})", + valueExpression, + 2 + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/AdminAdStatisticsService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/AdminAdStatisticsService.kt new file mode 100644 index 0000000..6e1bb5c --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/AdminAdStatisticsService.kt @@ -0,0 +1,18 @@ +package kr.co.vividnext.sodalive.admin.marketing.statistics + +import org.springframework.stereotype.Service + +@Service +class AdminAdStatisticsService( + private val repository: AdminAdStatisticsRepository +) { + fun getStatistics(offset: Long, limit: Long): GetAdminAdStatisticsResponse { + val totalCount = repository.getAdStatisticsDataTotalCount() + val items = repository.getAdStatisticsDataList(offset, limit) + + return GetAdminAdStatisticsResponse( + totalCount = totalCount, + items = items + ) + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/GetAdminAdStatisticsResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/GetAdminAdStatisticsResponse.kt new file mode 100644 index 0000000..1a7adff --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/statistics/GetAdminAdStatisticsResponse.kt @@ -0,0 +1,22 @@ +package kr.co.vividnext.sodalive.admin.marketing.statistics + +import com.querydsl.core.annotations.QueryProjection + +data class GetAdminAdStatisticsResponse( + val totalCount: Int, + val items: List +) + +data class GetAdminAdStatisticsItem @QueryProjection constructor( + val date: String, + val mediaGroup: String, + val pid: String, + val pidName: String, + val signUpCount: Int, + val firstPaymentCount: Int, + val firstPaymentTotalAmount: Double, + val repeatPaymentCount: Int, + val repeatPaymentTotalAmount: Double, + val allPaymentCount: Int, + val allPaymentTotalAmount: Double +) -- 2.40.1 From 75940bbb23710eaf869c5c21501b0ac06104f836 Mon Sep 17 00:00:00 2001 From: Klaus Date: Wed, 5 Mar 2025 22:47:29 +0900 Subject: [PATCH 2/2] =?UTF-8?q?=EB=A7=88=EC=BC=80=ED=8C=85=20-=20=EB=A7=A4?= =?UTF-8?q?=EC=B2=B4=20=ED=8C=8C=ED=8A=B8=EB=84=88=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20API=20-=20link=20=EA=B0=92=20=EC=88=98?= =?UTF-8?q?=EC=A0=95=20-=20utm=5Fcampaign=20=EA=B0=92=EC=9D=84=20pidName?= =?UTF-8?q?=EC=97=90=EC=84=9C=20pid=20=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/admin/marketing/AdminAdMediaPartnerService.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/AdminAdMediaPartnerService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/AdminAdMediaPartnerService.kt index 62d0759..2124b8f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/AdminAdMediaPartnerService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/marketing/AdminAdMediaPartnerService.kt @@ -70,7 +70,7 @@ class AdminAdMediaPartnerService(private val repository: AdMediaPartnerRepositor "&deep_link_sub4=${it.pidName}" + "&utm_source=${it.utmSource}" + "&utm_medium=${it.utmMedium}" + - "&utm_campaign=${it.pidName}" + "&utm_campaign=${it.pid}" it.link = link it -- 2.40.1