콘텐츠 메인 - 순위 정렬(매출, 후원, 댓글, 좋아요) 추가

This commit is contained in:
Klaus 2023-11-02 02:28:23 +09:00
parent c6c9073fa0
commit d45a34525b
7 changed files with 137 additions and 66 deletions

View File

@ -19,6 +19,9 @@ import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RequestPart import org.springframework.web.bind.annotation.RequestPart
import org.springframework.web.bind.annotation.RestController import org.springframework.web.bind.annotation.RestController
import org.springframework.web.multipart.MultipartFile import org.springframework.web.multipart.MultipartFile
import java.time.DayOfWeek
import java.time.LocalDateTime
import java.time.temporal.TemporalAdjusters
@RestController @RestController
@RequestMapping("/audio-content") @RequestMapping("/audio-content")
@ -152,15 +155,29 @@ class AudioContentController(private val service: AudioContentService) {
@GetMapping("/ranking") @GetMapping("/ranking")
fun getAudioContentRanking( fun getAudioContentRanking(
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?,
@RequestParam("sortType") sortType: String,
pageable: Pageable pageable: Pageable
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
val currentDateTime = LocalDateTime.now()
val startDate = currentDateTime
.withHour(15)
.withMinute(0)
.withSecond(0)
.minusWeeks(1)
.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
val endDate = startDate
.plusDays(7)
ApiResponse.ok( ApiResponse.ok(
service.getAudioContentRanking( service.getAudioContentRanking(
member = member, isAdult = member.auth != null,
startDate = startDate,
endDate = endDate,
offset = pageable.offset, offset = pageable.offset,
limit = pageable.pageSize.toLong() limit = pageable.pageSize.toLong(),
sortType = sortType
) )
) )
} }

View File

@ -2,8 +2,11 @@ package kr.co.vividnext.sodalive.content
import com.querydsl.core.types.dsl.Expressions import com.querydsl.core.types.dsl.Expressions
import com.querydsl.jpa.impl.JPAQueryFactory import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.can.use.QUseCan.useCan
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
import kr.co.vividnext.sodalive.content.QBundleAudioContent.bundleAudioContent import kr.co.vividnext.sodalive.content.QBundleAudioContent.bundleAudioContent
import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment
import kr.co.vividnext.sodalive.content.like.QAudioContentLike.audioContentLike
import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
import kr.co.vividnext.sodalive.content.main.GetNewContentUploadCreator import kr.co.vividnext.sodalive.content.main.GetNewContentUploadCreator
@ -88,7 +91,8 @@ interface AudioContentQueryRepository {
startDate: LocalDateTime, startDate: LocalDateTime,
endDate: LocalDateTime, endDate: LocalDateTime,
offset: Long = 0, offset: Long = 0,
limit: Long = 12 limit: Long = 12,
sortType: String = "매출"
): List<GetAudioContentRankingItem> ): List<GetAudioContentRankingItem>
} }
@ -443,11 +447,11 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
startDate: LocalDateTime, startDate: LocalDateTime,
endDate: LocalDateTime, endDate: LocalDateTime,
offset: Long, offset: Long,
limit: Long limit: Long,
sortType: String
): List<GetAudioContentRankingItem> { ): List<GetAudioContentRankingItem> {
var where = order.createdAt.goe(startDate) var where = audioContent.isActive.isTrue
.and(order.createdAt.lt(endDate)) .and(audioContent.member.id.ne(648))
.and(audioContent.isActive.isTrue)
.and(audioContent.member.isNotNull) .and(audioContent.member.isNotNull)
.and(audioContent.duration.isNotNull) .and(audioContent.duration.isNotNull)
.and(audioContent.member.isActive.isTrue) .and(audioContent.member.isActive.isTrue)
@ -457,7 +461,7 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
where = where.and(audioContent.isAdult.isFalse) where = where.and(audioContent.isAdult.isFalse)
} }
return queryFactory var select = queryFactory
.select( .select(
QGetAudioContentRankingItem( QGetAudioContentRankingItem(
audioContent.id, audioContent.id,
@ -470,13 +474,70 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory)
member.nickname member.nickname
) )
) )
select = when (sortType) {
"후원" -> {
select
.from(useCan)
.innerJoin(useCan.audioContent, audioContent)
.innerJoin(audioContent.member, member)
.innerJoin(audioContent.theme, audioContentTheme)
.where(
where
.and(audioContentComment.createdAt.goe(startDate))
.and(audioContentComment.createdAt.lt(endDate))
)
.groupBy(audioContent.id)
.orderBy(useCan.can.add(useCan.rewardCan).sum().desc(), audioContent.createdAt.asc())
}
"댓글" -> {
select
.from(audioContentComment)
.innerJoin(audioContentComment.audioContent, audioContent)
.innerJoin(audioContentComment.audioContent.member, member)
.innerJoin(audioContentComment.audioContent.theme, audioContentTheme)
.where(
where
.and(audioContentComment.createdAt.goe(startDate))
.and(audioContentComment.createdAt.lt(endDate))
)
.groupBy(audioContentComment.audioContent.id)
.orderBy(audioContentComment.id.count().desc(), audioContent.createdAt.asc())
}
"좋아요" -> {
select
.from(audioContentLike)
.innerJoin(audioContentLike.audioContent, audioContent)
.innerJoin(audioContentLike.audioContent.member, member)
.innerJoin(audioContentLike.audioContent.theme, audioContentTheme)
.where(
where
.and(audioContentLike.createdAt.goe(startDate))
.and(audioContentLike.createdAt.lt(endDate))
)
.groupBy(audioContentLike.audioContent.id)
.orderBy(audioContentLike.id.count().desc(), audioContent.createdAt.asc())
}
else -> {
select
.from(order) .from(order)
.innerJoin(order.audioContent, audioContent) .innerJoin(order.audioContent, audioContent)
.innerJoin(audioContent.member, member) .innerJoin(audioContent.member, member)
.innerJoin(audioContent.theme, audioContentTheme) .innerJoin(audioContent.theme, audioContentTheme)
.where(where) .where(
where
.and(order.createdAt.goe(startDate))
.and(order.createdAt.lt(endDate))
)
.groupBy(audioContent.id) .groupBy(audioContent.id)
.orderBy(order.can.sum().desc(), audioContent.createdAt.asc()) .orderBy(order.can.sum().desc(), audioContent.createdAt.asc())
}
}
return select
.offset(offset) .offset(offset)
.limit(limit) .limit(limit)
.fetch() .fetch()

View File

@ -24,17 +24,16 @@ import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.member.block.BlockMemberRepository import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
import kr.co.vividnext.sodalive.utils.generateFileName import kr.co.vividnext.sodalive.utils.generateFileName
import org.springframework.beans.factory.annotation.Value import org.springframework.beans.factory.annotation.Value
import org.springframework.cache.annotation.Cacheable
import org.springframework.context.ApplicationEventPublisher import org.springframework.context.ApplicationEventPublisher
import org.springframework.data.repository.findByIdOrNull import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional import org.springframework.transaction.annotation.Transactional
import org.springframework.web.multipart.MultipartFile import org.springframework.web.multipart.MultipartFile
import java.text.SimpleDateFormat import java.text.SimpleDateFormat
import java.time.DayOfWeek
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.ZoneId import java.time.ZoneId
import java.time.format.DateTimeFormatter import java.time.format.DateTimeFormatter
import java.time.temporal.TemporalAdjusters
import java.util.Locale import java.util.Locale
@Service @Service
@ -72,11 +71,10 @@ class AudioContentService(
) )
if (audioContentLike == null) { if (audioContentLike == null) {
audioContentLike = AudioContentLike( audioContentLike = AudioContentLike(memberId = member.id!!)
memberId = member.id!!,
contentId = request.contentId
)
val audioContent = repository.findByIdAndActive(request.contentId)
audioContentLike.audioContent = audioContent
audioContentLikeRepository.save(audioContentLike) audioContentLikeRepository.save(audioContentLike)
} else { } else {
audioContentLike.isActive = !audioContentLike.isActive audioContentLike.isActive = !audioContentLike.isActive
@ -579,21 +577,19 @@ class AudioContentService(
} }
} }
@Cacheable(
cacheNames = ["cache_ttl_3_days"],
key = "'contentRanking:' + ':' +" +
"#isAdult + ':' + #startDate + ':' + #endDate + ':' + #sortType + ':' + #offset + ':' + #limit"
)
fun getAudioContentRanking( fun getAudioContentRanking(
member: Member, isAdult: Boolean,
startDate: LocalDateTime,
endDate: LocalDateTime,
offset: Long, offset: Long,
limit: Long limit: Long,
sortType: String = "매출"
): GetAudioContentRanking { ): GetAudioContentRanking {
val currentDateTime = LocalDateTime.now()
val startDate = currentDateTime
.withHour(15)
.withMinute(0)
.withSecond(0)
.minusWeeks(1)
.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
val endDate = startDate
.plusDays(7)
val startDateFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일") val startDateFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일")
val endDateFormatter = DateTimeFormatter.ofPattern("MM월 dd일") val endDateFormatter = DateTimeFormatter.ofPattern("MM월 dd일")
@ -602,9 +598,10 @@ class AudioContentService(
cloudfrontHost = coverImageHost, cloudfrontHost = coverImageHost,
startDate = startDate.minusDays(1), startDate = startDate.minusDays(1),
endDate = endDate.minusDays(1), endDate = endDate.minusDays(1),
isAdult = member.auth != null, isAdult = isAdult,
offset = offset, offset = offset,
limit = limit limit = limit,
sortType = sortType
) )
return GetAudioContentRanking( return GetAudioContentRanking(
@ -613,4 +610,8 @@ class AudioContentService(
items = contentRankingItemList items = contentRankingItemList
) )
} }
fun getContentRankingSortTypeList(): List<String> {
return listOf("매출", "후원", "댓글", "좋아요")
}
} }

View File

@ -1,20 +1,21 @@
package kr.co.vividnext.sodalive.content.like package kr.co.vividnext.sodalive.content.like
import kr.co.vividnext.sodalive.content.AudioContent
import java.time.LocalDateTime import java.time.LocalDateTime
import javax.persistence.Entity import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.GeneratedValue import javax.persistence.GeneratedValue
import javax.persistence.GenerationType import javax.persistence.GenerationType
import javax.persistence.Id import javax.persistence.Id
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
import javax.persistence.PrePersist import javax.persistence.PrePersist
import javax.persistence.PreUpdate import javax.persistence.PreUpdate
import javax.persistence.Table import javax.persistence.Table
@Entity @Entity
@Table(name = "content_like") @Table(name = "content_like")
data class AudioContentLike( data class AudioContentLike(val memberId: Long) {
val memberId: Long,
val contentId: Long
) {
@Id @Id
@GeneratedValue(strategy = GenerationType.IDENTITY) @GeneratedValue(strategy = GenerationType.IDENTITY)
var id: Long? = null var id: Long? = null
@ -34,4 +35,8 @@ data class AudioContentLike(
} }
var isActive = true var isActive = true
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "content_id", nullable = false)
var audioContent: AudioContent? = null
} }

View File

@ -20,7 +20,7 @@ class AudioContentLikeQueryRepositoryImpl(private val queryFactory: JPAQueryFact
.selectFrom(audioContentLike) .selectFrom(audioContentLike)
.where( .where(
audioContentLike.memberId.eq(memberId) audioContentLike.memberId.eq(memberId)
.and(audioContentLike.contentId.eq(contentId)) .and(audioContentLike.audioContent.id.eq(contentId))
) )
.fetchFirst() .fetchFirst()
} }
@ -30,7 +30,7 @@ class AudioContentLikeQueryRepositoryImpl(private val queryFactory: JPAQueryFact
.select(audioContentLike.id) .select(audioContentLike.id)
.from(audioContentLike) .from(audioContentLike)
.where( .where(
audioContentLike.contentId.eq(contentId) audioContentLike.audioContent.id.eq(contentId)
.and(audioContentLike.isActive.isTrue) .and(audioContentLike.isActive.isTrue)
) )
.fetch() .fetch()

View File

@ -1,6 +1,7 @@
package kr.co.vividnext.sodalive.content.main package kr.co.vividnext.sodalive.content.main
import kr.co.vividnext.sodalive.content.AudioContentRepository import kr.co.vividnext.sodalive.content.AudioContentRepository
import kr.co.vividnext.sodalive.content.AudioContentService
import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerType import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerType
import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse
import kr.co.vividnext.sodalive.content.main.curation.GetAudioContentCurationResponse import kr.co.vividnext.sodalive.content.main.curation.GetAudioContentCurationResponse
@ -15,12 +16,12 @@ import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
import java.time.DayOfWeek import java.time.DayOfWeek
import java.time.LocalDateTime import java.time.LocalDateTime
import java.time.format.DateTimeFormatter
import java.time.temporal.TemporalAdjusters import java.time.temporal.TemporalAdjusters
@Service @Service
class AudioContentMainService( class AudioContentMainService(
private val repository: AudioContentRepository, private val repository: AudioContentRepository,
private val audioContentService: AudioContentService,
private val blockMemberRepository: BlockMemberRepository, private val blockMemberRepository: BlockMemberRepository,
private val orderService: OrderService, private val orderService: OrderService,
private val audioContentThemeRepository: AudioContentThemeQueryRepository, private val audioContentThemeRepository: AudioContentThemeQueryRepository,
@ -66,7 +67,14 @@ class AudioContentMainService(
.withSecond(0) .withSecond(0)
val endDate = startDate.plusDays(7) val endDate = startDate.plusDays(7)
val contentRanking = getContentRanking(isAdult = isAdult, startDate = startDate, endDate = endDate) val contentRankingSortTypeList = audioContentService.getContentRankingSortTypeList()
val contentRanking = audioContentService.getAudioContentRanking(
isAdult = isAdult,
startDate = startDate,
endDate = endDate,
offset = 0,
limit = 12
)
return GetAudioContentMainResponse( return GetAudioContentMainResponse(
newContentUploadCreatorList = newContentUploadCreatorList, newContentUploadCreatorList = newContentUploadCreatorList,
@ -75,6 +83,7 @@ class AudioContentMainService(
themeList = themeList, themeList = themeList,
newContentList = newContentList, newContentList = newContentList,
curationList = curationList, curationList = curationList,
contentRankingSortTypeList = contentRankingSortTypeList,
contentRanking = contentRanking contentRanking = contentRanking
) )
} }
@ -192,27 +201,4 @@ class AudioContentMainService(
} }
.filter { it.contents.isNotEmpty() } .filter { it.contents.isNotEmpty() }
.toList() .toList()
@Cacheable(
cacheNames = ["cache_ttl_3_days"],
key = "'contentRanking:' + ':' + #isAdult + ':' + #startDate + ':' + #endDate"
)
fun getContentRanking(isAdult: Boolean, startDate: LocalDateTime, endDate: LocalDateTime): GetAudioContentRanking {
val startDateFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일")
val endDateFormatter = DateTimeFormatter.ofPattern("MM월 dd일")
val contentRankingItemList = repository
.getAudioContentRanking(
cloudfrontHost = imageHost,
startDate = startDate.minusDays(1),
endDate = endDate.minusDays(1),
isAdult = isAdult
)
return GetAudioContentRanking(
startDate = startDate.format(startDateFormatter),
endDate = endDate.minusDays(1).format(endDateFormatter),
contentRankingItemList
)
}
} }

View File

@ -10,5 +10,6 @@ data class GetAudioContentMainResponse(
val themeList: List<String>, val themeList: List<String>,
val newContentList: List<GetAudioContentMainItem>, val newContentList: List<GetAudioContentMainItem>,
val curationList: List<GetAudioContentCurationResponse>, val curationList: List<GetAudioContentCurationResponse>,
val contentRankingSortTypeList: List<String>,
val contentRanking: GetAudioContentRanking val contentRanking: GetAudioContentRanking
) )