후원 랭킹 조회 캐시 적용

This commit is contained in:
2026-01-05 14:46:57 +09:00
parent c494ddcf20
commit 54bfd9987d
5 changed files with 59 additions and 37 deletions

View File

@@ -113,30 +113,6 @@ class ExplorerQueryRepository(
.fetchOne() .fetchOne()
} }
fun getMemberDonationRankingTotal(creatorId: Long): Int {
val userMember = QMember("user")
val donation = useCan.rewardCan.add(useCan.can).sum()
return queryFactory
.select(userMember.id)
.from(useCanCalculate)
.innerJoin(useCanCalculate.useCan, useCan)
.innerJoin(useCan.member, userMember)
.where(
useCan.isRefund.isFalse
.and(useCanCalculate.recipientCreatorId.eq(creatorId))
.and(
useCan.canUsage.eq(CanUsage.DONATION)
.or(useCan.canUsage.eq(CanUsage.SPIN_ROULETTE))
.or(useCan.canUsage.eq(CanUsage.LIVE))
)
)
.groupBy(useCan.member.id)
.orderBy(donation.desc())
.fetch()
.size
}
fun getMemberDonationRanking( fun getMemberDonationRanking(
creatorId: Long, creatorId: Long,
limit: Long, limit: Long,

View File

@@ -403,6 +403,14 @@ class ExplorerService(
val firstDayOfLastWeek = currentDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusDays(7) val firstDayOfLastWeek = currentDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusDays(7)
val firstDayOfMonth = currentDate.with(TemporalAdjusters.firstDayOfMonth()) val firstDayOfMonth = currentDate.with(TemporalAdjusters.firstDayOfMonth())
val donationMemberTotal = donationRankingService.getMemberDonationRankingTotal(creatorId)
val donationRanking = donationRankingService.getMemberDonationRanking(
creatorId = creatorId,
offset = pageable.offset,
limit = pageable.pageSize.toLong(),
withDonationCan = creatorId == member.id!!
)
return GetDonationAllResponse( return GetDonationAllResponse(
accumulatedCansToday = if (creatorId == member.id!!) { accumulatedCansToday = if (creatorId == member.id!!) {
queryRepository.getDonationCoinsDateRange( queryRepository.getDonationCoinsDateRange(
@@ -436,13 +444,8 @@ class ExplorerService(
} else { } else {
false false
}, },
totalCount = queryRepository.getMemberDonationRankingTotal(creatorId), totalCount = donationMemberTotal,
userDonationRanking = queryRepository.getMemberDonationRanking( userDonationRanking = donationRanking
creatorId,
offset = pageable.offset,
limit = pageable.pageSize.toLong(),
withDonationCan = creatorId == member.id!!
)
) )
} }

View File

@@ -13,6 +13,7 @@ import org.springframework.stereotype.Repository
class CreatorDonationRankingQueryRepository(private val queryFactory: JPAQueryFactory) { class CreatorDonationRankingQueryRepository(private val queryFactory: JPAQueryFactory) {
fun getMemberDonationRanking( fun getMemberDonationRanking(
creatorId: Long, creatorId: Long,
offset: Long,
limit: Long limit: Long
): List<DonationRankingProjection> { ): List<DonationRankingProjection> {
val donationCan = useCan.rewardCan.add(useCan.can).sum() val donationCan = useCan.rewardCan.add(useCan.can).sum()
@@ -38,12 +39,33 @@ class CreatorDonationRankingQueryRepository(private val queryFactory: JPAQueryFa
.or(useCan.canUsage.eq(CanUsage.LIVE)) .or(useCan.canUsage.eq(CanUsage.LIVE))
) )
) )
.offset(0) .offset(offset)
.limit(limit) .limit(limit)
.groupBy(member.id) .groupBy(member.id)
.orderBy(donationCan.desc(), member.id.desc()) .orderBy(donationCan.desc(), member.id.desc())
.fetch() .fetch()
} }
fun getMemberDonationRankingTotal(creatorId: Long): Int {
return queryFactory
.select(member.id)
.from(useCanCalculate)
.innerJoin(useCanCalculate.useCan, useCan)
.innerJoin(useCan.member, member)
.where(
useCan.member.isActive.isTrue
.and(useCan.isRefund.isFalse)
.and(useCanCalculate.recipientCreatorId.eq(creatorId))
.and(
useCan.canUsage.eq(CanUsage.DONATION)
.or(useCan.canUsage.eq(CanUsage.SPIN_ROULETTE))
.or(useCan.canUsage.eq(CanUsage.LIVE))
)
)
.groupBy(member.id)
.fetch()
.size
}
} }
data class DonationRankingProjection @QueryProjection constructor( data class DonationRankingProjection @QueryProjection constructor(

View File

@@ -20,18 +20,41 @@ class CreatorDonationRankingService(
@Value("\${cloud.aws.cloud-front.host}") @Value("\${cloud.aws.cloud-front.host}")
private val imageHost: String private val imageHost: String
) { ) {
fun getMemberDonationRankingTotal(creatorId: Long): Int {
val cacheKey = "creator_donation_ranking_member_total_v2:$creatorId"
val cachedTotal = redisTemplate.opsForValue().get(cacheKey) as? Int
if (cachedTotal != null) {
return cachedTotal
}
val total = repository.getMemberDonationRankingTotal(creatorId)
val now = LocalDateTime.now()
val nextMonday = now.with(TemporalAdjusters.next(DayOfWeek.MONDAY)).with(LocalTime.MIN)
val secondsUntilNextMonday = ChronoUnit.SECONDS.between(now, nextMonday)
redisTemplate.opsForValue().set(
cacheKey,
total,
Duration.ofSeconds(secondsUntilNextMonday)
)
return total
}
fun getMemberDonationRanking( fun getMemberDonationRanking(
creatorId: Long, creatorId: Long,
limit: Long, offset: Long = 0,
limit: Long = 10,
withDonationCan: Boolean withDonationCan: Boolean
): List<MemberDonationRankingResponse> { ): List<MemberDonationRankingResponse> {
val cacheKey = "creator_donation_ranking_v2:$creatorId:$limit:$withDonationCan" val cacheKey = "creator_donation_ranking_v2:$creatorId:$offset:$limit:$withDonationCan"
val cachedData = redisTemplate.opsForValue().get(cacheKey) as? MemberDonationRankingListResponse val cachedData = redisTemplate.opsForValue().get(cacheKey) as? MemberDonationRankingListResponse
if (cachedData != null) { if (cachedData != null) {
return cachedData.rankings return cachedData.rankings
} }
val memberDonationRanking = repository.getMemberDonationRanking(creatorId, limit) val memberDonationRanking = repository.getMemberDonationRanking(creatorId, offset, limit)
val result = memberDonationRanking.map { val result = memberDonationRanking.map {
MemberDonationRankingResponse( MemberDonationRankingResponse(

View File

@@ -888,9 +888,7 @@ class LiveRoomService(
3, 3,
withDonationCan = false withDonationCan = false
) )
.asSequence()
.map { it.userId } .map { it.userId }
.toList()
} else { } else {
listOf() listOf()
} }