test #341
| @@ -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 | ||||
|      * | ||||
|   | ||||
| @@ -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<ChatCharacterSearchResponse> { | ||||
|         val characters = chatCharacterRepository.searchCharactersNoPaging(searchTerm) | ||||
|         return characters.map { ChatCharacterSearchResponse.from(it, imageHost) } | ||||
|     } | ||||
| } | ||||
|   | ||||
| @@ -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) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 원작 상세 | ||||
|      */ | ||||
|   | ||||
| @@ -89,6 +89,12 @@ class AdminOriginalWorkService( | ||||
|         return originalWorkRepository.findByIsDeletedFalse(pageable) | ||||
|     } | ||||
|  | ||||
|     /** 원작 검색 (제목/콘텐츠타입/카테고리, 소프트 삭제 제외) - 무페이징 */ | ||||
|     @Transactional(readOnly = true) | ||||
|     fun searchOriginalWorksAll(searchTerm: String): List<OriginalWork> { | ||||
|         return originalWorkRepository.searchNoPaging(searchTerm) | ||||
|     } | ||||
|  | ||||
|     /** 원작에 기존 캐릭터들을 배정 */ | ||||
|     @Transactional | ||||
|     fun assignCharacters(originalWorkId: Long, characterIds: List<Long>) { | ||||
|   | ||||
| @@ -31,7 +31,7 @@ interface ChatCharacterRepository : JpaRepository<ChatCharacter, Long> { | ||||
|     fun countByIsActiveTrueAndCreatedAtGreaterThanEqual(since: java.time.LocalDateTime): Long | ||||
|  | ||||
|     /** | ||||
|      * 이름, 설명, MBTI, 태그로 캐릭터 검색 | ||||
|      * 이름, 설명, MBTI, 태그로 캐릭터 검색 - 페이징 | ||||
|      */ | ||||
|     @Query( | ||||
|         """ | ||||
| @@ -52,6 +52,28 @@ interface ChatCharacterRepository : JpaRepository<ChatCharacter, Long> { | ||||
|         pageable: Pageable | ||||
|     ): Page<ChatCharacter> | ||||
|  | ||||
|     /** | ||||
|      * 이름, 설명, 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<ChatCharacter> | ||||
|  | ||||
|     /** | ||||
|      * 특정 캐릭터와 태그를 공유하는 다른 캐릭터를 무작위로 조회 (현재 캐릭터 제외) | ||||
|      */ | ||||
|   | ||||
| @@ -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<OriginalWork, Long> { | ||||
|     fun findByTitleAndIsDeletedFalse(title: String): OriginalWork? | ||||
|     fun findByIdAndIsDeletedFalse(id: Long): Optional<OriginalWork> | ||||
|     fun findByIsDeletedFalse(pageable: Pageable): Page<OriginalWork> | ||||
|  | ||||
|     /** | ||||
|      * 제목/콘텐츠타입/카테고리 기준 부분 검색 (소프트 삭제 제외) - 무페이징 전체 목록 | ||||
|      */ | ||||
|     @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<OriginalWork> | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user