|
|
|
|
@@ -11,6 +11,7 @@ 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.dto.RecentCharactersResponse
|
|
|
|
|
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.translate.AiCharacterTranslation
|
|
|
|
|
@@ -33,6 +34,7 @@ import org.springframework.web.bind.annotation.PathVariable
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestMapping
|
|
|
|
|
import org.springframework.web.bind.annotation.RequestParam
|
|
|
|
|
import org.springframework.web.bind.annotation.RestController
|
|
|
|
|
import kotlin.collections.map
|
|
|
|
|
|
|
|
|
|
@RestController
|
|
|
|
|
@RequestMapping("/api/chat/character")
|
|
|
|
|
@@ -51,6 +53,7 @@ class ChatCharacterController(
|
|
|
|
|
) {
|
|
|
|
|
@GetMapping("/main")
|
|
|
|
|
fun getCharacterMain(
|
|
|
|
|
@RequestParam(required = false) languageCode: String? = null,
|
|
|
|
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
|
|
|
|
): ApiResponse<CharacterMainResponse> = run {
|
|
|
|
|
// 배너 조회 (최대 10개)
|
|
|
|
|
@@ -77,15 +80,110 @@ class ChatCharacterController(
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 최근 대화한 캐릭터 이름 번역 데이터 조회
|
|
|
|
|
*
|
|
|
|
|
* languageCode != null
|
|
|
|
|
* aiCharacterTranslationRepository 이용해 번역 콘텐츠를 조회한다. - characterId, locale
|
|
|
|
|
*
|
|
|
|
|
* 한 번에 조회하고 characterId 매핑하여 recentCharacters 캐릭터 이름을 번역 데이터로 변경한다
|
|
|
|
|
*/
|
|
|
|
|
val translatedRecentCharacters = if (!languageCode.isNullOrBlank()) {
|
|
|
|
|
val characterIds = recentCharacters.map { it.characterId }
|
|
|
|
|
|
|
|
|
|
if (characterIds.isNotEmpty()) {
|
|
|
|
|
val translations = aiCharacterTranslationRepository
|
|
|
|
|
.findByCharacterIdInAndLocale(characterIds = characterIds, locale = languageCode)
|
|
|
|
|
.associateBy { it.characterId }
|
|
|
|
|
|
|
|
|
|
recentCharacters.map { character ->
|
|
|
|
|
val translatedName = translations[character.characterId]?.renderedPayload?.name
|
|
|
|
|
if (translatedName.isNullOrBlank()) {
|
|
|
|
|
character
|
|
|
|
|
} else {
|
|
|
|
|
character.copy(name = translatedName)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
recentCharacters
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
recentCharacters
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 인기 캐릭터 조회
|
|
|
|
|
val popularCharacters = service.getPopularCharacters()
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* popularCharacters 캐릭터 이름과 description 번역 데이터 조회
|
|
|
|
|
*
|
|
|
|
|
* languageCode != null
|
|
|
|
|
* aiCharacterTranslationRepository 이용해 번역 콘텐츠를 조회한다. - characterId, locale
|
|
|
|
|
*
|
|
|
|
|
* 한 번에 조회하고 characterId 매핑하여 popularCharacters의 캐릭터 이름과 description을 번역 데이터로 변경한다
|
|
|
|
|
*/
|
|
|
|
|
val translatedPopularCharacters = if (!languageCode.isNullOrBlank()) {
|
|
|
|
|
val characterIds = popularCharacters.map { it.characterId }
|
|
|
|
|
|
|
|
|
|
if (characterIds.isNotEmpty()) {
|
|
|
|
|
val translations = aiCharacterTranslationRepository
|
|
|
|
|
.findByCharacterIdInAndLocale(characterIds = characterIds, locale = languageCode)
|
|
|
|
|
.associateBy { it.characterId }
|
|
|
|
|
|
|
|
|
|
popularCharacters.map { character ->
|
|
|
|
|
val translatedName = translations[character.characterId]?.renderedPayload?.name
|
|
|
|
|
val translatedDesc = translations[character.characterId]?.renderedPayload?.description
|
|
|
|
|
if (translatedName.isNullOrBlank() || translatedDesc.isNullOrBlank()) {
|
|
|
|
|
character
|
|
|
|
|
} else {
|
|
|
|
|
character.copy(name = translatedName, description = translatedDesc)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
popularCharacters
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
popularCharacters
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 최근 등록된 캐릭터 리스트 조회
|
|
|
|
|
val newCharacters = service.getRecentCharactersPage(
|
|
|
|
|
page = 0,
|
|
|
|
|
size = 50
|
|
|
|
|
).content
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 최근 등록된 캐릭터 이름 번역 데이터 조회
|
|
|
|
|
*
|
|
|
|
|
* languageCode != null
|
|
|
|
|
* aiCharacterTranslationRepository 이용해 번역 콘텐츠를 조회한다. - characterId, locale
|
|
|
|
|
*
|
|
|
|
|
* 한 번에 조회하고 characterId 매핑하여 newCharacters 캐릭터 이름과 description을 번역 데이터로 변경한다
|
|
|
|
|
*/
|
|
|
|
|
val translatedNewCharacters = if (!languageCode.isNullOrBlank()) {
|
|
|
|
|
val characterIds = newCharacters.map { it.characterId }
|
|
|
|
|
|
|
|
|
|
if (characterIds.isNotEmpty()) {
|
|
|
|
|
val translations = aiCharacterTranslationRepository
|
|
|
|
|
.findByCharacterIdInAndLocale(characterIds = characterIds, locale = languageCode)
|
|
|
|
|
.associateBy { it.characterId }
|
|
|
|
|
|
|
|
|
|
newCharacters.map { character ->
|
|
|
|
|
val translatedName = translations[character.characterId]?.renderedPayload?.name
|
|
|
|
|
val translatedDesc = translations[character.characterId]?.renderedPayload?.description
|
|
|
|
|
if (translatedName.isNullOrBlank() || translatedDesc.isNullOrBlank()) {
|
|
|
|
|
character
|
|
|
|
|
} else {
|
|
|
|
|
character.copy(name = translatedName, description = translatedDesc)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
newCharacters
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
newCharacters
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 추천 캐릭터 조회
|
|
|
|
|
// 최근 대화한 캐릭터를 제외한 랜덤 30개 조회
|
|
|
|
|
// Controller에서는 호출만
|
|
|
|
|
@@ -93,6 +191,38 @@ class ChatCharacterController(
|
|
|
|
|
val excludeIds = recentCharacters.map { it.characterId }
|
|
|
|
|
val recommendCharacters = service.getRecommendCharacters(excludeIds, 30)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 추천 캐릭터 이름 번역 데이터 조회
|
|
|
|
|
*
|
|
|
|
|
* languageCode != null
|
|
|
|
|
* aiCharacterTranslationRepository 이용해 번역 콘텐츠를 조회한다. - characterId, locale
|
|
|
|
|
*
|
|
|
|
|
* 한 번에 조회하고 characterId 매핑하여 recommendCharacters 캐릭터 이름과 description을 번역 데이터로 변경한다
|
|
|
|
|
*/
|
|
|
|
|
val translatedRecommendCharacters = if (!languageCode.isNullOrBlank()) {
|
|
|
|
|
val characterIds = recommendCharacters.map { it.characterId }
|
|
|
|
|
|
|
|
|
|
if (characterIds.isNotEmpty()) {
|
|
|
|
|
val translations = aiCharacterTranslationRepository
|
|
|
|
|
.findByCharacterIdInAndLocale(characterIds = characterIds, locale = languageCode)
|
|
|
|
|
.associateBy { it.characterId }
|
|
|
|
|
|
|
|
|
|
recommendCharacters.map { character ->
|
|
|
|
|
val translatedName = translations[character.characterId]?.renderedPayload?.name
|
|
|
|
|
val translatedDesc = translations[character.characterId]?.renderedPayload?.description
|
|
|
|
|
if (translatedName.isNullOrBlank() || translatedDesc.isNullOrBlank()) {
|
|
|
|
|
character
|
|
|
|
|
} else {
|
|
|
|
|
character.copy(name = translatedName, description = translatedDesc)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
recommendCharacters
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
recommendCharacters
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 큐레이션 섹션 (활성화된 큐레이션 + 캐릭터)
|
|
|
|
|
val curationSections = curationQueryService.getActiveCurationsWithCharacters()
|
|
|
|
|
.map { agg ->
|
|
|
|
|
@@ -115,10 +245,10 @@ class ChatCharacterController(
|
|
|
|
|
ApiResponse.ok(
|
|
|
|
|
CharacterMainResponse(
|
|
|
|
|
banners = banners,
|
|
|
|
|
recentCharacters = recentCharacters,
|
|
|
|
|
popularCharacters = popularCharacters,
|
|
|
|
|
newCharacters = newCharacters,
|
|
|
|
|
recommendCharacters = recommendCharacters,
|
|
|
|
|
recentCharacters = translatedRecentCharacters,
|
|
|
|
|
popularCharacters = translatedPopularCharacters,
|
|
|
|
|
newCharacters = translatedNewCharacters,
|
|
|
|
|
recommendCharacters = translatedRecommendCharacters,
|
|
|
|
|
curationSections = curationSections
|
|
|
|
|
)
|
|
|
|
|
)
|
|
|
|
|
@@ -291,6 +421,40 @@ class ChatCharacterController(
|
|
|
|
|
)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 다른 캐릭터 이름, 태그 번역 데이터 조회
|
|
|
|
|
*
|
|
|
|
|
* languageCode != null
|
|
|
|
|
* aiCharacterTranslationRepository 이용해 번역 콘텐츠를 조회한다. - characterId, locale
|
|
|
|
|
*
|
|
|
|
|
* 한 번에 조회하고 characterId 매핑하여 others 캐릭터 이름과 tags 번역 데이터로 변경한다
|
|
|
|
|
*/
|
|
|
|
|
val translatedOthers = if (!languageCode.isNullOrBlank()) {
|
|
|
|
|
val characterIds = others.map { it.characterId }
|
|
|
|
|
|
|
|
|
|
if (characterIds.isNotEmpty()) {
|
|
|
|
|
val translations = aiCharacterTranslationRepository
|
|
|
|
|
.findByCharacterIdInAndLocale(characterIds = characterIds, locale = languageCode)
|
|
|
|
|
.associateBy { it.characterId }
|
|
|
|
|
|
|
|
|
|
others.map { other ->
|
|
|
|
|
val payload = translations[other.characterId]?.renderedPayload
|
|
|
|
|
val translatedName = payload?.name
|
|
|
|
|
val translatedTags = payload?.tags
|
|
|
|
|
|
|
|
|
|
if (translatedName.isNullOrBlank() || translatedTags.isNullOrBlank()) {
|
|
|
|
|
other
|
|
|
|
|
} else {
|
|
|
|
|
other.copy(name = translatedName, tags = translatedTags)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
others
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
others
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
// 최신 댓글 1개 조회
|
|
|
|
|
val latestComment = characterCommentService.getLatestComment(imageHost, character.id!!)
|
|
|
|
|
|
|
|
|
|
@@ -311,7 +475,7 @@ class ChatCharacterController(
|
|
|
|
|
originalTitle = character.originalTitle,
|
|
|
|
|
originalLink = character.originalLink,
|
|
|
|
|
characterType = character.characterType,
|
|
|
|
|
others = others,
|
|
|
|
|
others = translatedOthers,
|
|
|
|
|
latestComment = latestComment,
|
|
|
|
|
totalComments = characterCommentService.getTotalCommentCount(character.id!!),
|
|
|
|
|
translated = translated
|
|
|
|
|
@@ -325,13 +489,53 @@ class ChatCharacterController(
|
|
|
|
|
* - 예외: 2주 이내 캐릭터가 0개인 경우, 최근 등록한 캐릭터 20개만 제공
|
|
|
|
|
*/
|
|
|
|
|
@GetMapping("/recent")
|
|
|
|
|
fun getRecentCharacters(@RequestParam("page", required = false) page: Int?) = run {
|
|
|
|
|
ApiResponse.ok(
|
|
|
|
|
service.getRecentCharactersPage(
|
|
|
|
|
page = page ?: 0,
|
|
|
|
|
size = 20
|
|
|
|
|
)
|
|
|
|
|
fun getRecentCharacters(
|
|
|
|
|
@RequestParam(required = false) languageCode: String? = null,
|
|
|
|
|
@RequestParam("page", required = false) page: Int?
|
|
|
|
|
): ApiResponse<RecentCharactersResponse> = run {
|
|
|
|
|
val characterPage = service.getRecentCharactersPage(
|
|
|
|
|
page = page ?: 0,
|
|
|
|
|
size = 20
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 최근 등록된 캐릭터 이름 번역 데이터 조회
|
|
|
|
|
*
|
|
|
|
|
* languageCode != null
|
|
|
|
|
* aiCharacterTranslationRepository 이용해 번역 콘텐츠를 조회한다. - characterId, locale
|
|
|
|
|
*
|
|
|
|
|
* 한 번에 조회하고 characterId 매핑하여 characterList 캐릭터 이름과 description을 번역 데이터로 변경한다
|
|
|
|
|
*/
|
|
|
|
|
val translatedContent = if (!languageCode.isNullOrBlank()) {
|
|
|
|
|
val characterIds = characterPage.content.map { it.characterId }
|
|
|
|
|
|
|
|
|
|
if (characterIds.isNotEmpty()) {
|
|
|
|
|
val translations = aiCharacterTranslationRepository
|
|
|
|
|
.findByCharacterIdInAndLocale(characterIds = characterIds, locale = languageCode)
|
|
|
|
|
.associateBy { it.characterId }
|
|
|
|
|
|
|
|
|
|
characterPage.content.map { character ->
|
|
|
|
|
val translatedName = translations[character.characterId]?.renderedPayload?.name
|
|
|
|
|
val translatedDesc = translations[character.characterId]?.renderedPayload?.description
|
|
|
|
|
if (translatedName.isNullOrBlank() || translatedDesc.isNullOrBlank()) {
|
|
|
|
|
character
|
|
|
|
|
} else {
|
|
|
|
|
character.copy(name = translatedName, description = translatedDesc)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
characterPage.content
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
characterPage.content
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
val translatedCharacterPage = RecentCharactersResponse(
|
|
|
|
|
totalCount = characterPage.totalCount,
|
|
|
|
|
content = translatedContent
|
|
|
|
|
)
|
|
|
|
|
|
|
|
|
|
ApiResponse.ok(translatedCharacterPage)
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
@@ -341,6 +545,7 @@ class ChatCharacterController(
|
|
|
|
|
*/
|
|
|
|
|
@GetMapping("/recommend")
|
|
|
|
|
fun getRecommendCharacters(
|
|
|
|
|
@RequestParam(required = false) languageCode: String? = null,
|
|
|
|
|
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
|
|
|
|
) = run {
|
|
|
|
|
val recent = if (member == null || member.auth == null) {
|
|
|
|
|
@@ -350,6 +555,40 @@ class ChatCharacterController(
|
|
|
|
|
.listMyChatRooms(member, 0, 50) // 최근 기록은 최대 50개까지만 제외 대상으로 고려
|
|
|
|
|
.map { it.characterId }
|
|
|
|
|
}
|
|
|
|
|
ApiResponse.ok(service.getRecommendCharacters(recent, 20))
|
|
|
|
|
val characterList = service.getRecommendCharacters(recent, 20)
|
|
|
|
|
|
|
|
|
|
/**
|
|
|
|
|
* 추천 캐릭터 이름 번역 데이터 조회
|
|
|
|
|
*
|
|
|
|
|
* languageCode != null
|
|
|
|
|
* aiCharacterTranslationRepository 이용해 번역 콘텐츠를 조회한다. - characterId, locale
|
|
|
|
|
*
|
|
|
|
|
* 한 번에 조회하고 characterId 매핑하여 characterList 캐릭터 이름과 description을 번역 데이터로 변경한다
|
|
|
|
|
*/
|
|
|
|
|
val translatedCharacterList = if (!languageCode.isNullOrBlank()) {
|
|
|
|
|
val characterIds = characterList.map { it.characterId }
|
|
|
|
|
|
|
|
|
|
if (characterIds.isNotEmpty()) {
|
|
|
|
|
val translations = aiCharacterTranslationRepository
|
|
|
|
|
.findByCharacterIdInAndLocale(characterIds = characterIds, locale = languageCode)
|
|
|
|
|
.associateBy { it.characterId }
|
|
|
|
|
|
|
|
|
|
characterList.map { character ->
|
|
|
|
|
val translatedName = translations[character.characterId]?.renderedPayload?.name
|
|
|
|
|
val translatedDesc = translations[character.characterId]?.renderedPayload?.description
|
|
|
|
|
if (translatedName.isNullOrBlank() || translatedDesc.isNullOrBlank()) {
|
|
|
|
|
character
|
|
|
|
|
} else {
|
|
|
|
|
character.copy(name = translatedName, description = translatedDesc)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
characterList
|
|
|
|
|
}
|
|
|
|
|
} else {
|
|
|
|
|
characterList
|
|
|
|
|
}
|
|
|
|
|
|
|
|
|
|
ApiResponse.ok(translatedCharacterList)
|
|
|
|
|
}
|
|
|
|
|
}
|
|
|
|
|
|