diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/AdminChatCharacterController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/AdminChatCharacterController.kt index 3718672..38db366 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/AdminChatCharacterController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/AdminChatCharacterController.kt @@ -70,6 +70,19 @@ class AdminChatCharacterController( ApiResponse.ok(response) } + /** + * 캐릭터 검색(관리자) + * - 이름/설명/MBTI/태그 기준 부분 검색, 활성 캐릭터만 대상 + * - 페이징 제거: 전체 목록 반환 + */ + @GetMapping("/search") + fun searchCharacters( + @RequestParam("searchTerm") searchTerm: String + ) = run { + val list = adminService.searchCharactersAll(searchTerm, imageHost) + ApiResponse.ok(list) + } + /** * 캐릭터 상세 정보 조회 API * diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/service/AdminChatCharacterService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/service/AdminChatCharacterService.kt index 07b7c7e..851f345 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/service/AdminChatCharacterService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/character/service/AdminChatCharacterService.kt @@ -65,12 +65,7 @@ class AdminChatCharacterService( } /** - * 캐릭터 검색 (이름, 설명, MBTI, 태그 기반) - * - * @param searchTerm 검색어 - * @param pageable 페이징 정보 - * @param imageHost 이미지 호스트 URL - * @return 검색된 캐릭터 목록 (페이징) + * 캐릭터 검색 (이름, 설명, MBTI, 태그 기반) - 페이징 (기존 사용처 호환용) */ @Transactional(readOnly = true) fun searchCharacters( @@ -81,4 +76,20 @@ class AdminChatCharacterService( val characters = chatCharacterRepository.searchCharacters(searchTerm, pageable) return characters.map { ChatCharacterSearchResponse.from(it, imageHost) } } + + /** + * 캐릭터 검색 (이름, 설명, MBTI, 태그 기반) - 무페이징 + * + * @param searchTerm 검색어 + * @param imageHost 이미지 호스트 URL + * @return 검색된 캐릭터 목록 (전체) + */ + @Transactional(readOnly = true) + fun searchCharactersAll( + searchTerm: String, + imageHost: String = "" + ): List { + val characters = chatCharacterRepository.searchCharactersNoPaging(searchTerm) + return characters.map { ChatCharacterSearchResponse.from(it, imageHost) } + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/original/AdminOriginalWorkController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/original/AdminOriginalWorkController.kt index c307626..a6f90a4 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/original/AdminOriginalWorkController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/original/AdminOriginalWorkController.kt @@ -109,6 +109,20 @@ class AdminOriginalWorkController( ApiResponse.ok(OriginalWorkPageResponse(totalCount = pageRes.totalElements, content = content)) } + /** + * 원작 검색(관리자) + * - 제목/콘텐츠타입/카테고리 기준 부분 검색, 소프트 삭제 제외 + * - 페이징 제거: 전체 목록 반환 + */ + @GetMapping("/search") + fun search( + @RequestParam("searchTerm") searchTerm: String + ) = run { + val list = originalWorkService.searchOriginalWorksAll(searchTerm) + val content = list.map { OriginalWorkResponse.from(it, imageHost) } + ApiResponse.ok(content) + } + /** * 원작 상세 */ diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/original/service/AdminOriginalWorkService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/original/service/AdminOriginalWorkService.kt index 78b009f..212e7b9 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/original/service/AdminOriginalWorkService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/chat/original/service/AdminOriginalWorkService.kt @@ -89,6 +89,12 @@ class AdminOriginalWorkService( return originalWorkRepository.findByIsDeletedFalse(pageable) } + /** 원작 검색 (제목/콘텐츠타입/카테고리, 소프트 삭제 제외) - 무페이징 */ + @Transactional(readOnly = true) + fun searchOriginalWorksAll(searchTerm: String): List { + return originalWorkRepository.searchNoPaging(searchTerm) + } + /** 원작에 기존 캐릭터들을 배정 */ @Transactional fun assignCharacters(originalWorkId: Long, characterIds: List) { diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/repository/ChatCharacterRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/repository/ChatCharacterRepository.kt index 20b74b9..f321f99 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/repository/ChatCharacterRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/repository/ChatCharacterRepository.kt @@ -31,7 +31,7 @@ interface ChatCharacterRepository : JpaRepository { fun countByIsActiveTrueAndCreatedAtGreaterThanEqual(since: java.time.LocalDateTime): Long /** - * 이름, 설명, MBTI, 태그로 캐릭터 검색 + * 이름, 설명, MBTI, 태그로 캐릭터 검색 - 페이징 */ @Query( """ @@ -52,6 +52,28 @@ interface ChatCharacterRepository : JpaRepository { pageable: Pageable ): Page + /** + * 이름, 설명, MBTI, 태그로 캐릭터 검색 - 무페이징 전체 목록 + */ + @Query( + """ + SELECT DISTINCT c FROM ChatCharacter c + LEFT JOIN c.tagMappings tm + LEFT JOIN tm.tag t + WHERE c.isActive = true AND + ( + LOWER(c.name) LIKE LOWER(CONCAT('%', :searchTerm, '%')) OR + LOWER(c.description) LIKE LOWER(CONCAT('%', :searchTerm, '%')) OR + (c.mbti IS NOT NULL AND LOWER(c.mbti) LIKE LOWER(CONCAT('%', :searchTerm, '%'))) OR + (t.tag IS NOT NULL AND LOWER(t.tag) LIKE LOWER(CONCAT('%', :searchTerm, '%'))) + ) + ORDER BY c.createdAt DESC + """ + ) + fun searchCharactersNoPaging( + @Param("searchTerm") searchTerm: String + ): List + /** * 특정 캐릭터와 태그를 공유하는 다른 캐릭터를 무작위로 조회 (현재 캐릭터 제외) */ diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/original/OriginalWorkRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/original/OriginalWorkRepository.kt index 8b69441..4cac026 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/original/OriginalWorkRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/original/OriginalWorkRepository.kt @@ -3,6 +3,8 @@ package kr.co.vividnext.sodalive.chat.original import org.springframework.data.domain.Page import org.springframework.data.domain.Pageable import org.springframework.data.jpa.repository.JpaRepository +import org.springframework.data.jpa.repository.Query +import org.springframework.data.repository.query.Param import org.springframework.stereotype.Repository import java.util.Optional @@ -11,4 +13,22 @@ interface OriginalWorkRepository : JpaRepository { fun findByTitleAndIsDeletedFalse(title: String): OriginalWork? fun findByIdAndIsDeletedFalse(id: Long): Optional fun findByIsDeletedFalse(pageable: Pageable): Page + + /** + * 제목/콘텐츠타입/카테고리 기준 부분 검색 (소프트 삭제 제외) - 무페이징 전체 목록 + */ + @Query( + """ + SELECT ow FROM OriginalWork ow + WHERE ow.isDeleted = false AND ( + LOWER(ow.title) LIKE LOWER(CONCAT('%', :searchTerm, '%')) OR + LOWER(ow.contentType) LIKE LOWER(CONCAT('%', :searchTerm, '%')) OR + LOWER(ow.category) LIKE LOWER(CONCAT('%', :searchTerm, '%')) + ) + ORDER BY ow.createdAt DESC + """ + ) + fun searchNoPaging( + @Param("searchTerm") searchTerm: String + ): List }