feat(content-ranking): 랭킹 스냅샷 제목 번역을 적용한다
This commit is contained in:
+22
-7
@@ -1,5 +1,6 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.content.ranking.adapter.out.persistence
|
package kr.co.vividnext.sodalive.v2.content.ranking.adapter.out.persistence
|
||||||
|
|
||||||
|
import kr.co.vividnext.sodalive.content.translation.ContentTranslationRepository
|
||||||
import kr.co.vividnext.sodalive.v2.content.ranking.domain.AudioRankingType
|
import kr.co.vividnext.sodalive.v2.content.ranking.domain.AudioRankingType
|
||||||
import kr.co.vividnext.sodalive.v2.content.ranking.port.out.AudioRankingSnapshotPort
|
import kr.co.vividnext.sodalive.v2.content.ranking.port.out.AudioRankingSnapshotPort
|
||||||
import kr.co.vividnext.sodalive.v2.content.ranking.port.out.AudioRankingSnapshotRecord
|
import kr.co.vividnext.sodalive.v2.content.ranking.port.out.AudioRankingSnapshotRecord
|
||||||
@@ -9,25 +10,28 @@ import java.time.LocalDateTime
|
|||||||
|
|
||||||
@Repository
|
@Repository
|
||||||
class DefaultAudioRankingSnapshotPersistenceAdapter(
|
class DefaultAudioRankingSnapshotPersistenceAdapter(
|
||||||
private val repository: AudioRankingSnapshotRepository
|
private val repository: AudioRankingSnapshotRepository,
|
||||||
|
private val contentTranslationRepository: ContentTranslationRepository
|
||||||
) : AudioRankingSnapshotPort {
|
) : AudioRankingSnapshotPort {
|
||||||
override fun findLatestVisibleSnapshots(
|
override fun findLatestVisibleSnapshots(
|
||||||
rankingType: AudioRankingType,
|
rankingType: AudioRankingType,
|
||||||
nowUtc: LocalDateTime
|
nowUtc: LocalDateTime,
|
||||||
|
locale: String
|
||||||
): List<AudioRankingSnapshotRecord> {
|
): List<AudioRankingSnapshotRecord> {
|
||||||
return repository.findLatestVisibleSnapshots(rankingType.name, nowUtc).map { it.toRecord() }
|
return repository.findLatestVisibleSnapshots(rankingType.name, nowUtc).toRecords(locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
override fun findPreviousVisibleSnapshots(
|
override fun findPreviousVisibleSnapshots(
|
||||||
rankingType: AudioRankingType,
|
rankingType: AudioRankingType,
|
||||||
currentAggregationStartAtUtc: LocalDateTime,
|
currentAggregationStartAtUtc: LocalDateTime,
|
||||||
nowUtc: LocalDateTime
|
nowUtc: LocalDateTime,
|
||||||
|
locale: String
|
||||||
): List<AudioRankingSnapshotRecord> {
|
): List<AudioRankingSnapshotRecord> {
|
||||||
return repository.findPreviousVisibleSnapshots(
|
return repository.findPreviousVisibleSnapshots(
|
||||||
rankingType = rankingType.name,
|
rankingType = rankingType.name,
|
||||||
currentAggregationStartAtUtc = currentAggregationStartAtUtc,
|
currentAggregationStartAtUtc = currentAggregationStartAtUtc,
|
||||||
nowUtc = nowUtc
|
nowUtc = nowUtc
|
||||||
).map { it.toRecord() }
|
).toRecords(locale)
|
||||||
}
|
}
|
||||||
|
|
||||||
@Transactional
|
@Transactional
|
||||||
@@ -46,14 +50,25 @@ class DefaultAudioRankingSnapshotPersistenceAdapter(
|
|||||||
repository.saveAll(newSnapshots.map { it.toEntity(visibleFromAtUtc) })
|
repository.saveAll(newSnapshots.map { it.toEntity(visibleFromAtUtc) })
|
||||||
}
|
}
|
||||||
|
|
||||||
private fun AudioRankingSnapshot.toRecord(): AudioRankingSnapshotRecord {
|
private fun List<AudioRankingSnapshot>.toRecords(locale: String): List<AudioRankingSnapshotRecord> {
|
||||||
|
if (isEmpty()) return emptyList()
|
||||||
|
val translatedTitleByContentId = contentTranslationRepository.findByContentIdInAndLocale(
|
||||||
|
map { it.contentId },
|
||||||
|
locale
|
||||||
|
).mapNotNull { translation ->
|
||||||
|
translation.renderedPayload.title.takeIf { it.isNotBlank() }?.let { translation.contentId to it }
|
||||||
|
}.toMap()
|
||||||
|
return map { snapshot -> snapshot.toRecord(translatedTitleByContentId[snapshot.contentId] ?: snapshot.title) }
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun AudioRankingSnapshot.toRecord(displayTitle: String = title): AudioRankingSnapshotRecord {
|
||||||
return AudioRankingSnapshotRecord(
|
return AudioRankingSnapshotRecord(
|
||||||
rankingType = rankingType,
|
rankingType = rankingType,
|
||||||
aggregationStartAtUtc = aggregationStartAtUtc,
|
aggregationStartAtUtc = aggregationStartAtUtc,
|
||||||
aggregationEndAtUtc = aggregationEndAtUtc,
|
aggregationEndAtUtc = aggregationEndAtUtc,
|
||||||
visibleFromAtUtc = visibleFromAtUtc,
|
visibleFromAtUtc = visibleFromAtUtc,
|
||||||
contentId = contentId,
|
contentId = contentId,
|
||||||
title = title,
|
title = displayTitle,
|
||||||
creatorMemberId = creatorMemberId,
|
creatorMemberId = creatorMemberId,
|
||||||
creatorNickname = creatorNickname,
|
creatorNickname = creatorNickname,
|
||||||
coverImageUrl = coverImageUrl,
|
coverImageUrl = coverImageUrl,
|
||||||
|
|||||||
+221
-8
@@ -1,8 +1,12 @@
|
|||||||
package kr.co.vividnext.sodalive.v2.content.ranking.adapter.out.persistence
|
package kr.co.vividnext.sodalive.v2.content.ranking.adapter.out.persistence
|
||||||
|
|
||||||
import kr.co.vividnext.sodalive.configs.QueryDslConfig
|
import kr.co.vividnext.sodalive.configs.QueryDslConfig
|
||||||
|
import kr.co.vividnext.sodalive.content.translation.ContentTranslation
|
||||||
|
import kr.co.vividnext.sodalive.content.translation.ContentTranslationPayload
|
||||||
|
import kr.co.vividnext.sodalive.content.translation.ContentTranslationRepository
|
||||||
import kr.co.vividnext.sodalive.v2.content.ranking.domain.AudioRankingType
|
import kr.co.vividnext.sodalive.v2.content.ranking.domain.AudioRankingType
|
||||||
import kr.co.vividnext.sodalive.v2.content.ranking.port.out.AudioRankingSnapshotRecord
|
import kr.co.vividnext.sodalive.v2.content.ranking.port.out.AudioRankingSnapshotRecord
|
||||||
|
import org.hibernate.SessionFactory
|
||||||
import org.junit.jupiter.api.Assertions.assertEquals
|
import org.junit.jupiter.api.Assertions.assertEquals
|
||||||
import org.junit.jupiter.api.DisplayName
|
import org.junit.jupiter.api.DisplayName
|
||||||
import org.junit.jupiter.api.Test
|
import org.junit.jupiter.api.Test
|
||||||
@@ -10,18 +14,164 @@ import org.springframework.beans.factory.annotation.Autowired
|
|||||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
||||||
import org.springframework.context.annotation.Import
|
import org.springframework.context.annotation.Import
|
||||||
import java.time.LocalDateTime
|
import java.time.LocalDateTime
|
||||||
|
import javax.persistence.EntityManager
|
||||||
|
import javax.persistence.EntityManagerFactory
|
||||||
|
|
||||||
@DataJpaTest(
|
@DataJpaTest(
|
||||||
properties = [
|
properties = [
|
||||||
"spring.cache.type=none",
|
"spring.cache.type=none",
|
||||||
"spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL;NON_KEYWORDS=VALUE"
|
"spring.datasource.url=jdbc:h2:mem:testdb;MODE=MySQL;NON_KEYWORDS=VALUE",
|
||||||
|
"spring.jpa.properties.hibernate.generate_statistics=true"
|
||||||
]
|
]
|
||||||
)
|
)
|
||||||
@Import(QueryDslConfig::class)
|
@Import(QueryDslConfig::class)
|
||||||
class DefaultAudioRankingSnapshotPersistenceAdapterTest @Autowired constructor(
|
class DefaultAudioRankingSnapshotPersistenceAdapterTest @Autowired constructor(
|
||||||
private val repository: AudioRankingSnapshotRepository
|
private val repository: AudioRankingSnapshotRepository,
|
||||||
|
private val contentTranslationRepository: ContentTranslationRepository,
|
||||||
|
private val entityManager: EntityManager,
|
||||||
|
private val entityManagerFactory: EntityManagerFactory
|
||||||
) {
|
) {
|
||||||
private val adapter = DefaultAudioRankingSnapshotPersistenceAdapter(repository)
|
private val adapter = DefaultAudioRankingSnapshotPersistenceAdapter(repository, contentTranslationRepository)
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("SQL NULL 번역 payload는 snapshot 제목으로 fallback한다")
|
||||||
|
fun shouldFallbackToSnapshotTitleWhenPersistedTranslationPayloadIsSqlNull() {
|
||||||
|
val snapshot = repository.save(snapshot(contentId = 1L, title = "snapshot-title", scoreBase = 10.0))
|
||||||
|
val translationId = saveContentTranslation(snapshot.contentId, "en", "en-title")
|
||||||
|
entityManager.createNativeQuery("update content_translation set rendered_payload = null where id = :id")
|
||||||
|
.setParameter("id", translationId)
|
||||||
|
.executeUpdate()
|
||||||
|
entityManager.flush()
|
||||||
|
entityManager.clear()
|
||||||
|
|
||||||
|
val records = adapter.findLatestVisibleSnapshots(
|
||||||
|
AudioRankingType.WEEKLY_POPULAR,
|
||||||
|
LocalDateTime.of(2026, 6, 8, 23, 59),
|
||||||
|
"en"
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(listOf("snapshot-title"), records.map { it.title })
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("빈 최신과 이전 스냅샷은 번역 조회 없이 빈 목록을 반환한다")
|
||||||
|
fun shouldSkipTranslationLookupForEmptyLatestAndPreviousSnapshots() {
|
||||||
|
val statistics = entityManagerFactory.unwrap(SessionFactory::class.java).statistics
|
||||||
|
statistics.clear()
|
||||||
|
|
||||||
|
assertEquals(
|
||||||
|
emptyList<AudioRankingSnapshotRecord>(),
|
||||||
|
adapter.findLatestVisibleSnapshots(
|
||||||
|
AudioRankingType.WEEKLY_POPULAR,
|
||||||
|
LocalDateTime.of(2026, 6, 8, 23, 59),
|
||||||
|
"en"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertEquals(1L, statistics.prepareStatementCount)
|
||||||
|
assertEquals(
|
||||||
|
emptyList<AudioRankingSnapshotRecord>(),
|
||||||
|
adapter.findPreviousVisibleSnapshots(
|
||||||
|
AudioRankingType.WEEKLY_POPULAR,
|
||||||
|
LocalDateTime.of(2026, 5, 31, 15, 0),
|
||||||
|
LocalDateTime.of(2026, 6, 8, 23, 59),
|
||||||
|
"en"
|
||||||
|
)
|
||||||
|
)
|
||||||
|
assertEquals(2L, statistics.prepareStatementCount)
|
||||||
|
}
|
||||||
|
|
||||||
|
@Test
|
||||||
|
@DisplayName("랭킹 스냅샷은 요청 locale 번역을 일괄 조회하고 snapshot 제목으로 fallback한다")
|
||||||
|
fun shouldTranslateSnapshotTitlesInBatchesWithSnapshotFallback() {
|
||||||
|
val latestStartAt = LocalDateTime.of(2026, 5, 31, 15, 0)
|
||||||
|
val latestEndAt = LocalDateTime.of(2026, 6, 7, 15, 0)
|
||||||
|
val previousStartAt = latestStartAt.minusWeeks(1)
|
||||||
|
val previousEndAt = latestEndAt.minusWeeks(1)
|
||||||
|
val contentIds = (1L..5L).toList()
|
||||||
|
val latestSnapshots = contentIds.mapIndexed { index, contentId ->
|
||||||
|
snapshot(
|
||||||
|
contentId = contentId,
|
||||||
|
aggregationStartAtUtc = latestStartAt,
|
||||||
|
aggregationEndAtUtc = latestEndAt,
|
||||||
|
rank = index + 1,
|
||||||
|
scoreBase = 10.0 + index
|
||||||
|
)
|
||||||
|
}
|
||||||
|
val previousSnapshots = contentIds.mapIndexed { index, contentId ->
|
||||||
|
snapshot(
|
||||||
|
contentId = contentId,
|
||||||
|
aggregationStartAtUtc = previousStartAt,
|
||||||
|
aggregationEndAtUtc = previousEndAt,
|
||||||
|
visibleFromAtUtc = previousEndAt.plusHours(9),
|
||||||
|
rank = index + 1,
|
||||||
|
title = "previous-audio-$contentId",
|
||||||
|
scoreBase = 20.0 + index
|
||||||
|
)
|
||||||
|
}
|
||||||
|
repository.saveAll(latestSnapshots + previousSnapshots)
|
||||||
|
saveContentTranslation(1L, "en", "en-title-1")
|
||||||
|
saveContentTranslation(1L, "ja", "ja-title-1")
|
||||||
|
saveContentTranslation(2L, "en", "")
|
||||||
|
saveContentTranslation(2L, "ja", "ja-title-2")
|
||||||
|
saveContentTranslation(3L, "en", " ")
|
||||||
|
saveContentTranslation(3L, "ja", "ja-title-3")
|
||||||
|
saveContentTranslation(4L, "ja", "ja-title-4")
|
||||||
|
entityManager.flush()
|
||||||
|
entityManager.clear()
|
||||||
|
val statistics = entityManagerFactory.unwrap(SessionFactory::class.java).statistics
|
||||||
|
statistics.clear()
|
||||||
|
|
||||||
|
val nowUtc = latestEndAt.plusDays(1).plusHours(8)
|
||||||
|
val english = adapter.findLatestVisibleSnapshots(AudioRankingType.WEEKLY_POPULAR, nowUtc, "en")
|
||||||
|
val japanese = adapter.findLatestVisibleSnapshots(AudioRankingType.WEEKLY_POPULAR, nowUtc, "ja")
|
||||||
|
val englishAgain = adapter.findLatestVisibleSnapshots(AudioRankingType.WEEKLY_POPULAR, nowUtc, "en")
|
||||||
|
val previousEnglish = adapter.findPreviousVisibleSnapshots(
|
||||||
|
AudioRankingType.WEEKLY_POPULAR,
|
||||||
|
latestStartAt,
|
||||||
|
nowUtc,
|
||||||
|
"en"
|
||||||
|
)
|
||||||
|
val previousJapanese = adapter.findPreviousVisibleSnapshots(
|
||||||
|
AudioRankingType.WEEKLY_POPULAR,
|
||||||
|
latestStartAt,
|
||||||
|
nowUtc,
|
||||||
|
"ja"
|
||||||
|
)
|
||||||
|
val previousEnglishAgain = adapter.findPreviousVisibleSnapshots(
|
||||||
|
AudioRankingType.WEEKLY_POPULAR,
|
||||||
|
latestStartAt,
|
||||||
|
nowUtc,
|
||||||
|
"en"
|
||||||
|
)
|
||||||
|
|
||||||
|
assertEquals(listOf("en-title-1", "audio-2", "audio-3", "audio-4", "audio-5"), english.map { it.title })
|
||||||
|
assertEquals(listOf("ja-title-1", "ja-title-2", "ja-title-3", "ja-title-4", "audio-5"), japanese.map { it.title })
|
||||||
|
assertEquals(english.map { it.withoutTitle() }, japanese.map { it.withoutTitle() })
|
||||||
|
assertEquals(english.map { it.withoutTitle() }, englishAgain.map { it.withoutTitle() })
|
||||||
|
assertEquals(
|
||||||
|
listOf("en-title-1", "previous-audio-2", "previous-audio-3", "previous-audio-4", "previous-audio-5"),
|
||||||
|
previousEnglish.map { it.title }
|
||||||
|
)
|
||||||
|
assertEquals(
|
||||||
|
listOf("ja-title-1", "ja-title-2", "ja-title-3", "ja-title-4", "previous-audio-5"),
|
||||||
|
previousJapanese.map { it.title }
|
||||||
|
)
|
||||||
|
assertEquals(previousEnglish.map { it.withoutTitle() }, previousJapanese.map { it.withoutTitle() })
|
||||||
|
assertEquals(previousEnglish.map { it.withoutTitle() }, previousEnglishAgain.map { it.withoutTitle() })
|
||||||
|
assertEquals(12L, statistics.prepareStatementCount)
|
||||||
|
|
||||||
|
val persistedSnapshots = repository.findAll().sortedBy { it.aggregationStartAtUtc }.groupBy { it.aggregationStartAtUtc }
|
||||||
|
assertEquals(latestSnapshots.map { it.title }, persistedSnapshots.getValue(latestStartAt).map { it.title })
|
||||||
|
assertEquals(
|
||||||
|
latestSnapshots.map { it.scoreValues() },
|
||||||
|
persistedSnapshots.getValue(latestStartAt).map { it.scoreValues() }
|
||||||
|
)
|
||||||
|
assertEquals(previousSnapshots.map { it.title }, persistedSnapshots.getValue(previousStartAt).map { it.title })
|
||||||
|
assertEquals(
|
||||||
|
previousSnapshots.map { it.scoreValues() },
|
||||||
|
persistedSnapshots.getValue(previousStartAt).map { it.scoreValues() }
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
@Test
|
@Test
|
||||||
@DisplayName("최신 visible 스냅샷만 랭킹 타입별 rank 순서로 조회한다")
|
@DisplayName("최신 visible 스냅샷만 랭킹 타입별 rank 순서로 조회한다")
|
||||||
@@ -59,7 +209,8 @@ class DefaultAudioRankingSnapshotPersistenceAdapterTest @Autowired constructor(
|
|||||||
|
|
||||||
val snapshots = adapter.findLatestVisibleSnapshots(
|
val snapshots = adapter.findLatestVisibleSnapshots(
|
||||||
rankingType = AudioRankingType.WEEKLY_POPULAR,
|
rankingType = AudioRankingType.WEEKLY_POPULAR,
|
||||||
nowUtc = LocalDateTime.of(2026, 6, 8, 23, 59)
|
nowUtc = LocalDateTime.of(2026, 6, 8, 23, 59),
|
||||||
|
locale = "ko"
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(listOf(3L, 2L), snapshots.map { it.contentId })
|
assertEquals(listOf(3L, 2L), snapshots.map { it.contentId })
|
||||||
@@ -92,7 +243,8 @@ class DefaultAudioRankingSnapshotPersistenceAdapterTest @Autowired constructor(
|
|||||||
|
|
||||||
val snapshots = adapter.findLatestVisibleSnapshots(
|
val snapshots = adapter.findLatestVisibleSnapshots(
|
||||||
rankingType = AudioRankingType.WEEKLY_POPULAR,
|
rankingType = AudioRankingType.WEEKLY_POPULAR,
|
||||||
nowUtc = LocalDateTime.of(2026, 6, 7, 23, 59)
|
nowUtc = LocalDateTime.of(2026, 6, 7, 23, 59),
|
||||||
|
locale = "ko"
|
||||||
)
|
)
|
||||||
|
|
||||||
assertEquals(listOf(1L), snapshots.map { it.contentId })
|
assertEquals(listOf(1L), snapshots.map { it.contentId })
|
||||||
@@ -153,7 +305,9 @@ class DefaultAudioRankingSnapshotPersistenceAdapterTest @Autowired constructor(
|
|||||||
aggregationEndAtUtc: LocalDateTime = LocalDateTime.of(2026, 6, 7, 15, 0),
|
aggregationEndAtUtc: LocalDateTime = LocalDateTime.of(2026, 6, 7, 15, 0),
|
||||||
visibleFromAtUtc: LocalDateTime = LocalDateTime.of(2026, 6, 8, 0, 0),
|
visibleFromAtUtc: LocalDateTime = LocalDateTime.of(2026, 6, 8, 0, 0),
|
||||||
rank: Int = 1,
|
rank: Int = 1,
|
||||||
isAdult: Boolean = false
|
isAdult: Boolean = false,
|
||||||
|
title: String = "audio-$contentId",
|
||||||
|
scoreBase: Double = 100.0
|
||||||
): AudioRankingSnapshot {
|
): AudioRankingSnapshot {
|
||||||
return AudioRankingSnapshot(
|
return AudioRankingSnapshot(
|
||||||
rankingType = rankingType,
|
rankingType = rankingType,
|
||||||
@@ -161,14 +315,31 @@ class DefaultAudioRankingSnapshotPersistenceAdapterTest @Autowired constructor(
|
|||||||
aggregationEndAtUtc = aggregationEndAtUtc,
|
aggregationEndAtUtc = aggregationEndAtUtc,
|
||||||
visibleFromAtUtc = visibleFromAtUtc,
|
visibleFromAtUtc = visibleFromAtUtc,
|
||||||
contentId = contentId,
|
contentId = contentId,
|
||||||
title = "audio-$contentId",
|
title = title,
|
||||||
creatorMemberId = 100L + contentId,
|
creatorMemberId = 100L + contentId,
|
||||||
creatorNickname = "creator-$contentId",
|
creatorNickname = "creator-$contentId",
|
||||||
coverImageUrl = "cover-$contentId.png",
|
coverImageUrl = "cover-$contentId.png",
|
||||||
releaseDate = LocalDateTime.of(2026, 6, 1, 0, 0),
|
releaseDate = LocalDateTime.of(2026, 6, 1, 0, 0),
|
||||||
isAdult = isAdult,
|
isAdult = isAdult,
|
||||||
rank = rank,
|
rank = rank,
|
||||||
finalScore = 100.0
|
finalScore = scoreBase,
|
||||||
|
normalizedScore = scoreBase + 0.1,
|
||||||
|
rawScore = scoreBase + 0.2,
|
||||||
|
revenueCanAmount = scoreBase.toLong() * 10,
|
||||||
|
salesCount = scoreBase.toLong() * 20,
|
||||||
|
viewCount = scoreBase.toLong() * 30,
|
||||||
|
likeCount = scoreBase.toLong() * 40,
|
||||||
|
commentCount = scoreBase.toLong() * 50,
|
||||||
|
previousSalesCount = scoreBase.toLong() * 2,
|
||||||
|
previousViewCount = scoreBase.toLong() * 3,
|
||||||
|
previousLikeCount = scoreBase.toLong() * 4,
|
||||||
|
previousCommentCount = scoreBase.toLong() * 5,
|
||||||
|
salesGrowthRate = scoreBase + 0.3,
|
||||||
|
viewGrowthRate = scoreBase + 0.4,
|
||||||
|
likeGrowthRate = scoreBase + 0.5,
|
||||||
|
commentGrowthRate = scoreBase + 0.6,
|
||||||
|
contentGrowthScore = scoreBase + 0.7,
|
||||||
|
boostMultiplier = scoreBase + 0.8
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -195,4 +366,46 @@ class DefaultAudioRankingSnapshotPersistenceAdapterTest @Autowired constructor(
|
|||||||
finalScore = 100.0
|
finalScore = 100.0
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private fun saveContentTranslation(contentId: Long, locale: String, title: String): Long {
|
||||||
|
val translation = ContentTranslation(
|
||||||
|
contentId = contentId,
|
||||||
|
locale = locale,
|
||||||
|
renderedPayload = ContentTranslationPayload(title = title, detail = "", tags = "")
|
||||||
|
)
|
||||||
|
entityManager.persist(translation)
|
||||||
|
entityManager.flush()
|
||||||
|
val payload = "{\"title\":\"$title\",\"detail\":\"\",\"tags\":\"\"}"
|
||||||
|
entityManager.createNativeQuery(
|
||||||
|
"update content_translation set rendered_payload = '$payload' format json where id = :id"
|
||||||
|
)
|
||||||
|
.setParameter("id", translation.id)
|
||||||
|
.executeUpdate()
|
||||||
|
return translation.id!!
|
||||||
|
}
|
||||||
|
|
||||||
|
private fun AudioRankingSnapshotRecord.withoutTitle(): AudioRankingSnapshotRecord = copy(title = "")
|
||||||
|
|
||||||
|
private fun AudioRankingSnapshot.scoreValues(): List<Number?> {
|
||||||
|
return listOf(
|
||||||
|
finalScore,
|
||||||
|
normalizedScore,
|
||||||
|
rawScore,
|
||||||
|
revenueCanAmount,
|
||||||
|
salesCount,
|
||||||
|
viewCount,
|
||||||
|
likeCount,
|
||||||
|
commentCount,
|
||||||
|
previousSalesCount,
|
||||||
|
previousViewCount,
|
||||||
|
previousLikeCount,
|
||||||
|
previousCommentCount,
|
||||||
|
salesGrowthRate,
|
||||||
|
viewGrowthRate,
|
||||||
|
likeGrowthRate,
|
||||||
|
commentGrowthRate,
|
||||||
|
contentGrowthScore,
|
||||||
|
boostMultiplier
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
+4
-2
@@ -241,13 +241,15 @@ private class FakeAudioRankingSnapshotPort : AudioRankingSnapshotPort {
|
|||||||
|
|
||||||
override fun findLatestVisibleSnapshots(
|
override fun findLatestVisibleSnapshots(
|
||||||
rankingType: AudioRankingType,
|
rankingType: AudioRankingType,
|
||||||
nowUtc: LocalDateTime
|
nowUtc: LocalDateTime,
|
||||||
|
locale: String
|
||||||
): List<AudioRankingSnapshotRecord> = snapshots
|
): List<AudioRankingSnapshotRecord> = snapshots
|
||||||
|
|
||||||
override fun findPreviousVisibleSnapshots(
|
override fun findPreviousVisibleSnapshots(
|
||||||
rankingType: AudioRankingType,
|
rankingType: AudioRankingType,
|
||||||
currentAggregationStartAtUtc: LocalDateTime,
|
currentAggregationStartAtUtc: LocalDateTime,
|
||||||
nowUtc: LocalDateTime
|
nowUtc: LocalDateTime,
|
||||||
|
locale: String
|
||||||
): List<AudioRankingSnapshotRecord> = snapshots
|
): List<AudioRankingSnapshotRecord> = snapshots
|
||||||
|
|
||||||
override fun replaceSnapshots(
|
override fun replaceSnapshots(
|
||||||
|
|||||||
Reference in New Issue
Block a user