feat(original): 원작별 캐릭터 조회 API 추가
This commit is contained in:
		| @@ -2,11 +2,14 @@ package kr.co.vividnext.sodalive.admin.chat.original | ||||
|  | ||||
| import com.amazonaws.services.s3.model.ObjectMetadata | ||||
| import com.fasterxml.jackson.databind.ObjectMapper | ||||
| import kr.co.vividnext.sodalive.admin.chat.character.dto.ChatCharacterSearchListPageResponse | ||||
| import kr.co.vividnext.sodalive.admin.chat.character.dto.ChatCharacterSearchResponse | ||||
| import kr.co.vividnext.sodalive.admin.chat.original.dto.OriginalWorkAssignCharactersRequest | ||||
| import kr.co.vividnext.sodalive.admin.chat.original.dto.OriginalWorkPageResponse | ||||
| import kr.co.vividnext.sodalive.admin.chat.original.dto.OriginalWorkRegisterRequest | ||||
| import kr.co.vividnext.sodalive.admin.chat.original.dto.OriginalWorkResponse | ||||
| import kr.co.vividnext.sodalive.admin.chat.original.dto.OriginalWorkUpdateRequest | ||||
| import kr.co.vividnext.sodalive.admin.chat.original.service.AdminOriginalWorkService | ||||
| import kr.co.vividnext.sodalive.aws.s3.S3Uploader | ||||
| import kr.co.vividnext.sodalive.common.ApiResponse | ||||
| import kr.co.vividnext.sodalive.common.SodaException | ||||
| @@ -34,7 +37,7 @@ import org.springframework.web.multipart.MultipartFile | ||||
| @RequestMapping("/admin/chat/original") | ||||
| @PreAuthorize("hasRole('ADMIN')") | ||||
| class AdminOriginalWorkController( | ||||
|     private val originalWorkService: kr.co.vividnext.sodalive.admin.chat.original.service.AdminOriginalWorkService, | ||||
|     private val originalWorkService: AdminOriginalWorkService, | ||||
|     private val s3Uploader: S3Uploader, | ||||
|     @Value("\${cloud.aws.s3.bucket}") | ||||
|     private val s3Bucket: String, | ||||
| @@ -157,6 +160,27 @@ class AdminOriginalWorkController( | ||||
|         ApiResponse.ok(null) | ||||
|     } | ||||
|  | ||||
|     /** | ||||
|      * 관리자용: 지정 원작에 속한 캐릭터 목록 페이징 조회 | ||||
|      * - 활성 캐릭터만 포함 | ||||
|      * - 응답 항목: 캐릭터 이미지(URL), 이름 | ||||
|      */ | ||||
|     @GetMapping("/{id}/characters") | ||||
|     fun listCharactersOfOriginal( | ||||
|         @PathVariable id: Long, | ||||
|         @RequestParam(defaultValue = "0") page: Int, | ||||
|         @RequestParam(defaultValue = "20") size: Int | ||||
|     ) = run { | ||||
|         val pageRes = originalWorkService.getCharactersOfOriginalWorkPage(id, page, size) | ||||
|         val content = pageRes.content.map { ChatCharacterSearchResponse.from(it, imageHost) } | ||||
|         ApiResponse.ok( | ||||
|             ChatCharacterSearchListPageResponse( | ||||
|                 totalCount = pageRes.totalElements, | ||||
|                 content = content | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     /** 이미지 업로드 공통 처리 */ | ||||
|     private fun uploadImage(originalWorkId: Long, image: MultipartFile): String { | ||||
|         try { | ||||
|   | ||||
| @@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.admin.chat.original.service | ||||
|  | ||||
| import kr.co.vividnext.sodalive.admin.chat.original.dto.OriginalWorkRegisterRequest | ||||
| import kr.co.vividnext.sodalive.admin.chat.original.dto.OriginalWorkUpdateRequest | ||||
| import kr.co.vividnext.sodalive.chat.character.ChatCharacter | ||||
| import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterRepository | ||||
| import kr.co.vividnext.sodalive.chat.original.OriginalWork | ||||
| import kr.co.vividnext.sodalive.chat.original.OriginalWorkRepository | ||||
| @@ -85,10 +86,33 @@ class AdminOriginalWorkService( | ||||
|     /** 원작 페이징 조회 */ | ||||
|     @Transactional(readOnly = true) | ||||
|     fun getOriginalWorkPage(page: Int, size: Int): Page<OriginalWork> { | ||||
|         val pageable = PageRequest.of(page, size, Sort.by("createdAt").descending()) | ||||
|         val safePage = if (page < 0) 0 else page | ||||
|         val safeSize = when { | ||||
|             size <= 0 -> 20 | ||||
|             size > 100 -> 100 | ||||
|             else -> size | ||||
|         } | ||||
|         val pageable = PageRequest.of(safePage, safeSize, Sort.by("createdAt").descending()) | ||||
|         return originalWorkRepository.findByIsDeletedFalse(pageable) | ||||
|     } | ||||
|  | ||||
|     /** 지정 원작에 속한 활성 캐릭터 페이징 조회 (최신순) */ | ||||
|     @Transactional(readOnly = true) | ||||
|     fun getCharactersOfOriginalWorkPage(originalWorkId: Long, page: Int, size: Int): Page<ChatCharacter> { | ||||
|         // 원작 존재 및 소프트 삭제 여부 확인 | ||||
|         originalWorkRepository.findByIdAndIsDeletedFalse(originalWorkId) | ||||
|             .orElseThrow { SodaException("해당 원작을 찾을 수 없습니다") } | ||||
|  | ||||
|         val safePage = if (page < 0) 0 else page | ||||
|         val safeSize = when { | ||||
|             size <= 0 -> 20 | ||||
|             size > 100 -> 100 | ||||
|             else -> size | ||||
|         } | ||||
|         val pageable = PageRequest.of(safePage, safeSize, Sort.by("createdAt").descending()) | ||||
|         return chatCharacterRepository.findByOriginalWorkIdAndIsActiveTrue(originalWorkId, pageable) | ||||
|     } | ||||
|  | ||||
|     /** 원작 검색 (제목/콘텐츠타입/카테고리, 소프트 삭제 제외) - 무페이징 */ | ||||
|     @Transactional(readOnly = true) | ||||
|     fun searchOriginalWorksAll(searchTerm: String): List<OriginalWork> { | ||||
|   | ||||
| @@ -13,6 +13,7 @@ interface ChatCharacterRepository : JpaRepository<ChatCharacter, Long> { | ||||
|     fun findByName(name: String): ChatCharacter? | ||||
|     fun findByIsActiveTrue(pageable: Pageable): Page<ChatCharacter> | ||||
|     fun findByOriginalWorkIdAndIsActiveTrueOrderByCreatedAtDesc(originalWorkId: Long): List<ChatCharacter> | ||||
|     fun findByOriginalWorkIdAndIsActiveTrue(originalWorkId: Long, pageable: Pageable): Page<ChatCharacter> | ||||
|  | ||||
|     /** | ||||
|      * 2주 이내(파라미터 since 이상) 활성 캐릭터 페이징 조회 | ||||
|   | ||||
		Reference in New Issue
	
	Block a user