fix(calculate): 콘텐츠별 정산 요율을 정산 조회에 우선 반영한다

Ultraworked with [Sisyphus](https://github.com/code-yeongyu/oh-my-openagent)

Co-authored-by: Sisyphus <clio-agent@sisyphuslabs.ai>
This commit is contained in:
2026-04-07 13:38:27 +09:00
parent 9e6326f08a
commit 3eda0abcfc
3 changed files with 149 additions and 12 deletions

View File

@@ -86,6 +86,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
val pointGroup = CaseBuilder() val pointGroup = CaseBuilder()
.`when`(order.point.loe(0)).then(0) .`when`(order.point.loe(0)).then(0)
.otherwise(1) .otherwise(1)
val contentSettlementRatio = audioContent.settlementRatio.coalesce(creatorSettlementRatio.contentSettlementRatio)
return queryFactory return queryFactory
.select(audioContent.id) .select(audioContent.id)
@@ -108,7 +109,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
orderFormattedDate, orderFormattedDate,
order.can, order.can,
pointGroup, pointGroup,
creatorSettlementRatio.contentSettlementRatio contentSettlementRatio
) )
.fetch() .fetch()
.size .size
@@ -124,6 +125,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
val pointGroup = CaseBuilder() val pointGroup = CaseBuilder()
.`when`(order.point.loe(0)).then(0) .`when`(order.point.loe(0)).then(0)
.otherwise(1) .otherwise(1)
val contentSettlementRatio = audioContent.settlementRatio.coalesce(creatorSettlementRatio.contentSettlementRatio)
return queryFactory return queryFactory
.select( .select(
@@ -137,7 +139,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
order.id.count(), order.id.count(),
order.can.sum(), order.can.sum(),
order.point.sum(), order.point.sum(),
creatorSettlementRatio.contentSettlementRatio contentSettlementRatio
) )
) )
.from(order) .from(order)
@@ -159,7 +161,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
orderFormattedDate, orderFormattedDate,
order.can, order.can,
pointGroup, pointGroup,
creatorSettlementRatio.contentSettlementRatio contentSettlementRatio
) )
.orderBy(member.id.desc(), orderFormattedDate.desc(), audioContent.id.asc()) .orderBy(member.id.desc(), orderFormattedDate.desc(), audioContent.id.asc())
.offset(offset) .offset(offset)
@@ -182,13 +184,23 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
} }
fun getCumulativeSalesByContentTotalCount(): Int { fun getCumulativeSalesByContentTotalCount(): Int {
val pointGroup = CaseBuilder()
.`when`(order.point.loe(0)).then(0)
.otherwise(1)
val contentSettlementRatio = audioContent.settlementRatio.coalesce(creatorSettlementRatio.contentSettlementRatio)
return queryFactory return queryFactory
.select(audioContent.id) .select(audioContent.id)
.from(order) .from(order)
.innerJoin(order.audioContent, audioContent) .innerJoin(order.audioContent, audioContent)
.innerJoin(audioContent.member, member) .innerJoin(audioContent.member, member)
.leftJoin(creatorSettlementRatio)
.on(
member.id.eq(creatorSettlementRatio.member.id)
.and(creatorSettlementRatio.deletedAt.isNull)
)
.where(order.isActive.isTrue) .where(order.isActive.isTrue)
.groupBy(member.id, audioContent.id, order.can) .groupBy(member.id, audioContent.id, order.type, order.can, pointGroup, contentSettlementRatio)
.fetch() .fetch()
.size .size
} }
@@ -197,6 +209,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
val pointGroup = CaseBuilder() val pointGroup = CaseBuilder()
.`when`(order.point.loe(0)).then(0) .`when`(order.point.loe(0)).then(0)
.otherwise(1) .otherwise(1)
val contentSettlementRatio = audioContent.settlementRatio.coalesce(creatorSettlementRatio.contentSettlementRatio)
return queryFactory return queryFactory
.select( .select(
@@ -209,7 +222,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
order.id.count(), order.id.count(),
order.can.sum(), order.can.sum(),
order.point.sum(), order.point.sum(),
creatorSettlementRatio.contentSettlementRatio contentSettlementRatio
) )
) )
.from(order) .from(order)
@@ -227,7 +240,7 @@ class AdminCalculateQueryRepository(private val queryFactory: JPAQueryFactory) {
order.type, order.type,
order.can, order.can,
pointGroup, pointGroup,
creatorSettlementRatio.contentSettlementRatio contentSettlementRatio
) )
.offset(offset) .offset(offset)
.limit(limit) .limit(limit)

View File

@@ -74,18 +74,28 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
memberId: Long memberId: Long
): Int { ): Int {
val orderFormattedDate = getFormattedDate(order.createdAt) 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 return queryFactory
.select(audioContent.id) .select(audioContent.id)
.from(order) .from(order)
.innerJoin(order.audioContent, audioContent) .innerJoin(order.audioContent, audioContent)
.innerJoin(audioContent.member, member) .innerJoin(audioContent.member, member)
.leftJoin(creatorSettlementRatio)
.on(
member.id.eq(creatorSettlementRatio.member.id)
.and(creatorSettlementRatio.deletedAt.isNull)
)
.where( .where(
order.createdAt.goe(startDate) order.createdAt.goe(startDate)
.and(order.createdAt.loe(endDate)) .and(order.createdAt.loe(endDate))
.and(order.isActive.isTrue) .and(order.isActive.isTrue)
.and(order.creator.id.eq(memberId)) .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()) .orderBy(member.id.desc(), orderFormattedDate.desc(), audioContent.id.asc())
.fetch() .fetch()
.size .size
@@ -102,6 +112,7 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
val pointGroup = CaseBuilder() val pointGroup = CaseBuilder()
.`when`(order.point.loe(0)).then(0) .`when`(order.point.loe(0)).then(0)
.otherwise(1) .otherwise(1)
val contentSettlementRatio = audioContent.settlementRatio.coalesce(creatorSettlementRatio.contentSettlementRatio)
return queryFactory return queryFactory
.select( .select(
@@ -115,7 +126,7 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
order.id.count(), order.id.count(),
order.can.sum(), order.can.sum(),
order.point.sum(), order.point.sum(),
creatorSettlementRatio.contentSettlementRatio contentSettlementRatio
) )
) )
.from(order) .from(order)
@@ -138,7 +149,7 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
orderFormattedDate, orderFormattedDate,
order.can, order.can,
pointGroup, pointGroup,
creatorSettlementRatio.contentSettlementRatio contentSettlementRatio
) )
.offset(offset) .offset(offset)
.limit(limit) .limit(limit)
@@ -161,16 +172,26 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
} }
fun getCumulativeSalesByContentTotalCount(memberId: Long): Int { 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 return queryFactory
.select(audioContent.id) .select(audioContent.id)
.from(order) .from(order)
.innerJoin(order.audioContent, audioContent) .innerJoin(order.audioContent, audioContent)
.innerJoin(audioContent.member, member) .innerJoin(audioContent.member, member)
.leftJoin(creatorSettlementRatio)
.on(
member.id.eq(creatorSettlementRatio.member.id)
.and(creatorSettlementRatio.deletedAt.isNull)
)
.where( .where(
audioContent.member.id.eq(memberId) audioContent.member.id.eq(memberId)
.and(order.isActive.isTrue) .and(order.isActive.isTrue)
) )
.groupBy(member.id, audioContent.id, order.can) .groupBy(member.id, audioContent.id, order.type, order.can, pointGroup, contentSettlementRatio)
.fetch() .fetch()
.size .size
} }
@@ -183,6 +204,7 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
val pointGroup = CaseBuilder() val pointGroup = CaseBuilder()
.`when`(order.point.loe(0)).then(0) .`when`(order.point.loe(0)).then(0)
.otherwise(1) .otherwise(1)
val contentSettlementRatio = audioContent.settlementRatio.coalesce(creatorSettlementRatio.contentSettlementRatio)
return queryFactory return queryFactory
.select( .select(
@@ -195,7 +217,7 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
order.id.count(), order.id.count(),
order.can.sum(), order.can.sum(),
order.point.sum(), order.point.sum(),
creatorSettlementRatio.contentSettlementRatio contentSettlementRatio
) )
) )
.from(order) .from(order)
@@ -216,7 +238,7 @@ class CreatorAdminCalculateQueryRepository(private val queryFactory: JPAQueryFac
order.type, order.type,
order.can, order.can,
pointGroup, pointGroup,
creatorSettlementRatio.contentSettlementRatio contentSettlementRatio
) )
.offset(offset) .offset(offset)
.limit(limit) .limit(limit)

View File

@@ -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)
}
}