diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/controller/ChatCharacterController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/controller/ChatCharacterController.kt index 2f1dfa6..2f3ff8e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/controller/ChatCharacterController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/chat/character/controller/ChatCharacterController.kt @@ -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 = 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 = 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) } }