feat(channel-donation-calculate): 채널 후원 정산 응답에 기간 합계를 추가한다

This commit is contained in:
2026-02-26 19:44:37 +09:00
parent 19d3544c72
commit e6ecf8aca1
17 changed files with 213 additions and 2 deletions

View File

@@ -16,6 +16,29 @@ import java.time.LocalDateTime
class AdminChannelDonationCalculateQueryRepository(
private val queryFactory: JPAQueryFactory
) {
fun getChannelDonationByCreatorTotal(
startDate: LocalDateTime,
endDate: LocalDateTime
): GetAdminChannelDonationSettlementTotalQueryData {
return queryFactory
.select(
QGetAdminChannelDonationSettlementTotalQueryData(
useCan.id.countDistinct(),
useCanCalculate.can.sum()
)
)
.from(useCanCalculate)
.innerJoin(useCanCalculate.useCan, useCan)
.innerJoin(member)
.on(member.id.eq(useCanCalculate.recipientCreatorId))
.where(baseWhereCondition(startDate, endDate))
.fetchOne()
?: GetAdminChannelDonationSettlementTotalQueryData(
count = 0L,
totalCan = 0
)
}
fun getChannelDonationByCreatorTotalCount(startDate: LocalDateTime, endDate: LocalDateTime): Int {
val formattedDate = getFormattedDate(useCan.createdAt)
val distinctGroupKey = Expressions.stringTemplate(

View File

@@ -18,11 +18,12 @@ class AdminChannelDonationCalculateService(
val startDate = startDateStr.convertLocalDateTime()
val endDate = endDateStr.convertLocalDateTime(hour = 23, minute = 59, second = 59)
val total = repository.getChannelDonationByCreatorTotal(startDate, endDate).toResponseTotal()
val totalCount = repository.getChannelDonationByCreatorTotalCount(startDate, endDate)
val items = repository
.getChannelDonationByCreator(startDate, endDate, offset, limit)
.map { it.toResponseItem() }
return GetAdminChannelDonationSettlementResponse(totalCount, items)
return GetAdminChannelDonationSettlementResponse(totalCount, total, items)
}
}

View File

@@ -2,5 +2,6 @@ package kr.co.vividnext.sodalive.admin.calculate.channelDonation
data class GetAdminChannelDonationSettlementResponse(
val totalCount: Int,
val total: GetAdminChannelDonationSettlementTotal,
val items: List<GetAdminChannelDonationSettlementItem>
)

View File

@@ -0,0 +1,13 @@
package kr.co.vividnext.sodalive.admin.calculate.channelDonation
import com.fasterxml.jackson.annotation.JsonProperty
data class GetAdminChannelDonationSettlementTotal(
@JsonProperty("count") val count: Int,
@JsonProperty("totalCan") val totalCan: Int,
@JsonProperty("krw") val krw: Int,
@JsonProperty("fee") val fee: Int,
@JsonProperty("settlementAmount") val settlementAmount: Int,
@JsonProperty("withholdingTax") val withholdingTax: Int,
@JsonProperty("depositAmount") val depositAmount: Int
)

View File

@@ -0,0 +1,24 @@
package kr.co.vividnext.sodalive.admin.calculate.channelDonation
import com.querydsl.core.annotations.QueryProjection
import kr.co.vividnext.sodalive.calculate.channelDonation.ChannelDonationSettlementCalculator
data class GetAdminChannelDonationSettlementTotalQueryData @QueryProjection constructor(
val count: Long?,
val totalCan: Int?
) {
fun toResponseTotal(): GetAdminChannelDonationSettlementTotal {
val totalCan = totalCan ?: 0
val settlement = ChannelDonationSettlementCalculator.calculate(totalCan)
return GetAdminChannelDonationSettlementTotal(
count = (count ?: 0L).toInt(),
totalCan = totalCan,
krw = settlement.krw,
fee = settlement.fee,
settlementAmount = settlement.settlementAmount,
withholdingTax = settlement.withholdingTax,
depositAmount = settlement.depositAmount
)
}
}

View File

@@ -15,6 +15,28 @@ import java.time.LocalDateTime
class CreatorAdminChannelDonationCalculateQueryRepository(
private val queryFactory: JPAQueryFactory
) {
fun getChannelDonationSettlementTotal(
startDate: LocalDateTime,
endDate: LocalDateTime,
memberId: Long
): GetCreatorChannelDonationSettlementTotalQueryData {
return queryFactory
.select(
QGetCreatorChannelDonationSettlementTotalQueryData(
useCan.id.countDistinct(),
useCanCalculate.can.sum()
)
)
.from(useCanCalculate)
.innerJoin(useCanCalculate.useCan, useCan)
.where(baseWhereCondition(startDate, endDate, memberId))
.fetchOne()
?: GetCreatorChannelDonationSettlementTotalQueryData(
count = 0L,
totalCan = 0
)
}
fun getChannelDonationTotalCount(startDate: LocalDateTime, endDate: LocalDateTime, memberId: Long): Int {
val formattedDate = getFormattedDate(useCan.createdAt)

View File

@@ -20,11 +20,12 @@ class CreatorAdminChannelDonationCalculateService(
val startDate = startDateStr.convertLocalDateTime()
val endDate = endDateStr.convertLocalDateTime(hour = 23, minute = 59, second = 59)
val total = repository.getChannelDonationSettlementTotal(startDate, endDate, memberId).toResponseTotal()
val totalCount = repository.getChannelDonationTotalCount(startDate, endDate, memberId)
val items = repository
.getChannelDonation(startDate, endDate, memberId, offset, limit)
.map { it.toResponseItem(creatorNickname) }
return GetCreatorChannelDonationSettlementResponse(totalCount, items)
return GetCreatorChannelDonationSettlementResponse(totalCount, total, items)
}
}

View File

@@ -2,5 +2,6 @@ package kr.co.vividnext.sodalive.creator.admin.calculate.channelDonation
data class GetCreatorChannelDonationSettlementResponse(
val totalCount: Int,
val total: GetCreatorChannelDonationSettlementTotal,
val items: List<GetCreatorChannelDonationSettlementItem>
)

View File

@@ -0,0 +1,13 @@
package kr.co.vividnext.sodalive.creator.admin.calculate.channelDonation
import com.fasterxml.jackson.annotation.JsonProperty
data class GetCreatorChannelDonationSettlementTotal(
@JsonProperty("count") val count: Int,
@JsonProperty("totalCan") val totalCan: Int,
@JsonProperty("krw") val krw: Int,
@JsonProperty("fee") val fee: Int,
@JsonProperty("settlementAmount") val settlementAmount: Int,
@JsonProperty("withholdingTax") val withholdingTax: Int,
@JsonProperty("depositAmount") val depositAmount: Int
)

View File

@@ -0,0 +1,24 @@
package kr.co.vividnext.sodalive.creator.admin.calculate.channelDonation
import com.querydsl.core.annotations.QueryProjection
import kr.co.vividnext.sodalive.calculate.channelDonation.ChannelDonationSettlementCalculator
data class GetCreatorChannelDonationSettlementTotalQueryData @QueryProjection constructor(
val count: Long?,
val totalCan: Int?
) {
fun toResponseTotal(): GetCreatorChannelDonationSettlementTotal {
val totalCan = totalCan ?: 0
val settlement = ChannelDonationSettlementCalculator.calculate(totalCan)
return GetCreatorChannelDonationSettlementTotal(
count = (count ?: 0L).toInt(),
totalCan = totalCan,
krw = settlement.krw,
fee = settlement.fee,
settlementAmount = settlement.settlementAmount,
withholdingTax = settlement.withholdingTax,
depositAmount = settlement.depositAmount
)
}
}

View File

@@ -22,6 +22,15 @@ class AdminChannelDonationCalculateControllerTest {
fun shouldForwardDateRangeAndPageableToService() {
val response = GetAdminChannelDonationSettlementResponse(
totalCount = 1,
total = GetAdminChannelDonationSettlementTotal(
count = 5,
totalCan = 35,
krw = 3500,
fee = 231,
settlementAmount = 2770,
withholdingTax = 91,
depositAmount = 2679
),
items = listOf(
GetAdminChannelDonationSettlementItem(
date = "2026-02-26",
@@ -54,6 +63,7 @@ class AdminChannelDonationCalculateControllerTest {
assertEquals(true, apiResponse.success)
assertEquals(1, apiResponse.data!!.totalCount)
assertEquals(35, apiResponse.data!!.total.totalCan)
assertEquals("creator-a", apiResponse.data!!.items[0].creator)
assertEquals(2, apiResponse.data!!.items[0].count)
assertEquals(20, apiResponse.data!!.items[0].totalCan)

View File

@@ -65,9 +65,12 @@ class AdminChannelDonationCalculateQueryRepositoryTest @Autowired constructor(
val startDate = LocalDateTime.of(2026, 2, 20, 0, 0, 0)
val endDate = LocalDateTime.of(2026, 2, 20, 23, 59, 59)
val total = repository.getChannelDonationByCreatorTotal(startDate, endDate)
val totalCount = repository.getChannelDonationByCreatorTotalCount(startDate, endDate)
val items = repository.getChannelDonationByCreator(startDate, endDate, offset = 0, limit = 20)
assertEquals(1L, total.count)
assertEquals(50, total.totalCan)
assertEquals(1, totalCount)
assertEquals(1, items.size)
assertEquals("2026-02-20", items[0].date)

View File

@@ -26,7 +26,17 @@ class AdminChannelDonationCalculateServiceTest {
count = 3L,
totalCan = 100
)
val totalQueryData = GetAdminChannelDonationSettlementTotalQueryData(
count = 8L,
totalCan = 250
)
Mockito.`when`(
repository.getChannelDonationByCreatorTotal(
"2026-02-20".convertLocalDateTime(),
"2026-02-21".convertLocalDateTime(hour = 23, minute = 59, second = 59)
)
).thenReturn(totalQueryData)
Mockito.`when`(
repository.getChannelDonationByCreatorTotalCount(
"2026-02-20".convertLocalDateTime(),
@@ -50,6 +60,9 @@ class AdminChannelDonationCalculateServiceTest {
)
assertEquals(1, result.totalCount)
assertEquals(8, result.total.count)
assertEquals(250, result.total.totalCan)
assertEquals(25_000, result.total.krw)
assertEquals(1, result.items.size)
assertEquals("2026-02-26", result.items[0].date)
assertEquals("creator-a", result.items[0].creator)
@@ -58,6 +71,10 @@ class AdminChannelDonationCalculateServiceTest {
assertEquals(10_000, result.items[0].krw)
assertEquals(660, result.items[0].fee)
Mockito.verify(repository).getChannelDonationByCreatorTotal(
"2026-02-20".convertLocalDateTime(),
"2026-02-21".convertLocalDateTime(hour = 23, minute = 59, second = 59)
)
Mockito.verify(repository).getChannelDonationByCreatorTotalCount(
"2026-02-20".convertLocalDateTime(),
"2026-02-21".convertLocalDateTime(hour = 23, minute = 59, second = 59)

View File

@@ -42,6 +42,15 @@ class CreatorAdminChannelDonationCalculateControllerTest {
val member = createMember(7L)
val response = GetCreatorChannelDonationSettlementResponse(
totalCount = 1,
total = GetCreatorChannelDonationSettlementTotal(
count = 7,
totalCan = 40,
krw = 4000,
fee = 264,
settlementAmount = 3176,
withholdingTax = 105,
depositAmount = 3071
),
items = listOf(
GetCreatorChannelDonationSettlementItem(
date = "2026-02-26",
@@ -77,6 +86,7 @@ class CreatorAdminChannelDonationCalculateControllerTest {
assertEquals(true, apiResponse.success)
assertEquals(1, apiResponse.data!!.totalCount)
assertEquals(40, apiResponse.data!!.total.totalCan)
assertEquals("creator-self", apiResponse.data!!.items[0].creator)
assertEquals(4, apiResponse.data!!.items[0].count)
assertEquals(10, apiResponse.data!!.items[0].totalCan)

View File

@@ -65,9 +65,12 @@ class CreatorAdminChannelDonationCalculateQueryRepositoryTest @Autowired constru
val startDate = LocalDateTime.of(2026, 2, 20, 0, 0, 0)
val endDate = LocalDateTime.of(2026, 2, 20, 23, 59, 59)
val total = repository.getChannelDonationSettlementTotal(startDate, endDate, creator.id!!)
val totalCount = repository.getChannelDonationTotalCount(startDate, endDate, creator.id!!)
val items = repository.getChannelDonation(startDate, endDate, creator.id!!, offset = 0, limit = 20)
assertEquals(1L, total.count)
assertEquals(50, total.totalCan)
assertEquals(1, totalCount)
assertEquals(1, items.size)
assertEquals("2026-02-20", items[0].date)

View File

@@ -25,7 +25,18 @@ class CreatorAdminChannelDonationCalculateServiceTest {
count = 2L,
totalCan = 50
)
val totalQueryData = GetCreatorChannelDonationSettlementTotalQueryData(
count = 4L,
totalCan = 120
)
Mockito.`when`(
repository.getChannelDonationSettlementTotal(
"2026-02-20".convertLocalDateTime(),
"2026-02-21".convertLocalDateTime(hour = 23, minute = 59, second = 59),
7L
)
).thenReturn(totalQueryData)
Mockito.`when`(
repository.getChannelDonationTotalCount(
"2026-02-20".convertLocalDateTime(),
@@ -53,12 +64,20 @@ class CreatorAdminChannelDonationCalculateServiceTest {
)
assertEquals(1, result.totalCount)
assertEquals(4, result.total.count)
assertEquals(120, result.total.totalCan)
assertEquals(12_000, result.total.krw)
assertEquals(1, result.items.size)
assertEquals("creator-self", result.items[0].creator)
assertEquals(2, result.items[0].count)
assertEquals(50, result.items[0].totalCan)
assertEquals(5_000, result.items[0].krw)
Mockito.verify(repository).getChannelDonationSettlementTotal(
"2026-02-20".convertLocalDateTime(),
"2026-02-21".convertLocalDateTime(hour = 23, minute = 59, second = 59),
7L
)
Mockito.verify(repository).getChannelDonationTotalCount(
"2026-02-20".convertLocalDateTime(),
"2026-02-21".convertLocalDateTime(hour = 23, minute = 59, second = 59),