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 b8f8873..d86afd3 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt @@ -196,4 +196,26 @@ class AudioContentController(private val service: AudioContentService) { fun releaseContent() = run { ApiResponse.ok(service.releaseContent()) } + + @PostMapping("/pin-to-the-top/{id}") + @PreAuthorize("hasRole('CREATOR')") + fun pinToTheTop( + @PathVariable id: Long, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok(service.pinToTheTop(contentId = id, member = member)) + } + + @PutMapping("/unpin-at-the-top/{id}") + @PreAuthorize("hasRole('CREATOR')") + fun unpinAtTheTop( + @PathVariable id: Long, + @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? + ) = run { + if (member == null) throw SodaException("로그인 정보를 확인해주세요.") + + ApiResponse.ok(service.unpinAtTheTop(contentId = id, member = member)) + } } 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 09a65f1..7d5e387 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentService.kt @@ -16,6 +16,8 @@ import kr.co.vividnext.sodalive.content.like.PutAudioContentLikeResponse import kr.co.vividnext.sodalive.content.main.GetAudioContentRanking import kr.co.vividnext.sodalive.content.order.OrderRepository import kr.co.vividnext.sodalive.content.order.OrderType +import kr.co.vividnext.sodalive.content.pin.PinContent +import kr.co.vividnext.sodalive.content.pin.PinContentRepository import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository import kr.co.vividnext.sodalive.extensions.convertLocalDateTime @@ -49,6 +51,7 @@ class AudioContentService( private val playbackTrackingRepository: PlaybackTrackingRepository, private val commentRepository: AudioContentCommentRepository, private val audioContentLikeRepository: AudioContentLikeRepository, + private val pinContentRepository: PinContentRepository, private val s3Uploader: S3Uploader, private val objectMapper: ObjectMapper, @@ -696,4 +699,31 @@ class AudioContentService( fun getContentRankingSortTypeList(): List { return listOf("매출", "댓글", "좋아요") } + + @Transactional + fun pinToTheTop(contentId: Long, member: Member) { + val audioContent = repository.findByIdAndCreatorId(contentId = contentId, creatorId = member.id!!) + ?: throw SodaException("잘못된 콘텐츠 입니다.\n다시 시도해 주세요.") + + val pinContentList = pinContentRepository.getPinContentList(memberId = member.id!!) + val pinContent = if (pinContentList.size >= 3) { + pinContentList[0] + } else { + PinContent() + } + + pinContent.member = member + pinContent.content = audioContent + pinContentRepository.save(pinContent) + } + + @Transactional + fun unpinAtTheTop(contentId: Long, member: Member) { + val pinContent = pinContentRepository.findByContentIdAndMemberId( + contentId = contentId, + memberId = member.id!! + ) ?: throw SodaException("잘못된 콘텐츠 입니다.\n다시 시도해 주세요.") + + pinContent.isActive = false + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/pin/PinContent.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/pin/PinContent.kt new file mode 100644 index 0000000..2d6fcc3 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/pin/PinContent.kt @@ -0,0 +1,20 @@ +package kr.co.vividnext.sodalive.content.pin + +import kr.co.vividnext.sodalive.common.BaseEntity +import kr.co.vividnext.sodalive.content.AudioContent +import kr.co.vividnext.sodalive.member.Member +import javax.persistence.Entity +import javax.persistence.FetchType +import javax.persistence.JoinColumn +import javax.persistence.ManyToOne + +@Entity +data class PinContent(var isActive: Boolean = true) : BaseEntity() { + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "member_id", nullable = false) + var member: Member? = null + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "content_id", nullable = false) + var content: AudioContent? = null +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/pin/PinContentRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/pin/PinContentRepository.kt new file mode 100644 index 0000000..a33f642 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/pin/PinContentRepository.kt @@ -0,0 +1,33 @@ +package kr.co.vividnext.sodalive.content.pin + +import com.querydsl.jpa.impl.JPAQueryFactory +import kr.co.vividnext.sodalive.content.pin.QPinContent.pinContent +import org.springframework.data.jpa.repository.JpaRepository + +interface PinContentRepository : JpaRepository, PinContentQueryRepository + +interface PinContentQueryRepository { + fun getPinContentList(memberId: Long): List + + fun findByContentIdAndMemberId(contentId: Long, memberId: Long): PinContent? +} + +class PinContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : PinContentQueryRepository { + override fun getPinContentList(memberId: Long): List { + return queryFactory + .selectFrom(pinContent) + .where(pinContent.member.id.eq(memberId)) + .orderBy(pinContent.updatedAt.asc()) + .fetch() + } + + override fun findByContentIdAndMemberId(contentId: Long, memberId: Long): PinContent? { + return queryFactory + .selectFrom(pinContent) + .where( + pinContent.content.id.eq(contentId) + .and(pinContent.member.id.eq(memberId)) + ) + .fetchFirst() + } +}