feat(chat): ChatCharacter와 다른 엔티티 간 관계 구현

ChatCharacter와 Memory, Personality, Background, Relationship 간 1:N 관계 설정
Tag, Value, Hobby, Goal 엔티티의 중복 방지 및 관계 매핑 구현
관계 설정을 위한 서비스 및 리포지토리 클래스 추가
This commit is contained in:
Klaus 2025-08-06 17:42:48 +09:00
parent 73038222cc
commit 689f9fe48f
20 changed files with 818 additions and 0 deletions

View File

@ -0,0 +1,115 @@
package kr.co.vividnext.sodalive.chat.character
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.CascadeType
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.OneToMany
@Entity
class ChatCharacter(
val characterUUID: String,
// 캐릭터 이름 (API 키 내에서 유일해야 함)
val name: String,
// 캐릭터 설명
val description: String,
// AI 시스템 프롬프트
val systemPrompt: String,
// 나이
val age: Int? = null,
// 성별
val gender: String? = null,
// mbti
val mbti: String? = null,
// 말투 패턴 설명
val speechPattern: String? = null,
// 대화 스타일
val speechStyle: String? = null,
// 외모 설명
val appearance: String? = null,
val isActive: Boolean = true
) : BaseEntity() {
var imagePath: String? = null
@OneToMany(mappedBy = "chatCharacter", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var memories: MutableList<ChatCharacterMemory> = mutableListOf()
@OneToMany(mappedBy = "chatCharacter", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var personalities: MutableList<ChatCharacterPersonality> = mutableListOf()
@OneToMany(mappedBy = "chatCharacter", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var backgrounds: MutableList<ChatCharacterBackground> = mutableListOf()
@OneToMany(mappedBy = "chatCharacter", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var relationships: MutableList<ChatCharacterRelationship> = mutableListOf()
@OneToMany(mappedBy = "chatCharacter", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var tagMappings: MutableList<ChatCharacterTagMapping> = mutableListOf()
@OneToMany(mappedBy = "chatCharacter", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var valueMappings: MutableList<ChatCharacterValueMapping> = mutableListOf()
@OneToMany(mappedBy = "chatCharacter", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var hobbyMappings: MutableList<ChatCharacterHobbyMapping> = mutableListOf()
@OneToMany(mappedBy = "chatCharacter", cascade = [CascadeType.ALL], fetch = FetchType.LAZY)
var goalMappings: MutableList<ChatCharacterGoalMapping> = mutableListOf()
// 태그 추가 헬퍼 메소드
fun addTag(tag: ChatCharacterTag) {
val mapping = ChatCharacterTagMapping(this, tag)
tagMappings.add(mapping)
}
// 가치관 추가 헬퍼 메소드
fun addValue(value: ChatCharacterValue) {
val mapping = ChatCharacterValueMapping(this, value)
valueMappings.add(mapping)
}
// 취미 추가 헬퍼 메소드
fun addHobby(hobby: ChatCharacterHobby) {
val mapping = ChatCharacterHobbyMapping(this, hobby)
hobbyMappings.add(mapping)
}
// 목표 추가 헬퍼 메소드
fun addGoal(goal: ChatCharacterGoal) {
val mapping = ChatCharacterGoalMapping(this, goal)
goalMappings.add(mapping)
}
// 기억 추가 헬퍼 메소드
fun addMemory(title: String, content: String, emotion: String) {
val memory = ChatCharacterMemory(title, content, emotion, this)
memories.add(memory)
}
// 성격 추가 헬퍼 메소드
fun addPersonality(trait: String, description: String) {
val personality = ChatCharacterPersonality(trait, description, this)
personalities.add(personality)
}
// 배경 추가 헬퍼 메소드
fun addBackground(topic: String, description: String) {
val background = ChatCharacterBackground(topic, description, this)
backgrounds.add(background)
}
// 관계 추가 헬퍼 메소드
fun addRelationship(relationShip: String) {
val relationship = ChatCharacterRelationship(relationShip, this)
relationships.add(relationship)
}
}

View File

@ -0,0 +1,24 @@
package kr.co.vividnext.sodalive.chat.character
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
/**
* 캐릭터 배경 정보
*/
@Entity
class ChatCharacterBackground(
// 배경 주제
val topic: String,
// 배경 설명
val description: String,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "chat_character_id")
val chatCharacter: ChatCharacter
) : BaseEntity()

View File

@ -0,0 +1,22 @@
package kr.co.vividnext.sodalive.chat.character
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.OneToMany
import javax.persistence.Table
import javax.persistence.UniqueConstraint
/**
* 캐릭터 목표
*/
@Entity
@Table(uniqueConstraints = [UniqueConstraint(columnNames = ["goal"])])
class ChatCharacterGoal(
@Column(nullable = false)
val goal: String
) : BaseEntity() {
@OneToMany(mappedBy = "goal")
var goalMappings: MutableList<ChatCharacterGoalMapping> = mutableListOf()
}

View File

@ -0,0 +1,22 @@
package kr.co.vividnext.sodalive.chat.character
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
/**
* ChatCharacter와 ChatCharacterGoal 간의 매핑 엔티티
* ChatCharacterGoal의 중복을 방지하기 위한 매핑 테이블
*/
@Entity
class ChatCharacterGoalMapping(
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "chat_character_id")
val chatCharacter: ChatCharacter,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "goal_id")
val goal: ChatCharacterGoal
) : BaseEntity()

View File

@ -0,0 +1,22 @@
package kr.co.vividnext.sodalive.chat.character
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.OneToMany
import javax.persistence.Table
import javax.persistence.UniqueConstraint
/**
* 캐릭터 취미
*/
@Entity
@Table(uniqueConstraints = [UniqueConstraint(columnNames = ["hobby"])])
class ChatCharacterHobby(
@Column(nullable = false)
val hobby: String
) : BaseEntity() {
@OneToMany(mappedBy = "hobby")
var hobbyMappings: MutableList<ChatCharacterHobbyMapping> = mutableListOf()
}

View File

@ -0,0 +1,22 @@
package kr.co.vividnext.sodalive.chat.character
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
/**
* ChatCharacter와 ChatCharacterHobby 간의 매핑 엔티티
* ChatCharacterHobby의 중복을 방지하기 위한 매핑 테이블
*/
@Entity
class ChatCharacterHobbyMapping(
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "chat_character_id")
val chatCharacter: ChatCharacter,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "hobby_id")
val hobby: ChatCharacterHobby
) : BaseEntity()

View File

@ -0,0 +1,27 @@
package kr.co.vividnext.sodalive.chat.character
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
/**
* 캐릭터 기억
*/
@Entity
class ChatCharacterMemory(
// 기억 제목
val title: String,
// 기억 내용
val content: String,
// 감정
val emotion: String,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "chat_character_id")
val chatCharacter: ChatCharacter
) : BaseEntity()

View File

@ -0,0 +1,24 @@
package kr.co.vividnext.sodalive.chat.character
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
/**
* 캐릭터 성격 특성
*/
@Entity
class ChatCharacterPersonality(
// 성격 특성
val trait: String,
// 성격 특성 설명
val description: String,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "chat_character_id")
val chatCharacter: ChatCharacter
) : BaseEntity()

View File

@ -0,0 +1,20 @@
package kr.co.vividnext.sodalive.chat.character
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
/**
* 캐릭터 관계
*/
@Entity
class ChatCharacterRelationship(
val relationShip: String,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "chat_character_id")
val chatCharacter: ChatCharacter
) : BaseEntity()

View File

@ -0,0 +1,22 @@
package kr.co.vividnext.sodalive.chat.character
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.OneToMany
import javax.persistence.Table
import javax.persistence.UniqueConstraint
/**
* 캐릭터 태그
*/
@Entity
@Table(uniqueConstraints = [UniqueConstraint(columnNames = ["tag"])])
class ChatCharacterTag(
@Column(nullable = false)
val tag: String
) : BaseEntity() {
@OneToMany(mappedBy = "tag")
var tagMappings: MutableList<ChatCharacterTagMapping> = mutableListOf()
}

View File

@ -0,0 +1,22 @@
package kr.co.vividnext.sodalive.chat.character
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
/**
* ChatCharacter와 ChatCharacterTag 간의 매핑 엔티티
* ChatCharacterTag의 중복을 방지하기 위한 매핑 테이블
*/
@Entity
class ChatCharacterTagMapping(
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "chat_character_id")
val chatCharacter: ChatCharacter,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "tag_id")
val tag: ChatCharacterTag
) : BaseEntity()

View File

@ -0,0 +1,22 @@
package kr.co.vividnext.sodalive.chat.character
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.Column
import javax.persistence.Entity
import javax.persistence.OneToMany
import javax.persistence.Table
import javax.persistence.UniqueConstraint
/**
* 캐릭터 가치관
*/
@Entity
@Table(uniqueConstraints = [UniqueConstraint(columnNames = ["value"])])
class ChatCharacterValue(
@Column(nullable = false)
val value: String
) : BaseEntity() {
@OneToMany(mappedBy = "value")
var valueMappings: MutableList<ChatCharacterValueMapping> = mutableListOf()
}

View File

@ -0,0 +1,22 @@
package kr.co.vividnext.sodalive.chat.character
import kr.co.vividnext.sodalive.common.BaseEntity
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
/**
* ChatCharacter와 ChatCharacterValue 간의 매핑 엔티티
* ChatCharacterValue의 중복을 방지하기 위한 매핑 테이블
*/
@Entity
class ChatCharacterValueMapping(
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "chat_character_id")
val chatCharacter: ChatCharacter,
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "value_id")
val value: ChatCharacterValue
) : BaseEntity()

View File

@ -0,0 +1,118 @@
# ChatCharacter 엔티티 관계 구현
## 개요
이 구현은 ChatCharacter와 다른 엔티티들 간의 1:N 관계를 설정합니다. ChatCharacter가 저장될 때 관련 엔티티들도 함께 저장되며, Tag, Value, Hobby, Goal은 중복을 허용하지 않고 기존 내용과 동일한 내용이 들어오면 관계만 맺습니다.
## 엔티티 구조
### 주요 엔티티
- **ChatCharacter**: 메인 엔티티로, 캐릭터의 기본 정보를 저장합니다.
- **ChatCharacterMemory**: 캐릭터의 기억 정보를 저장합니다.
- **ChatCharacterPersonality**: 캐릭터의 성격 특성을 저장합니다.
- **ChatCharacterBackground**: 캐릭터의 배경 정보를 저장합니다.
- **ChatCharacterRelationship**: 캐릭터의 관계 정보를 저장합니다.
### 중복 방지를 위한 엔티티
- **ChatCharacterTag**: 캐릭터 태그 정보를 저장합니다. 태그 이름은 유일합니다.
- **ChatCharacterValue**: 캐릭터 가치관 정보를 저장합니다. 가치관 이름은 유일합니다.
- **ChatCharacterHobby**: 캐릭터 취미 정보를 저장합니다. 취미 이름은 유일합니다.
- **ChatCharacterGoal**: 캐릭터 목표 정보를 저장합니다. 목표 이름은 유일합니다.
### 매핑 엔티티
- **ChatCharacterTagMapping**: ChatCharacter와 ChatCharacterTag 간의 관계를 맺습니다.
- **ChatCharacterValueMapping**: ChatCharacter와 ChatCharacterValue 간의 관계를 맺습니다.
- **ChatCharacterHobbyMapping**: ChatCharacter와 ChatCharacterHobby 간의 관계를 맺습니다.
- **ChatCharacterGoalMapping**: ChatCharacter와 ChatCharacterGoal 간의 관계를 맺습니다.
## 관계 설정
- ChatCharacter와 Memory, Personality, Background, Relationship은 1:N 관계입니다.
- ChatCharacter와 Tag, Value, Hobby, Goal은 매핑 엔티티를 통한 N:M 관계입니다.
- 모든 관계는 ChatCharacter가 저장될 때 함께 저장됩니다(CascadeType.ALL).
## 서비스 기능
ChatCharacterService는 다음과 같은 기능을 제공합니다:
1. 캐릭터 생성 및 저장
2. 캐릭터 조회 (UUID, 이름, 전체 목록)
3. 캐릭터에 태그, 가치관, 취미, 목표 추가
4. 캐릭터에 기억, 성격 특성, 배경 정보, 관계 추가
5. 모든 정보를 포함한 캐릭터 생성
## 사용 예시
```kotlin
// 캐릭터 생성
val chatCharacter = chatCharacterService.createChatCharacter(
characterUUID = "uuid-123",
name = "AI 어시스턴트",
description = "친절한 AI 어시스턴트",
systemPrompt = "당신은 친절한 AI 어시스턴트입니다."
)
// 태그 추가
chatCharacterService.addTagToCharacter(chatCharacter, "친절함")
chatCharacterService.addTagToCharacter(chatCharacter, "도움")
// 가치관 추가
chatCharacterService.addValueToCharacter(chatCharacter, "정직")
// 취미 추가
chatCharacterService.addHobbyToCharacter(chatCharacter, "독서")
// 목표 추가
chatCharacterService.addGoalToCharacter(chatCharacter, "사용자 만족")
// 기억 추가
chatCharacterService.addMemoryToChatCharacter(
chatCharacter,
"첫 만남",
"사용자와의 첫 대화",
"기쁨"
)
// 성격 특성 추가
chatCharacterService.addPersonalityToChatCharacter(
chatCharacter,
"친절함",
"항상 친절하게 대응합니다."
)
// 배경 정보 추가
chatCharacterService.addBackgroundToChatCharacter(
chatCharacter,
"생성 배경",
"사용자를 돕기 위해 만들어졌습니다."
)
// 관계 추가
chatCharacterService.addRelationshipToChatCharacter(
chatCharacter,
"사용자와의 관계: 도우미"
)
// 모든 정보를 포함한 캐릭터 생성
val completeCharacter = chatCharacterService.createChatCharacterWithDetails(
characterUUID = "uuid-456",
name = "종합 AI",
description = "모든 정보를 가진 AI",
systemPrompt = "당신은 모든 정보를 가진 AI입니다.",
tags = listOf("종합", "지식"),
values = listOf("정확성", "유용성"),
hobbies = listOf("학습", "정보 수집"),
goals = listOf("정확한 정보 제공"),
memories = listOf(Triple("학습 시작", "처음 학습을 시작했습니다.", "호기심")),
personalities = listOf(Pair("분석적", "정보를 분석적으로 처리합니다.")),
backgrounds = listOf(Pair("개발 목적", "정보 제공을 위해 개발되었습니다.")),
relationships = listOf("사용자와의 관계: 정보 제공자")
)
```
## 중복 방지 메커니즘
Tag, Value, Hobby, Goal 엔티티는 각각 고유한 필드(tag, value, hobby, goal)에 대해 유니크 제약 조건을 가지고 있습니다. 서비스 레이어에서는 이미 존재하는 엔티티를 찾아 재사용하거나, 존재하지 않는 경우 새로 생성합니다. 이를 통해 중복을 방지하고 관계만 맺을 수 있습니다.

View File

@ -0,0 +1,10 @@
package kr.co.vividnext.sodalive.chat.character.repository
import kr.co.vividnext.sodalive.chat.character.ChatCharacterGoal
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface ChatCharacterGoalRepository : JpaRepository<ChatCharacterGoal, Long> {
fun findByGoal(goal: String): ChatCharacterGoal?
}

View File

@ -0,0 +1,10 @@
package kr.co.vividnext.sodalive.chat.character.repository
import kr.co.vividnext.sodalive.chat.character.ChatCharacterHobby
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface ChatCharacterHobbyRepository : JpaRepository<ChatCharacterHobby, Long> {
fun findByHobby(hobby: String): ChatCharacterHobby?
}

View File

@ -0,0 +1,11 @@
package kr.co.vividnext.sodalive.chat.character.repository
import kr.co.vividnext.sodalive.chat.character.ChatCharacter
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface ChatCharacterRepository : JpaRepository<ChatCharacter, Long> {
fun findByCharacterUUID(characterUUID: String): ChatCharacter?
fun findByName(name: String): ChatCharacter?
}

View File

@ -0,0 +1,10 @@
package kr.co.vividnext.sodalive.chat.character.repository
import kr.co.vividnext.sodalive.chat.character.ChatCharacterTag
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface ChatCharacterTagRepository : JpaRepository<ChatCharacterTag, Long> {
fun findByTag(tag: String): ChatCharacterTag?
}

View File

@ -0,0 +1,10 @@
package kr.co.vividnext.sodalive.chat.character.repository
import kr.co.vividnext.sodalive.chat.character.ChatCharacterValue
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
@Repository
interface ChatCharacterValueRepository : JpaRepository<ChatCharacterValue, Long> {
fun findByValue(value: String): ChatCharacterValue?
}

View File

@ -0,0 +1,263 @@
package kr.co.vividnext.sodalive.chat.character.service
import kr.co.vividnext.sodalive.chat.character.ChatCharacter
import kr.co.vividnext.sodalive.chat.character.ChatCharacterGoal
import kr.co.vividnext.sodalive.chat.character.ChatCharacterHobby
import kr.co.vividnext.sodalive.chat.character.ChatCharacterTag
import kr.co.vividnext.sodalive.chat.character.ChatCharacterValue
import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterGoalRepository
import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterHobbyRepository
import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterRepository
import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterTagRepository
import kr.co.vividnext.sodalive.chat.character.repository.ChatCharacterValueRepository
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
@Service
class ChatCharacterService(
private val chatCharacterRepository: ChatCharacterRepository,
private val tagRepository: ChatCharacterTagRepository,
private val valueRepository: ChatCharacterValueRepository,
private val hobbyRepository: ChatCharacterHobbyRepository,
private val goalRepository: ChatCharacterGoalRepository
) {
/**
* 태그를 찾거나 생성하여 캐릭터에 연결
*/
@Transactional
fun addTagToCharacter(chatCharacter: ChatCharacter, tagName: String) {
val tag = tagRepository.findByTag(tagName) ?: ChatCharacterTag(tagName)
if (tag.id == null) {
tagRepository.save(tag)
}
chatCharacter.addTag(tag)
}
/**
* 가치관을 찾거나 생성하여 캐릭터에 연결
*/
@Transactional
fun addValueToCharacter(chatCharacter: ChatCharacter, valueName: String) {
val value = valueRepository.findByValue(valueName) ?: ChatCharacterValue(valueName)
if (value.id == null) {
valueRepository.save(value)
}
chatCharacter.addValue(value)
}
/**
* 취미를 찾거나 생성하여 캐릭터에 연결
*/
@Transactional
fun addHobbyToCharacter(chatCharacter: ChatCharacter, hobbyName: String) {
val hobby = hobbyRepository.findByHobby(hobbyName) ?: ChatCharacterHobby(hobbyName)
if (hobby.id == null) {
hobbyRepository.save(hobby)
}
chatCharacter.addHobby(hobby)
}
/**
* 목표를 찾거나 생성하여 캐릭터에 연결
*/
@Transactional
fun addGoalToCharacter(chatCharacter: ChatCharacter, goalName: String) {
val goal = goalRepository.findByGoal(goalName) ?: ChatCharacterGoal(goalName)
if (goal.id == null) {
goalRepository.save(goal)
}
chatCharacter.addGoal(goal)
}
/**
* 여러 태그를 한번에 캐릭터에 연결
*/
@Transactional
fun addTagsToCharacter(chatCharacter: ChatCharacter, tags: List<String>) {
tags.forEach { addTagToCharacter(chatCharacter, it) }
}
/**
* 여러 가치관을 한번에 캐릭터에 연결
*/
@Transactional
fun addValuesToCharacter(chatCharacter: ChatCharacter, values: List<String>) {
values.forEach { addValueToCharacter(chatCharacter, it) }
}
/**
* 여러 취미를 한번에 캐릭터에 연결
*/
@Transactional
fun addHobbiesToCharacter(chatCharacter: ChatCharacter, hobbies: List<String>) {
hobbies.forEach { addHobbyToCharacter(chatCharacter, it) }
}
/**
* 여러 목표를 한번에 캐릭터에 연결
*/
@Transactional
fun addGoalsToCharacter(chatCharacter: ChatCharacter, goals: List<String>) {
goals.forEach { addGoalToCharacter(chatCharacter, it) }
}
/**
* 캐릭터 저장
*/
@Transactional
fun saveChatCharacter(chatCharacter: ChatCharacter): ChatCharacter {
return chatCharacterRepository.save(chatCharacter)
}
/**
* UUID로 캐릭터 조회
*/
@Transactional(readOnly = true)
fun findByCharacterUUID(characterUUID: String): ChatCharacter? {
return chatCharacterRepository.findByCharacterUUID(characterUUID)
}
/**
* 이름으로 캐릭터 조회
*/
@Transactional(readOnly = true)
fun findByName(name: String): ChatCharacter? {
return chatCharacterRepository.findByName(name)
}
/**
* 모든 캐릭터 조회
*/
@Transactional(readOnly = true)
fun findAll(): List<ChatCharacter> {
return chatCharacterRepository.findAll()
}
/**
* 캐릭터 생성 관련 엔티티 연결
*/
@Transactional
fun createChatCharacter(
characterUUID: String,
name: String,
description: String,
systemPrompt: String,
age: Int? = null,
gender: String? = null,
mbti: String? = null,
speechPattern: String? = null,
speechStyle: String? = null,
appearance: String? = null,
tags: List<String> = emptyList(),
values: List<String> = emptyList(),
hobbies: List<String> = emptyList(),
goals: List<String> = emptyList()
): ChatCharacter {
val chatCharacter = ChatCharacter(
characterUUID = characterUUID,
name = name,
description = description,
systemPrompt = systemPrompt,
age = age,
gender = gender,
mbti = mbti,
speechPattern = speechPattern,
speechStyle = speechStyle,
appearance = appearance
)
// 관련 엔티티 연결
addTagsToCharacter(chatCharacter, tags)
addValuesToCharacter(chatCharacter, values)
addHobbiesToCharacter(chatCharacter, hobbies)
addGoalsToCharacter(chatCharacter, goals)
return saveChatCharacter(chatCharacter)
}
/**
* 캐릭터에 기억 추가
*/
@Transactional
fun addMemoryToChatCharacter(chatCharacter: ChatCharacter, title: String, content: String, emotion: String) {
chatCharacter.addMemory(title, content, emotion)
saveChatCharacter(chatCharacter)
}
/**
* 캐릭터에 성격 특성 추가
*/
@Transactional
fun addPersonalityToChatCharacter(chatCharacter: ChatCharacter, trait: String, description: String) {
chatCharacter.addPersonality(trait, description)
saveChatCharacter(chatCharacter)
}
/**
* 캐릭터에 배경 정보 추가
*/
@Transactional
fun addBackgroundToChatCharacter(chatCharacter: ChatCharacter, topic: String, description: String) {
chatCharacter.addBackground(topic, description)
saveChatCharacter(chatCharacter)
}
/**
* 캐릭터에 관계 추가
*/
@Transactional
fun addRelationshipToChatCharacter(chatCharacter: ChatCharacter, relationShip: String) {
chatCharacter.addRelationship(relationShip)
saveChatCharacter(chatCharacter)
}
/**
* 캐릭터 생성 기본 정보와 함께 추가 정보도 설정
*/
@Transactional
fun createChatCharacterWithDetails(
characterUUID: String,
name: String,
description: String,
systemPrompt: String,
age: Int? = null,
gender: String? = null,
mbti: String? = null,
speechPattern: String? = null,
speechStyle: String? = null,
appearance: String? = null,
tags: List<String> = emptyList(),
values: List<String> = emptyList(),
hobbies: List<String> = emptyList(),
goals: List<String> = emptyList(),
memories: List<Triple<String, String, String>> = emptyList(),
personalities: List<Pair<String, String>> = emptyList(),
backgrounds: List<Pair<String, String>> = emptyList(),
relationships: List<String> = emptyList()
): ChatCharacter {
val chatCharacter = createChatCharacter(
characterUUID, name, description, systemPrompt, age, gender, mbti,
speechPattern, speechStyle, appearance, tags, values, hobbies, goals
)
// 추가 정보 설정
memories.forEach { (title, content, emotion) ->
chatCharacter.addMemory(title, content, emotion)
}
personalities.forEach { (trait, description) ->
chatCharacter.addPersonality(trait, description)
}
backgrounds.forEach { (topic, description) ->
chatCharacter.addBackground(topic, description)
}
relationships.forEach { relationShip ->
chatCharacter.addRelationship(relationShip)
}
return saveChatCharacter(chatCharacter)
}
}