관리자 태그별 추천 단편

- 등록, 수정, 삭제, 순서변경 API 추가
This commit is contained in:
Klaus 2025-02-18 15:27:39 +09:00
parent 43ea4191c3
commit e0565f7eed
8 changed files with 391 additions and 0 deletions

View File

@ -0,0 +1,72 @@
package kr.co.vividnext.sodalive.admin.content.curation.tag
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
import kr.co.vividnext.sodalive.content.main.curation.tag.ContentHashTagCurationItem
import kr.co.vividnext.sodalive.content.main.curation.tag.QContentHashTagCuration.contentHashTagCuration
import kr.co.vividnext.sodalive.content.main.curation.tag.QContentHashTagCurationItem.contentHashTagCurationItem
import org.springframework.beans.factory.annotation.Value
import org.springframework.data.jpa.repository.JpaRepository
interface AdminContentHashTagCurationItemRepository :
JpaRepository<ContentHashTagCurationItem, Long>,
AdminContentHashTagCurationItemQueryRepository
interface AdminContentHashTagCurationItemQueryRepository {
fun getContentHashTagCurationItemList(curationId: Long): List<GetAdminHashTagCurationItemResponse>
fun findByCurationIdAndContentId(curationId: Long, contentId: Long?): ContentHashTagCurationItem?
fun findByCurationIdAndItemId(curationId: Long, itemId: Long): ContentHashTagCurationItem?
}
class AdminContentHashTagCurationItemQueryRepositoryImpl(
val queryFactory: JPAQueryFactory,
@Value("\${cloud.aws.cloud-front.host}")
private val imageHost: String
) : AdminContentHashTagCurationItemQueryRepository {
override fun getContentHashTagCurationItemList(curationId: Long): List<GetAdminHashTagCurationItemResponse> {
return queryFactory
.select(
QGetAdminHashTagCurationItemResponse(
contentHashTagCurationItem.id,
audioContent.title,
audioContent.detail,
audioContent.coverImage.prepend("/").prepend(imageHost),
audioContent.member.nickname.coalesce(""),
audioContent.isAdult
)
)
.from(contentHashTagCurationItem)
.innerJoin(contentHashTagCurationItem.curation, contentHashTagCuration)
.innerJoin(contentHashTagCurationItem.content, audioContent)
.where(
contentHashTagCuration.id.eq(curationId),
contentHashTagCurationItem.isActive.isTrue,
audioContent.isActive.isTrue
)
.orderBy(contentHashTagCurationItem.orders.asc())
.fetch()
}
override fun findByCurationIdAndContentId(curationId: Long, contentId: Long?): ContentHashTagCurationItem? {
return queryFactory
.selectFrom(contentHashTagCurationItem)
.innerJoin(contentHashTagCurationItem.curation, contentHashTagCuration)
.innerJoin(contentHashTagCurationItem.content, audioContent)
.where(
contentHashTagCuration.id.eq(curationId),
audioContent.id.eq(contentId)
)
.fetchFirst()
}
override fun findByCurationIdAndItemId(curationId: Long, itemId: Long): ContentHashTagCurationItem? {
return queryFactory.selectFrom(contentHashTagCurationItem)
.innerJoin(contentHashTagCurationItem.curation, contentHashTagCuration)
.where(
contentHashTagCuration.id.eq(curationId),
contentHashTagCurationItem.id.eq(itemId)
)
.fetchFirst()
}
}

View File

@ -0,0 +1,63 @@
package kr.co.vividnext.sodalive.admin.content.curation.tag
import kr.co.vividnext.sodalive.admin.content.curation.AddItemToCurationRequest
import kr.co.vividnext.sodalive.admin.content.curation.RemoveItemInCurationRequest
import kr.co.vividnext.sodalive.admin.content.curation.UpdateCurationItemOrdersRequest
import kr.co.vividnext.sodalive.common.ApiResponse
import org.springframework.security.access.prepost.PreAuthorize
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PostMapping
import org.springframework.web.bind.annotation.PutMapping
import org.springframework.web.bind.annotation.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
@RestController
@RequestMapping("/admin/audio-content/tag/curation")
@PreAuthorize("hasRole('ADMIN')")
class AdminHashTagCurationController(private val service: AdminHashTagCurationService) {
@GetMapping
fun getContentHashTagCurationList() = ApiResponse.ok(service.getContentHashTagCurationList())
@PostMapping
fun createContentHashTagCuration(
@RequestBody request: CreateContentHashTagCurationRequest
) = ApiResponse.ok(service.createContentHashTagCuration(request))
@PutMapping
fun updateContentHashTagCuration(
@RequestBody request: UpdateContentHashTagCurationRequest
) = ApiResponse.ok(service.updateContentHashTagCuration(request))
@PutMapping("/orders")
fun updateContentHashTagCurationOrders(
@RequestBody request: UpdateContentHashTagCurationOrderRequest
) = ApiResponse.ok(service.updateContentHashTagCurationOrders(request.ids), "수정되었습니다.")
@GetMapping("/items")
fun getHashTagCurationItemList(
@RequestParam curationId: Long
) = ApiResponse.ok(service.getHashTagCurationItemList(curationId = curationId))
@GetMapping("/search/content")
fun searchHashTagCurationContentItem(
@RequestParam curationId: Long,
@RequestParam searchWord: String
) = ApiResponse.ok(service.searchHashTagCurationContentItem(curationId, searchWord))
@PostMapping("/add/item")
fun addItemToHashTagCuration(
@RequestBody request: AddItemToCurationRequest
) = ApiResponse.ok(service.addItemToHashTagCuration(request), "큐레이션 아이템을 등록했습니다.")
@PutMapping("/remove/item")
fun removeItemInHashTagCuration(
@RequestBody request: RemoveItemInCurationRequest
) = ApiResponse.ok(service.removeItemInHashTagCuration(request), "큐레이션 아이템을 제거했습니다.")
@PutMapping("/orders/item")
fun updateItemInHashTagCurationOrders(
@RequestBody request: UpdateCurationItemOrdersRequest
) = ApiResponse.ok(service.updateItemInHashTagCurationOrders(request), "수정되었습니다.")
}

View File

@ -0,0 +1,72 @@
package kr.co.vividnext.sodalive.admin.content.curation.tag
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.admin.content.curation.QSearchCurationItemResponse
import kr.co.vividnext.sodalive.admin.content.curation.SearchCurationItemResponse
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
import kr.co.vividnext.sodalive.content.main.curation.tag.ContentHashTagCuration
import kr.co.vividnext.sodalive.content.main.curation.tag.QContentHashTagCuration.contentHashTagCuration
import kr.co.vividnext.sodalive.content.main.curation.tag.QContentHashTagCurationItem.contentHashTagCurationItem
import org.springframework.beans.factory.annotation.Value
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
interface AdminHashTagCurationRepository :
JpaRepository<ContentHashTagCuration, Long>,
AdminHashTagCurationQueryRepository
interface AdminHashTagCurationQueryRepository {
fun getContentHashTagCurationList(): List<GetAdminContentHashTagCurationResponse>
fun searchHashTagCurationContentItem(curationId: Long, searchWord: String): List<SearchCurationItemResponse>
}
@Repository
class AdminHashTagCurationQueryRepositoryImpl(
private val queryFactory: JPAQueryFactory,
@Value("\${cloud.aws.cloud-front.host}")
private val imageHost: String
) : AdminHashTagCurationQueryRepository {
override fun getContentHashTagCurationList(): List<GetAdminContentHashTagCurationResponse> {
return queryFactory
.select(
QGetAdminContentHashTagCurationResponse(
contentHashTagCuration.id,
contentHashTagCuration.tag,
contentHashTagCuration.isAdult
)
)
.from(contentHashTagCuration)
.where(contentHashTagCuration.isActive.isTrue)
.orderBy(contentHashTagCuration.orders.asc())
.fetch()
}
override fun searchHashTagCurationContentItem(
curationId: Long,
searchWord: String
): List<SearchCurationItemResponse> {
return queryFactory
.select(
QSearchCurationItemResponse(
audioContent.id,
audioContent.title,
audioContent.coverImage.prepend("/").prepend(imageHost)
)
)
.from(audioContent)
.leftJoin(contentHashTagCurationItem)
.on(
audioContent.id.eq(contentHashTagCurationItem.content.id)
.and(contentHashTagCurationItem.curation.id.eq(curationId))
)
.where(
audioContent.duration.isNotNull
.and(audioContent.member.isNotNull)
.and(audioContent.isActive.isTrue)
.and(audioContent.title.contains(searchWord))
.and(contentHashTagCurationItem.id.isNull)
)
.fetch()
}
}

View File

@ -0,0 +1,116 @@
package kr.co.vividnext.sodalive.admin.content.curation.tag
import kr.co.vividnext.sodalive.admin.content.curation.AddItemToCurationRequest
import kr.co.vividnext.sodalive.admin.content.curation.RemoveItemInCurationRequest
import kr.co.vividnext.sodalive.admin.content.curation.SearchCurationItemResponse
import kr.co.vividnext.sodalive.admin.content.curation.UpdateCurationItemOrdersRequest
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.content.AudioContentRepository
import kr.co.vividnext.sodalive.content.main.curation.tag.ContentHashTagCuration
import kr.co.vividnext.sodalive.content.main.curation.tag.ContentHashTagCurationItem
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
@Service
class AdminHashTagCurationService(
private val repository: AdminHashTagCurationRepository,
private val itemRepository: AdminContentHashTagCurationItemRepository,
private val audioContentRepository: AudioContentRepository
) {
@Transactional
fun createContentHashTagCuration(request: CreateContentHashTagCurationRequest) {
repository.save(
ContentHashTagCuration(
tag = request.tag,
isAdult = request.isAdult
)
)
}
@Transactional
fun updateContentHashTagCuration(request: UpdateContentHashTagCurationRequest) {
val hashTagCuration = repository.findByIdOrNull(id = request.id)
?: throw SodaException("잘못된 요청입니다.")
if (request.tag != null) {
hashTagCuration.tag = request.tag
}
if (request.isAdult != null) {
hashTagCuration.isAdult = request.isAdult
}
if (request.isActive != null) {
hashTagCuration.isActive = request.isActive
}
}
@Transactional
fun updateContentHashTagCurationOrders(ids: List<Long>) {
for (index in ids.indices) {
val contentHashTagCuration = repository.findByIdOrNull(ids[index])
if (contentHashTagCuration != null) {
contentHashTagCuration.orders = index + 1
}
}
}
fun getContentHashTagCurationList(): List<GetAdminContentHashTagCurationResponse> {
return repository.getContentHashTagCurationList()
}
fun getHashTagCurationItemList(curationId: Long): List<GetAdminHashTagCurationItemResponse> {
return itemRepository.getContentHashTagCurationItemList(curationId)
}
fun searchHashTagCurationContentItem(curationId: Long, searchWord: String): List<SearchCurationItemResponse> {
return repository.searchHashTagCurationContentItem(curationId, searchWord)
}
@Transactional
fun addItemToHashTagCuration(request: AddItemToCurationRequest) {
val curation = repository.findByIdOrNull(id = request.curationId)
?: throw SodaException("잘못된 요청입니다.")
request.itemIdList.forEach { contentId ->
val audioContent = audioContentRepository.findByIdAndActive(contentId)
if (audioContent != null) {
val item = itemRepository.findByCurationIdAndContentId(
curationId = request.curationId,
contentId = audioContent.id
) ?: ContentHashTagCurationItem()
item.curation = curation
item.content = audioContent
itemRepository.save(item)
}
}
}
@Transactional
fun removeItemInHashTagCuration(request: RemoveItemInCurationRequest) {
val item = itemRepository.findByCurationIdAndItemId(
curationId = request.curationId,
itemId = request.itemId
)
item?.isActive = false
}
@Transactional
fun updateItemInHashTagCurationOrders(request: UpdateCurationItemOrdersRequest) {
val ids = request.itemIds
for (index in ids.indices) {
val item = itemRepository.findByCurationIdAndItemId(
curationId = request.curationId,
itemId = ids[index]
)
if (item != null) {
item.orders = index + 1
}
}
}
}

View File

@ -0,0 +1,17 @@
package kr.co.vividnext.sodalive.admin.content.curation.tag
data class CreateContentHashTagCurationRequest(
val tag: String,
val isAdult: Boolean
)
data class UpdateContentHashTagCurationRequest(
val id: Long,
val tag: String?,
val isAdult: Boolean?,
val isActive: Boolean?
)
data class UpdateContentHashTagCurationOrderRequest(
val ids: List<Long>
)

View File

@ -0,0 +1,9 @@
package kr.co.vividnext.sodalive.admin.content.curation.tag
import com.querydsl.core.annotations.QueryProjection
data class GetAdminContentHashTagCurationResponse @QueryProjection constructor(
val id: Long,
val tag: String,
val isAdult: Boolean
)

View File

@ -0,0 +1,12 @@
package kr.co.vividnext.sodalive.admin.content.curation.tag
import com.querydsl.core.annotations.QueryProjection
data class GetAdminHashTagCurationItemResponse @QueryProjection constructor(
val id: Long,
val title: String,
val desc: String,
val coverImageUrl: String,
val creatorNickname: String,
val isAdult: Boolean
)

View File

@ -0,0 +1,30 @@
package kr.co.vividnext.sodalive.content.main.curation.tag
import kr.co.vividnext.sodalive.common.BaseEntity
import kr.co.vividnext.sodalive.content.AudioContent
import javax.persistence.Entity
import javax.persistence.FetchType
import javax.persistence.JoinColumn
import javax.persistence.ManyToOne
@Entity
data class ContentHashTagCuration(
var tag: String,
var orders: Int = 1,
var isAdult: Boolean = false,
var isActive: Boolean = true
) : BaseEntity()
@Entity
data class ContentHashTagCurationItem(
var orders: Int = 1,
var isActive: Boolean = true
) : BaseEntity() {
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "curation_id", nullable = false)
var curation: ContentHashTagCuration? = null
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "content_id", nullable = false)
var content: AudioContent? = null
}