에이전트 기능 #416

Merged
klaus merged 26 commits from test into main 2026-04-14 06:29:22 +00:00
4 changed files with 79 additions and 8 deletions
Showing only changes of commit 765c087af3 - Show all commits

View File

@@ -3,6 +3,8 @@ package kr.co.vividnext.sodalive.admin.partner.agent.read
import com.querydsl.core.types.dsl.BooleanExpression
import com.querydsl.core.types.dsl.Expressions
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.admin.member.AdminSimpleMemberResponse
import kr.co.vividnext.sodalive.admin.member.QAdminSimpleMemberResponse
import kr.co.vividnext.sodalive.member.MemberRole
import kr.co.vividnext.sodalive.member.QMember
import kr.co.vividnext.sodalive.member.QMember.member
@@ -136,6 +138,25 @@ class AdminAgentReadQueryRepository(
.fetch()
}
fun searchAgentByNickname(searchWord: String, limit: Long): List<AdminSimpleMemberResponse> {
return queryFactory
.select(
QAdminSimpleMemberResponse(
member.id,
member.nickname
)
)
.from(member)
.where(
member.role.eq(MemberRole.AGENT)
.and(member.nickname.contains(searchWord))
.and(member.isActive.isTrue)
)
.orderBy(member.id.desc())
.limit(limit)
.fetch()
}
fun getAssignedCreatorTotalCount(agentId: Long, currentTime: LocalDateTime): Int {
return queryFactory
.select(agentCreatorRelation.id.count())

View File

@@ -1,5 +1,6 @@
package kr.co.vividnext.sodalive.admin.partner.agent.read
import kr.co.vividnext.sodalive.admin.member.AdminSimpleMemberResponse
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.member.MemberRepository
import kr.co.vividnext.sodalive.member.MemberRole
@@ -44,6 +45,13 @@ class AdminAgentReadService(
)
}
@Transactional(readOnly = true)
fun searchAgentByNickname(searchWord: String, size: Int = 20): List<AdminSimpleMemberResponse> {
if (searchWord.length < 2) throw SodaException(messageKey = "admin.member.search_word_min_length")
val limit = if (size <= 0) 20 else size
return queryRepository.searchAgentByNickname(searchWord = searchWord, limit = limit.toLong())
}
@Transactional(readOnly = true)
fun getAssignedCreators(agentId: Long, offset: Long, limit: Long): GetAdminAgentAssignedCreatorResponse {
validateAgent(agentId)

View File

@@ -81,6 +81,20 @@ class AdminAgentReadQueryRepositoryTest @Autowired constructor(
assertEquals(listOf(null, "agent-search"), items.map { it.currentAgentNickname })
}
@Test
@DisplayName("에이전트 닉네임 검색은 활성 AGENT만 id와 nickname으로 반환한다")
fun shouldSearchActiveAgentsByNickname() {
val matchedAgent = saveMember("agent-alpha", MemberRole.AGENT)
saveMember("agent-beta", MemberRole.AGENT, isActive = false)
saveMember("creator-agent", MemberRole.CREATOR)
val items = repository.searchAgentByNickname(searchWord = "agent", limit = 20)
assertEquals(1, items.size)
assertEquals(matchedAgent.id, items.first().id)
assertEquals("agent-alpha", items.first().nickname)
}
@Test
@DisplayName("특정 에이전트 소속 크리에이터 목록은 assignedAt을 포함해 현재 활성 구간만 반환한다")
fun shouldGetAssignedCreatorsForAdminDetail() {
@@ -101,15 +115,15 @@ class AdminAgentReadQueryRepositoryTest @Autowired constructor(
assertEquals(now.minusDays(3), items.first().assignedAt)
}
private fun saveMember(nickname: String, role: MemberRole): Member {
return memberRepository.saveAndFlush(
Member(
email = "$nickname@test.com",
password = "password",
nickname = nickname,
role = role
)
private fun saveMember(nickname: String, role: MemberRole, isActive: Boolean = true): Member {
val member = Member(
email = "$nickname@test.com",
password = "password",
nickname = nickname,
role = role
)
member.isActive = isActive
return memberRepository.saveAndFlush(member)
}
private fun saveRelation(agent: Member, creator: Member, assignedAt: LocalDateTime, unassignedAt: LocalDateTime?) {

View File

@@ -1,5 +1,6 @@
package kr.co.vividnext.sodalive.admin.partner.agent.read
import kr.co.vividnext.sodalive.admin.member.AdminSimpleMemberResponse
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.MemberRepository
@@ -41,6 +42,33 @@ class AdminAgentReadServiceTest {
assertEquals("admin.member.search_word_min_length", exception.messageKey)
}
@Test
@DisplayName("에이전트 닉네임 검색은 두 글자 미만 검색어를 거부한다")
fun shouldRejectTooShortAgentSearchWord() {
val exception = assertThrows(SodaException::class.java) {
service.searchAgentByNickname(searchWord = "a", size = 20)
}
assertEquals("admin.member.search_word_min_length", exception.messageKey)
}
@Test
@DisplayName("에이전트 닉네임 검색은 size가 0 이하이면 20으로 보정한다")
fun shouldDefaultAgentSearchSizeToTwenty() {
val expected = listOf(
AdminSimpleMemberResponse(
id = 11L,
nickname = "agent-a"
)
)
Mockito.`when`(queryRepository.searchAgentByNickname(searchWord = "agent", limit = 20L)).thenReturn(expected)
val actual = service.searchAgentByNickname(searchWord = "agent", size = 0)
assertEquals(expected, actual)
Mockito.verify(queryRepository).searchAgentByNickname(searchWord = "agent", limit = 20L)
}
@Test
@DisplayName("에이전트 목록 조회는 현재 월 summary 필드를 그대로 반환한다")
fun shouldReturnAgentListWithCurrentMonthSummaryFields() {