캐릭터 챗봇 #338

Merged
klaus merged 119 commits from test into main 2025-09-10 06:08:47 +00:00
4 changed files with 68 additions and 1 deletions
Showing only changes of commit 01ef738d31 - Show all commits

View File

@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.chat.character.dto.CharacterDetailResponse
import kr.co.vividnext.sodalive.chat.character.dto.CharacterMainResponse import kr.co.vividnext.sodalive.chat.character.dto.CharacterMainResponse
import kr.co.vividnext.sodalive.chat.character.dto.CharacterPersonalityResponse import kr.co.vividnext.sodalive.chat.character.dto.CharacterPersonalityResponse
import kr.co.vividnext.sodalive.chat.character.dto.CurationSection import kr.co.vividnext.sodalive.chat.character.dto.CurationSection
import kr.co.vividnext.sodalive.chat.character.dto.OtherCharacter
import kr.co.vividnext.sodalive.chat.character.dto.RecentCharacter import kr.co.vividnext.sodalive.chat.character.dto.RecentCharacter
import kr.co.vividnext.sodalive.chat.character.service.ChatCharacterBannerService import kr.co.vividnext.sodalive.chat.character.service.ChatCharacterBannerService
import kr.co.vividnext.sodalive.chat.character.service.ChatCharacterService import kr.co.vividnext.sodalive.chat.character.service.ChatCharacterService
@ -127,6 +128,20 @@ class ChatCharacterController(
) )
} }
// 다른 캐릭터 조회 (태그 기반, 랜덤 10개, 현재 캐릭터 제외)
val others = service.getOtherCharactersBySharedTags(characterId, 10)
.map { other ->
val otherTags = other.tagMappings
.map { it.tag.tag }
.joinToString(" ") { if (it.startsWith("#")) it else "#$it" }
OtherCharacter(
characterId = other.id!!,
name = other.name,
imageUrl = "$imageHost/${other.imagePath ?: "profile/default-profile.png"}",
tags = otherTags
)
}
// 응답 생성 // 응답 생성
ApiResponse.ok( ApiResponse.ok(
CharacterDetailResponse( CharacterDetailResponse(
@ -137,7 +152,11 @@ class ChatCharacterController(
imageUrl = "$imageHost/${character.imagePath ?: "profile/default-profile.png"}", imageUrl = "$imageHost/${character.imagePath ?: "profile/default-profile.png"}",
personalities = personality, personalities = personality,
backgrounds = background, backgrounds = background,
tags = tags tags = tags,
originalTitle = character.originalTitle,
originalLink = character.originalLink,
characterType = character.characterType,
others = others
) )
) )
} }

View File

@ -1,5 +1,7 @@
package kr.co.vividnext.sodalive.chat.character.dto package kr.co.vividnext.sodalive.chat.character.dto
import kr.co.vividnext.sodalive.chat.character.CharacterType
data class CharacterDetailResponse( data class CharacterDetailResponse(
val characterId: Long, val characterId: Long,
val name: String, val name: String,
@ -8,6 +10,17 @@ data class CharacterDetailResponse(
val imageUrl: String, val imageUrl: String,
val personalities: CharacterPersonalityResponse?, val personalities: CharacterPersonalityResponse?,
val backgrounds: CharacterBackgroundResponse?, val backgrounds: CharacterBackgroundResponse?,
val tags: String,
val originalTitle: String?,
val originalLink: String?,
val characterType: CharacterType,
val others: List<OtherCharacter>
)
data class OtherCharacter(
val characterId: Long,
val name: String,
val imageUrl: String,
val tags: String val tags: String
) )

View File

@ -66,4 +66,25 @@ interface ChatCharacterRepository : JpaRepository<ChatCharacter, Long> {
@Param("member") member: Member, @Param("member") member: Member,
pageable: Pageable pageable: Pageable
): List<ChatCharacter> ): List<ChatCharacter>
/**
* 특정 캐릭터와 태그를 공유하는 다른 캐릭터를 무작위로 조회 (현재 캐릭터 제외)
*/
@Query(
"""
SELECT DISTINCT c FROM ChatCharacter c
JOIN c.tagMappings tm
JOIN tm.tag t
WHERE c.isActive = true
AND c.id <> :characterId
AND t.id IN (
SELECT t2.id FROM ChatCharacterTagMapping tm2 JOIN tm2.tag t2 WHERE tm2.chatCharacter.id = :characterId
)
ORDER BY function('RAND')
"""
)
fun findRandomBySharedTags(
@Param("characterId") characterId: Long,
pageable: Pageable
): List<ChatCharacter>
} }

View File

@ -53,6 +53,20 @@ class ChatCharacterService(
return chatCharacterRepository.findByIsActiveTrueOrderByCreatedAtDesc(PageRequest.of(0, limit)) return chatCharacterRepository.findByIsActiveTrueOrderByCreatedAtDesc(PageRequest.of(0, limit))
} }
/**
* 특정 캐릭터와 태그를 공유하는 다른 캐릭터를 무작위로 조회 (현재 캐릭터 제외)
*/
@Transactional(readOnly = true)
fun getOtherCharactersBySharedTags(characterId: Long, limit: Int = 10): List<ChatCharacter> {
val others = chatCharacterRepository.findRandomBySharedTags(
characterId,
PageRequest.of(0, limit)
)
// 태그 초기화 (지연 로딩 문제 방지)
others.forEach { it.tagMappings.size }
return others
}
/** /**
* 태그를 찾거나 생성하여 캐릭터에 연결 * 태그를 찾거나 생성하여 캐릭터에 연결
*/ */