fix(agent-ratio): 정산 비율 목록을 current history 구조로 정리한다

This commit is contained in:
2026-04-10 13:50:35 +09:00
parent 17065bb4d1
commit 53f37b93fb
5 changed files with 124 additions and 21 deletions

View File

@@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.partner.agent.ratio.AgentSettlementRatio
import kr.co.vividnext.sodalive.partner.agent.ratio.AgentSettlementRatioRepository import kr.co.vividnext.sodalive.partner.agent.ratio.AgentSettlementRatioRepository
import kr.co.vividnext.sodalive.partner.agent.ratio.CreateAgentSettlementRatioRequest import kr.co.vividnext.sodalive.partner.agent.ratio.CreateAgentSettlementRatioRequest
import kr.co.vividnext.sodalive.partner.agent.ratio.GetAgentSettlementRatioResponse import kr.co.vividnext.sodalive.partner.agent.ratio.GetAgentSettlementRatioResponse
import kr.co.vividnext.sodalive.partner.agent.ratio.toGroupedResponseItems
import org.springframework.dao.DataIntegrityViolationException import org.springframework.dao.DataIntegrityViolationException
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional import org.springframework.transaction.annotation.Transactional
@@ -49,6 +50,7 @@ class AdminAgentSettlementRatioService(
fun getAgentSettlementRatio(offset: Long, limit: Long): GetAgentSettlementRatioResponse { fun getAgentSettlementRatio(offset: Long, limit: Long): GetAgentSettlementRatioResponse {
val totalCount = repository.getAgentSettlementRatioTotalCount() val totalCount = repository.getAgentSettlementRatioTotalCount()
val items = repository.getAgentSettlementRatio(offset = offset, limit = limit) val items = repository.getAgentSettlementRatio(offset = offset, limit = limit)
.toGroupedResponseItems()
return GetAgentSettlementRatioResponse(totalCount = totalCount, items = items) return GetAgentSettlementRatioResponse(totalCount = totalCount, items = items)
} }

View File

@@ -13,17 +13,31 @@ interface AgentSettlementRatioRepository :
} }
interface AgentSettlementRatioQueryRepository { interface AgentSettlementRatioQueryRepository {
fun getAgentSettlementRatio(offset: Long, limit: Long): List<GetAgentSettlementRatioItem> fun getAgentSettlementRatio(offset: Long, limit: Long): List<GetAgentSettlementRatioRow>
fun getAgentSettlementRatioTotalCount(): Int fun getAgentSettlementRatioTotalCount(): Int
} }
class AgentSettlementRatioQueryRepositoryImpl( class AgentSettlementRatioQueryRepositoryImpl(
private val queryFactory: JPAQueryFactory private val queryFactory: JPAQueryFactory
) : AgentSettlementRatioQueryRepository { ) : AgentSettlementRatioQueryRepository {
override fun getAgentSettlementRatio(offset: Long, limit: Long): List<GetAgentSettlementRatioItem> { override fun getAgentSettlementRatio(offset: Long, limit: Long): List<GetAgentSettlementRatioRow> {
val memberIds = queryFactory
.select(member.id)
.from(agentSettlementRatio)
.innerJoin(agentSettlementRatio.member, member)
.groupBy(member.id)
.orderBy(member.id.desc())
.offset(offset)
.limit(limit)
.fetch()
if (memberIds.isEmpty()) {
return emptyList()
}
return queryFactory return queryFactory
.select( .select(
QGetAgentSettlementRatioItem( QGetAgentSettlementRatioRow(
member.id, member.id,
member.nickname, member.nickname,
agentSettlementRatio.settlementRatio, agentSettlementRatio.settlementRatio,
@@ -33,16 +47,16 @@ class AgentSettlementRatioQueryRepositoryImpl(
) )
.from(agentSettlementRatio) .from(agentSettlementRatio)
.innerJoin(agentSettlementRatio.member, member) .innerJoin(agentSettlementRatio.member, member)
.orderBy(agentSettlementRatio.id.asc()) .where(member.id.`in`(memberIds))
.offset(offset) .orderBy(member.id.desc(), agentSettlementRatio.effectiveFrom.desc())
.limit(limit)
.fetch() .fetch()
} }
override fun getAgentSettlementRatioTotalCount(): Int { override fun getAgentSettlementRatioTotalCount(): Int {
return queryFactory return queryFactory
.select(agentSettlementRatio.id.count()) .select(member.id.countDistinct())
.from(agentSettlementRatio) .from(agentSettlementRatio)
.innerJoin(agentSettlementRatio.member, member)
.fetchOne() .fetchOne()
?.toInt() ?.toInt()
?: 0 ?: 0

View File

@@ -8,10 +8,41 @@ data class GetAgentSettlementRatioResponse(
val items: List<GetAgentSettlementRatioItem> val items: List<GetAgentSettlementRatioItem>
) )
data class GetAgentSettlementRatioItem @QueryProjection constructor( data class GetAgentSettlementRatioItem(
val memberId: Long,
val nickname: String,
val current: GetAgentSettlementRatioHistoryItem?,
val history: List<GetAgentSettlementRatioHistoryItem>
)
data class GetAgentSettlementRatioHistoryItem(
val settlementRatio: Int,
val effectiveFrom: LocalDateTime,
val effectiveTo: LocalDateTime?
)
data class GetAgentSettlementRatioRow @QueryProjection constructor(
val memberId: Long, val memberId: Long,
val nickname: String, val nickname: String,
val settlementRatio: Int, val settlementRatio: Int,
val effectiveFrom: LocalDateTime, val effectiveFrom: LocalDateTime,
val effectiveTo: LocalDateTime? val effectiveTo: LocalDateTime?
) )
fun List<GetAgentSettlementRatioRow>.toGroupedResponseItems(): List<GetAgentSettlementRatioItem> {
return groupBy { it.memberId to it.nickname }
.map { (memberInfo, rows) ->
GetAgentSettlementRatioItem(
memberId = memberInfo.first,
nickname = memberInfo.second,
current = rows.firstOrNull { it.effectiveTo == null }?.toHistoryItem(),
history = rows.filter { it.effectiveTo != null }.map { it.toHistoryItem() }
)
}
}
private fun GetAgentSettlementRatioRow.toHistoryItem() = GetAgentSettlementRatioHistoryItem(
settlementRatio = settlementRatio,
effectiveFrom = effectiveFrom,
effectiveTo = effectiveTo
)

View File

@@ -1,6 +1,7 @@
package kr.co.vividnext.sodalive.admin.partner.agent.ratio package kr.co.vividnext.sodalive.admin.partner.agent.ratio
import kr.co.vividnext.sodalive.partner.agent.ratio.CreateAgentSettlementRatioRequest import kr.co.vividnext.sodalive.partner.agent.ratio.CreateAgentSettlementRatioRequest
import kr.co.vividnext.sodalive.partner.agent.ratio.GetAgentSettlementRatioHistoryItem
import kr.co.vividnext.sodalive.partner.agent.ratio.GetAgentSettlementRatioItem import kr.co.vividnext.sodalive.partner.agent.ratio.GetAgentSettlementRatioItem
import kr.co.vividnext.sodalive.partner.agent.ratio.GetAgentSettlementRatioResponse import kr.co.vividnext.sodalive.partner.agent.ratio.GetAgentSettlementRatioResponse
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
@@ -60,9 +61,18 @@ class AdminAgentSettlementRatioControllerTest {
GetAgentSettlementRatioItem( GetAgentSettlementRatioItem(
memberId = 31L, memberId = 31L,
nickname = "agent-a", nickname = "agent-a",
current = GetAgentSettlementRatioHistoryItem(
settlementRatio = 15, settlementRatio = 15,
effectiveFrom = LocalDateTime.of(2026, 4, 9, 10, 0), effectiveFrom = LocalDateTime.of(2026, 4, 9, 10, 0),
effectiveTo = null effectiveTo = null
),
history = listOf(
GetAgentSettlementRatioHistoryItem(
settlementRatio = 10,
effectiveFrom = LocalDateTime.of(2026, 4, 1, 0, 0),
effectiveTo = LocalDateTime.of(2026, 4, 9, 10, 0)
)
)
) )
) )
) )
@@ -73,9 +83,10 @@ class AdminAgentSettlementRatioControllerTest {
assertEquals(true, response.success) assertEquals(true, response.success)
assertEquals(1, response.data!!.totalCount) assertEquals(1, response.data!!.totalCount)
assertEquals(15, response.data!!.items[0].settlementRatio) assertEquals(15, response.data!!.items[0].current!!.settlementRatio)
assertEquals(LocalDateTime.of(2026, 4, 9, 10, 0), response.data!!.items[0].effectiveFrom) assertEquals(LocalDateTime.of(2026, 4, 9, 10, 0), response.data!!.items[0].current!!.effectiveFrom)
assertEquals(null, response.data!!.items[0].effectiveTo) assertEquals(null, response.data!!.items[0].current!!.effectiveTo)
assertEquals(1, response.data!!.items[0].history.size)
Mockito.verify(service).getAgentSettlementRatio(offset = 20L, limit = 20L) Mockito.verify(service).getAgentSettlementRatio(offset = 20L, limit = 20L)
} }
} }

View File

@@ -7,7 +7,9 @@ import kr.co.vividnext.sodalive.member.MemberRole
import kr.co.vividnext.sodalive.partner.agent.ratio.AgentSettlementRatio import kr.co.vividnext.sodalive.partner.agent.ratio.AgentSettlementRatio
import kr.co.vividnext.sodalive.partner.agent.ratio.AgentSettlementRatioRepository import kr.co.vividnext.sodalive.partner.agent.ratio.AgentSettlementRatioRepository
import kr.co.vividnext.sodalive.partner.agent.ratio.CreateAgentSettlementRatioRequest import kr.co.vividnext.sodalive.partner.agent.ratio.CreateAgentSettlementRatioRequest
import kr.co.vividnext.sodalive.partner.agent.ratio.GetAgentSettlementRatioHistoryItem
import kr.co.vividnext.sodalive.partner.agent.ratio.GetAgentSettlementRatioItem import kr.co.vividnext.sodalive.partner.agent.ratio.GetAgentSettlementRatioItem
import kr.co.vividnext.sodalive.partner.agent.ratio.GetAgentSettlementRatioRow
import org.junit.jupiter.api.Assertions.assertEquals import org.junit.jupiter.api.Assertions.assertEquals
import org.junit.jupiter.api.Assertions.assertThrows import org.junit.jupiter.api.Assertions.assertThrows
import org.junit.jupiter.api.BeforeEach import org.junit.jupiter.api.BeforeEach
@@ -345,24 +347,67 @@ class AgentSettlementRatioServiceTest {
@DisplayName("관리자는 에이전트 정산 비율 목록을 조회할 수 있다") @DisplayName("관리자는 에이전트 정산 비율 목록을 조회할 수 있다")
fun shouldGetAgentSettlementRatioList() { fun shouldGetAgentSettlementRatioList() {
val items = listOf( val items = listOf(
GetAgentSettlementRatioItem( GetAgentSettlementRatioRow(
memberId = 31L, memberId = 31L,
nickname = "agent-a", nickname = "agent-a",
settlementRatio = 15, settlementRatio = 15,
effectiveFrom = LocalDateTime.of(2026, 4, 9, 10, 0), effectiveFrom = LocalDateTime.of(2026, 4, 9, 10, 0),
effectiveTo = null effectiveTo = null
),
GetAgentSettlementRatioRow(
memberId = 31L,
nickname = "agent-a",
settlementRatio = 10,
effectiveFrom = LocalDateTime.of(2026, 4, 1, 0, 0),
effectiveTo = LocalDateTime.of(2026, 4, 9, 10, 0)
),
GetAgentSettlementRatioRow(
memberId = 21L,
nickname = "agent-b",
settlementRatio = 12,
effectiveFrom = LocalDateTime.of(2026, 3, 1, 0, 0),
effectiveTo = LocalDateTime.of(2026, 3, 15, 0, 0)
) )
) )
Mockito.`when`(repository.getAgentSettlementRatioTotalCount()).thenReturn(1) Mockito.`when`(repository.getAgentSettlementRatioTotalCount()).thenReturn(2)
Mockito.`when`(repository.getAgentSettlementRatio(offset = 20L, limit = 20L)).thenReturn(items) Mockito.`when`(repository.getAgentSettlementRatio(offset = 20L, limit = 20L)).thenReturn(items)
val response = service.getAgentSettlementRatio(offset = 20L, limit = 20L) val response = service.getAgentSettlementRatio(offset = 20L, limit = 20L)
assertEquals(1, response.totalCount) assertEquals(2, response.totalCount)
assertEquals(1, response.items.size) assertEquals(2, response.items.size)
assertEquals(15, response.items[0].settlementRatio) assertEquals(
assertEquals(LocalDateTime.of(2026, 4, 9, 10, 0), response.items[0].effectiveFrom) GetAgentSettlementRatioItem(
assertEquals(null, response.items[0].effectiveTo) memberId = 31L,
nickname = "agent-a",
current = GetAgentSettlementRatioHistoryItem(
settlementRatio = 15,
effectiveFrom = LocalDateTime.of(2026, 4, 9, 10, 0),
effectiveTo = null
),
history = listOf(
GetAgentSettlementRatioHistoryItem(
settlementRatio = 10,
effectiveFrom = LocalDateTime.of(2026, 4, 1, 0, 0),
effectiveTo = LocalDateTime.of(2026, 4, 9, 10, 0)
)
)
),
response.items[0]
)
assertEquals(21L, response.items[1].memberId)
assertEquals("agent-b", response.items[1].nickname)
assertEquals(null, response.items[1].current)
assertEquals(
listOf(
GetAgentSettlementRatioHistoryItem(
settlementRatio = 12,
effectiveFrom = LocalDateTime.of(2026, 3, 1, 0, 0),
effectiveTo = LocalDateTime.of(2026, 3, 15, 0, 0)
)
),
response.items[1].history
)
} }
} }