feat(ranking): 크리에이터 랭킹 조회 서비스를 추가한다
This commit is contained in:
@@ -0,0 +1,92 @@
|
||||
package kr.co.vividnext.sodalive.v2.ranking.application
|
||||
|
||||
import kr.co.vividnext.sodalive.v2.ranking.domain.CreatorRankingItem
|
||||
import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingBlockPort
|
||||
import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotPort
|
||||
import kr.co.vividnext.sodalive.v2.ranking.port.out.CreatorRankingSnapshotRecord
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
|
||||
@Service
|
||||
class CreatorRankingQueryService(
|
||||
private val snapshotPort: CreatorRankingSnapshotPort,
|
||||
private val blockPort: CreatorRankingBlockPort,
|
||||
@Value("\${cloud.aws.cloud-front.host}")
|
||||
private val cloudFrontHost: String
|
||||
) {
|
||||
@Transactional(readOnly = true)
|
||||
fun getCreatorRankings(viewerMemberId: Long?): CreatorRankingResult {
|
||||
val latestItems = snapshotPort.findLatestSnapshots().toRankedItems()
|
||||
if (latestItems.isEmpty()) {
|
||||
return CreatorRankingResult(showRankChange = false, items = emptyList())
|
||||
}
|
||||
|
||||
val previousItems = snapshotPort.findPreviousCompletedSnapshots().toRankedItems()
|
||||
val previousRankByCreatorId = previousItems.associate { it.creatorId to it.rank }
|
||||
val showRankChange = previousRankByCreatorId.isNotEmpty()
|
||||
val blockedCreatorIds = findBlockedCreatorIds(viewerMemberId = viewerMemberId, items = latestItems)
|
||||
val items = latestItems.map { item ->
|
||||
val previousRank = previousRankByCreatorId[item.creatorId]
|
||||
item.copy(
|
||||
rankChange = if (showRankChange && previousRank != null) previousRank - item.rank else null,
|
||||
isNew = showRankChange && previousRank == null
|
||||
).maskIfBlocked(blockedCreatorIds)
|
||||
}
|
||||
|
||||
return CreatorRankingResult(showRankChange = showRankChange, items = items)
|
||||
}
|
||||
|
||||
private fun List<CreatorRankingSnapshotRecord>.toRankedItems(): List<CreatorRankingItem> {
|
||||
return groupBy { it.finalScore }
|
||||
.toSortedMap(compareByDescending { it })
|
||||
.values
|
||||
.flatMap { it.shuffled() }
|
||||
.take(RANKING_LIMIT)
|
||||
.mapIndexed { index, snapshot -> snapshot.toItem(rank = index + 1) }
|
||||
}
|
||||
|
||||
private fun CreatorRankingSnapshotRecord.toItem(rank: Int): CreatorRankingItem {
|
||||
return CreatorRankingItem(
|
||||
rank = rank,
|
||||
rankChange = null,
|
||||
isNew = false,
|
||||
creatorId = creatorId,
|
||||
nickname = nickname,
|
||||
profileImageUrl = profileImageUrl
|
||||
)
|
||||
}
|
||||
|
||||
private fun findBlockedCreatorIds(viewerMemberId: Long?, items: List<CreatorRankingItem>): Set<Long> {
|
||||
if (viewerMemberId == null) {
|
||||
return emptySet()
|
||||
}
|
||||
return blockPort.findBlockedCreatorIds(
|
||||
memberId = viewerMemberId,
|
||||
creatorIds = items.map { it.creatorId }
|
||||
)
|
||||
}
|
||||
|
||||
private fun CreatorRankingItem.maskIfBlocked(blockedCreatorIds: Set<Long>): CreatorRankingItem {
|
||||
if (!blockedCreatorIds.contains(creatorId)) {
|
||||
return this
|
||||
}
|
||||
return copy(
|
||||
creatorId = MASKED_CREATOR_ID,
|
||||
nickname = MASKED_NICKNAME,
|
||||
profileImageUrl = "$cloudFrontHost/$DEFAULT_PROFILE_IMAGE_PATH"
|
||||
)
|
||||
}
|
||||
|
||||
companion object {
|
||||
private const val RANKING_LIMIT = 20
|
||||
private const val MASKED_CREATOR_ID = 0L
|
||||
private const val MASKED_NICKNAME = ""
|
||||
private const val DEFAULT_PROFILE_IMAGE_PATH = "profile/default-profile.png"
|
||||
}
|
||||
}
|
||||
|
||||
data class CreatorRankingResult(
|
||||
val showRankChange: Boolean,
|
||||
val items: List<CreatorRankingItem>
|
||||
)
|
||||
@@ -0,0 +1,5 @@
|
||||
package kr.co.vividnext.sodalive.v2.ranking.port.out
|
||||
|
||||
interface CreatorRankingBlockPort {
|
||||
fun findBlockedCreatorIds(memberId: Long, creatorIds: Collection<Long>): Set<Long>
|
||||
}
|
||||
Reference in New Issue
Block a user