From fe76ecdfa90c27bc575f6a4d60f4dbeea4297376 Mon Sep 17 00:00:00 2001 From: Klaus Date: Tue, 11 Nov 2025 23:02:58 +0900 Subject: [PATCH] =?UTF-8?q?feat(chat-character):=20=EB=B3=B4=EC=98=A8=20?= =?UTF-8?q?=EC=A3=BC=EA=B0=84=20=EC=B0=A8=ED=8A=B8=20=EC=BD=98=ED=85=90?= =?UTF-8?q?=EC=B8=A0=20=EC=A0=95=EB=A0=AC=20=EA=B8=B0=EC=A4=80=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 매출, 판매량, 댓글 수, 좋아요 수, 후원 --- .../sodalive/api/home/HomeController.kt | 25 ++++++++++++ .../sodalive/api/home/HomeService.kt | 38 ++++++++++++++++++- .../sodalive/rank/ContentRankingSortType.kt | 21 ++++++++++ .../sodalive/rank/RankingRepository.kt | 20 ++++++++++ .../vividnext/sodalive/rank/RankingService.kt | 32 ++++++++++++++++ 5 files changed, 135 insertions(+), 1 deletion(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/rank/ContentRankingSortType.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeController.kt index 3b44d1e..986a35b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeController.kt @@ -4,6 +4,7 @@ import kr.co.vividnext.sodalive.common.ApiResponse import kr.co.vividnext.sodalive.content.ContentType import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek import kr.co.vividnext.sodalive.member.Member +import kr.co.vividnext.sodalive.rank.ContentRankingSortType import org.springframework.security.core.annotation.AuthenticationPrincipal import org.springframework.web.bind.annotation.GetMapping import org.springframework.web.bind.annotation.RequestMapping @@ -79,4 +80,28 @@ class HomeController(private val service: HomeService) { ) ) } + + // 콘텐츠 랭킹 엔드포인트 + @GetMapping("/content-ranking") + fun getContentRanking( + @RequestParam("sort", required = false) sort: ContentRankingSortType? = null, + @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, + @RequestParam("contentType", required = false) contentType: ContentType? = null, + @RequestParam("offset", required = false) offset: Long? = null, + @RequestParam("limit", required = false) limit: Long? = null, + @RequestParam("theme", required = false) theme: String? = null, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + ApiResponse.ok( + service.getContentRankingBySort( + sort = sort ?: ContentRankingSortType.REVENUE, + isAdultContentVisible = isAdultContentVisible ?: true, + contentType = contentType ?: ContentType.ALL, + offset = offset, + limit = limit, + theme = theme, + member = member + ) + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt index 7283f5c..6fde4af 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt @@ -5,6 +5,7 @@ import kr.co.vividnext.sodalive.chat.character.service.ChatCharacterService import kr.co.vividnext.sodalive.content.AudioContentMainItem import kr.co.vividnext.sodalive.content.AudioContentService import kr.co.vividnext.sodalive.content.ContentType +import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationService import kr.co.vividnext.sodalive.content.series.ContentSeriesService @@ -18,6 +19,7 @@ import kr.co.vividnext.sodalive.live.room.LiveRoomStatus import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.MemberService import kr.co.vividnext.sodalive.query.recommend.RecommendChannelQueryService +import kr.co.vividnext.sodalive.rank.ContentRankingSortType import kr.co.vividnext.sodalive.rank.RankingRepository import kr.co.vividnext.sodalive.rank.RankingService import org.springframework.beans.factory.annotation.Value @@ -153,7 +155,7 @@ class HomeService( contentType = contentType, startDate = startDate.minusDays(1), endDate = endDate, - sortType = "매출" + sort = ContentRankingSortType.REVENUE ) val recommendChannelList = recommendChannelService.getRecommendChannel( @@ -277,6 +279,40 @@ class HomeService( ) } + fun getContentRankingBySort( + sort: ContentRankingSortType, + isAdultContentVisible: Boolean, + contentType: ContentType, + offset: Long?, + limit: Long?, + theme: String?, + member: Member? + ): List { + val memberId = member?.id + val isAdult = member?.auth != null && isAdultContentVisible + + val currentDateTime = LocalDateTime.now() + val startDate = currentDateTime + .withHour(15) + .withMinute(0) + .withSecond(0) + .minusWeeks(1) + .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) + val endDate = startDate.plusDays(6) + + return rankingService.getContentRanking( + memberId = memberId, + isAdult = isAdult, + contentType = contentType, + startDate = startDate.minusDays(1), + endDate = endDate, + offset = offset ?: 0, + limit = limit ?: 12, + sort = sort, + theme = theme ?: "" + ) + } + private fun getDayOfWeekByTimezone(timezone: String): SeriesPublishedDaysOfWeek { val systemTime = LocalDateTime.now() val zoneId = ZoneId.of(timezone) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/rank/ContentRankingSortType.kt b/src/main/kotlin/kr/co/vividnext/sodalive/rank/ContentRankingSortType.kt new file mode 100644 index 0000000..312b44c --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/rank/ContentRankingSortType.kt @@ -0,0 +1,21 @@ +package kr.co.vividnext.sodalive.rank + +/** + * 콘텐츠 랭킹 정렬 기준 + */ +enum class ContentRankingSortType { + // 매출: order.can.sum.desc + REVENUE, + + // 판매량: order.id.count.desc + SALES_COUNT, + + // 댓글 수: audioContentComment.id.count.desc + COMMENT_COUNT, + + // 좋아요 수: audioContentLike.id.count.desc + LIKE_COUNT, + + // 후원: audioContentComment.donationCan.sum.desc + DONATION +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingRepository.kt index ddf2288..7e81aa5 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingRepository.kt @@ -132,6 +132,14 @@ class RankingRepository( .innerJoin(audioContent.theme, audioContentTheme) } + "판매량" -> { + select + .from(order) + .innerJoin(order.audioContent, audioContent) + .innerJoin(audioContent.member, member) + .innerJoin(audioContent.theme, audioContentTheme) + } + else -> { select .from(order) @@ -184,6 +192,18 @@ class RankingRepository( .orderBy(audioContentLike.id.count().desc(), audioContent.createdAt.asc()) } + "판매량" -> { + select + .where( + where + .and(order.isActive.isTrue) + .and(order.createdAt.goe(startDate)) + .and(order.createdAt.lt(endDate)) + ) + .groupBy(audioContent.id) + .orderBy(order.id.count().desc(), audioContent.createdAt.asc()) + } + else -> { select .where( diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingService.kt index 7b5fdeb..3ae4d09 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/rank/RankingService.kt @@ -76,6 +76,38 @@ class RankingService( return contentList } + private fun toSortString(sort: ContentRankingSortType): String = when (sort) { + ContentRankingSortType.REVENUE -> "매출" + ContentRankingSortType.SALES_COUNT -> "판매량" + ContentRankingSortType.COMMENT_COUNT -> "댓글" + ContentRankingSortType.LIKE_COUNT -> "좋아요" + ContentRankingSortType.DONATION -> "후원" + } + + fun getContentRanking( + memberId: Long?, + isAdult: Boolean, + contentType: ContentType, + startDate: LocalDateTime, + endDate: LocalDateTime, + offset: Long = 0, + limit: Long = 12, + sort: ContentRankingSortType, + theme: String = "" + ): List { + return getContentRanking( + memberId = memberId, + isAdult = isAdult, + contentType = contentType, + startDate = startDate, + endDate = endDate, + offset = offset, + limit = limit, + sortType = toSortString(sort), + theme = theme + ) + } + fun getSeriesRanking( memberId: Long?, isAdult: Boolean,