캐릭터 챗봇 #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>
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue