feat(translation): 커뮤니티 원문 추출 대상을 추가한다

This commit is contained in:
2026-09-10 15:10:55 +09:00
parent 46803f40b5
commit 3e9ba0b23e
3 changed files with 67 additions and 4 deletions
@@ -18,7 +18,8 @@ enum class LanguageTranslationTargetType {
ORIGINAL_WORK, ORIGINAL_WORK,
CREATOR_CONTENT_CATEGORY CREATOR_CONTENT_CATEGORY,
CREATOR_COMMUNITY
} }
class LanguageTranslationEvent( class LanguageTranslationEvent(
@@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.chat.original.OriginalWorkRepository
import kr.co.vividnext.sodalive.content.AudioContentRepository import kr.co.vividnext.sodalive.content.AudioContentRepository
import kr.co.vividnext.sodalive.content.category.CategoryRepository import kr.co.vividnext.sodalive.content.category.CategoryRepository
import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository
import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunityRepository
import org.springframework.data.repository.findByIdOrNull import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Component import org.springframework.stereotype.Component
@@ -19,7 +20,8 @@ data class TranslationSource(
val resourceType: LanguageTranslationTargetType, val resourceType: LanguageTranslationTargetType,
val resourceId: Long, val resourceId: Long,
val sourceLanguage: String, val sourceLanguage: String,
val fields: List<TranslationSourceField> val fields: List<TranslationSourceField>,
val sourceRevision: Long? = null
) )
@Component @Component
@@ -30,7 +32,8 @@ class TranslationSourceExtractor(
private val seriesRepository: AdminContentSeriesRepository, private val seriesRepository: AdminContentSeriesRepository,
private val seriesGenreRepository: AdminContentSeriesGenreRepository, private val seriesGenreRepository: AdminContentSeriesGenreRepository,
private val originalWorkRepository: OriginalWorkRepository, private val originalWorkRepository: OriginalWorkRepository,
private val categoryRepository: CategoryRepository private val categoryRepository: CategoryRepository,
private val creatorCommunityRepository: CreatorCommunityRepository
) { ) {
fun extract(resourceType: LanguageTranslationTargetType, resourceId: Long): TranslationSource? { fun extract(resourceType: LanguageTranslationTargetType, resourceId: Long): TranslationSource? {
return when (resourceType) { return when (resourceType) {
@@ -41,6 +44,7 @@ class TranslationSourceExtractor(
LanguageTranslationTargetType.SERIES_GENRE -> extractSeriesGenre(resourceId) LanguageTranslationTargetType.SERIES_GENRE -> extractSeriesGenre(resourceId)
LanguageTranslationTargetType.ORIGINAL_WORK -> extractOriginalWork(resourceId) LanguageTranslationTargetType.ORIGINAL_WORK -> extractOriginalWork(resourceId)
LanguageTranslationTargetType.CREATOR_CONTENT_CATEGORY -> extractCategory(resourceId) LanguageTranslationTargetType.CREATOR_CONTENT_CATEGORY -> extractCategory(resourceId)
LanguageTranslationTargetType.CREATOR_COMMUNITY -> extractCreatorCommunity(resourceId)
} }
} }
@@ -152,4 +156,16 @@ class TranslationSourceExtractor(
fields = listOf(TranslationSourceField("category", category.title)) fields = listOf(TranslationSourceField("category", category.title))
) )
} }
private fun extractCreatorCommunity(resourceId: Long): TranslationSource? {
val creatorCommunity = creatorCommunityRepository.findByIdAndIsActiveTrue(resourceId) ?: return null
val sourceLanguage = creatorCommunity.languageCode?.takeIf { it.isNotBlank() } ?: return null
return TranslationSource(
resourceType = LanguageTranslationTargetType.CREATOR_COMMUNITY,
resourceId = resourceId,
sourceLanguage = sourceLanguage,
fields = listOf(TranslationSourceField("content", creatorCommunity.content)),
sourceRevision = creatorCommunity.contentRevision
)
}
} }
@@ -75,6 +75,51 @@ class TranslationJobWorkerTest {
Mockito.verify(materializer, Mockito.times(1)).materialize(LanguageTranslationTargetType.CONTENT, 10L, "en") Mockito.verify(materializer, Mockito.times(1)).materialize(LanguageTranslationTargetType.CONTENT, 10L, "en")
} }
@Test
fun shouldUseExistingMemoryBeforeMaterializingCreatorCommunity() {
val jobRepository = Mockito.mock(TranslationJobRepository::class.java)
val memoryRepository = Mockito.mock(TranslationMemoryRepository::class.java)
val provider = failingProvider()
val materializer = Mockito.mock(TranslationReadModelMaterializer::class.java)
val worker = TranslationJobWorker(
translationJobRepository = jobRepository,
translationMemoryRepository = memoryRepository,
translationProvider = provider,
materializer = materializer,
transactionManager = TestTransactionManager()
)
val job = translationJob(
resourceType = LanguageTranslationTargetType.CREATOR_COMMUNITY,
fieldKey = "content",
sourceText = "본문"
).also { it.id = 100L }
Mockito.`when`(jobRepository.findNextPendingGroupJobIdsForUpdate(anyLocalDateTime())).thenReturn(listOf(100L))
Mockito.`when`(jobRepository.findById(100L)).thenReturn(Optional.of(job))
Mockito.`when`(
memoryRepository.findBySourceHashAndSourceLanguageAndTargetLanguageAndProviderAndNormalizationVersion(
job.sourceHash,
"ko",
"en",
"papago",
SourceTextNormalizer.NORMALIZATION_VERSION
)
).thenReturn(
TranslationMemory(
sourceHash = job.sourceHash,
sourceText = job.sourceText,
sourceLanguage = "ko",
targetLanguage = "en",
translatedText = "translated",
provider = "papago",
providerVersion = "nmt-v1"
)
)
worker.processNextGroup()
Mockito.verify(materializer).materialize(LanguageTranslationTargetType.CREATOR_COMMUNITY, 10L, "en")
}
@Test @Test
fun shouldRetryGroupWhenMaterializationFails() { fun shouldRetryGroupWhenMaterializationFails() {
val jobRepository = Mockito.mock(TranslationJobRepository::class.java) val jobRepository = Mockito.mock(TranslationJobRepository::class.java)
@@ -174,11 +219,12 @@ class TranslationJobWorkerTest {
private fun translationJob( private fun translationJob(
resourceId: Long = 10L, resourceId: Long = 10L,
resourceType: LanguageTranslationTargetType = LanguageTranslationTargetType.CONTENT,
fieldKey: String = "title", fieldKey: String = "title",
sourceText: String = "제목" sourceText: String = "제목"
): TranslationJob { ): TranslationJob {
return TranslationJob( return TranslationJob(
resourceType = LanguageTranslationTargetType.CONTENT, resourceType = resourceType,
resourceId = resourceId, resourceId = resourceId,
fieldKey = fieldKey, fieldKey = fieldKey,
sourceHash = SourceTextNormalizer.hash(sourceText), sourceHash = SourceTextNormalizer.hash(sourceText),