캐릭터 챗봇 #338
| @@ -1,6 +1,7 @@ | |||||||
| package kr.co.vividnext.sodalive.admin.chat.character.curation | package kr.co.vividnext.sodalive.admin.chat.character.curation | ||||||
|  |  | ||||||
| import kr.co.vividnext.sodalive.common.ApiResponse | import kr.co.vividnext.sodalive.common.ApiResponse | ||||||
|  | import kr.co.vividnext.sodalive.common.SodaException | ||||||
| import org.springframework.beans.factory.annotation.Value | import org.springframework.beans.factory.annotation.Value | ||||||
| import org.springframework.security.access.prepost.PreAuthorize | import org.springframework.security.access.prepost.PreAuthorize | ||||||
| import org.springframework.web.bind.annotation.DeleteMapping | import org.springframework.web.bind.annotation.DeleteMapping | ||||||
| @@ -60,7 +61,12 @@ class CharacterCurationAdminController( | |||||||
|     fun addCharacter( |     fun addCharacter( | ||||||
|         @PathVariable curationId: Long, |         @PathVariable curationId: Long, | ||||||
|         @RequestBody request: CharacterCurationAddCharacterRequest |         @RequestBody request: CharacterCurationAddCharacterRequest | ||||||
|     ) = ApiResponse.ok(service.addCharacter(curationId, request.characterId)) |     ): ApiResponse<Boolean> { | ||||||
|  |         val ids = request.characterIds.filter { it > 0 }.distinct() | ||||||
|  |         if (ids.isEmpty()) throw SodaException("등록할 캐릭터 ID 리스트가 비어있습니다") | ||||||
|  |         service.addCharacters(curationId, ids) | ||||||
|  |         return ApiResponse.ok(true) | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @DeleteMapping("/{curationId}/characters/{characterId}") |     @DeleteMapping("/{curationId}/characters/{characterId}") | ||||||
|     fun removeCharacter( |     fun removeCharacter( | ||||||
|   | |||||||
| @@ -18,7 +18,7 @@ data class CharacterCurationOrderUpdateRequest( | |||||||
| ) | ) | ||||||
|  |  | ||||||
| data class CharacterCurationAddCharacterRequest( | data class CharacterCurationAddCharacterRequest( | ||||||
|     val characterId: Long |     val characterIds: List<Long> | ||||||
| ) | ) | ||||||
|  |  | ||||||
| data class CharacterCurationReorderCharactersRequest( | data class CharacterCurationReorderCharactersRequest( | ||||||
|   | |||||||
| @@ -60,26 +60,42 @@ class CharacterCurationAdminService( | |||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Transactional |     @Transactional | ||||||
|     fun addCharacter(curationId: Long, characterId: Long) { |     fun addCharacters(curationId: Long, characterIds: List<Long>) { | ||||||
|  |         if (characterIds.isEmpty()) throw SodaException("등록할 캐릭터 ID 리스트가 비어있습니다") | ||||||
|  |  | ||||||
|         val curation = curationRepository.findById(curationId) |         val curation = curationRepository.findById(curationId) | ||||||
|             .orElseThrow { SodaException("큐레이션을 찾을 수 없습니다: $curationId") } |             .orElseThrow { SodaException("큐레이션을 찾을 수 없습니다: $curationId") } | ||||||
|         if (!curation.isActive) throw SodaException("비활성화된 큐레이션입니다: $curationId") |         if (!curation.isActive) throw SodaException("비활성화된 큐레이션입니다: $curationId") | ||||||
|  |  | ||||||
|         val character = characterRepository.findById(characterId) |         val uniqueIds = characterIds.filter { it > 0 }.distinct() | ||||||
|             .orElseThrow { SodaException("캐릭터를 찾을 수 없습니다: $characterId") } |         if (uniqueIds.isEmpty()) throw SodaException("유효한 캐릭터 ID가 없습니다") | ||||||
|         if (!character.isActive) throw SodaException("비활성화된 캐릭터는 추가할 수 없습니다: $characterId") |  | ||||||
|  |  | ||||||
|         val existing = mappingRepository.findByCuration(curation) |         // 활성 캐릭터만 조회 (조회 단계에서 검증 포함) | ||||||
|             .firstOrNull { it.chatCharacter.id == characterId } |         val characters = characterRepository.findByIdInAndIsActiveTrue(uniqueIds) | ||||||
|         if (existing != null) return // 이미 존재하면 무시 |         val characterMap = characters.associateBy { it.id!! } | ||||||
|  |  | ||||||
|         val nextOrder = (mappingRepository.findByCuration(curation).maxOfOrNull { it.sortOrder } ?: 0) + 1 |         // 조회 결과에 존재하는 캐릭터만 유효 | ||||||
|         val mapping = CharacterCurationMapping( |         val validIds = uniqueIds.filter { id -> characterMap.containsKey(id) } | ||||||
|  |  | ||||||
|  |         val existingMappings = mappingRepository.findByCuration(curation) | ||||||
|  |         val existingCharacterIds = existingMappings.mapNotNull { it.chatCharacter.id }.toSet() | ||||||
|  |         var nextOrder = (existingMappings.maxOfOrNull { it.sortOrder } ?: 0) + 1 | ||||||
|  |  | ||||||
|  |         val toSave = mutableListOf<CharacterCurationMapping>() | ||||||
|  |         validIds.forEach { id -> | ||||||
|  |             if (!existingCharacterIds.contains(id)) { | ||||||
|  |                 val character = characterMap[id] ?: return@forEach | ||||||
|  |                 toSave += CharacterCurationMapping( | ||||||
|                     curation = curation, |                     curation = curation, | ||||||
|                     chatCharacter = character, |                     chatCharacter = character, | ||||||
|             sortOrder = nextOrder |                     sortOrder = nextOrder++ | ||||||
|                 ) |                 ) | ||||||
|         mappingRepository.save(mapping) |             } | ||||||
|  |         } | ||||||
|  |  | ||||||
|  |         if (toSave.isNotEmpty()) { | ||||||
|  |             mappingRepository.saveAll(toSave) | ||||||
|  |         } | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Transactional |     @Transactional | ||||||
|   | |||||||
| @@ -61,4 +61,6 @@ interface ChatCharacterRepository : JpaRepository<ChatCharacter, Long> { | |||||||
|         @Param("characterId") characterId: Long, |         @Param("characterId") characterId: Long, | ||||||
|         pageable: Pageable |         pageable: Pageable | ||||||
|     ): List<ChatCharacter> |     ): List<ChatCharacter> | ||||||
|  |  | ||||||
|  |     fun findByIdInAndIsActiveTrue(ids: List<Long>): List<ChatCharacter> | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user