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.amazonaws.services.s3.model.ObjectMetadata | ||||||
| import com.fasterxml.jackson.databind.ObjectMapper | 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.OriginalWorkAssignCharactersRequest | ||||||
| import kr.co.vividnext.sodalive.admin.chat.original.dto.OriginalWorkPageResponse | 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.OriginalWorkRegisterRequest | ||||||
| import kr.co.vividnext.sodalive.admin.chat.original.dto.OriginalWorkResponse | 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.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.aws.s3.S3Uploader | ||||||
| import kr.co.vividnext.sodalive.common.ApiResponse | import kr.co.vividnext.sodalive.common.ApiResponse | ||||||
| import kr.co.vividnext.sodalive.common.SodaException | import kr.co.vividnext.sodalive.common.SodaException | ||||||
| @@ -34,7 +37,7 @@ import org.springframework.web.multipart.MultipartFile | |||||||
| @RequestMapping("/admin/chat/original") | @RequestMapping("/admin/chat/original") | ||||||
| @PreAuthorize("hasRole('ADMIN')") | @PreAuthorize("hasRole('ADMIN')") | ||||||
| class AdminOriginalWorkController( | class AdminOriginalWorkController( | ||||||
|     private val originalWorkService: kr.co.vividnext.sodalive.admin.chat.original.service.AdminOriginalWorkService, |     private val originalWorkService: AdminOriginalWorkService, | ||||||
|     private val s3Uploader: S3Uploader, |     private val s3Uploader: S3Uploader, | ||||||
|     @Value("\${cloud.aws.s3.bucket}") |     @Value("\${cloud.aws.s3.bucket}") | ||||||
|     private val s3Bucket: String, |     private val s3Bucket: String, | ||||||
| @@ -157,6 +160,27 @@ class AdminOriginalWorkController( | |||||||
|         ApiResponse.ok(null) |         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 { |     private fun uploadImage(originalWorkId: Long, image: MultipartFile): String { | ||||||
|         try { |         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.OriginalWorkRegisterRequest | ||||||
| import kr.co.vividnext.sodalive.admin.chat.original.dto.OriginalWorkUpdateRequest | 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.character.repository.ChatCharacterRepository | ||||||
| import kr.co.vividnext.sodalive.chat.original.OriginalWork | import kr.co.vividnext.sodalive.chat.original.OriginalWork | ||||||
| import kr.co.vividnext.sodalive.chat.original.OriginalWorkRepository | import kr.co.vividnext.sodalive.chat.original.OriginalWorkRepository | ||||||
| @@ -85,10 +86,33 @@ class AdminOriginalWorkService( | |||||||
|     /** 원작 페이징 조회 */ |     /** 원작 페이징 조회 */ | ||||||
|     @Transactional(readOnly = true) |     @Transactional(readOnly = true) | ||||||
|     fun getOriginalWorkPage(page: Int, size: Int): Page<OriginalWork> { |     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) |         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) |     @Transactional(readOnly = true) | ||||||
|     fun searchOriginalWorksAll(searchTerm: String): List<OriginalWork> { |     fun searchOriginalWorksAll(searchTerm: String): List<OriginalWork> { | ||||||
|   | |||||||
| @@ -13,6 +13,7 @@ interface ChatCharacterRepository : JpaRepository<ChatCharacter, Long> { | |||||||
|     fun findByName(name: String): ChatCharacter? |     fun findByName(name: String): ChatCharacter? | ||||||
|     fun findByIsActiveTrue(pageable: Pageable): Page<ChatCharacter> |     fun findByIsActiveTrue(pageable: Pageable): Page<ChatCharacter> | ||||||
|     fun findByOriginalWorkIdAndIsActiveTrueOrderByCreatedAtDesc(originalWorkId: Long): List<ChatCharacter> |     fun findByOriginalWorkIdAndIsActiveTrueOrderByCreatedAtDesc(originalWorkId: Long): List<ChatCharacter> | ||||||
|  |     fun findByOriginalWorkIdAndIsActiveTrue(originalWorkId: Long, pageable: Pageable): Page<ChatCharacter> | ||||||
|  |  | ||||||
|     /** |     /** | ||||||
|      * 2주 이내(파라미터 since 이상) 활성 캐릭터 페이징 조회 |      * 2주 이내(파라미터 since 이상) 활성 캐릭터 페이징 조회 | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user