From a8da17162afad16a15df81395a7cf077f91536d4 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 3 Jul 2025 15:26:35 +0900 Subject: [PATCH 01/13] =?UTF-8?q?feat:=20=EC=BB=A4=EB=AE=A4=EB=8B=88?= =?UTF-8?q?=ED=8B=B0=20=EA=B8=80=20=EB=93=B1=EB=A1=9D/=EC=88=98=EC=A0=95?= =?UTF-8?q?=20-=20=EC=9C=A0=EB=A3=8C=20=EA=B8=80=EC=97=90=EC=84=9C?= =?UTF-8?q?=EB=A7=8C=20gif=EB=A5=BC=20=EB=93=B1=EB=A1=9D=ED=95=A0=20?= =?UTF-8?q?=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle.kts | 3 +++ .../CreatorCommunityService.kt | 5 +++++ .../sodalive/utils/ImageValidation.kt | 21 +++++++++++++++++++ 3 files changed, 29 insertions(+) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/utils/ImageValidation.kt diff --git a/build.gradle.kts b/build.gradle.kts index e9cca79..ef92369 100644 --- a/build.gradle.kts +++ b/build.gradle.kts @@ -70,6 +70,9 @@ dependencies { implementation("org.apache.poi:poi-ooxml:5.2.3") implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.6.4") + // file mimetype check + implementation("org.apache.tika:tika-core:3.2.0") + developmentOnly("org.springframework.boot:spring-boot-devtools") runtimeOnly("com.h2database:h2") runtimeOnly("com.mysql:mysql-connector-j") diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityService.kt index b8e5415..a8deee9 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityService.kt @@ -21,6 +21,7 @@ import kr.co.vividnext.sodalive.fcm.FcmEventType import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.block.BlockMemberRepository import kr.co.vividnext.sodalive.utils.generateFileName +import kr.co.vividnext.sodalive.utils.validateImage import org.springframework.beans.factory.annotation.Value import org.springframework.context.ApplicationEventPublisher import org.springframework.data.repository.findByIdOrNull @@ -71,6 +72,8 @@ class CreatorCommunityService( throw SodaException("오디오 등록을 위해서는 이미지가 필요합니다.") } + postImage?.let { validateImage(it, request.price > 0) } + val post = CreatorCommunity( content = request.content, price = request.price, @@ -129,6 +132,8 @@ class CreatorCommunityService( val post = repository.findByIdAndMemberId(id = request.creatorCommunityId, memberId = member.id!!) ?: throw SodaException("잘못된 요청입니다.") + postImage?.let { validateImage(it, post.price > 0) } + if (request.content != null) { post.content = request.content } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/utils/ImageValidation.kt b/src/main/kotlin/kr/co/vividnext/sodalive/utils/ImageValidation.kt new file mode 100644 index 0000000..2443064 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/utils/ImageValidation.kt @@ -0,0 +1,21 @@ +package kr.co.vividnext.sodalive.utils + +import kr.co.vividnext.sodalive.common.SodaException +import org.apache.tika.Tika +import org.springframework.web.multipart.MultipartFile + +/** + * 이미지 파일인지 확인하고, + * GIF의 경우 외부 조건(gifAllowed)에 따라 허용 여부 판단 + */ +fun validateImage(file: MultipartFile, gifAllowed: Boolean) { + val mimeType = Tika().detect(file.bytes) + + if (!mimeType.startsWith("image/")) { + throw SodaException("이미지 파일만 업로드할 수 있습니다.") + } + + if (mimeType == "image/gif" && !gifAllowed) { + throw SodaException("GIF 파일은 유료 게시물만 업로드 할 수 있습니다.") + } +} -- 2.40.1 From 22fc8b22b8a3dfff14b6b4624298d7539fcdcd58 Mon Sep 17 00:00:00 2001 From: Klaus Date: Thu, 10 Jul 2025 15:31:41 +0900 Subject: [PATCH 02/13] =?UTF-8?q?feat:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20?= =?UTF-8?q?-=20API=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/api/home/GetHomeResponse.kt | 26 ++ .../sodalive/api/home/HomeController.kt | 66 +++++ .../sodalive/api/home/HomeService.kt | 243 ++++++++++++++++++ .../sodalive/audition/AuditionRepository.kt | 24 ++ .../sodalive/audition/AuditionService.kt | 4 + .../sodalive/content/AudioContentMainItem.kt | 13 + .../content/AudioContentRepository.kt | 139 ++++++++++ .../sodalive/content/AudioContentService.kt | 18 ++ .../curation/AudioContentCurationService.kt | 37 +++ .../AudioContentMainTabSeriesController.kt | 1 - .../AudioContentMainTabSeriesService.kt | 5 +- .../content/series/ContentSeriesController.kt | 2 - .../content/series/ContentSeriesRepository.kt | 66 +++-- .../content/series/ContentSeriesService.kt | 38 ++- .../content/theme/AudioContentThemeService.kt | 13 + .../vividnext/sodalive/event/EventService.kt | 2 - .../RecommendChannelQueryRepository.kt | 82 ++++++ .../recommend/RecommendChannelQueryService.kt | 30 +++ .../recommend/RecommendChannelResponse.kt | 20 ++ 19 files changed, 799 insertions(+), 30 deletions(-) create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/api/home/GetHomeResponse.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeController.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentMainItem.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryRepository.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryService.kt create mode 100644 src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelResponse.kt diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/api/home/GetHomeResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/GetHomeResponse.kt new file mode 100644 index 0000000..671ae98 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/GetHomeResponse.kt @@ -0,0 +1,26 @@ +package kr.co.vividnext.sodalive.api.home + +import kr.co.vividnext.sodalive.audition.GetAuditionListItem +import kr.co.vividnext.sodalive.content.AudioContentMainItem +import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem +import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse +import kr.co.vividnext.sodalive.content.series.GetSeriesListResponse +import kr.co.vividnext.sodalive.event.GetEventResponse +import kr.co.vividnext.sodalive.explorer.GetExplorerSectionCreatorResponse +import kr.co.vividnext.sodalive.live.room.GetRoomListResponse +import kr.co.vividnext.sodalive.query.recommend.RecommendChannelResponse + +data class GetHomeResponse( + val liveList: List, + val creatorRanking: List, + val latestContentThemeList: List, + val latestContentList: List, + val eventBannerList: GetEventResponse, + val originalAudioDramaList: List, + val auditionList: List, + val dayOfWeekSeriesList: List, + val contentRanking: List, + val recommendChannelList: List, + val freeContentList: List, + val curationList: List +) 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 new file mode 100644 index 0000000..6941ac3 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeController.kt @@ -0,0 +1,66 @@ +package kr.co.vividnext.sodalive.api.home + +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 org.springframework.security.core.annotation.AuthenticationPrincipal +import org.springframework.web.bind.annotation.GetMapping +import org.springframework.web.bind.annotation.RequestMapping +import org.springframework.web.bind.annotation.RequestParam +import org.springframework.web.bind.annotation.RestController + +@RestController +@RequestMapping("/api/home") +class HomeController(private val service: HomeService) { + @GetMapping + fun fetchData( + @RequestParam timezone: String, + @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, + @RequestParam("contentType", required = false) contentType: ContentType? = null, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + ApiResponse.ok( + service.fetchData( + timezone = timezone, + isAdultContentVisible = isAdultContentVisible ?: true, + contentType = contentType ?: ContentType.ALL, + member + ) + ) + } + + @GetMapping("/latest-content") + fun getLatestContentByTheme( + @RequestParam("theme") theme: String, + @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, + @RequestParam("contentType", required = false) contentType: ContentType? = null, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + ApiResponse.ok( + service.getLatestContentByTheme( + theme = theme, + isAdultContentVisible = isAdultContentVisible ?: true, + contentType = contentType ?: ContentType.ALL, + member + ) + ) + } + + @GetMapping("/day-of-week-series") + fun getDayOfWeekSeriesList( + @RequestParam("dayOfWeek") dayOfWeek: SeriesPublishedDaysOfWeek, + @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, + @RequestParam("contentType", required = false) contentType: ContentType? = null, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + ApiResponse.ok( + service.getDayOfWeekSeriesList( + dayOfWeek = dayOfWeek, + isAdultContentVisible = isAdultContentVisible ?: true, + contentType = contentType ?: ContentType.ALL, + 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 new file mode 100644 index 0000000..8201601 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/api/home/HomeService.kt @@ -0,0 +1,243 @@ +package kr.co.vividnext.sodalive.api.home + +import kr.co.vividnext.sodalive.audition.AuditionService +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.curation.AudioContentCurationService +import kr.co.vividnext.sodalive.content.series.ContentSeriesService +import kr.co.vividnext.sodalive.content.series.GetSeriesListResponse +import kr.co.vividnext.sodalive.content.theme.AudioContentThemeService +import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek +import kr.co.vividnext.sodalive.event.EventService +import kr.co.vividnext.sodalive.live.room.LiveRoomService +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.RankingRepository +import kr.co.vividnext.sodalive.rank.RankingService +import org.springframework.beans.factory.annotation.Value +import org.springframework.data.domain.Pageable +import org.springframework.stereotype.Service +import java.time.DayOfWeek +import java.time.LocalDateTime +import java.time.ZoneId +import java.time.temporal.TemporalAdjusters + +@Service +class HomeService( + private val eventService: EventService, + private val memberService: MemberService, + private val liveRoomService: LiveRoomService, + private val auditionService: AuditionService, + private val seriesService: ContentSeriesService, + private val contentService: AudioContentService, + private val curationService: AudioContentCurationService, + private val contentThemeService: AudioContentThemeService, + private val recommendChannelService: RecommendChannelQueryService, + + private val rankingService: RankingService, + private val rankingRepository: RankingRepository, + + @Value("\${cloud.aws.cloud-front.host}") + private val imageHost: String +) { + fun fetchData( + timezone: String, + isAdultContentVisible: Boolean, + contentType: ContentType, + member: Member? + ): GetHomeResponse { + val memberId = member?.id + val isAdult = member?.auth != null && isAdultContentVisible + + val liveList = liveRoomService.getRoomList( + dateString = null, + status = LiveRoomStatus.NOW, + isAdultContentVisible = isAdultContentVisible, + pageable = Pageable.ofSize(10), + member = member, + timezone = timezone + ) + + val creatorRanking = rankingRepository + .getCreatorRankings() + .filter { + if (memberId != null) { + !memberService.isBlocked(blockedMemberId = memberId, memberId = it.id!!) + } else { + true + } + } + .map { it.toExplorerSectionCreator(imageHost) } + + val latestContentThemeList = contentThemeService.getActiveThemeOfContent( + isAdult = isAdult, + contentType = contentType + ) + + val latestContentList = contentService.getLatestContentByTheme( + theme = latestContentThemeList, + contentType = contentType, + isFree = false, + isAdult = isAdult + ).filter { + if (memberId != null) { + !memberService.isBlocked(blockedMemberId = memberId, memberId = it.creatorId) + } else { + true + } + } + + val eventBannerList = eventService.getEventList(isAdult = isAdult) + + val originalAudioDramaList = seriesService.getOriginalAudioDramaList( + isAdult = isAdult, + contentType = contentType + ) + + val auditionList = auditionService.getInProgressAuditionList(isAdult = isAdult) + + val dayOfWeekSeriesList = seriesService.getDayOfWeekSeriesList( + memberId = memberId, + isAdult = isAdult, + contentType = contentType, + dayOfWeek = getDayOfWeekByTimezone(timezone) + ) + + 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) + + val contentRanking = rankingService.getContentRanking( + memberId = memberId, + isAdult = isAdult, + contentType = contentType, + startDate = startDate.minusDays(1), + endDate = endDate, + sortType = "매출" + ) + + // TODO 오디오 북 + + val recommendChannelList = recommendChannelService.getRecommendChannel( + memberId = memberId, + isAdult = isAdult, + contentType = contentType + ) + + val freeContentList = contentService.getLatestContentByTheme( + theme = contentThemeService.getActiveThemeOfContent( + isAdult = isAdult, + isFree = true, + contentType = contentType + ), + contentType = contentType, + isFree = true, + isAdult = isAdult + ).filter { + if (memberId != null) { + !memberService.isBlocked(blockedMemberId = memberId, memberId = it.creatorId) + } else { + true + } + } + + val curationList = curationService.getContentCurationList( + tabId = 3L, // 기존에 사용하던 단편 탭의 큐레이션을 사용 + isAdult = isAdult, + contentType = contentType, + memberId = memberId + ) + + return GetHomeResponse( + liveList = liveList, + creatorRanking = creatorRanking, + latestContentThemeList = latestContentThemeList, + latestContentList = latestContentList, + eventBannerList = eventBannerList, + originalAudioDramaList = originalAudioDramaList, + auditionList = auditionList, + dayOfWeekSeriesList = dayOfWeekSeriesList, + contentRanking = contentRanking, + recommendChannelList = recommendChannelList, + freeContentList = freeContentList, + curationList = curationList + ) + } + + fun getLatestContentByTheme( + theme: String, + isAdultContentVisible: Boolean, + contentType: ContentType, + member: Member? + ): List { + val memberId = member?.id + val isAdult = member?.auth != null && isAdultContentVisible + + val themeList = if (theme.isBlank()) { + contentThemeService.getActiveThemeOfContent( + isAdult = isAdult, + isFree = true, + contentType = contentType + ) + } else { + listOf(theme) + } + + return contentService.getLatestContentByTheme( + theme = themeList, + contentType = contentType, + isFree = false, + isAdult = isAdult + ).filter { + if (memberId != null) { + !memberService.isBlocked(blockedMemberId = memberId, memberId = it.creatorId) + } else { + true + } + } + } + + fun getDayOfWeekSeriesList( + dayOfWeek: SeriesPublishedDaysOfWeek, + isAdultContentVisible: Boolean, + contentType: ContentType, + member: Member? + ): List { + val memberId = member?.id + val isAdult = member?.auth != null && isAdultContentVisible + + return seriesService.getDayOfWeekSeriesList( + memberId = memberId, + isAdult = isAdult, + contentType = contentType, + dayOfWeek = dayOfWeek + ) + } + + private fun getDayOfWeekByTimezone(timezone: String): SeriesPublishedDaysOfWeek { + val systemTime = LocalDateTime.now() + val zoneId = ZoneId.of(timezone) + val zonedDateTime = systemTime.atZone(ZoneId.systemDefault()).withZoneSameInstant(zoneId) + + val dayToSeriesPublishedDaysOfWeek = mapOf( + DayOfWeek.MONDAY to SeriesPublishedDaysOfWeek.MON, + DayOfWeek.TUESDAY to SeriesPublishedDaysOfWeek.TUE, + DayOfWeek.WEDNESDAY to SeriesPublishedDaysOfWeek.WED, + DayOfWeek.THURSDAY to SeriesPublishedDaysOfWeek.THU, + DayOfWeek.FRIDAY to SeriesPublishedDaysOfWeek.FRI, + DayOfWeek.SATURDAY to SeriesPublishedDaysOfWeek.SAT, + DayOfWeek.SUNDAY to SeriesPublishedDaysOfWeek.SUN + ) + + return dayToSeriesPublishedDaysOfWeek[zonedDateTime.dayOfWeek] ?: SeriesPublishedDaysOfWeek.RANDOM + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt index 1c63e6f..2d3fdec 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionRepository.kt @@ -12,6 +12,7 @@ interface AuditionQueryRepository { fun getCompletedAuditionCount(isAdult: Boolean): Int fun getAuditionList(offset: Long, limit: Long, isAdult: Boolean): List fun getAuditionDetail(auditionId: Long): GetAuditionDetailRawData + fun getInProgressAuditionList(isAdult: Boolean): List } class AuditionQueryRepositoryImpl( @@ -94,4 +95,27 @@ class AuditionQueryRepositoryImpl( .where(audition.id.eq(auditionId)) .fetchFirst() } + + override fun getInProgressAuditionList(isAdult: Boolean): List { + var where = audition.isActive.isTrue + .and(audition.status.eq(AuditionStatus.IN_PROGRESS)) + + if (!isAdult) { + where = where.and(audition.isAdult.isFalse) + } + + return queryFactory + .select( + QGetAuditionListItem( + audition.id, + audition.title, + audition.imagePath.prepend("/").prepend(cloudFrontHost), + audition.status.eq(AuditionStatus.COMPLETED) + ) + ) + .from(audition) + .where(where) + .orderBy(audition.status.desc()) + .fetch() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionService.kt index 2f6286c..7871bcd 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/audition/AuditionService.kt @@ -28,4 +28,8 @@ class AuditionService( roleList = roleList ) } + + fun getInProgressAuditionList(isAdult: Boolean): List { + return repository.getInProgressAuditionList(isAdult) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentMainItem.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentMainItem.kt new file mode 100644 index 0000000..6329435 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentMainItem.kt @@ -0,0 +1,13 @@ +package kr.co.vividnext.sodalive.content + +import com.fasterxml.jackson.annotation.JsonProperty +import com.querydsl.core.annotations.QueryProjection + +data class AudioContentMainItem @QueryProjection constructor( + @JsonProperty("contentId") val contentId: Long, + @JsonProperty("creatorId") val creatorId: Long, + @JsonProperty("title") val title: String, + @JsonProperty("coverImageUrl") val coverImageUrl: String, + @JsonProperty("creatorNickname") val creatorNickname: String, + @JsonProperty("isPointAvailable") val isPointAvailable: Boolean +) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt index 0f6d651..2236884 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt @@ -176,6 +176,23 @@ interface AudioContentQueryRepository { fun findContentHashTagByContentIdAndIsActive(contentId: Long, isActive: Boolean): List fun findContentIdAndHashTagId(contentId: Long, hashTagId: Int): AudioContentHashTag? + + fun getLatestContentByTheme( + theme: List, + contentType: ContentType, + offset: Long, + limit: Long, + isFree: Boolean, + isAdult: Boolean + ): List + + fun findContentByCurationId( + curationId: Long, + isAdult: Boolean, + contentType: ContentType, + offset: Long = 0, + limit: Long = 20 + ): List } @Repository @@ -1281,4 +1298,126 @@ class AudioContentQueryRepositoryImpl( .orderBy(audioContentHashTag.id.asc()) .fetchFirst() } + + override fun getLatestContentByTheme( + theme: List, + contentType: ContentType, + offset: Long, + limit: Long, + isFree: Boolean, + isAdult: Boolean + ): List { + var where = audioContent.isActive.isTrue + .and(audioContent.duration.isNotNull) + .and( + audioContent.releaseDate.isNull + .or(audioContent.releaseDate.loe(LocalDateTime.now())) + ) + .and(blockMember.id.isNull) + + if (!isAdult) { + where = where.and(audioContent.isAdult.isFalse) + } else { + if (contentType != ContentType.ALL) { + where = where.and( + audioContent.member.isNull.or( + audioContent.member.auth.gender.eq( + if (contentType == ContentType.MALE) { + 0 + } else { + 1 + } + ) + ) + ) + } + } + + if (theme.isNotEmpty()) { + where = where.and(audioContentTheme.theme.`in`(theme)) + } + + where = if (isFree) { + where.and(audioContent.price.loe(0)) + } else { + where.and(audioContent.price.gt(0)) + } + + return queryFactory + .select( + QAudioContentMainItem( + audioContent.id, + member.id, + audioContent.title, + audioContent.coverImage.prepend("/").prepend(imageHost), + member.nickname, + audioContent.isPointAvailable + ) + ) + .from(audioContent) + .innerJoin(audioContent.member, member) + .innerJoin(audioContent.theme, audioContentTheme) + .where(where) + .offset(offset) + .limit(limit) + .fetch() + } + + override fun findContentByCurationId( + curationId: Long, + isAdult: Boolean, + contentType: ContentType, + offset: Long, + limit: Long + ): List { + var where = audioContentCuration.isActive.isTrue + .and(audioContentCurationItem.isActive.isTrue) + .and(audioContent.isActive.isTrue) + .and(audioContent.member.isNotNull) + .and(audioContent.duration.isNotNull) + .and(audioContent.member.isActive.isTrue) + .and(audioContentCuration.id.eq(curationId)) + + if (!isAdult) { + where = where.and(audioContent.isAdult.isFalse) + } else { + if (contentType != ContentType.ALL) { + where = where.and( + audioContent.member.isNull.or( + audioContent.member.auth.gender.eq( + if (contentType == ContentType.MALE) { + 0 + } else { + 1 + } + ) + ) + ) + } + } + + return queryFactory + .select( + QGetAudioContentMainItem( + audioContent.id, + audioContent.coverImage.prepend("/").prepend(imageHost), + audioContent.title, + member.id, + member.profileImage.prepend("/").prepend(imageHost), + member.nickname, + audioContent.price, + audioContent.duration, + audioContent.isPointAvailable + ) + ) + .from(audioContentCurationItem) + .innerJoin(audioContentCurationItem.content, audioContent) + .innerJoin(audioContentCurationItem.curation, audioContentCuration) + .innerJoin(audioContent.member, member) + .where(where) + .orderBy(audioContentCurationItem.orders.asc()) + .offset(offset) + .limit(limit) + .fetch() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt index 39c917d..b651166 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt @@ -938,4 +938,22 @@ class AudioContentService( return GenerateUrlResponse(contentUrl) } + + fun getLatestContentByTheme( + theme: List, + contentType: ContentType, + offset: Long = 0, + limit: Long = 20, + isFree: Boolean = false, + isAdult: Boolean = false + ): List { + return repository.getLatestContentByTheme( + theme = theme, + contentType = contentType, + offset = offset, + limit = limit, + isFree = isFree, + isAdult = isAdult + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationService.kt index 13f22fe..082f573 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationService.kt @@ -1,16 +1,21 @@ package kr.co.vividnext.sodalive.content.main.curation +import kr.co.vividnext.sodalive.content.AudioContentRepository import kr.co.vividnext.sodalive.content.ContentType import kr.co.vividnext.sodalive.content.SortType +import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.block.BlockMemberRepository import org.springframework.beans.factory.annotation.Value +import org.springframework.cache.annotation.Cacheable import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional @Service class AudioContentCurationService( private val repository: AudioContentCurationQueryRepository, + private val contentRepository: AudioContentRepository, private val blockMemberRepository: BlockMemberRepository, @Value("\${cloud.aws.cloud-front.host}") @@ -46,4 +51,36 @@ class AudioContentCurationService( items = audioContentList ) } + + @Transactional(readOnly = true) + @Cacheable( + cacheNames = ["cache_ttl_3_days"], + key = "'getContentCurationList:' + ':' +" + + "#isAdult + ':' + #tabId + ':' + #contentType + ':' + (#memberId ?: 'guest')" + ) + fun getContentCurationList( + tabId: Long, + isAdult: Boolean, + contentType: ContentType, + memberId: Long? + ): List { + return repository.findByContentMainTabId(tabId = tabId, isAdult = isAdult) + .map { + GetContentCurationResponse( + title = it.title, + items = contentRepository.findContentByCurationId( + curationId = it.id!!, + isAdult = isAdult, + contentType = contentType + ).filter { item -> + if (memberId != null) { + !blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = item.creatorId) + } else { + true + } + } + ) + } + .filter { it.items.isNotEmpty() } + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/AudioContentMainTabSeriesController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/AudioContentMainTabSeriesController.kt index 753e11b..7dcabf5 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/AudioContentMainTabSeriesController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/AudioContentMainTabSeriesController.kt @@ -42,7 +42,6 @@ class AudioContentMainTabSeriesController(private val service: AudioContentMainT ApiResponse.ok( service.getOriginalAudioDramaList( - memberId = member.id!!, isAdult = member.auth != null && (isAdultContentVisible ?: true), contentType = contentType ?: ContentType.ALL, offset = pageable.offset, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/AudioContentMainTabSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/AudioContentMainTabSeriesService.kt index 692f04d..b5c85b7 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/AudioContentMainTabSeriesService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/series/AudioContentMainTabSeriesService.kt @@ -41,7 +41,6 @@ class AudioContentMainTabSeriesService( ) val originalAudioDrama = seriesService.getOriginalAudioDramaList( - memberId = memberId, isAdult = isAdult, contentType = contentType, offset = 0, @@ -158,15 +157,13 @@ class AudioContentMainTabSeriesService( } fun getOriginalAudioDramaList( - memberId: Long, isAdult: Boolean, contentType: ContentType, offset: Long, limit: Long ): GetSeriesListResponse { - val totalCount = seriesService.getOriginalAudioDramaTotalCount(memberId, isAdult, contentType) + val totalCount = seriesService.getOriginalAudioDramaTotalCount(isAdult, contentType) val items = seriesService.getOriginalAudioDramaList( - memberId = memberId, isAdult = isAdult, contentType = contentType, offset = offset, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesController.kt index cba81ba..998bbc7 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesController.kt @@ -19,7 +19,6 @@ class ContentSeriesController(private val service: ContentSeriesService) { @GetMapping fun getSeriesList( @RequestParam creatorId: Long, - @RequestParam("sortType", required = false) sortType: SeriesSortType? = SeriesSortType.NEWEST, @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, @RequestParam("contentType", required = false) contentType: ContentType? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, @@ -30,7 +29,6 @@ class ContentSeriesController(private val service: ContentSeriesService) { ApiResponse.ok( service.getSeriesList( creatorId = creatorId, - sortType = sortType ?: SeriesSortType.NEWEST, isAdultContentVisible = isAdultContentVisible ?: true, contentType = contentType ?: ContentType.ALL, member = member, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt index a76e5db..b94d943 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt @@ -13,6 +13,7 @@ import kr.co.vividnext.sodalive.content.series.content.QGetSeriesContentMinMaxPr import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series import kr.co.vividnext.sodalive.creator.admin.content.series.QSeriesContent.seriesContent import kr.co.vividnext.sodalive.creator.admin.content.series.Series +import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek import kr.co.vividnext.sodalive.creator.admin.content.series.keyword.QSeriesKeyword.seriesKeyword import kr.co.vividnext.sodalive.member.MemberRole import kr.co.vividnext.sodalive.member.QMember.member @@ -37,16 +38,22 @@ interface ContentSeriesQueryRepository { fun getSeriesContentMinMaxPrice(seriesId: Long): GetSeriesContentMinMaxPriceResponse fun getRecommendSeriesList(isAuth: Boolean, contentType: ContentType, limit: Long): List fun getOriginalAudioDramaList( - memberId: Long, isAdult: Boolean, contentType: ContentType, offset: Long = 0, limit: Long = 20 ): List - fun getOriginalAudioDramaTotalCount(memberId: Long, isAdult: Boolean, contentType: ContentType): Int + fun getOriginalAudioDramaTotalCount(isAdult: Boolean, contentType: ContentType): Int fun getGenreList(isAdult: Boolean, memberId: Long, contentType: ContentType): List fun findByCurationId(curationId: Long, memberId: Long, isAdult: Boolean, contentType: ContentType): List + fun getDayOfWeekSeriesList( + dayOfWeek: SeriesPublishedDaysOfWeek, + contentType: ContentType, + isAdult: Boolean, + offset: Long, + limit: Long + ): List } class ContentSeriesQueryRepositoryImpl( @@ -207,19 +214,13 @@ class ContentSeriesQueryRepositoryImpl( } override fun getOriginalAudioDramaList( - memberId: Long, isAdult: Boolean, contentType: ContentType, offset: Long, limit: Long ): List { - val blockMemberCondition = blockMember.member.id.eq(member.id) - .and(blockMember.isActive.isTrue) - .and(blockMember.blockedMember.id.eq(memberId)) - var where = series.isOriginal.isTrue .and(series.isActive.isTrue) - .and(blockMember.id.isNull) if (!isAdult) { where = where.and(series.isAdult.isFalse) @@ -242,7 +243,6 @@ class ContentSeriesQueryRepositoryImpl( return queryFactory .selectFrom(series) .innerJoin(series.member, member) - .leftJoin(blockMember).on(blockMemberCondition) .where(where) .orderBy(series.id.desc()) .offset(offset) @@ -250,14 +250,9 @@ class ContentSeriesQueryRepositoryImpl( .fetch() } - override fun getOriginalAudioDramaTotalCount(memberId: Long, isAdult: Boolean, contentType: ContentType): Int { - val blockMemberCondition = blockMember.member.id.eq(member.id) - .and(blockMember.isActive.isTrue) - .and(blockMember.blockedMember.id.eq(memberId)) - + override fun getOriginalAudioDramaTotalCount(isAdult: Boolean, contentType: ContentType): Int { var where = series.isOriginal.isTrue .and(series.isActive.isTrue) - .and(blockMember.id.isNull) if (!isAdult) { where = where.and(series.isAdult.isFalse) @@ -281,7 +276,6 @@ class ContentSeriesQueryRepositoryImpl( .select(series.id) .from(series) .innerJoin(series.member, member) - .leftJoin(blockMember).on(blockMemberCondition) .where(where) .fetch() .size @@ -385,4 +379,44 @@ class ContentSeriesQueryRepositoryImpl( .orderBy(audioContentCurationItem.orders.asc()) .fetch() } + + override fun getDayOfWeekSeriesList( + dayOfWeek: SeriesPublishedDaysOfWeek, + contentType: ContentType, + isAdult: Boolean, + offset: Long, + limit: Long + ): List { + var where = series.isActive.isTrue + .and(series.publishedDaysOfWeek.contains(dayOfWeek)) + + if (!isAdult) { + where = where.and(series.isAdult.isFalse) + } else { + if (contentType != ContentType.ALL) { + where = where.and( + series.member.isNull.or( + series.member.auth.gender.eq( + if (contentType == ContentType.MALE) { + 0 + } else { + 1 + } + ) + ) + ) + } + } + + return queryFactory + .selectFrom(series) + .innerJoin(series.member, member) + .innerJoin(series.contentList, seriesContent) + .innerJoin(seriesContent.content, audioContent) + .where(where) + .orderBy(seriesContent.content.createdAt.desc()) + .offset(offset) + .limit(limit) + .fetch() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesService.kt index 0e4f7db..20404de 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesService.kt @@ -30,18 +30,17 @@ class ContentSeriesService( @Value("\${cloud.aws.cloud-front.host}") private val coverImageHost: String ) { - fun getOriginalAudioDramaTotalCount(memberId: Long, isAdult: Boolean, contentType: ContentType): Int { - return repository.getOriginalAudioDramaTotalCount(memberId, isAdult, contentType) + fun getOriginalAudioDramaTotalCount(isAdult: Boolean, contentType: ContentType): Int { + return repository.getOriginalAudioDramaTotalCount(isAdult, contentType) } fun getOriginalAudioDramaList( - memberId: Long, isAdult: Boolean, contentType: ContentType, offset: Long = 0, limit: Long = 20 ): List { - val originalAudioDramaList = repository.getOriginalAudioDramaList(memberId, isAdult, contentType, offset, limit) + val originalAudioDramaList = repository.getOriginalAudioDramaList(isAdult, contentType, offset, limit) return seriesToSeriesListItem(originalAudioDramaList, isAdult, contentType) } @@ -54,7 +53,6 @@ class ContentSeriesService( isAdultContentVisible: Boolean, contentType: ContentType, member: Member, - sortType: SeriesSortType = SeriesSortType.NEWEST, offset: Long = 0, limit: Long = 10 ): GetSeriesListResponse { @@ -224,6 +222,36 @@ class ContentSeriesService( return seriesToSeriesListItem(seriesList, isAdult, contentType) } + fun getDayOfWeekSeriesList( + memberId: Long?, + isAdult: Boolean, + contentType: ContentType, + dayOfWeek: SeriesPublishedDaysOfWeek, + offset: Long = 0, + limit: Long = 10 + ): List { + var seriesList = repository.getDayOfWeekSeriesList( + dayOfWeek = dayOfWeek, + contentType = contentType, + isAdult = isAdult, + offset = offset, + limit = limit + ) + + seriesList = if (memberId != null) { + seriesList.filter { + !blockMemberRepository.isBlocked( + blockedMemberId = memberId, + memberId = it.member!!.id!! + ) + } + } else { + seriesList + } + + return seriesToSeriesListItem(seriesList, isAdult, contentType) + } + private fun seriesToSeriesListItem( seriesList: List, isAdult: Boolean, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentThemeService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentThemeService.kt index 584e797..6bafdcf 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentThemeService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentThemeService.kt @@ -19,6 +19,19 @@ class AudioContentThemeService( return queryRepository.getActiveThemes() } + @Transactional(readOnly = true) + fun getActiveThemeOfContent( + isAdult: Boolean = false, + isFree: Boolean = false, + contentType: ContentType + ): List { + return queryRepository.getActiveThemeOfContent( + isAdult = isAdult, + isFree = isFree, + contentType = contentType + ) + } + @Transactional(readOnly = true) fun getContentByTheme( themeId: Long, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/event/EventService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/event/EventService.kt index 2418514..1a99eff 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/event/EventService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/event/EventService.kt @@ -26,7 +26,6 @@ class EventService( @Transactional(readOnly = true) fun getEventList(isAdult: Boolean? = null): GetEventResponse { val eventList = repository.getEventList(isAdult) - .asSequence() .map { if (!it.thumbnailImageUrl.startsWith("https://")) { it.thumbnailImageUrl = "$cloudFrontHost/${it.thumbnailImageUrl}" @@ -42,7 +41,6 @@ class EventService( it } - .toList() return GetEventResponse(0, eventList) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryRepository.kt new file mode 100644 index 0000000..3150d08 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryRepository.kt @@ -0,0 +1,82 @@ +package kr.co.vividnext.sodalive.query.recommend + +import com.querydsl.core.types.dsl.Expressions +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.content.ContentType +import kr.co.vividnext.sodalive.content.QAudioContent.audioContent +import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment +import kr.co.vividnext.sodalive.content.like.QAudioContentLike.audioContentLike +import kr.co.vividnext.sodalive.member.MemberRole +import kr.co.vividnext.sodalive.member.QMember.member +import kr.co.vividnext.sodalive.member.auth.QAuth.auth +import org.springframework.beans.factory.annotation.Value +import org.springframework.stereotype.Repository + +@Repository +class RecommendChannelQueryRepository( + private val queryFactory: JPAQueryFactory, + + @Value("\${cloud.aws.cloud-front.host}") + private val imageHost: String +) { + fun getRecommendChannelList(isAdult: Boolean, contentType: ContentType): List { + var where = member.role.eq(MemberRole.CREATOR) + .and(audioContent.isActive.isTrue) + + if (!isAdult) { + where = where.and(audioContent.isAdult.isFalse) + } else { + if (contentType != ContentType.ALL) { + where = where.and( + member.auth.gender.eq( + if (contentType == ContentType.MALE) { + 0 + } else { + 1 + } + ) + ) + } + } + + return queryFactory + .select( + QRecommendChannelResponse( + member.id, + member.nickname, + member.profileImage.prepend("/").prepend(imageHost), + audioContent.id.count(), + Expressions.constant(emptyList()) + ) + ) + .from(member) + .innerJoin(auth).on(auth.member.id.eq(member.id)) + .innerJoin(audioContent).on(audioContent.member.id.eq(member.id)) + .where(where) + .groupBy(member.id) + .having(audioContent.id.count().goe(3)) + .orderBy(Expressions.numberTemplate(Double::class.java, "function('rand')").asc()) + .limit(6) + .fetch() + } + + fun getContentsByCreatorIdLikeDesc(creatorId: Long): List { + queryFactory + .select( + QRecommendChannelContentItem( + audioContent.id, + audioContent.title, + audioContent.coverImage.prepend("/").prepend(imageHost), + audioContentLike.id.countDistinct(), + audioContentComment.id.countDistinct() + ) + ) + .from(audioContent) + .leftJoin(audioContentLike).on(audioContentLike.audioContent.id.eq(audioContent.id)) + .leftJoin(audioContentComment).on(audioContentComment.audioContent.id.eq(audioContent.id)) + .where(audioContent.member.id.eq(creatorId)) + .groupBy(audioContent.id) + .fetch() + return listOf() + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryService.kt new file mode 100644 index 0000000..716ec47 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryService.kt @@ -0,0 +1,30 @@ +package kr.co.vividnext.sodalive.query.recommend + +import kr.co.vividnext.sodalive.content.ContentType +import org.springframework.cache.annotation.Cacheable +import org.springframework.stereotype.Service +import org.springframework.transaction.annotation.Transactional + +@Service +@Transactional(readOnly = true) +class RecommendChannelQueryService(private val repository: RecommendChannelQueryRepository) { + @Cacheable( + cacheNames = ["default"], + key = "'recommendChannel:' + (#memberId ?: 'guest') + ':' + #isAdult + ':' + #contentType" + ) + fun getRecommendChannel( + memberId: Long?, + isAdult: Boolean, + contentType: ContentType + ): List { + val recommendChannelList = repository.getRecommendChannelList( + isAdult = isAdult, + contentType = contentType + ) + + return recommendChannelList.map { + it.contentList = repository.getContentsByCreatorIdLikeDesc(it.channelId) + it + } + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelResponse.kt new file mode 100644 index 0000000..ec97e54 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelResponse.kt @@ -0,0 +1,20 @@ +package kr.co.vividnext.sodalive.query.recommend + +import com.fasterxml.jackson.annotation.JsonProperty +import com.querydsl.core.annotations.QueryProjection + +data class RecommendChannelResponse @QueryProjection constructor( + @JsonProperty("channelId") val channelId: Long, + @JsonProperty("creatorNickname") val creatorNickname: String, + @JsonProperty("creatorProfileImageUrl") val creatorProfileImageUrl: String, + @JsonProperty("contentCount") val contentCount: Long, + @JsonProperty("contentList") var contentList: List +) + +data class RecommendChannelContentItem @QueryProjection constructor( + @JsonProperty("contentId") val contentId: Long, + @JsonProperty("title") val title: String, + @JsonProperty("thumbnailImageUrl") val thumbnailImageUrl: String, + @JsonProperty("likeCount") val likeCount: Long, + @JsonProperty("commentCount") val commentCount: Long +) -- 2.40.1 From a4b5185f6b46af419f363f0b6fdde51512274d32 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 11 Jul 2025 14:04:08 +0900 Subject: [PATCH 03/13] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20-?= =?UTF-8?q?=20=EC=B5=9C=EA=B7=BC=20=EC=BD=98=ED=85=90=EC=B8=A0=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20-=20join=20=ED=95=98=EC=A7=80=20=EC=95=8A=EC=9D=80?= =?UTF-8?q?=20blockMember=20=EC=A0=9C=EA=B1=B0=20-=20=EC=A0=95=EB=A0=AC=20?= =?UTF-8?q?=EC=A1=B0=EA=B1=B4=20=EC=B6=94=EA=B0=80=20-=20id=20=EB=82=B4?= =?UTF-8?q?=EB=A6=BC=EC=B0=A8=EC=88=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/content/AudioContentRepository.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt index 2236884..7b046e4 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt @@ -1313,7 +1313,6 @@ class AudioContentQueryRepositoryImpl( audioContent.releaseDate.isNull .or(audioContent.releaseDate.loe(LocalDateTime.now())) ) - .and(blockMember.id.isNull) if (!isAdult) { where = where.and(audioContent.isAdult.isFalse) @@ -1360,6 +1359,7 @@ class AudioContentQueryRepositoryImpl( .where(where) .offset(offset) .limit(limit) + .orderBy(audioContent.id.desc()) .fetch() } -- 2.40.1 From da7e4c2156c84cace29d79526b8d038e4d04916f Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 11 Jul 2025 17:46:22 +0900 Subject: [PATCH 04/13] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20-?= =?UTF-8?q?=20GetContentCurationResponse=20-=20JsonProperty=EB=A5=BC=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=98=EC=97=AC=20Redis=20Cache=EC=97=90?= =?UTF-8?q?=EC=84=9C=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EA=B0=80=EC=A0=B8?= =?UTF-8?q?=EC=98=AC=20=EB=96=84=20=ED=8C=8C=EC=8B=B1=EC=9D=B4=20=EC=9D=B4?= =?UTF-8?q?=EB=A4=84=EC=A7=88=20=EC=88=98=20=EC=9E=88=EB=8F=84=EB=A1=9D=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/content/main/tab/GetContentCurationResponse.kt | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/GetContentCurationResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/GetContentCurationResponse.kt index b020326..78f6d7e 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/GetContentCurationResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/tab/GetContentCurationResponse.kt @@ -1,8 +1,9 @@ package kr.co.vividnext.sodalive.content.main.tab +import com.fasterxml.jackson.annotation.JsonProperty import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem data class GetContentCurationResponse( - val title: String, - val items: List + @JsonProperty("title") val title: String, + @JsonProperty("items") val items: List ) -- 2.40.1 From 69a63a77d347a9a97d60b9a8531ff4f9630a29b8 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 11 Jul 2025 18:02:52 +0900 Subject: [PATCH 05/13] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20-?= =?UTF-8?q?=20GetAudioContentMainItem=20-=20JsonProperty=EB=A5=BC=20pointA?= =?UTF-8?q?vailable=20=EC=88=98=EC=A0=95=ED=95=98=EC=97=AC=20Redis=20Cache?= =?UTF-8?q?=EC=97=90=EC=84=9C=20=EB=8D=B0=EC=9D=B4=ED=84=B0=20=EA=B0=80?= =?UTF-8?q?=EC=A0=B8=EC=98=AC=20=EB=96=84=20=ED=8C=8C=EC=8B=B1=EC=9D=B4=20?= =?UTF-8?q?=EC=9D=B4=EB=A4=84=EC=A7=88=20=EC=88=98=20=EC=9E=88=EB=8F=84?= =?UTF-8?q?=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vividnext/sodalive/content/main/GetAudioContentMainItem.kt | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentMainItem.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentMainItem.kt index 269fa8c..c53c49c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentMainItem.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentMainItem.kt @@ -12,5 +12,5 @@ data class GetAudioContentMainItem @QueryProjection constructor( @JsonProperty("creatorNickname") val creatorNickname: String, @JsonProperty("price") val price: Int, @JsonProperty("duration") val duration: String, - @JsonProperty("isPointAvailable") val isPointAvailable: Boolean + @JsonProperty("pointAvailable") val isPointAvailable: Boolean ) -- 2.40.1 From ab9fd2bc1666c360bf3e445845accd97659ae1a6 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 11 Jul 2025 18:43:07 +0900 Subject: [PATCH 06/13] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20-?= =?UTF-8?q?=20GetAudioContentMainItem=20-=20JsonProperty=EB=A5=BC=20isPoin?= =?UTF-8?q?tAvailable=20=EC=88=98=EC=A0=95=20-=20@JsonProperty=EB=A5=BC=20?= =?UTF-8?q?->=20@get:JsonProperty,=20@param:JsonProperty=EB=A1=9C=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/content/main/GetAudioContentMainItem.kt | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentMainItem.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentMainItem.kt index c53c49c..b690ffc 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentMainItem.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/GetAudioContentMainItem.kt @@ -12,5 +12,7 @@ data class GetAudioContentMainItem @QueryProjection constructor( @JsonProperty("creatorNickname") val creatorNickname: String, @JsonProperty("price") val price: Int, @JsonProperty("duration") val duration: String, - @JsonProperty("pointAvailable") val isPointAvailable: Boolean + @get:JsonProperty("isPointAvailable") + @param:JsonProperty("isPointAvailable") + val isPointAvailable: Boolean ) -- 2.40.1 From 704ad12ccfac54642285c8ee4687e732b8e5ee3d Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 11 Jul 2025 19:36:29 +0900 Subject: [PATCH 07/13] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20-?= =?UTF-8?q?=20getContentCurationList=20-=20=EC=BA=90=EC=8B=9C=20=EC=A0=9C?= =?UTF-8?q?=EA=B1=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../content/main/curation/AudioContentCurationService.kt | 6 ------ 1 file changed, 6 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationService.kt index 082f573..b21d862 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/curation/AudioContentCurationService.kt @@ -7,7 +7,6 @@ import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.block.BlockMemberRepository import org.springframework.beans.factory.annotation.Value -import org.springframework.cache.annotation.Cacheable import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -53,11 +52,6 @@ class AudioContentCurationService( } @Transactional(readOnly = true) - @Cacheable( - cacheNames = ["cache_ttl_3_days"], - key = "'getContentCurationList:' + ':' +" + - "#isAdult + ':' + #tabId + ':' + #contentType + ':' + (#memberId ?: 'guest')" - ) fun getContentCurationList( tabId: Long, isAdult: Boolean, -- 2.40.1 From a49b82a7c2680c051e2d9eb2ffe2630d4f87c186 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 11 Jul 2025 20:00:39 +0900 Subject: [PATCH 08/13] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20-?= =?UTF-8?q?=20=EC=9D=B8=EA=B8=B0=20=ED=81=AC=EB=A6=AC=EC=97=90=EC=9D=B4?= =?UTF-8?q?=ED=84=B0=20-=20=ED=8C=94=EB=A1=9C=EC=9B=8C=20=EC=88=98=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kr/co/vividnext/sodalive/api/home/HomeService.kt | 7 ++++++- .../co/vividnext/sodalive/explorer/GetExplorerResponse.kt | 3 ++- src/main/kotlin/kr/co/vividnext/sodalive/member/Member.kt | 5 +++-- 3 files changed, 11 insertions(+), 4 deletions(-) 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 8201601..10cb898 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 @@ -10,6 +10,7 @@ import kr.co.vividnext.sodalive.content.series.GetSeriesListResponse import kr.co.vividnext.sodalive.content.theme.AudioContentThemeService import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek import kr.co.vividnext.sodalive.event.EventService +import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository import kr.co.vividnext.sodalive.live.room.LiveRoomService import kr.co.vividnext.sodalive.live.room.LiveRoomStatus import kr.co.vividnext.sodalive.member.Member @@ -39,6 +40,7 @@ class HomeService( private val rankingService: RankingService, private val rankingRepository: RankingRepository, + private val explorerQueryRepository: ExplorerQueryRepository, @Value("\${cloud.aws.cloud-front.host}") private val imageHost: String @@ -70,7 +72,10 @@ class HomeService( true } } - .map { it.toExplorerSectionCreator(imageHost) } + .map { + val followerCount = explorerQueryRepository.getNotificationUserIds(it.id!!).size + it.toExplorerSectionCreator(imageHost, followerCount) + } val latestContentThemeList = contentThemeService.getActiveThemeOfContent( isAdult = isAdult, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/GetExplorerResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/GetExplorerResponse.kt index 7617809..0ff055c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/GetExplorerResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/GetExplorerResponse.kt @@ -14,5 +14,6 @@ data class GetExplorerSectionCreatorResponse( val id: Long, val nickname: String, val tags: String, - val profileImageUrl: String + val profileImageUrl: String, + val followerCount: Int ) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/member/Member.kt b/src/main/kotlin/kr/co/vividnext/sodalive/member/Member.kt index 43921e9..d4ca483 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/member/Member.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/member/Member.kt @@ -123,7 +123,7 @@ data class Member( } } - fun toExplorerSectionCreator(imageHost: String): GetExplorerSectionCreatorResponse { + fun toExplorerSectionCreator(imageHost: String, followerCount: Int = 0): GetExplorerSectionCreatorResponse { return GetExplorerSectionCreatorResponse( id = id!!, nickname = nickname, @@ -135,7 +135,8 @@ data class Member( "$imageHost/$profileImage" } else { "$imageHost/profile/default-profile.png" - } + }, + followerCount = followerCount ) } } -- 2.40.1 From a7e167a95f8ceed58b888b528d14c981649c3032 Mon Sep 17 00:00:00 2001 From: Klaus Date: Fri, 11 Jul 2025 23:39:55 +0900 Subject: [PATCH 09/13] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20-?= =?UTF-8?q?=20=EC=9A=94=EC=9D=BC=EB=B3=84=20=EC=8B=9C=EB=A6=AC=EC=A6=88=20?= =?UTF-8?q?-=20groupBy=20=EC=B6=94=EA=B0=80=ED=95=98=EC=97=AC=20=EB=8F=99?= =?UTF-8?q?=EC=9D=BC=ED=95=9C=20=EC=8B=9C=EB=A6=AC=EC=A6=88=EA=B0=80=20?= =?UTF-8?q?=EC=97=AC=EB=9F=AC=EA=B0=9C=20=EC=B6=94=EA=B0=80=EB=90=98?= =?UTF-8?q?=EC=96=B4=20=EC=9E=88=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=EC=88=98?= =?UTF-8?q?=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../vividnext/sodalive/content/series/ContentSeriesRepository.kt | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt index b94d943..201f3bf 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt @@ -414,6 +414,7 @@ class ContentSeriesQueryRepositoryImpl( .innerJoin(series.contentList, seriesContent) .innerJoin(seriesContent.content, audioContent) .where(where) + .groupBy(series.id) .orderBy(seriesContent.content.createdAt.desc()) .offset(offset) .limit(limit) -- 2.40.1 From ca870392e245520a28c6735cef6beb4e0b2b356b Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 12 Jul 2025 00:43:45 +0900 Subject: [PATCH 10/13] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20-?= =?UTF-8?q?=20=EC=9A=94=EC=9D=BC=EB=B3=84=20=EC=8B=9C=EB=A6=AC=EC=A6=88=20?= =?UTF-8?q?-=20groupBy=20=EC=9D=B4=ED=9B=84=20=EC=97=86=EB=8A=94=20?= =?UTF-8?q?=EC=BB=AC=EB=9F=BC=EC=9C=BC=EB=A1=9C=20=EC=A0=95=EB=A0=AC?= =?UTF-8?q?=ED=95=9C=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../sodalive/content/series/ContentSeriesRepository.kt | 4 ---- 1 file changed, 4 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt index 201f3bf..667abbe 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/series/ContentSeriesRepository.kt @@ -410,12 +410,8 @@ class ContentSeriesQueryRepositoryImpl( return queryFactory .selectFrom(series) - .innerJoin(series.member, member) - .innerJoin(series.contentList, seriesContent) - .innerJoin(seriesContent.content, audioContent) .where(where) .groupBy(series.id) - .orderBy(seriesContent.content.createdAt.desc()) .offset(offset) .limit(limit) .fetch() -- 2.40.1 From 81e82ad7314bb7c87affce38a6681c50c3215b6a Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 12 Jul 2025 02:53:31 +0900 Subject: [PATCH 11/13] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20-?= =?UTF-8?q?=20=EC=B6=94=EC=B2=9C=20=EC=B1=84=EB=84=90=20-=20=EC=BD=98?= =?UTF-8?q?=ED=85=90=EC=B8=A0=EA=B0=80=20=EB=B9=88=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A1=9C=20=EB=B0=98=ED=99=98=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../query/recommend/RecommendChannelQueryRepository.kt | 3 +-- 1 file changed, 1 insertion(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryRepository.kt index 3150d08..3915203 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryRepository.kt @@ -61,7 +61,7 @@ class RecommendChannelQueryRepository( } fun getContentsByCreatorIdLikeDesc(creatorId: Long): List { - queryFactory + return queryFactory .select( QRecommendChannelContentItem( audioContent.id, @@ -77,6 +77,5 @@ class RecommendChannelQueryRepository( .where(audioContent.member.id.eq(creatorId)) .groupBy(audioContent.id) .fetch() - return listOf() } } -- 2.40.1 From f59f45d9a4241753843a70887709f5fb67277df1 Mon Sep 17 00:00:00 2001 From: Klaus Date: Sat, 12 Jul 2025 03:18:37 +0900 Subject: [PATCH 12/13] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20-?= =?UTF-8?q?=20=EC=B6=94=EC=B2=9C=20=EC=B1=84=EB=84=90=20-=20=EC=BD=98?= =?UTF-8?q?=ED=85=90=EC=B8=A0=EA=B0=80=20=EB=B9=88=20=EB=A6=AC=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=EB=A1=9C=20=EB=B0=98=ED=99=98=EB=90=98=EB=8A=94=20?= =?UTF-8?q?=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../recommend/RecommendChannelQueryRepository.kt | 14 ++++++++++++-- 1 file changed, 12 insertions(+), 2 deletions(-) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryRepository.kt index 3915203..fadfdc2 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/query/recommend/RecommendChannelQueryRepository.kt @@ -72,10 +72,20 @@ class RecommendChannelQueryRepository( ) ) .from(audioContent) - .leftJoin(audioContentLike).on(audioContentLike.audioContent.id.eq(audioContent.id)) - .leftJoin(audioContentComment).on(audioContentComment.audioContent.id.eq(audioContent.id)) + .leftJoin(audioContentLike) + .on( + audioContentLike.audioContent.id.eq(audioContent.id) + .and(audioContentLike.isActive.isTrue) + ) + .leftJoin(audioContentComment) + .on( + audioContentComment.audioContent.id.eq(audioContent.id) + .and(audioContentComment.isActive.isTrue) + ) .where(audioContent.member.id.eq(creatorId)) .groupBy(audioContent.id) + .orderBy(audioContentLike.id.countDistinct().desc()) + .limit(3) .fetch() } } -- 2.40.1 From 7551a19b34038abe0118925c7d05bf5726f0e246 Mon Sep 17 00:00:00 2001 From: Klaus Date: Mon, 14 Jul 2025 18:48:57 +0900 Subject: [PATCH 13/13] =?UTF-8?q?fix:=20=EB=A9=94=EC=9D=B8=20=ED=99=88=20A?= =?UTF-8?q?PI=20-=20=EB=A1=9C=EA=B7=B8=EC=9D=B8=20=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EA=B3=A0=20=EC=A1=B0=ED=9A=8C=EA=B0=80=20=EA=B0=80?= =?UTF-8?q?=EB=8A=A5=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt b/src/main/kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt index 8c4bc9f..e6c2470 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/configs/SecurityConfig.kt @@ -80,6 +80,9 @@ class SecurityConfig( .antMatchers("/v2/audio-content/main/home").permitAll() .antMatchers("/v2/audio-content/main/home/popular-content-by-creator").permitAll() .antMatchers("/v2/audio-content/main/home/content/ranking").permitAll() + .antMatchers("/api/home").permitAll() + .antMatchers("/api/home/latest-content").permitAll() + .antMatchers("/api/home/day-of-week-series").permitAll() .antMatchers(HttpMethod.GET, "/faq").permitAll() .antMatchers(HttpMethod.GET, "/faq/category").permitAll() .antMatchers("/audition").permitAll() -- 2.40.1