콘텐츠 상단 고정 기능 추가 #120
@@ -196,4 +196,26 @@ class AudioContentController(private val service: AudioContentService) {
 | 
				
			|||||||
    fun releaseContent() = run {
 | 
					    fun releaseContent() = run {
 | 
				
			||||||
        ApiResponse.ok(service.releaseContent())
 | 
					        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))
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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.main.GetAudioContentRanking
 | 
				
			||||||
import kr.co.vividnext.sodalive.content.order.OrderRepository
 | 
					import kr.co.vividnext.sodalive.content.order.OrderRepository
 | 
				
			||||||
import kr.co.vividnext.sodalive.content.order.OrderType
 | 
					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.content.theme.AudioContentThemeQueryRepository
 | 
				
			||||||
import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository
 | 
					import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository
 | 
				
			||||||
import kr.co.vividnext.sodalive.extensions.convertLocalDateTime
 | 
					import kr.co.vividnext.sodalive.extensions.convertLocalDateTime
 | 
				
			||||||
@@ -49,6 +51,7 @@ class AudioContentService(
 | 
				
			|||||||
    private val playbackTrackingRepository: PlaybackTrackingRepository,
 | 
					    private val playbackTrackingRepository: PlaybackTrackingRepository,
 | 
				
			||||||
    private val commentRepository: AudioContentCommentRepository,
 | 
					    private val commentRepository: AudioContentCommentRepository,
 | 
				
			||||||
    private val audioContentLikeRepository: AudioContentLikeRepository,
 | 
					    private val audioContentLikeRepository: AudioContentLikeRepository,
 | 
				
			||||||
 | 
					    private val pinContentRepository: PinContentRepository,
 | 
				
			||||||
 | 
					
 | 
				
			||||||
    private val s3Uploader: S3Uploader,
 | 
					    private val s3Uploader: S3Uploader,
 | 
				
			||||||
    private val objectMapper: ObjectMapper,
 | 
					    private val objectMapper: ObjectMapper,
 | 
				
			||||||
@@ -696,4 +699,31 @@ class AudioContentService(
 | 
				
			|||||||
    fun getContentRankingSortTypeList(): List<String> {
 | 
					    fun getContentRankingSortTypeList(): List<String> {
 | 
				
			||||||
        return listOf("매출", "댓글", "좋아요")
 | 
					        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
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
}
 | 
					}
 | 
				
			||||||
 
 | 
				
			|||||||
@@ -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
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
@@ -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<PinContent, Long>, PinContentQueryRepository
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					interface PinContentQueryRepository {
 | 
				
			||||||
 | 
					    fun getPinContentList(memberId: Long): List<PinContent>
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					    fun findByContentIdAndMemberId(contentId: Long, memberId: Long): PinContent?
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
 | 
					
 | 
				
			||||||
 | 
					class PinContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : PinContentQueryRepository {
 | 
				
			||||||
 | 
					    override fun getPinContentList(memberId: Long): List<PinContent> {
 | 
				
			||||||
 | 
					        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()
 | 
				
			||||||
 | 
					    }
 | 
				
			||||||
 | 
					}
 | 
				
			||||||
		Reference in New Issue
	
	Block a user