From 3eda0abcfcf193352c3919f77d8d080c8f7b036b Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 7 Apr 2026 13:38:27 +0900 Subject: [PATCH] =?UTF-8?q?fix(calculate):=20=EC=BD=98=ED=85=90=EC=B8=A0?= =?UTF-8?q?=EB=B3=84=20=EC=A0=95=EC=82=B0=20=EC=9A=94=EC=9C=A8=EC=9D=84=20?= =?UTF-8?q?=EC=A0=95=EC=82=B0=20=EC=A1=B0=ED=9A=8C=EC=97=90=20=EC=9A=B0?= =?UTF-8?q?=EC=84=A0=20=EB=B0=98=EC=98=81=ED=95=9C=EB=8B=A4?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent) Co-authored-by: Sisyphus --- .../AdminCalculateQueryRepository.kt | 25 +++-- .../CreatorAdminCalculateQueryRepository.kt | 34 ++++-- .../ContentSettlementCalculationTest.kt | 102 ++++++++++++++++++ 3 files changed, 149 insertions(+), 12 deletions(-) create mode 100644 src/test/kotlin/kr/co/vividnext/sodalive/admin/calculate/ContentSettlementCalculationTest.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateQueryRepository.kt index 4d8e7de3..3f203e70 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateQueryRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/calculate/AdminCalculateQueryRepository.kt @@ -86,6 +86,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { val pointGroup = CaseBuilder() .`when`(order.point.loe(0)).then(0) .otherwise(1) + val contentSettlementRatio = audioContent.settlementRatio.coalesce(creatorSettlementRatio.contentSettlementRatio) return queryFactory .select(audioContent.id) @@ -108,7 +109,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { orderFormattedDate, order.can, pointGroup, - creatorSettlementRatio.contentSettlementRatio + contentSettlementRatio ) .fetch() .size @@ -124,6 +125,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { val pointGroup = CaseBuilder() .`when`(order.point.loe(0)).then(0) .otherwise(1) + val contentSettlementRatio = audioContent.settlementRatio.coalesce(creatorSettlementRatio.contentSettlementRatio) return queryFactory .select( @@ -137,7 +139,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { order.id.count(), order.can.sum(), order.point.sum(), - creatorSettlementRatio.contentSettlementRatio + contentSettlementRatio ) ) .from(order) @@ -159,7 +161,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { orderFormattedDate, order.can, pointGroup, - creatorSettlementRatio.contentSettlementRatio + contentSettlementRatio ) .orderBy(member.id.desc(), orderFormattedDate.desc(), audioContent.id.asc()) .offset(offset) @@ -182,13 +184,23 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { } fun getCumulativeSalesByContentTotalCount(): Int { + val pointGroup = CaseBuilder() + .`when`(order.point.loe(0)).then(0) + .otherwise(1) + val contentSettlementRatio = audioContent.settlementRatio.coalesce(creatorSettlementRatio.contentSettlementRatio) + return queryFactory .select(audioContent.id) .from(order) .innerJoin(order.audioContent, audioContent) .innerJoin(audioContent.member, member) + .leftJoin(creatorSettlementRatio) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where(order.isActive.isTrue) - .groupBy(member.id, audioContent.id, order.can) + .groupBy(member.id, audioContent.id, order.type, order.can, pointGroup, contentSettlementRatio) .fetch() .size } @@ -197,6 +209,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { val pointGroup = CaseBuilder() .`when`(order.point.loe(0)).then(0) .otherwise(1) + val contentSettlementRatio = audioContent.settlementRatio.coalesce(creatorSettlementRatio.contentSettlementRatio) return queryFactory .select( @@ -209,7 +222,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { order.id.count(), order.can.sum(), order.point.sum(), - creatorSettlementRatio.contentSettlementRatio + contentSettlementRatio ) ) .from(order) @@ -227,7 +240,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) { order.type, order.can, pointGroup, - creatorSettlementRatio.contentSettlementRatio + contentSettlementRatio ) .offset(offset) .limit(limit) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/calculate/CreatorAdminCalculateQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/calculate/CreatorAdminCalculateQueryRepository.kt index eba2d596..0c782da1 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/calculate/CreatorAdminCalculateQueryRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/creator/admin/calculate/CreatorAdminCalculateQueryRepository.kt @@ -74,18 +74,28 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac memberId: Long ): Int { val orderFormattedDate = getFormattedDate(order.createdAt) + val pointGroup = CaseBuilder() + .`when`(order.point.loe(0)).then(0) + .otherwise(1) + val contentSettlementRatio = audioContent.settlementRatio.coalesce(creatorSettlementRatio.contentSettlementRatio) + return queryFactory .select(audioContent.id) .from(order) .innerJoin(order.audioContent, audioContent) .innerJoin(audioContent.member, member) + .leftJoin(creatorSettlementRatio) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( order.createdAt.goe(startDate) .and(order.createdAt.loe(endDate)) .and(order.isActive.isTrue) .and(order.creator.id.eq(memberId)) ) - .groupBy(audioContent.id, order.type, orderFormattedDate, order.can) + .groupBy(audioContent.id, order.type, orderFormattedDate, order.can, pointGroup, contentSettlementRatio) .orderBy(member.id.desc(), orderFormattedDate.desc(), audioContent.id.asc()) .fetch() .size @@ -102,6 +112,7 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac val pointGroup = CaseBuilder() .`when`(order.point.loe(0)).then(0) .otherwise(1) + val contentSettlementRatio = audioContent.settlementRatio.coalesce(creatorSettlementRatio.contentSettlementRatio) return queryFactory .select( @@ -115,7 +126,7 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac order.id.count(), order.can.sum(), order.point.sum(), - creatorSettlementRatio.contentSettlementRatio + contentSettlementRatio ) ) .from(order) @@ -138,7 +149,7 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac orderFormattedDate, order.can, pointGroup, - creatorSettlementRatio.contentSettlementRatio + contentSettlementRatio ) .offset(offset) .limit(limit) @@ -161,16 +172,26 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac } fun getCumulativeSalesByContentTotalCount(memberId: Long): Int { + val pointGroup = CaseBuilder() + .`when`(order.point.loe(0)).then(0) + .otherwise(1) + val contentSettlementRatio = audioContent.settlementRatio.coalesce(creatorSettlementRatio.contentSettlementRatio) + return queryFactory .select(audioContent.id) .from(order) .innerJoin(order.audioContent, audioContent) .innerJoin(audioContent.member, member) + .leftJoin(creatorSettlementRatio) + .on( + member.id.eq(creatorSettlementRatio.member.id) + .and(creatorSettlementRatio.deletedAt.isNull) + ) .where( audioContent.member.id.eq(memberId) .and(order.isActive.isTrue) ) - .groupBy(member.id, audioContent.id, order.can) + .groupBy(member.id, audioContent.id, order.type, order.can, pointGroup, contentSettlementRatio) .fetch() .size } @@ -183,6 +204,7 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac val pointGroup = CaseBuilder() .`when`(order.point.loe(0)).then(0) .otherwise(1) + val contentSettlementRatio = audioContent.settlementRatio.coalesce(creatorSettlementRatio.contentSettlementRatio) return queryFactory .select( @@ -195,7 +217,7 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac order.id.count(), order.can.sum(), order.point.sum(), - creatorSettlementRatio.contentSettlementRatio + contentSettlementRatio ) ) .from(order) @@ -216,7 +238,7 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac order.type, order.can, pointGroup, - creatorSettlementRatio.contentSettlementRatio + contentSettlementRatio ) .offset(offset) .limit(limit) diff --git a/src/test/kotlin/kr/co/vividnext/sodalive/admin/calculate/ContentSettlementCalculationTest.kt b/src/test/kotlin/kr/co/vividnext/sodalive/admin/calculate/ContentSettlementCalculationTest.kt new file mode 100644 index 00000000..75f095e7 --- /dev/null +++ b/src/test/kotlin/kr/co/vividnext/sodalive/admin/calculate/ContentSettlementCalculationTest.kt @@ -0,0 +1,102 @@ +package kr.co.vividnext.sodalive.admin.calculate + +import kr.co.vividnext.sodalive.content.order.OrderType +import org.junit.jupiter.api.Assertions.assertEquals +import org.junit.jupiter.api.DisplayName +import org.junit.jupiter.api.Test + +class ContentSettlementCalculationTest { + @Test + @DisplayName("콘텐츠 정산 응답은 콘텐츠별 정산 요율을 우선 적용한다") + fun shouldApplyExplicitSettlementRatioForCalculateContentResponse() { + val result = GetCalculateContentQueryData( + nickname = "creator", + title = "content", + registrationDate = "2026-04-07", + saleDate = "2026-04-07", + orderType = OrderType.KEEP, + orderPrice = 100, + numberOfPeople = 2, + totalCan = 100, + totalPoint = 0, + settlementRatio = 80 + ).toGetCalculateContentResponse() + + assertEquals("소장", result.orderType) + assertEquals(10_000, result.totalKrw) + assertEquals(660, result.paymentFee) + assertEquals(7_472, result.settlementAmount) + assertEquals(247, result.tax) + assertEquals(7_225, result.depositAmount) + } + + @Test + @DisplayName("콘텐츠 정산 응답은 정산 요율이 없으면 70퍼센트 기본값으로 계산한다") + fun shouldFallbackToDefaultSettlementRatioForCalculateContentResponse() { + val result = GetCalculateContentQueryData( + nickname = "creator", + title = "content", + registrationDate = "2026-04-07", + saleDate = "2026-04-07", + orderType = OrderType.RENTAL, + orderPrice = 70, + numberOfPeople = 1, + totalCan = 100, + totalPoint = 0, + settlementRatio = null + ).toGetCalculateContentResponse() + + assertEquals("대여", result.orderType) + assertEquals(10_000, result.totalKrw) + assertEquals(660, result.paymentFee) + assertEquals(6_538, result.settlementAmount) + assertEquals(216, result.tax) + assertEquals(6_322, result.depositAmount) + } + + @Test + @DisplayName("누적 콘텐츠 정산 응답은 콘텐츠별 정산 요율을 우선 적용한다") + fun shouldApplyExplicitSettlementRatioForCumulativeSalesResponse() { + val result = GetCumulativeSalesByContentQueryData( + nickname = "creator", + title = "content", + registrationDate = "2026-04-07", + orderType = OrderType.KEEP, + orderPrice = 100, + numberOfPeople = 2, + totalCan = 100, + totalPoint = 0, + settlementRatio = 80 + ).toCumulativeSalesByContentItem() + + assertEquals("소장", result.orderType) + assertEquals(10_000, result.totalKrw) + assertEquals(660, result.paymentFee) + assertEquals(7_472, result.settlementAmount) + assertEquals(247, result.tax) + assertEquals(7_225, result.depositAmount) + } + + @Test + @DisplayName("누적 콘텐츠 정산 응답은 정산 요율이 없으면 70퍼센트 기본값으로 계산한다") + fun shouldFallbackToDefaultSettlementRatioForCumulativeSalesResponse() { + val result = GetCumulativeSalesByContentQueryData( + nickname = "creator", + title = "content", + registrationDate = "2026-04-07", + orderType = OrderType.RENTAL, + orderPrice = 70, + numberOfPeople = 1, + totalCan = 100, + totalPoint = 0, + settlementRatio = null + ).toCumulativeSalesByContentItem() + + assertEquals("대여", result.orderType) + assertEquals(10_000, result.totalKrw) + assertEquals(660, result.paymentFee) + assertEquals(6_538, result.settlementAmount) + assertEquals(216, result.tax) + assertEquals(6_322, result.depositAmount) + } +}