From 2f55303d16fb18c41318bf5855c7269e20439edf Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 1 Sep 2025 14:06:01 +0900 Subject: [PATCH] =?UTF-8?q?feat(admin-curation):=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A0=95=ED=95=A9=EC=84=B1=20=EA=B0=9C=EC=84=A0=20?= =?UTF-8?q?=EB=B0=8F=20=ED=99=9C=EC=84=B1=20=EC=BA=90=EB=A6=AD=ED=84=B0=20?= =?UTF-8?q?=EC=88=98=20DB=20=EC=A7=91=EA=B3=84=20=EC=A0=81=EC=9A=A9?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 비활성(삭제) 큐레이션을 목록에서 제외: findByIsActiveTrueOrderBySortOrderAsc 사용 - 리스트 항목에 characterCount 추가 및 DB GROUP BY + COUNT로 직접 집계 - CharacterCurationMappingRepository: 집계용 프로젝션(CharacterCountPerCuration)과 countActiveCharactersByCurations 쿼리 추가 - CharacterCurationAdminService: listAll에서 집계 결과를 활용해 characterCount 매핑 (대량 엔티티 로딩 제거) - CharacterCurationRepository: findMaxSortOrder 쿼리로 신규 등록 정렬 순서 계산에 활용 - 컨트롤러: 캐릭터 리스트 응답 DTO(CharacterCurationCharacterItemResponse) 사용, 이미지 URL은 CloudFront host + imagePath로 조립 --- .../curation/CharacterCurationAdminDto.kt | 3 ++- .../curation/CharacterCurationAdminService.kt | 18 ++++++++++++++++-- .../CharacterCurationMappingRepository.kt | 15 +++++++++++++++ .../repository/CharacterCurationRepository.kt | 1 - 4 files changed, 33 insertions(+), 4 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/curation/CharacterCurationAdminDto.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/curation/CharacterCurationAdminDto.kt index 6266ebd..a5d6a58 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/curation/CharacterCurationAdminDto.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/curation/CharacterCurationAdminDto.kt @@ -29,7 +29,8 @@ data class CharacterCurationListItemResponse( val id: Long, val title: String, val isAdult: Boolean, - val isActive: Boolean + val isActive: Boolean, + val characterCount: Int ) // 관리자 큐레이션 상세 - 캐릭터 리스트 항목 응답 DTO diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/curation/CharacterCurationAdminService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/curation/CharacterCurationAdminService.kt index df5e6d0..77da8f6 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/curation/CharacterCurationAdminService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/curation/CharacterCurationAdminService.kt @@ -125,8 +125,22 @@ class CharacterCurationAdminService( @Transactional(readOnly = true) fun listAll(): List { - return curationRepository.findAllByOrderBySortOrderAsc() - .map { CharacterCurationListItemResponse(it.id!!, it.title, it.isAdult, it.isActive) } + val curations = curationRepository.findByIsActiveTrueOrderBySortOrderAsc() + if (curations.isEmpty()) return emptyList() + + // DB 집계로 활성 캐릭터 수 카운트 + val counts = mappingRepository.countActiveCharactersByCurations(curations) + val countByCurationId: Map = counts.associate { it.curationId to it.count.toInt() } + + return curations.map { curation -> + CharacterCurationListItemResponse( + id = curation.id!!, + title = curation.title, + isAdult = curation.isAdult, + isActive = curation.isActive, + characterCount = countByCurationId[curation.id!!] ?: 0 + ) + } } @Transactional(readOnly = true) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/curation/repository/CharacterCurationMappingRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/curation/repository/CharacterCurationMappingRepository.kt index db42281..0f4a027 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/curation/repository/CharacterCurationMappingRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/curation/repository/CharacterCurationMappingRepository.kt @@ -30,4 +30,19 @@ interface CharacterCurationMappingRepository : JpaRepository + + interface CharacterCountPerCuration { + val curationId: Long + val count: Long + } + + @Query( + "select m.curation.id as curationId, count(m.id) as count " + + "from CharacterCurationMapping m join m.chatCharacter ch " + + "where m.curation in :curations and ch.isActive = true " + + "group by m.curation.id" + ) + fun countActiveCharactersByCurations( + @Param("curations") curations: List + ): List } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/curation/repository/CharacterCurationRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/curation/repository/CharacterCurationRepository.kt index ed658d1..a8d71f0 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/curation/repository/CharacterCurationRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/curation/repository/CharacterCurationRepository.kt @@ -8,7 +8,6 @@ import org.springframework.stereotype.Repository @Repository interface CharacterCurationRepository : JpaRepository { fun findByIsActiveTrueOrderBySortOrderAsc(): List - fun findAllByOrderBySortOrderAsc(): List @Query("SELECT MAX(c.sortOrder) FROM CharacterCuration c WHERE c.isActive = true") fun findMaxSortOrder(): Int?