캐릭터 챗봇 #338
|
@ -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.CharacterPersonalityResponse
|
||||
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.service.ChatCharacterBannerService
|
||||
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(
|
||||
CharacterDetailResponse(
|
||||
|
@ -137,7 +152,11 @@ class ChatCharacterController(
|
|||
imageUrl = "$imageHost/${character.imagePath ?: "profile/default-profile.png"}",
|
||||
personalities = personality,
|
||||
backgrounds = background,
|
||||
tags = tags
|
||||
tags = tags,
|
||||
originalTitle = character.originalTitle,
|
||||
originalLink = character.originalLink,
|
||||
characterType = character.characterType,
|
||||
others = others
|
||||
)
|
||||
)
|
||||
}
|
||||
|
|
|
@ -1,5 +1,7 @@
|
|||
package kr.co.vividnext.sodalive.chat.character.dto
|
||||
|
||||
import kr.co.vividnext.sodalive.chat.character.CharacterType
|
||||
|
||||
data class CharacterDetailResponse(
|
||||
val characterId: Long,
|
||||
val name: String,
|
||||
|
@ -8,6 +10,17 @@ data class CharacterDetailResponse(
|
|||
val imageUrl: String,
|
||||
val personalities: CharacterPersonalityResponse?,
|
||||
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
|
||||
)
|
||||
|
||||
|
|
|
@ -66,4 +66,25 @@ interface ChatCharacterRepository : JpaRepository<ChatCharacter, Long> {
|
|||
@Param("member") member: Member,
|
||||
pageable: Pageable
|
||||
): 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>
|
||||
}
|
||||
|
|
|
@ -53,6 +53,20 @@ class ChatCharacterService(
|
|||
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
|
||||
}
|
||||
|
||||
/**
|
||||
* 태그를 찾거나 생성하여 캐릭터에 연결
|
||||
*/
|
||||
|
|
Loading…
Reference in New Issue