캐릭터 챗봇 #338
|
@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.admin.chat.character.dto.ChatCharacterUpdateRequ
|
|||
import kr.co.vividnext.sodalive.admin.chat.character.dto.ExternalApiResponse
|
||||
import kr.co.vividnext.sodalive.admin.chat.character.service.AdminChatCharacterService
|
||||
import kr.co.vividnext.sodalive.aws.s3.S3Uploader
|
||||
import kr.co.vividnext.sodalive.chat.character.CharacterType
|
||||
import kr.co.vividnext.sodalive.chat.character.service.ChatCharacterService
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.common.SodaException
|
||||
|
@ -119,6 +120,12 @@ class AdminChatCharacterController(
|
|||
speechPattern = request.speechPattern,
|
||||
speechStyle = request.speechStyle,
|
||||
appearance = request.appearance,
|
||||
originalTitle = request.originalTitle,
|
||||
originalLink = request.originalLink,
|
||||
characterType = request.characterType?.let {
|
||||
runCatching { CharacterType.valueOf(it) }
|
||||
.getOrDefault(CharacterType.CHARACTER)
|
||||
} ?: CharacterType.CHARACTER,
|
||||
tags = request.tags,
|
||||
values = request.values,
|
||||
hobbies = request.hobbies,
|
||||
|
@ -152,7 +159,27 @@ class AdminChatCharacterController(
|
|||
headers.set("x-api-key", apiKey) // 실제 API 키로 대체 필요
|
||||
headers.contentType = MediaType.APPLICATION_JSON
|
||||
|
||||
val httpEntity = HttpEntity(request, headers)
|
||||
// 외부 API에 전달하지 않을 필드(originalTitle, originalLink, characterType)를 제외하고 바디 구성
|
||||
val body = mutableMapOf<String, Any>()
|
||||
body["name"] = request.name
|
||||
body["systemPrompt"] = request.systemPrompt
|
||||
body["description"] = request.description
|
||||
request.age?.let { body["age"] = it }
|
||||
request.gender?.let { body["gender"] = it }
|
||||
request.mbti?.let { body["mbti"] = it }
|
||||
request.speechPattern?.let { body["speechPattern"] = it }
|
||||
request.speechStyle?.let { body["speechStyle"] = it }
|
||||
request.appearance?.let { body["appearance"] = it }
|
||||
if (request.tags.isNotEmpty()) body["tags"] = request.tags
|
||||
if (request.hobbies.isNotEmpty()) body["hobbies"] = request.hobbies
|
||||
if (request.values.isNotEmpty()) body["values"] = request.values
|
||||
if (request.goals.isNotEmpty()) body["goals"] = request.goals
|
||||
if (request.relationships.isNotEmpty()) body["relationships"] = request.relationships
|
||||
if (request.personalities.isNotEmpty()) body["personalities"] = request.personalities
|
||||
if (request.backgrounds.isNotEmpty()) body["backgrounds"] = request.backgrounds
|
||||
if (request.memories.isNotEmpty()) body["memories"] = request.memories
|
||||
|
||||
val httpEntity = HttpEntity(body, headers)
|
||||
|
||||
val response = restTemplate.exchange(
|
||||
"$apiUrl/api/characters",
|
||||
|
@ -223,16 +250,22 @@ class AdminChatCharacterController(
|
|||
val request = objectMapper.readValue(requestString, ChatCharacterUpdateRequest::class.java)
|
||||
|
||||
// 2. ChatCharacterUpdateRequest를 확인해서 변경한 데이터가 있는지 확인
|
||||
val hasChangedData = hasChanges(request)
|
||||
val hasChangedData = hasChanges(request) // 외부 API 대상으로의 변경 여부(3가지 필드 제외)
|
||||
|
||||
// 3. 이미지 있는지 확인
|
||||
val hasImage = image != null && !image.isEmpty
|
||||
|
||||
if (!hasChangedData && !hasImage) {
|
||||
// 3가지만 변경된 경우(외부 API 변경은 없지만 DB 변경은 있는 경우)를 허용하기 위해 별도 플래그 계산
|
||||
val hasDbOnlyChanges =
|
||||
request.originalTitle != null ||
|
||||
request.originalLink != null ||
|
||||
request.characterType != null
|
||||
|
||||
if (!hasChangedData && !hasImage && !hasDbOnlyChanges) {
|
||||
throw SodaException("변경된 데이터가 없습니다.")
|
||||
}
|
||||
|
||||
// 변경된 데이터가 있으면 외부 API 호출
|
||||
// 외부 API로 전달할 변경이 있을 때만 외부 API 호출(3가지 필드만 변경된 경우는 호출하지 않음)
|
||||
if (hasChangedData) {
|
||||
val chatCharacter = service.findById(request.id)
|
||||
?: throw SodaException("해당 ID의 캐릭터를 찾을 수 없습니다: ${request.id}")
|
||||
|
|
|
@ -33,6 +33,9 @@ data class ChatCharacterRegisterRequest(
|
|||
@JsonProperty("speechPattern") val speechPattern: String?,
|
||||
@JsonProperty("speechStyle") val speechStyle: String?,
|
||||
@JsonProperty("appearance") val appearance: String?,
|
||||
@JsonProperty("originalTitle") val originalTitle: String? = null,
|
||||
@JsonProperty("originalLink") val originalLink: String? = null,
|
||||
@JsonProperty("characterType") val characterType: String? = null,
|
||||
@JsonProperty("tags") val tags: List<String> = emptyList(),
|
||||
@JsonProperty("hobbies") val hobbies: List<String> = emptyList(),
|
||||
@JsonProperty("values") val values: List<String> = emptyList(),
|
||||
|
@ -64,6 +67,9 @@ data class ChatCharacterUpdateRequest(
|
|||
@JsonProperty("speechPattern") val speechPattern: String? = null,
|
||||
@JsonProperty("speechStyle") val speechStyle: String? = null,
|
||||
@JsonProperty("appearance") val appearance: String? = null,
|
||||
@JsonProperty("originalTitle") val originalTitle: String? = null,
|
||||
@JsonProperty("originalLink") val originalLink: String? = null,
|
||||
@JsonProperty("characterType") val characterType: String? = null,
|
||||
@JsonProperty("isActive") val isActive: Boolean? = null,
|
||||
@JsonProperty("tags") val tags: List<String>? = null,
|
||||
@JsonProperty("hobbies") val hobbies: List<String>? = null,
|
||||
|
|
|
@ -4,6 +4,8 @@ import kr.co.vividnext.sodalive.common.BaseEntity
|
|||
import javax.persistence.CascadeType
|
||||
import javax.persistence.Column
|
||||
import javax.persistence.Entity
|
||||
import javax.persistence.EnumType
|
||||
import javax.persistence.Enumerated
|
||||
import javax.persistence.FetchType
|
||||
import javax.persistence.OneToMany
|
||||
|
||||
|
@ -41,6 +43,19 @@ class ChatCharacter(
|
|||
@Column(columnDefinition = "TEXT")
|
||||
var appearance: String? = null,
|
||||
|
||||
// 원작 (optional)
|
||||
@Column(nullable = true)
|
||||
var originalTitle: String? = null,
|
||||
|
||||
// 원작 링크 (optional)
|
||||
@Column(nullable = true)
|
||||
var originalLink: String? = null,
|
||||
|
||||
// 캐릭터 유형
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(nullable = false)
|
||||
var characterType: CharacterType = CharacterType.CHARACTER,
|
||||
|
||||
var isActive: Boolean = true
|
||||
) : BaseEntity() {
|
||||
var imagePath: String? = null
|
||||
|
@ -117,3 +132,8 @@ class ChatCharacter(
|
|||
relationships.add(relationship)
|
||||
}
|
||||
}
|
||||
|
||||
enum class CharacterType {
|
||||
CLONE,
|
||||
CHARACTER
|
||||
}
|
||||
|
|
|
@ -1,6 +1,7 @@
|
|||
package kr.co.vividnext.sodalive.chat.character.service
|
||||
|
||||
import kr.co.vividnext.sodalive.admin.chat.character.dto.ChatCharacterUpdateRequest
|
||||
import kr.co.vividnext.sodalive.chat.character.CharacterType
|
||||
import kr.co.vividnext.sodalive.chat.character.ChatCharacter
|
||||
import kr.co.vividnext.sodalive.chat.character.ChatCharacterGoal
|
||||
import kr.co.vividnext.sodalive.chat.character.ChatCharacterHobby
|
||||
|
@ -208,6 +209,9 @@ class ChatCharacterService(
|
|||
speechPattern: String? = null,
|
||||
speechStyle: String? = null,
|
||||
appearance: String? = null,
|
||||
originalTitle: String? = null,
|
||||
originalLink: String? = null,
|
||||
characterType: CharacterType = CharacterType.CHARACTER,
|
||||
tags: List<String> = emptyList(),
|
||||
values: List<String> = emptyList(),
|
||||
hobbies: List<String> = emptyList(),
|
||||
|
@ -223,7 +227,10 @@ class ChatCharacterService(
|
|||
mbti = mbti,
|
||||
speechPattern = speechPattern,
|
||||
speechStyle = speechStyle,
|
||||
appearance = appearance
|
||||
appearance = appearance,
|
||||
originalTitle = originalTitle,
|
||||
originalLink = originalLink,
|
||||
characterType = characterType
|
||||
)
|
||||
|
||||
// 관련 엔티티 연결
|
||||
|
@ -286,6 +293,9 @@ class ChatCharacterService(
|
|||
speechPattern: String? = null,
|
||||
speechStyle: String? = null,
|
||||
appearance: String? = null,
|
||||
originalTitle: String? = null,
|
||||
originalLink: String? = null,
|
||||
characterType: CharacterType = CharacterType.CHARACTER,
|
||||
tags: List<String> = emptyList(),
|
||||
values: List<String> = emptyList(),
|
||||
hobbies: List<String> = emptyList(),
|
||||
|
@ -296,8 +306,23 @@ class ChatCharacterService(
|
|||
relationships: List<Pair<String, String>> = emptyList()
|
||||
): ChatCharacter {
|
||||
val chatCharacter = createChatCharacter(
|
||||
characterUUID, name, description, systemPrompt, age, gender, mbti,
|
||||
speechPattern, speechStyle, appearance, tags, values, hobbies, goals
|
||||
characterUUID = characterUUID,
|
||||
name = name,
|
||||
description = description,
|
||||
systemPrompt = systemPrompt,
|
||||
age = age,
|
||||
gender = gender,
|
||||
mbti = mbti,
|
||||
speechPattern = speechPattern,
|
||||
speechStyle = speechStyle,
|
||||
appearance = appearance,
|
||||
originalTitle = originalTitle,
|
||||
originalLink = originalLink,
|
||||
characterType = characterType,
|
||||
tags = tags,
|
||||
values = values,
|
||||
hobbies = hobbies,
|
||||
goals = goals
|
||||
)
|
||||
|
||||
// 추가 정보 설정
|
||||
|
@ -365,6 +390,11 @@ class ChatCharacterService(
|
|||
request.speechPattern?.let { chatCharacter.speechPattern = it }
|
||||
request.speechStyle?.let { chatCharacter.speechStyle = it }
|
||||
request.appearance?.let { chatCharacter.appearance = it }
|
||||
request.originalTitle?.let { chatCharacter.originalTitle = it }
|
||||
request.originalLink?.let { chatCharacter.originalLink = it }
|
||||
request.characterType?.let {
|
||||
runCatching { CharacterType.valueOf(it) }.getOrNull()?.let { ct -> chatCharacter.characterType = ct }
|
||||
}
|
||||
|
||||
// request에서 변경된 데이터만 업데이트
|
||||
if (request.tags != null) {
|
||||
|
|
Loading…
Reference in New Issue