diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt index 4b4549a..f5a529a 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt @@ -131,11 +131,19 @@ class AudioContentController(private val service: AudioContentService) { fun getDetail( @PathVariable id: Long, @RequestParam timezone: String, + @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? ) = run { if (member == null) throw SodaException("로그인 정보를 확인해주세요.") - ApiResponse.ok(service.getDetail(id = id, member = member, timezone = timezone)) + ApiResponse.ok( + service.getDetail( + id = id, + member = member, + isAdultContentVisible = isAdultContentVisible ?: true, + timezone = timezone + ) + ) } @GetMapping("/{id}/generate-url") 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 47a96d4..fcffe5f 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt @@ -22,6 +22,8 @@ import kr.co.vividnext.sodalive.content.pin.QPinContent.pinContent import kr.co.vividnext.sodalive.content.playlist.AudioContentPlaylistContent import kr.co.vividnext.sodalive.content.playlist.QAudioContentPlaylistContent import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme +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.event.QEvent.event import kr.co.vividnext.sodalive.member.MemberRole import kr.co.vividnext.sodalive.member.QMember.member @@ -163,6 +165,10 @@ interface AudioContentQueryRepository { fun fetchContentForPlaylist(contentIdList: List): List fun getCoverImageById(id: Long): String? + fun findPreviousContent(seriesId: Long, title: String, isAdult: Boolean): OtherContentResponse? + fun findNextContent(seriesId: Long, title: String, isAdult: Boolean): OtherContentResponse? + + fun findSeriesIdByContentId(contentId: Long, isAdult: Boolean): Long? } @Repository @@ -1152,4 +1158,85 @@ class AudioContentQueryRepositoryImpl( .where(audioContent.id.eq(id)) .fetchFirst() } + + override fun findPreviousContent(seriesId: Long, title: String, isAdult: Boolean): OtherContentResponse? { + var where = series.isActive.isTrue + .and(series.id.eq(seriesId)) + .and(audioContent.isActive.isTrue) + .and(audioContent.title.lt(title)) + .and(audioContent.limited.isNull) + .and(audioContent.releaseDate.loe(LocalDateTime.now())) + + if (!isAdult) { + where = where.and(audioContent.isAdult.isFalse) + } + + return queryFactory + .select( + QOtherContentResponse( + audioContent.id, + audioContent.title, + audioContent.coverImage.prepend("$imageHost/") + ) + ) + .from(seriesContent) + .innerJoin(seriesContent.series, series) + .innerJoin(seriesContent.content, audioContent) + .where(where) + .orderBy(audioContent.title.desc()) + .limit(1) + .fetchFirst() + } + + override fun findNextContent(seriesId: Long, title: String, isAdult: Boolean): OtherContentResponse? { + var where = series.isActive.isTrue + .and(series.id.eq(seriesId)) + .and(audioContent.isActive.isTrue) + .and(audioContent.title.gt(title)) + .and(audioContent.limited.isNull) + .and(audioContent.releaseDate.loe(LocalDateTime.now())) + + if (!isAdult) { + where = where.and(audioContent.isAdult.isFalse) + } + + return queryFactory + .select( + QOtherContentResponse( + audioContent.id, + audioContent.title, + audioContent.coverImage.prepend("$imageHost/") + ) + ) + .from(seriesContent) + .innerJoin(seriesContent.series, series) + .innerJoin(seriesContent.content, audioContent) + .innerJoin(series.member, member) + .where(where) + .orderBy(audioContent.title.asc()) + .limit(1) + .fetchFirst() + } + + override fun findSeriesIdByContentId(contentId: Long, isAdult: Boolean): Long? { + var where = series.isActive.isTrue + .and(audioContent.isActive.isTrue) + .and(audioContent.id.eq(contentId)) + .and(member.role.eq(MemberRole.CREATOR)) + + if (!isAdult) { + where = where.and(series.isAdult.isFalse) + } + + return queryFactory + .select(series.id) + .from(seriesContent) + .innerJoin(seriesContent.series, series) + .innerJoin(seriesContent.content, audioContent) + .innerJoin(series.member, member) + .where(where) + .orderBy(seriesContent.id.asc()) + .limit(1) + .fetchFirst() + } } 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 53e86d9..2082361 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt @@ -440,7 +440,14 @@ class AudioContentService( } } - fun getDetail(id: Long, member: Member, timezone: String): GetAudioContentDetailResponse { + fun getDetail( + id: Long, + member: Member, + isAdultContentVisible: Boolean, + timezone: String + ): GetAudioContentDetailResponse { + val isAdult = member.auth != null && isAdultContentVisible + // 오디오 콘텐츠 조회 (content_id, 제목, 내용, 테마, 태그, 19여부, 이미지, 콘텐츠 PATH) val audioContent = repository.findByIdOrNull(id) ?: throw SodaException("잘못된 콘텐츠 입니다.\n다시 시도해 주세요.") @@ -473,6 +480,27 @@ class AudioContentService( null } + val seriesId = repository.findSeriesIdByContentId(audioContent.id!!, isAdult) + val previousContent = if (seriesId != null) { + repository.findPreviousContent( + seriesId = seriesId, + title = audioContent.title, + isAdult = isAdult + ) + } else { + null + } + + val nextContent = if (seriesId != null) { + repository.findNextContent( + seriesId = seriesId, + title = audioContent.title, + isAdult = isAdult + ) + } else { + null + } + if ( !isExistsAudioContent && !audioContent.isActive && @@ -666,7 +694,9 @@ class AudioContentService( isFollowing = creatorFollowing?.isFollow ?: false, isFollow = creatorFollowing?.isFollow ?: false, isNotify = creatorFollowing?.isNotify ?: false - ) + ), + previousContent = previousContent, + nextContent = nextContent ) } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/GetAudioContentDetailResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/GetAudioContentDetailResponse.kt index d7346ef..67dd32b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/GetAudioContentDetailResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/GetAudioContentDetailResponse.kt @@ -35,7 +35,9 @@ data class GetAudioContentDetailResponse( val commentCount: Int, val isPin: Boolean, val isAvailablePin: Boolean, - val creator: AudioContentCreator + val creator: AudioContentCreator, + val previousContent: OtherContentResponse?, + val nextContent: OtherContentResponse? ) data class OtherContentResponse @QueryProjection constructor(