Compare commits

..

4 Commits

Author SHA1 Message Date
klaus 9aac591591 Merge pull request 'test' (#301) from test into main
Reviewed-on: #301
2025-04-01 13:31:24 +00:00
Klaus 6e229af790 콘텐츠 상세
- 이전화/다음화 추가
2025-04-01 18:27:59 +09:00
Klaus ce8cc3eb29 콘텐츠 상세
- 이전화/다음화 추가
2025-04-01 17:36:32 +09:00
Klaus 198ecddc89 콘텐츠 상세
- 이전화/다음화 추가
2025-04-01 16:21:32 +09:00
4 changed files with 131 additions and 4 deletions

View File

@ -131,11 +131,19 @@ class AudioContentController(private val service: AudioContentService) {
fun getDetail( fun getDetail(
@PathVariable id: Long, @PathVariable id: Long,
@RequestParam timezone: String, @RequestParam timezone: String,
@RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") 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") @GetMapping("/{id}/generate-url")

View File

@ -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.AudioContentPlaylistContent
import kr.co.vividnext.sodalive.content.playlist.QAudioContentPlaylistContent import kr.co.vividnext.sodalive.content.playlist.QAudioContentPlaylistContent
import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme 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.event.QEvent.event
import kr.co.vividnext.sodalive.member.MemberRole import kr.co.vividnext.sodalive.member.MemberRole
import kr.co.vividnext.sodalive.member.QMember.member import kr.co.vividnext.sodalive.member.QMember.member
@ -163,6 +165,10 @@ interface AudioContentQueryRepository {
fun fetchContentForPlaylist(contentIdList: List<Long>): List<AudioContentPlaylistContent> fun fetchContentForPlaylist(contentIdList: List<Long>): List<AudioContentPlaylistContent>
fun getCoverImageById(id: Long): String? 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 @Repository
@ -1152,4 +1158,85 @@ class AudioContentQueryRepositoryImpl(
.where(audioContent.id.eq(id)) .where(audioContent.id.eq(id))
.fetchFirst() .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()
}
} }

View File

@ -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) // 오디오 콘텐츠 조회 (content_id, 제목, 내용, 테마, 태그, 19여부, 이미지, 콘텐츠 PATH)
val audioContent = repository.findByIdOrNull(id) val audioContent = repository.findByIdOrNull(id)
?: throw SodaException("잘못된 콘텐츠 입니다.\n다시 시도해 주세요.") ?: throw SodaException("잘못된 콘텐츠 입니다.\n다시 시도해 주세요.")
@ -473,6 +480,27 @@ class AudioContentService(
null 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 ( if (
!isExistsAudioContent && !isExistsAudioContent &&
!audioContent.isActive && !audioContent.isActive &&
@ -666,7 +694,9 @@ class AudioContentService(
isFollowing = creatorFollowing?.isFollow ?: false, isFollowing = creatorFollowing?.isFollow ?: false,
isFollow = creatorFollowing?.isFollow ?: false, isFollow = creatorFollowing?.isFollow ?: false,
isNotify = creatorFollowing?.isNotify ?: false isNotify = creatorFollowing?.isNotify ?: false
) ),
previousContent = previousContent,
nextContent = nextContent
) )
} }

View File

@ -35,7 +35,9 @@ data class GetAudioContentDetailResponse(
val commentCount: Int, val commentCount: Int,
val isPin: Boolean, val isPin: Boolean,
val isAvailablePin: Boolean, val isAvailablePin: Boolean,
val creator: AudioContentCreator val creator: AudioContentCreator,
val previousContent: OtherContentResponse?,
val nextContent: OtherContentResponse?
) )
data class OtherContentResponse @QueryProjection constructor( data class OtherContentResponse @QueryProjection constructor(