캐릭터 챗봇 #338
| @@ -21,6 +21,7 @@ import org.springframework.retry.annotation.Backoff | |||||||
| import org.springframework.retry.annotation.Retryable | import org.springframework.retry.annotation.Retryable | ||||||
| import org.springframework.security.access.prepost.PreAuthorize | import org.springframework.security.access.prepost.PreAuthorize | ||||||
| import org.springframework.web.bind.annotation.GetMapping | import org.springframework.web.bind.annotation.GetMapping | ||||||
|  | import org.springframework.web.bind.annotation.PathVariable | ||||||
| import org.springframework.web.bind.annotation.PostMapping | import org.springframework.web.bind.annotation.PostMapping | ||||||
| import org.springframework.web.bind.annotation.PutMapping | import org.springframework.web.bind.annotation.PutMapping | ||||||
| import org.springframework.web.bind.annotation.RequestMapping | import org.springframework.web.bind.annotation.RequestMapping | ||||||
| @@ -68,6 +69,21 @@ class AdminChatCharacterController( | |||||||
|         ApiResponse.ok(response) |         ApiResponse.ok(response) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 캐릭터 상세 정보 조회 API | ||||||
|  |      * | ||||||
|  |      * @param characterId 캐릭터 ID | ||||||
|  |      * @return 캐릭터 상세 정보 | ||||||
|  |      */ | ||||||
|  |     @GetMapping("/{characterId}") | ||||||
|  |     fun getCharacterDetail( | ||||||
|  |         @PathVariable characterId: Long | ||||||
|  |     ) = run { | ||||||
|  |         val response = adminService.getChatCharacterDetail(characterId, imageHost) | ||||||
|  |  | ||||||
|  |         ApiResponse.ok(response) | ||||||
|  |     } | ||||||
|  |  | ||||||
|     @PostMapping("/register") |     @PostMapping("/register") | ||||||
|     @Retryable( |     @Retryable( | ||||||
|         value = [Exception::class], |         value = [Exception::class], | ||||||
|   | |||||||
| @@ -0,0 +1,83 @@ | |||||||
|  | package kr.co.vividnext.sodalive.admin.chat.character.dto | ||||||
|  |  | ||||||
|  | import kr.co.vividnext.sodalive.chat.character.ChatCharacter | ||||||
|  |  | ||||||
|  | data class ChatCharacterDetailResponse( | ||||||
|  |     val id: Long, | ||||||
|  |     val characterUUID: String, | ||||||
|  |     val name: String, | ||||||
|  |     val imageUrl: String?, | ||||||
|  |     val description: String, | ||||||
|  |     val systemPrompt: String, | ||||||
|  |     val age: Int?, | ||||||
|  |     val gender: String?, | ||||||
|  |     val mbti: String?, | ||||||
|  |     val speechPattern: String?, | ||||||
|  |     val speechStyle: String?, | ||||||
|  |     val appearance: String?, | ||||||
|  |     val isActive: Boolean, | ||||||
|  |     val tags: List<String>, | ||||||
|  |     val hobbies: List<String>, | ||||||
|  |     val values: List<String>, | ||||||
|  |     val goals: List<String>, | ||||||
|  |     val relationships: List<String>, | ||||||
|  |     val personalities: List<PersonalityResponse>, | ||||||
|  |     val backgrounds: List<BackgroundResponse>, | ||||||
|  |     val memories: List<MemoryResponse> | ||||||
|  | ) { | ||||||
|  |     companion object { | ||||||
|  |         fun from(chatCharacter: ChatCharacter, imageHost: String = ""): ChatCharacterDetailResponse { | ||||||
|  |             val fullImagePath = if (chatCharacter.imagePath != null && imageHost.isNotEmpty()) { | ||||||
|  |                 "$imageHost/${chatCharacter.imagePath}" | ||||||
|  |             } else { | ||||||
|  |                 chatCharacter.imagePath | ||||||
|  |             } | ||||||
|  |  | ||||||
|  |             return ChatCharacterDetailResponse( | ||||||
|  |                 id = chatCharacter.id!!, | ||||||
|  |                 characterUUID = chatCharacter.characterUUID, | ||||||
|  |                 name = chatCharacter.name, | ||||||
|  |                 imageUrl = fullImagePath, | ||||||
|  |                 description = chatCharacter.description, | ||||||
|  |                 systemPrompt = chatCharacter.systemPrompt, | ||||||
|  |                 age = chatCharacter.age, | ||||||
|  |                 gender = chatCharacter.gender, | ||||||
|  |                 mbti = chatCharacter.mbti, | ||||||
|  |                 speechPattern = chatCharacter.speechPattern, | ||||||
|  |                 speechStyle = chatCharacter.speechStyle, | ||||||
|  |                 appearance = chatCharacter.appearance, | ||||||
|  |                 isActive = chatCharacter.isActive, | ||||||
|  |                 tags = chatCharacter.tagMappings.map { it.tag.tag }, | ||||||
|  |                 hobbies = chatCharacter.hobbyMappings.map { it.hobby.hobby }, | ||||||
|  |                 values = chatCharacter.valueMappings.map { it.value.value }, | ||||||
|  |                 goals = chatCharacter.goalMappings.map { it.goal.goal }, | ||||||
|  |                 relationships = chatCharacter.relationships.map { it.relationShip }, | ||||||
|  |                 personalities = chatCharacter.personalities.map { | ||||||
|  |                     PersonalityResponse(it.trait, it.description) | ||||||
|  |                 }, | ||||||
|  |                 backgrounds = chatCharacter.backgrounds.map { | ||||||
|  |                     BackgroundResponse(it.topic, it.description) | ||||||
|  |                 }, | ||||||
|  |                 memories = chatCharacter.memories.map { | ||||||
|  |                     MemoryResponse(it.title, it.content, it.emotion) | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |         } | ||||||
|  |     } | ||||||
|  | } | ||||||
|  |  | ||||||
|  | data class PersonalityResponse( | ||||||
|  |     val trait: String, | ||||||
|  |     val description: String | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | data class BackgroundResponse( | ||||||
|  |     val topic: String, | ||||||
|  |     val description: String | ||||||
|  | ) | ||||||
|  |  | ||||||
|  | data class MemoryResponse( | ||||||
|  |     val title: String, | ||||||
|  |     val content: String, | ||||||
|  |     val emotion: String | ||||||
|  | ) | ||||||
| @@ -1,8 +1,10 @@ | |||||||
| package kr.co.vividnext.sodalive.admin.chat.character.service | package kr.co.vividnext.sodalive.admin.chat.character.service | ||||||
|  |  | ||||||
|  | import kr.co.vividnext.sodalive.admin.chat.character.dto.ChatCharacterDetailResponse | ||||||
| import kr.co.vividnext.sodalive.admin.chat.character.dto.ChatCharacterListPageResponse | import kr.co.vividnext.sodalive.admin.chat.character.dto.ChatCharacterListPageResponse | ||||||
| import kr.co.vividnext.sodalive.admin.chat.character.dto.ChatCharacterListResponse | import kr.co.vividnext.sodalive.admin.chat.character.dto.ChatCharacterListResponse | ||||||
| import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterRepository | import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterRepository | ||||||
|  | import kr.co.vividnext.sodalive.common.SodaException | ||||||
| import org.springframework.data.domain.PageRequest | import org.springframework.data.domain.PageRequest | ||||||
| import org.springframework.data.domain.Pageable | import org.springframework.data.domain.Pageable | ||||||
| import org.springframework.data.domain.Sort | import org.springframework.data.domain.Sort | ||||||
| @@ -43,4 +45,20 @@ class AdminChatCharacterService( | |||||||
|     fun createDefaultPageRequest(page: Int = 0, size: Int = 20): PageRequest { |     fun createDefaultPageRequest(page: Int = 0, size: Int = 20): PageRequest { | ||||||
|         return PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt")) |         return PageRequest.of(page, size, Sort.by(Sort.Direction.DESC, "createdAt")) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     /** | ||||||
|  |      * 캐릭터 상세 정보 조회 | ||||||
|  |      * | ||||||
|  |      * @param characterId 캐릭터 ID | ||||||
|  |      * @param imageHost 이미지 호스트 URL | ||||||
|  |      * @return 캐릭터 상세 정보 | ||||||
|  |      * @throws SodaException 캐릭터를 찾을 수 없는 경우 | ||||||
|  |      */ | ||||||
|  |     @Transactional(readOnly = true) | ||||||
|  |     fun getChatCharacterDetail(characterId: Long, imageHost: String = ""): ChatCharacterDetailResponse { | ||||||
|  |         val chatCharacter = chatCharacterRepository.findById(characterId) | ||||||
|  |             .orElseThrow { SodaException("해당 ID의 캐릭터를 찾을 수 없습니다: $characterId") } | ||||||
|  |  | ||||||
|  |         return ChatCharacterDetailResponse.from(chatCharacter, imageHost) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user