fix(channel-donation): 관리자 채널후원 정산 조회를 날짜별과 크리에이터별로 분리하고 엑셀 다운로드를 추가한다
This commit is contained in:
@@ -1,11 +1,15 @@
|
||||
package kr.co.vividnext.sodalive.admin.calculate.channelDonation
|
||||
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertNotNull
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
import org.mockito.Mockito
|
||||
import org.springframework.core.io.InputStreamResource
|
||||
import org.springframework.data.domain.PageRequest
|
||||
import org.springframework.http.HttpHeaders
|
||||
import java.io.ByteArrayInputStream
|
||||
|
||||
class AdminChannelDonationCalculateControllerTest {
|
||||
private lateinit var service: AdminChannelDonationCalculateService
|
||||
@@ -18,8 +22,8 @@ class AdminChannelDonationCalculateControllerTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("관리자 컨트롤러는 날짜/페이지 파라미터를 서비스로 전달한다")
|
||||
fun shouldForwardDateRangeAndPageableToService() {
|
||||
@DisplayName("관리자 컨트롤러는 날짜별 조회 파라미터를 서비스로 전달한다")
|
||||
fun shouldForwardDateRangeAndPageableToDateService() {
|
||||
val response = GetAdminChannelDonationSettlementResponse(
|
||||
totalCount = 1,
|
||||
total = GetAdminChannelDonationSettlementTotal(
|
||||
@@ -47,7 +51,7 @@ class AdminChannelDonationCalculateControllerTest {
|
||||
)
|
||||
|
||||
Mockito.`when`(
|
||||
service.getChannelDonationByCreator(
|
||||
service.getChannelDonationByDate(
|
||||
startDateStr = "2026-02-20",
|
||||
endDateStr = "2026-02-21",
|
||||
offset = 15L,
|
||||
@@ -55,7 +59,7 @@ class AdminChannelDonationCalculateControllerTest {
|
||||
)
|
||||
).thenReturn(response)
|
||||
|
||||
val apiResponse = controller.getChannelDonationByCreator(
|
||||
val apiResponse = controller.getChannelDonationByDate(
|
||||
startDateStr = "2026-02-20",
|
||||
endDateStr = "2026-02-21",
|
||||
pageable = PageRequest.of(1, 15)
|
||||
@@ -68,11 +72,98 @@ class AdminChannelDonationCalculateControllerTest {
|
||||
assertEquals(2, apiResponse.data!!.items[0].count)
|
||||
assertEquals(20, apiResponse.data!!.items[0].totalCan)
|
||||
|
||||
Mockito.verify(service).getChannelDonationByCreator(
|
||||
Mockito.verify(service).getChannelDonationByDate(
|
||||
startDateStr = "2026-02-20",
|
||||
endDateStr = "2026-02-21",
|
||||
offset = 15L,
|
||||
limit = 15L
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("관리자 컨트롤러는 크리에이터별 조회 파라미터를 서비스로 전달한다")
|
||||
fun shouldForwardDateRangeAndPageableToCreatorService() {
|
||||
val response = GetAdminChannelDonationSettlementByCreatorResponse(
|
||||
totalCount = 1,
|
||||
total = GetAdminChannelDonationSettlementTotal(
|
||||
count = 5,
|
||||
totalCan = 35,
|
||||
krw = 3500,
|
||||
fee = 231,
|
||||
settlementAmount = 2770,
|
||||
withholdingTax = 91,
|
||||
depositAmount = 2679
|
||||
),
|
||||
items = listOf(
|
||||
GetAdminChannelDonationSettlementByCreatorItem(
|
||||
creator = "creator-a",
|
||||
count = 2,
|
||||
totalCan = 20,
|
||||
krw = 2000,
|
||||
fee = 132,
|
||||
settlementAmount = 1588,
|
||||
withholdingTax = 52,
|
||||
depositAmount = 1536
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
Mockito.`when`(
|
||||
service.getChannelDonationByCreator(
|
||||
startDateStr = "2026-02-20",
|
||||
endDateStr = "2026-02-21",
|
||||
offset = 0L,
|
||||
limit = 20L
|
||||
)
|
||||
).thenReturn(response)
|
||||
|
||||
val apiResponse = controller.getChannelDonationByCreator(
|
||||
startDateStr = "2026-02-20",
|
||||
endDateStr = "2026-02-21",
|
||||
pageable = PageRequest.of(0, 20)
|
||||
)
|
||||
|
||||
assertEquals(true, apiResponse.success)
|
||||
assertEquals(1, apiResponse.data!!.totalCount)
|
||||
assertEquals("creator-a", apiResponse.data!!.items[0].creator)
|
||||
|
||||
Mockito.verify(service).getChannelDonationByCreator(
|
||||
startDateStr = "2026-02-20",
|
||||
endDateStr = "2026-02-21",
|
||||
offset = 0L,
|
||||
limit = 20L
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("관리자 컨트롤러는 크리에이터별 정산 엑셀을 다운로드한다")
|
||||
fun shouldDownloadCreatorSettlementExcel() {
|
||||
Mockito.`when`(
|
||||
service.downloadChannelDonationByCreatorExcel(
|
||||
startDateStr = "2026-02-01",
|
||||
endDateStr = "2026-02-29"
|
||||
)
|
||||
).thenReturn(ByteArrayInputStream(byteArrayOf(1, 2, 3)))
|
||||
|
||||
val response = controller.downloadChannelDonationByCreatorExcel(
|
||||
startDateStr = "2026-02-01",
|
||||
endDateStr = "2026-02-29"
|
||||
)
|
||||
|
||||
assertEquals(200, response.statusCode.value())
|
||||
val contentDispositionHeader = response.headers.getFirst(HttpHeaders.CONTENT_DISPOSITION)
|
||||
assertNotNull(contentDispositionHeader)
|
||||
assertEquals(true, contentDispositionHeader?.contains("attachment; filename*="))
|
||||
assertEquals(
|
||||
"application/vnd.openxmlformats-officedocument.spreadsheetml.sheet",
|
||||
response.headers.contentType.toString()
|
||||
)
|
||||
assertNotNull(response.body)
|
||||
assertEquals(true, response.body is InputStreamResource)
|
||||
|
||||
Mockito.verify(service).downloadChannelDonationByCreatorExcel(
|
||||
startDateStr = "2026-02-01",
|
||||
endDateStr = "2026-02-29"
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -40,8 +40,8 @@ class AdminChannelDonationCalculateQueryRepositoryTest @Autowired constructor(
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("동일 후원의 분할 정산 레코드는 건수를 중복 집계하지 않는다")
|
||||
fun shouldCountDistinctUseCanWhenDonationIsSplitAcrossCalculations() {
|
||||
@DisplayName("날짜별 조회는 동일 후원의 분할 정산 레코드를 건수 중복 집계하지 않는다")
|
||||
fun shouldCountDistinctUseCanWhenDonationIsSplitAcrossCalculationsByDate() {
|
||||
val creator = saveMember(nickname = "creator-admin", role = MemberRole.CREATOR)
|
||||
val sender = saveMember(nickname = "sender-admin", role = MemberRole.USER)
|
||||
val useCan = saveUseCan(member = sender, can = 50, rewardCan = 0)
|
||||
@@ -65,9 +65,9 @@ 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)
|
||||
val total = repository.getChannelDonationByDateTotal(startDate, endDate)
|
||||
val totalCount = repository.getChannelDonationByDateTotalCount(startDate, endDate)
|
||||
val items = repository.getChannelDonationByDate(startDate, endDate, offset = 0, limit = 20)
|
||||
|
||||
assertEquals(1L, total.count)
|
||||
assertEquals(50, total.totalCan)
|
||||
@@ -79,6 +79,55 @@ class AdminChannelDonationCalculateQueryRepositoryTest @Autowired constructor(
|
||||
assertEquals(50, items[0].totalCan)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("크리에이터별 조회는 크리에이터 기준으로 집계한다")
|
||||
fun shouldAggregateByCreator() {
|
||||
val creatorA = saveMember(nickname = "creator-a", role = MemberRole.CREATOR)
|
||||
val creatorB = saveMember(nickname = "creator-b", role = MemberRole.CREATOR)
|
||||
val sender = saveMember(nickname = "sender-admin", role = MemberRole.USER)
|
||||
|
||||
val useCanA = saveUseCan(member = sender, can = 70, rewardCan = 0)
|
||||
val useCanB = saveUseCan(member = sender, can = 30, rewardCan = 0)
|
||||
|
||||
saveUseCanCalculate(
|
||||
useCan = useCanA,
|
||||
recipientCreatorId = creatorA.id!!,
|
||||
can = 70,
|
||||
paymentGateway = PaymentGateway.PG
|
||||
)
|
||||
saveUseCanCalculate(
|
||||
useCan = useCanB,
|
||||
recipientCreatorId = creatorB.id!!,
|
||||
can = 30,
|
||||
paymentGateway = PaymentGateway.PG
|
||||
)
|
||||
|
||||
updateUseCanCreatedAt(useCanA.id!!, LocalDateTime.of(2026, 2, 20, 10, 0, 0))
|
||||
updateUseCanCreatedAt(useCanB.id!!, LocalDateTime.of(2026, 2, 20, 11, 0, 0))
|
||||
entityManager.flush()
|
||||
entityManager.clear()
|
||||
|
||||
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)
|
||||
val excelItems = repository.getChannelDonationByCreatorForExcel(startDate, endDate)
|
||||
|
||||
assertEquals(2L, total.count)
|
||||
assertEquals(100, total.totalCan)
|
||||
assertEquals(2, totalCount)
|
||||
assertEquals(2, items.size)
|
||||
assertEquals(2, excelItems.size)
|
||||
assertEquals("creator-b", items[0].creator)
|
||||
assertEquals(1L, items[0].count)
|
||||
assertEquals(30, items[0].totalCan)
|
||||
assertEquals("creator-a", items[1].creator)
|
||||
assertEquals(1L, items[1].count)
|
||||
assertEquals(70, items[1].totalCan)
|
||||
}
|
||||
|
||||
private fun saveMember(nickname: String, role: MemberRole): Member {
|
||||
return memberRepository.saveAndFlush(
|
||||
Member(
|
||||
|
||||
@@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.admin.calculate.channelDonation
|
||||
|
||||
import kr.co.vividnext.sodalive.extensions.convertLocalDateTime
|
||||
import org.junit.jupiter.api.Assertions.assertEquals
|
||||
import org.junit.jupiter.api.Assertions.assertTrue
|
||||
import org.junit.jupiter.api.BeforeEach
|
||||
import org.junit.jupiter.api.DisplayName
|
||||
import org.junit.jupiter.api.Test
|
||||
@@ -18,8 +19,8 @@ class AdminChannelDonationCalculateServiceTest {
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("관리자 정산 조회는 날짜 범위를 변환하고 크리에이터 그룹 응답을 반환한다")
|
||||
fun shouldConvertDateRangeAndReturnCreatorGroupedItems() {
|
||||
@DisplayName("관리자 날짜별 정산 조회는 날짜 범위를 변환하고 그룹 응답을 반환한다")
|
||||
fun shouldConvertDateRangeAndReturnDateGroupedItems() {
|
||||
val queryData = GetAdminChannelDonationSettlementQueryData(
|
||||
date = "2026-02-26",
|
||||
creator = "creator-a",
|
||||
@@ -31,6 +32,75 @@ class AdminChannelDonationCalculateServiceTest {
|
||||
totalCan = 250
|
||||
)
|
||||
|
||||
Mockito.`when`(
|
||||
repository.getChannelDonationByDateTotal(
|
||||
"2026-02-20".convertLocalDateTime(),
|
||||
"2026-02-21".convertLocalDateTime(hour = 23, minute = 59, second = 59)
|
||||
)
|
||||
).thenReturn(totalQueryData)
|
||||
Mockito.`when`(
|
||||
repository.getChannelDonationByDateTotalCount(
|
||||
"2026-02-20".convertLocalDateTime(),
|
||||
"2026-02-21".convertLocalDateTime(hour = 23, minute = 59, second = 59)
|
||||
)
|
||||
).thenReturn(1)
|
||||
Mockito.`when`(
|
||||
repository.getChannelDonationByDate(
|
||||
"2026-02-20".convertLocalDateTime(),
|
||||
"2026-02-21".convertLocalDateTime(hour = 23, minute = 59, second = 59),
|
||||
0L,
|
||||
20L
|
||||
)
|
||||
).thenReturn(listOf(queryData))
|
||||
|
||||
val result = service.getChannelDonationByDate(
|
||||
startDateStr = "2026-02-20",
|
||||
endDateStr = "2026-02-21",
|
||||
offset = 0,
|
||||
limit = 20
|
||||
)
|
||||
|
||||
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)
|
||||
assertEquals(3, result.items[0].count)
|
||||
assertEquals(100, result.items[0].totalCan)
|
||||
assertEquals(10_000, result.items[0].krw)
|
||||
assertEquals(660, result.items[0].fee)
|
||||
|
||||
Mockito.verify(repository).getChannelDonationByDateTotal(
|
||||
"2026-02-20".convertLocalDateTime(),
|
||||
"2026-02-21".convertLocalDateTime(hour = 23, minute = 59, second = 59)
|
||||
)
|
||||
Mockito.verify(repository).getChannelDonationByDateTotalCount(
|
||||
"2026-02-20".convertLocalDateTime(),
|
||||
"2026-02-21".convertLocalDateTime(hour = 23, minute = 59, second = 59)
|
||||
)
|
||||
Mockito.verify(repository).getChannelDonationByDate(
|
||||
"2026-02-20".convertLocalDateTime(),
|
||||
"2026-02-21".convertLocalDateTime(hour = 23, minute = 59, second = 59),
|
||||
0L,
|
||||
20L
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("관리자 크리에이터별 정산 조회는 날짜 범위를 변환하고 크리에이터 그룹 응답을 반환한다")
|
||||
fun shouldConvertDateRangeAndReturnCreatorGroupedItems() {
|
||||
val queryData = GetAdminChannelDonationSettlementByCreatorQueryData(
|
||||
creator = "creator-a",
|
||||
count = 3L,
|
||||
totalCan = 100
|
||||
)
|
||||
val totalQueryData = GetAdminChannelDonationSettlementTotalQueryData(
|
||||
count = 8L,
|
||||
totalCan = 250
|
||||
)
|
||||
|
||||
Mockito.`when`(
|
||||
repository.getChannelDonationByCreatorTotal(
|
||||
"2026-02-20".convertLocalDateTime(),
|
||||
@@ -62,14 +132,10 @@ 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)
|
||||
assertEquals(3, result.items[0].count)
|
||||
assertEquals(100, result.items[0].totalCan)
|
||||
assertEquals(10_000, result.items[0].krw)
|
||||
assertEquals(660, result.items[0].fee)
|
||||
|
||||
Mockito.verify(repository).getChannelDonationByCreatorTotal(
|
||||
"2026-02-20".convertLocalDateTime(),
|
||||
@@ -86,4 +152,35 @@ class AdminChannelDonationCalculateServiceTest {
|
||||
20L
|
||||
)
|
||||
}
|
||||
|
||||
@Test
|
||||
@DisplayName("관리자 크리에이터별 정산 엑셀 다운로드는 xlsx 바이트를 생성한다")
|
||||
fun shouldGenerateCreatorSettlementExcelBytes() {
|
||||
Mockito.`when`(
|
||||
repository.getChannelDonationByCreatorForExcel(
|
||||
"2026-02-20".convertLocalDateTime(),
|
||||
"2026-02-21".convertLocalDateTime(hour = 23, minute = 59, second = 59)
|
||||
)
|
||||
).thenReturn(
|
||||
listOf(
|
||||
GetAdminChannelDonationSettlementByCreatorQueryData(
|
||||
creator = "creator-a",
|
||||
count = 3L,
|
||||
totalCan = 100
|
||||
)
|
||||
)
|
||||
)
|
||||
|
||||
val response = service.downloadChannelDonationByCreatorExcel(
|
||||
startDateStr = "2026-02-20",
|
||||
endDateStr = "2026-02-21"
|
||||
)
|
||||
|
||||
assertTrue(response.readAllBytes().isNotEmpty())
|
||||
|
||||
Mockito.verify(repository).getChannelDonationByCreatorForExcel(
|
||||
"2026-02-20".convertLocalDateTime(),
|
||||
"2026-02-21".convertLocalDateTime(hour = 23, minute = 59, second = 59)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user