후원랭킹 전체보기 API 추가

This commit is contained in:
Klaus 2023-08-28 20:53:14 +09:00
parent 1629bc8bf7
commit f25888c88e
7 changed files with 131 additions and 66 deletions

View File

@ -307,7 +307,7 @@ class AudioContentService(
// 크리에이터(유저) 정보
val creatorId = audioContent.member!!.id!!
val creator = explorerQueryRepository.getAccount(creatorId)
val creator = explorerQueryRepository.getMember(creatorId)
?: throw SodaException("없는 사용자 입니다.")
val notificationUserIds = explorerQueryRepository.getNotificationUserIds(creatorId)

View File

@ -49,6 +49,16 @@ class ExplorerController(private val service: ExplorerService) {
ApiResponse.ok(service.getCreatorProfile(creatorId, timezone, member))
}
@GetMapping("/profile/{id}/donation-rank")
fun getCreatorProfileDonationRanking(
@PathVariable("id") creatorId: Long,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
pageable: Pageable
) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
ApiResponse.ok(service.getCreatorProfileDonationRanking(creatorId, pageable, member))
}
@PostMapping("/profile/cheers")
fun writeCheers(
@RequestBody request: PostWriteCheersRequest,

View File

@ -6,6 +6,8 @@ import com.querydsl.core.types.dsl.Expressions
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.can.use.CanUsage
import kr.co.vividnext.sodalive.can.use.QUseCan.useCan
import kr.co.vividnext.sodalive.can.use.QUseCanCalculate.useCanCalculate
import kr.co.vividnext.sodalive.can.use.UseCanCalculateStatus
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
import kr.co.vividnext.sodalive.explorer.follower.GetFollowerListDto
@ -55,6 +57,59 @@ class ExplorerQueryRepository(
.fetch()
}
fun getDonationCoinsDateRange(creatorId: Long, startDate: LocalDateTime, endDate: LocalDateTime): Int? {
val donationCoin = useCanCalculate.can.sum()
return queryFactory
.select(donationCoin)
.from(useCanCalculate)
.innerJoin(useCanCalculate.useCan, useCan)
.where(
useCanCalculate.status.eq(UseCanCalculateStatus.RECEIVED)
.and(useCan.isRefund.isFalse)
.and(useCanCalculate.recipientCreatorId.eq(creatorId))
.and(
useCanCalculate.createdAt.goe(
startDate
.atZone(ZoneId.of("Asia/Seoul"))
.withZoneSameInstant(ZoneId.of("UTC"))
.toLocalDateTime()
)
)
.and(
useCanCalculate.createdAt.lt(
endDate
.atZone(ZoneId.of("Asia/Seoul"))
.withZoneSameInstant(ZoneId.of("UTC"))
.toLocalDateTime()
)
)
)
.fetchOne()
}
fun getMemberDonationRankingTotal(creatorId: Long): Int {
val creatorMember = QMember("creator")
val userMember = QMember("user")
val donation = useCan.rewardCan.add(useCan.can).sum()
return queryFactory
.select(userMember.id)
.from(useCan)
.join(useCan.room, liveRoom)
.join(liveRoom.member, creatorMember)
.join(useCan.member, userMember)
.where(
useCan.canUsage.eq(CanUsage.DONATION)
.and(useCan.isRefund.isFalse)
.and(creatorMember.id.eq(creatorId))
)
.groupBy(useCan.member.id)
.orderBy(donation.desc())
.fetch()
.size
}
fun getMemberDonationRanking(
creatorId: Long,
limit: Long,
@ -161,55 +216,13 @@ class ExplorerQueryRepository(
.fetch()
}
fun getAccount(userId: Long): Member? {
fun getMember(memberId: Long): Member? {
return queryFactory
.selectFrom(member)
.where(member.id.eq(userId))
.where(member.id.eq(memberId))
.fetchFirst()
}
fun getUserDonationRanking(
creatorId: Long,
limit: Long,
offset: Long = 0,
withDonationCan: Boolean
): List<UserDonationRankingResponse> {
val creatorMember = QMember("creator")
val userMember = QMember("user")
val donation = useCan.rewardCan.add(useCan.can).sum()
return queryFactory
.select(userMember, donation)
.from(useCan)
.join(useCan.room, liveRoom)
.join(liveRoom.member, creatorMember)
.join(useCan.member, userMember)
.offset(offset)
.limit(limit)
.where(
useCan.canUsage.eq(CanUsage.DONATION)
.and(useCan.isRefund.isFalse)
.and(creatorMember.id.eq(creatorId))
)
.groupBy(useCan.member.id)
.orderBy(donation.desc(), userMember.id.desc())
.fetch()
.map {
val account = it.get(userMember)!!
val donationCan = it.get(donation)!!
UserDonationRankingResponse(
account.id!!,
account.nickname,
if (account.profileImage != null) {
"$cloudFrontHost/${account.profileImage}"
} else {
"$cloudFrontHost/profile/default-profile.png"
},
if (withDonationCan) donationCan else 0
)
}
}
fun getSimilarCreatorList(creatorId: Long): List<SimilarCreatorResponse> {
val creator = queryFactory
.selectFrom(member)

View File

@ -22,6 +22,9 @@ import org.springframework.context.ApplicationEventPublisher
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.DayOfWeek
import java.time.LocalDate
import java.time.temporal.TemporalAdjusters
@Service
@Transactional(readOnly = true)
@ -164,7 +167,7 @@ class ExplorerService(
fun getCreatorProfile(creatorId: Long, timezone: String, member: Member): GetCreatorProfileResponse {
// 크리에이터(유저) 정보
val creatorAccount = queryRepository.getAccount(creatorId) ?: throw SodaException("없는 사용자 입니다.")
val creatorAccount = queryRepository.getMember(creatorId) ?: throw SodaException("없는 사용자 입니다.")
// 차단된 사용자 체크
val isBlocked = memberService.isBlocked(blockedMemberId = member.id!!, memberId = creatorId)
@ -175,7 +178,7 @@ class ExplorerService(
val notificationRecipientCount = notificationUserIds.size
// 후원랭킹
val userDonationRanking = queryRepository.getUserDonationRanking(
val memberDonationRanking = queryRepository.getMemberDonationRanking(
creatorId,
10,
withDonationCan = creatorId == member.id!!
@ -234,7 +237,7 @@ class ExplorerService(
isNotification = isNotification,
notificationRecipientCount = notificationRecipientCount
),
userDonationRanking = userDonationRanking,
memberDonationRanking = memberDonationRanking,
similarCreatorList = similarCreatorList,
liveRoomList = liveRoomList,
contentList = contentList,
@ -250,6 +253,53 @@ class ExplorerService(
)
}
fun getCreatorProfileDonationRanking(
creatorId: Long,
pageable: Pageable,
member: Member
): GetDonationAllResponse {
val currentDate = LocalDate.now().atTime(0, 0, 0)
val firstDayOfLastWeek = currentDate.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)).minusDays(7)
val firstDayOfMonth = currentDate.with(TemporalAdjusters.firstDayOfMonth())
return GetDonationAllResponse(
accumulatedCansToday = if (creatorId == member.id!!) {
queryRepository.getDonationCoinsDateRange(
creatorId,
currentDate,
currentDate.plusDays(1)
) ?: 0
} else {
0
},
accumulatedCansLastWeek = if (creatorId == member.id!!) {
queryRepository.getDonationCoinsDateRange(
creatorId,
firstDayOfLastWeek,
firstDayOfLastWeek.plusDays(7)
) ?: 0
} else {
0
},
accumulatedCansThisMonth = if (creatorId == member.id!!) {
queryRepository.getDonationCoinsDateRange(
creatorId,
firstDayOfMonth,
currentDate
) ?: 0
} else {
0
},
totalCount = queryRepository.getMemberDonationRankingTotal(creatorId),
memberDonationRanking = queryRepository.getMemberDonationRanking(
creatorId,
offset = pageable.offset,
limit = pageable.pageSize.toLong(),
withDonationCan = creatorId == member.id!!
)
)
}
fun getFollowerList(
creatorId: Long,
member: Member,
@ -281,7 +331,7 @@ class ExplorerService(
@Transactional
fun writeCheers(request: PostWriteCheersRequest, member: Member) {
val creator = queryRepository.getAccount(request.creatorId) ?: throw SodaException("없는 사용자 입니다.")
val creator = queryRepository.getMember(request.creatorId) ?: throw SodaException("없는 사용자 입니다.")
val isBlocked = memberService.isBlocked(blockedMemberId = member.id!!, memberId = request.creatorId)
if (isBlocked) throw SodaException("${creator.nickname}님의 요청으로 팬토크 작성이 제한됩니다.")

View File

@ -4,7 +4,7 @@ import kr.co.vividnext.sodalive.content.GetAudioContentListItem
data class GetCreatorProfileResponse(
val creator: CreatorResponse,
val userDonationRanking: List<UserDonationRankingResponse>,
val memberDonationRanking: List<MemberDonationRankingResponse>,
val similarCreatorList: List<SimilarCreatorResponse>,
val liveRoomList: List<LiveRoomResponse>,
val contentList: List<GetAudioContentListItem>,

View File

@ -1,5 +1,13 @@
package kr.co.vividnext.sodalive.explorer
data class GetDonationAllResponse(
val accumulatedCansToday: Int,
val accumulatedCansLastWeek: Int,
val accumulatedCansThisMonth: Int,
val totalCount: Int,
val memberDonationRanking: List<MemberDonationRankingResponse>
)
data class MemberDonationRankingResponse(
val userId: Long,
val nickname: String,

View File

@ -1,16 +0,0 @@
package kr.co.vividnext.sodalive.explorer
data class GetDonationAllResponse(
val accumulatedCansToday: Int,
val accumulatedCansLastWeek: Int,
val accumulatedCansThisMonth: Int,
val totalCount: Int,
val userDonationRanking: List<UserDonationRankingResponse>
)
data class UserDonationRankingResponse(
val userId: Long,
val nickname: String,
val profileImage: String,
val donationCan: Int
)