Merge pull request 'test' (#371) from test into main

Reviewed-on: #371
This commit is contained in:
2026-01-05 06:33:29 +00:00
5 changed files with 65 additions and 50 deletions

View File

@@ -113,30 +113,6 @@ class ExplorerQueryRepository(
.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(
creatorId: Long,
limit: Long,

View File

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

View File

@@ -8,23 +8,14 @@ import kr.co.vividnext.sodalive.can.use.QUseCan.useCan
import kr.co.vividnext.sodalive.can.use.QUseCanCalculate.useCanCalculate
import kr.co.vividnext.sodalive.member.QMember.member
import org.springframework.stereotype.Repository
import java.time.DayOfWeek
import java.time.LocalDateTime
import java.time.LocalTime
import java.time.temporal.TemporalAdjusters
@Repository
class CreatorDonationRankingQueryRepository(private val queryFactory: JPAQueryFactory) {
fun getMemberDonationRanking(
creatorId: Long,
offset: Long,
limit: Long
): List<DonationRankingProjection> {
val now = LocalDateTime.now()
val lastMonday = now.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
.minusWeeks(1)
.with(LocalTime.MIN)
val lastSunday = lastMonday.plusDays(6).with(LocalTime.MAX)
val donationCan = useCan.rewardCan.add(useCan.can).sum()
return queryFactory
.select(
@@ -47,14 +38,34 @@ class CreatorDonationRankingQueryRepository(private val queryFactory: JPAQueryFa
.or(useCan.canUsage.eq(CanUsage.SPIN_ROULETTE))
.or(useCan.canUsage.eq(CanUsage.LIVE))
)
.and(useCan.createdAt.between(lastMonday, lastSunday))
)
.offset(0)
.offset(offset)
.limit(limit)
.groupBy(member.id)
.orderBy(donationCan.desc(), member.id.desc())
.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(

View File

@@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.explorer.profile
import kr.co.vividnext.sodalive.explorer.MemberDonationRankingListResponse
import kr.co.vividnext.sodalive.explorer.MemberDonationRankingResponse
import org.springframework.beans.factory.annotation.Value
import org.springframework.data.redis.core.RedisTemplate
import org.springframework.stereotype.Service
import java.time.DayOfWeek
@@ -14,26 +15,52 @@ import java.time.temporal.TemporalAdjusters
@Service
class CreatorDonationRankingService(
private val repository: CreatorDonationRankingQueryRepository,
private val redisTemplate: RedisTemplate<String, Any>
private val redisTemplate: RedisTemplate<String, Any>,
@Value("\${cloud.aws.cloud-front.host}")
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(
creatorId: Long,
limit: Long,
offset: Long = 0,
limit: Long = 10,
withDonationCan: Boolean
): List<MemberDonationRankingResponse> {
val cacheKey = "creator_donation_ranking:$creatorId:$limit:$withDonationCan"
val cacheKey = "creator_donation_ranking_v2:$creatorId:$offset:$limit:$withDonationCan"
val cachedData = redisTemplate.opsForValue().get(cacheKey) as? MemberDonationRankingListResponse
if (cachedData != null) {
return cachedData.rankings
}
val memberDonationRanking = repository.getMemberDonationRanking(creatorId, limit)
val memberDonationRanking = repository.getMemberDonationRanking(creatorId, offset, limit)
val result = memberDonationRanking.map {
MemberDonationRankingResponse(
it.memberId,
it.nickname,
it.profileImage,
"$imageHost/${it.profileImage}",
if (withDonationCan) it.donationCan else 0
)
}

View File

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