Merge pull request 'test' (#259) from test into main

Reviewed-on: #259
This commit is contained in:
klaus 2025-02-18 14:48:09 +00:00
commit 25083fb0e4
33 changed files with 797 additions and 197 deletions

View File

@ -60,4 +60,9 @@ class AdminContentCurationController(private val service: AdminContentCurationSe
fun removeItemInCuration( fun removeItemInCuration(
@RequestBody request: RemoveItemInCurationRequest @RequestBody request: RemoveItemInCurationRequest
) = ApiResponse.ok(service.removeItemInCuration(request), "큐레이션 아이템을 제거했습니다.") ) = ApiResponse.ok(service.removeItemInCuration(request), "큐레이션 아이템을 제거했습니다.")
@PutMapping("/orders/item")
fun updateItemInCurationOrders(
@RequestBody request: UpdateCurationItemOrdersRequest
) = ApiResponse.ok(service.updateItemInCurationOrders(request), "수정되었습니다.")
} }

View File

@ -77,6 +77,7 @@ class AdminContentCurationItemQueryRepositoryImpl(
audioContentCuration.id.eq(curationId), audioContentCuration.id.eq(curationId),
audioContentCurationItem.isActive.isTrue audioContentCurationItem.isActive.isTrue
) )
.orderBy(audioContentCurationItem.orders.asc())
.fetch() .fetch()
} }
@ -99,6 +100,7 @@ class AdminContentCurationItemQueryRepositoryImpl(
audioContentCuration.id.eq(curationId), audioContentCuration.id.eq(curationId),
audioContentCurationItem.isActive.isTrue audioContentCurationItem.isActive.isTrue
) )
.orderBy(audioContentCurationItem.orders.asc())
.fetch() .fetch()
} }
} }

View File

@ -150,4 +150,19 @@ class AdminContentCurationService(
audioContentCurationItem?.isActive = false audioContentCurationItem?.isActive = false
} }
@Transactional
fun updateItemInCurationOrders(request: UpdateCurationItemOrdersRequest) {
val ids = request.itemIds
for (index in ids.indices) {
val item = contentCurationItemRepository.findByCurationIdAndItemId(
curationId = request.curationId,
itemId = ids[index]
)
if (item != null) {
item.orders = index + 1
}
}
}
} }

View File

@ -0,0 +1,6 @@
package kr.co.vividnext.sodalive.admin.content.curation
data class UpdateCurationItemOrdersRequest(
val curationId: Long,
val itemIds: List<Long>
)

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,84 @@
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>
fun isExistsTag(tag: String): Boolean
}
@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()
}
override fun isExistsTag(tag: String): Boolean {
return queryFactory
.select(contentHashTagCuration.id)
.from(contentHashTagCuration)
.where(
contentHashTagCuration.tag.eq(tag),
contentHashTagCuration.isActive.isTrue
)
.fetch().isNotEmpty()
}
}

View File

@ -0,0 +1,133 @@
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) {
var tag = request.tag.trim()
if (!tag.startsWith("#")) {
tag = "#$tag"
}
val isExists = repository.isExistsTag(tag = tag)
if (isExists) {
throw SodaException("이미 등록된 태그 입니다.")
}
repository.save(
ContentHashTagCuration(
tag = tag,
isAdult = request.isAdult
)
)
}
@Transactional
fun updateContentHashTagCuration(request: UpdateContentHashTagCurationRequest) {
val hashTagCuration = repository.findByIdOrNull(id = request.id)
?: throw SodaException("잘못된 요청입니다.")
if (request.tag != null) {
var tag = request.tag.trim()
if (!tag.startsWith("#")) {
tag = "#$tag"
}
hashTagCuration.tag = 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
item.isActive = true
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

@ -32,6 +32,7 @@ data class AudioContent(
var title: String, var title: String,
@Column(columnDefinition = "TEXT", nullable = false) @Column(columnDefinition = "TEXT", nullable = false)
var detail: String, var detail: String,
var playCount: Long = 0,
var price: Int = 0, var price: Int = 0,
var releaseDate: LocalDateTime? = null, var releaseDate: LocalDateTime? = null,
val limited: Int? = null, val limited: Int? = null,

View File

@ -81,6 +81,12 @@ interface AudioContentQueryRepository {
limit: Long = 20 limit: Long = 20
): List<GetAudioContentMainItem> ): List<GetAudioContentMainItem>
fun totalAlarmCountByTheme(
memberId: Long,
theme: List<String>,
isAdult: Boolean = false
): Int
fun totalCountByTheme( fun totalCountByTheme(
memberId: Long, memberId: Long,
theme: String = "", theme: String = "",
@ -486,6 +492,39 @@ class AudioContentQueryRepositoryImpl(
.fetch() .fetch()
} }
override fun totalAlarmCountByTheme(memberId: Long, theme: List<String>, isAdult: Boolean): Int {
val blockMemberCondition = blockMember.member.id.eq(member.id)
.and(blockMember.isActive.isTrue)
.and(blockMember.blockedMember.id.eq(memberId))
var where = audioContent.isActive.isTrue
.and(audioContent.duration.isNotNull)
.and(
audioContent.releaseDate.isNull
.or(audioContent.releaseDate.loe(LocalDateTime.now()))
.or(audioContent.member.id.eq(memberId))
)
.and(blockMember.id.isNull)
if (!isAdult) {
where = where.and(audioContent.isAdult.isFalse)
}
if (theme.isNotEmpty()) {
where = where.and(audioContentTheme.theme.`in`(theme))
}
return queryFactory
.select(audioContent.id)
.from(audioContent)
.innerJoin(audioContent.member, member)
.innerJoin(audioContent.theme, audioContentTheme)
.leftJoin(blockMember).on(blockMemberCondition)
.where(where)
.fetch()
.size
}
override fun totalCountByTheme(memberId: Long, theme: String, isAdult: Boolean, contentType: ContentType): Int { override fun totalCountByTheme(memberId: Long, theme: String, isAdult: Boolean, contentType: ContentType): Int {
var where = audioContent.isActive.isTrue var where = audioContent.isActive.isTrue
.and(audioContent.duration.isNotNull) .and(audioContent.duration.isNotNull)
@ -659,6 +698,7 @@ class AudioContentQueryRepositoryImpl(
override fun getAudioContentMainBannerList(isAdult: Boolean): List<AudioContentBanner> { override fun getAudioContentMainBannerList(isAdult: Boolean): List<AudioContentBanner> {
var where = audioContentBanner.isActive.isTrue var where = audioContentBanner.isActive.isTrue
.and(audioContentBanner.tab.isNull)
if (!isAdult) { if (!isAdult) {
where = where.and(audioContentBanner.isAdult.isFalse) where = where.and(audioContentBanner.isAdult.isFalse)
@ -666,6 +706,7 @@ class AudioContentQueryRepositoryImpl(
return queryFactory return queryFactory
.selectFrom(audioContentBanner) .selectFrom(audioContentBanner)
.leftJoin(audioContentBanner.tab, audioContentMainTab)
.leftJoin(audioContentBanner.event, event) .leftJoin(audioContentBanner.event, event)
.leftJoin(audioContentBanner.creator, member) .leftJoin(audioContentBanner.creator, member)
.where(where) .where(where)

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
}

View File

@ -31,6 +31,7 @@ class AudioContentMainTabRepository(
val where = member.isActive.isTrue val where = member.isActive.isTrue
.and(member.role.eq(MemberRole.CREATOR)) .and(member.role.eq(MemberRole.CREATOR))
.and(audioContent.isActive.isTrue) .and(audioContent.isActive.isTrue)
.and(audioContent.price.gt(0))
.and(audioContent.duration.isNotNull) .and(audioContent.duration.isNotNull)
.and(audioContent.limited.isNull) .and(audioContent.limited.isNull)
.and(audioContentTheme.isActive.isTrue) .and(audioContentTheme.isActive.isTrue)
@ -59,4 +60,39 @@ class AudioContentMainTabRepository(
.limit(20) .limit(20)
.fetch() .fetch()
} }
fun findCreatorWithHasFreeContent(memberId: Long, minCount: Int): List<ContentCreatorResponse> {
val blockMemberCondition = blockMember.member.id.eq(member.id)
.and(blockMember.isActive.isTrue)
.and(blockMember.blockedMember.id.eq(memberId))
val where = member.isActive.isTrue
.and(member.role.eq(MemberRole.CREATOR))
.and(audioContent.isActive.isTrue)
.and(audioContent.price.loe(0))
.and(audioContent.duration.isNotNull)
.and(audioContent.limited.isNull)
.and(blockMember.id.isNull)
return queryFactory
.select(
QContentCreatorResponse(
member.id,
member.nickname,
member.profileImage.prepend("/").prepend(imageHost)
)
)
.from(member)
.innerJoin(audioContent).on(member.id.eq(audioContent.member.id))
.leftJoin(blockMember).on(blockMemberCondition)
.where(where)
.groupBy(member.id)
.having(audioContent.id.count().goe(minCount))
.orderBy(
Expressions.numberTemplate(Double::class.java, "function('rand')").asc()
)
.offset(0)
.limit(20)
.fetch()
}
} }

View File

@ -1,8 +0,0 @@
package kr.co.vividnext.sodalive.content.main.tab
import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
data class GetPopularContentByCreatorResponse(
val salesRankContentList: List<GetAudioContentRankingItem>,
val salesCountRankContentList: List<GetAudioContentRankingItem>
)

View File

@ -1,7 +1,7 @@
package kr.co.vividnext.sodalive.content.main.tab.alarm package kr.co.vividnext.sodalive.content.main.tab.alarm
import kr.co.vividnext.sodalive.content.AudioContentRepository import kr.co.vividnext.sodalive.content.AudioContentRepository
import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.content.main.GetNewContentAllResponse
import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService
import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository
import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
@ -86,7 +86,7 @@ class AudioContentMainTabAlarmService(
member: Member, member: Member,
offset: Long, offset: Long,
limit: Long limit: Long
): List<GetAudioContentMainItem> { ): GetNewContentAllResponse {
val alarmThemeList = if (theme.isNotBlank()) { val alarmThemeList = if (theme.isNotBlank()) {
listOf(theme) listOf(theme)
} else { } else {
@ -96,12 +96,20 @@ class AudioContentMainTabAlarmService(
val memberId = member.id!! val memberId = member.id!!
val isAdult = member.auth != null val isAdult = member.auth != null
return contentRepository.findAlarmContentByTheme( val totalCount = contentRepository.totalAlarmCountByTheme(
memberId = memberId,
theme = alarmThemeList,
isAdult = isAdult
)
val items = contentRepository.findAlarmContentByTheme(
memberId = memberId, memberId = memberId,
theme = alarmThemeList, theme = alarmThemeList,
isAdult = isAdult, isAdult = isAdult,
offset = offset, offset = offset,
limit = limit limit = limit
) )
return GetNewContentAllResponse(totalCount, items)
} }
} }

View File

@ -2,11 +2,11 @@ package kr.co.vividnext.sodalive.content.main.tab.asmr
import kr.co.vividnext.sodalive.content.AudioContentRepository import kr.co.vividnext.sodalive.content.AudioContentRepository
import kr.co.vividnext.sodalive.content.ContentType import kr.co.vividnext.sodalive.content.ContentType
import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService
import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository
import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTabRepository import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTabRepository
import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
import kr.co.vividnext.sodalive.content.main.tab.GetPopularContentByCreatorResponse
import kr.co.vividnext.sodalive.event.EventService import kr.co.vividnext.sodalive.event.EventService
import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.rank.RankingService import kr.co.vividnext.sodalive.rank.RankingService
@ -68,18 +68,8 @@ class AudioContentMainTabAsmrService(
minCount = 4 minCount = 4
) )
val salesRankContentList = if (creatorList.isNotEmpty()) {
rankingService.fetchCreatorContentBySalesTop2(
creatorId = creatorList[0].creatorId,
isAdult = isAdult,
theme = theme
)
} else {
emptyList()
}
val salesCountRankContentList = if (creatorList.isNotEmpty()) { val salesCountRankContentList = if (creatorList.isNotEmpty()) {
rankingService.fetchCreatorContentBySalesCountTop2( rankingService.fetchCreatorContentBySalesCountTop4(
creatorId = creatorList[0].creatorId, creatorId = creatorList[0].creatorId,
isAdult = isAdult, isAdult = isAdult,
theme = theme theme = theme
@ -107,30 +97,17 @@ class AudioContentMainTabAsmrService(
newAsmrContentList = newAsmrContentList, newAsmrContentList = newAsmrContentList,
rankAsmrContentList = rankAsmrContentList, rankAsmrContentList = rankAsmrContentList,
creatorList = creatorList, creatorList = creatorList,
salesRankContentList = salesRankContentList,
salesCountRankContentList = salesCountRankContentList, salesCountRankContentList = salesCountRankContentList,
eventBannerList = eventBannerList, eventBannerList = eventBannerList,
curationList = curationList curationList = curationList
) )
} }
fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): GetPopularContentByCreatorResponse { fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> {
val theme = "ASMR" return rankingService.fetchCreatorContentBySalesCountTop4(
val salesRankContentList = rankingService.fetchCreatorContentBySalesTop2(
creatorId = creatorId, creatorId = creatorId,
isAdult = isAdult, isAdult = isAdult,
theme = theme theme = "ASMR"
)
val salesCountRankContentList = rankingService.fetchCreatorContentBySalesCountTop2(
creatorId = creatorId,
isAdult = isAdult,
theme = theme
)
return GetPopularContentByCreatorResponse(
salesRankContentList = salesRankContentList,
salesCountRankContentList = salesCountRankContentList
) )
} }
} }

View File

@ -13,7 +13,6 @@ data class GetContentMainTabAsmrResponse(
val newAsmrContentList: List<GetAudioContentMainItem>, val newAsmrContentList: List<GetAudioContentMainItem>,
val rankAsmrContentList: List<GetAudioContentRankingItem>, val rankAsmrContentList: List<GetAudioContentRankingItem>,
val creatorList: List<ContentCreatorResponse>, val creatorList: List<ContentCreatorResponse>,
val salesRankContentList: List<GetAudioContentRankingItem>,
val salesCountRankContentList: List<GetAudioContentRankingItem>, val salesCountRankContentList: List<GetAudioContentRankingItem>,
val eventBannerList: GetEventResponse, val eventBannerList: GetEventResponse,
val curationList: List<GetContentCurationResponse> val curationList: List<GetContentCurationResponse>

View File

@ -82,4 +82,15 @@ class AudioContentMainTabContentController(private val service: AudioContentMain
) )
) )
} }
@GetMapping("/recommend-content-by-tag")
fun getRecommendedContentByTag(
@RequestParam tag: String,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
ApiResponse.ok(
service.getRecommendedContentByTag(memberId = member.id!!, tag = tag)
)
}
} }

View File

@ -5,7 +5,8 @@ import kr.co.vividnext.sodalive.content.ContentType
import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService
import kr.co.vividnext.sodalive.content.main.tab.GetPopularContentByCreatorResponse import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository
import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository
import kr.co.vividnext.sodalive.event.EventService import kr.co.vividnext.sodalive.event.EventService
import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.Member
@ -19,7 +20,9 @@ class AudioContentMainTabContentService(
private val audioContentRepository: AudioContentRepository, private val audioContentRepository: AudioContentRepository,
private val audioContentThemeRepository: AudioContentThemeQueryRepository, private val audioContentThemeRepository: AudioContentThemeQueryRepository,
private val rankingService: RankingService, private val rankingService: RankingService,
private val eventService: EventService private val eventService: EventService,
private val tagCurationService: ContentMainTabTagCurationService,
private val curationRepository: AudioContentCurationQueryRepository
) { ) {
fun fetchData( fun fetchData(
isAdultContentVisible: Boolean, isAdultContentVisible: Boolean,
@ -28,10 +31,11 @@ class AudioContentMainTabContentService(
): GetContentMainTabContentResponse { ): GetContentMainTabContentResponse {
val memberId = member.id!! val memberId = member.id!!
val isAdult = member.auth != null val isAdult = member.auth != null
val tabId = 3L
// 단편 배너 // 단편 배너
val contentBannerList = bannerService.getBannerList( val contentBannerList = bannerService.getBannerList(
tabId = 3, tabId = tabId,
memberId = memberId, memberId = memberId,
isAdult = isAdult isAdult = isAdult
) )
@ -69,14 +73,14 @@ class AudioContentMainTabContentService(
// 이벤트 배너 // 이벤트 배너
val eventBannerList = eventService.getEventList(isAdult = isAdult) val eventBannerList = eventService.getEventList(isAdult = isAdult)
val contentRankCreatorList = rankingService.fetchCreatorByContentRevenueRankTop20( val contentRankCreatorList = rankingService.fetchCreatorBySellContentCountRankTop20(
memberId = member.id!!, memberId = member.id!!,
startDate = dailyRankingStartDate.minusDays(1), startDate = dailyRankingStartDate.minusDays(1),
endDate = dailyRankingEndDate endDate = dailyRankingEndDate
) )
val salesRankContentList = if (contentRankCreatorList.isNotEmpty()) { val salesCountRankContentList = if (contentRankCreatorList.isNotEmpty()) {
rankingService.fetchCreatorContentBySalesTop2( rankingService.fetchCreatorContentBySalesCountTop4(
creatorId = contentRankCreatorList[0].creatorId, creatorId = contentRankCreatorList[0].creatorId,
isAdult = member.auth != null isAdult = member.auth != null
) )
@ -84,15 +88,30 @@ class AudioContentMainTabContentService(
emptyList() emptyList()
} }
val salesCountRankContentList = if (contentRankCreatorList.isNotEmpty()) { val tagList = if (isAdult) {
rankingService.fetchCreatorContentBySalesCountTop2( tagCurationService.getTagList(isAdult = isAdult)
creatorId = contentRankCreatorList[0].creatorId,
isAdult = member.auth != null
)
} else { } else {
emptyList() emptyList()
} }
val tagCurationContentList = if (tagList.isNotEmpty()) {
tagCurationService.getTagCurationContentList(memberId = memberId, tag = tagList[0])
} else {
emptyList()
}
val curationList = curationRepository.findByContentMainTabId(tabId = tabId, isAdult = isAdult)
.map {
GetContentCurationResponse(
title = it.title,
items = audioContentRepository.findAudioContentByCurationIdV2(
curationId = it.id!!,
memberId = memberId,
isAdult = isAdult
)
)
}
return GetContentMainTabContentResponse( return GetContentMainTabContentResponse(
bannerList = contentBannerList, bannerList = contentBannerList,
contentThemeList = themeOfContentList, contentThemeList = themeOfContentList,
@ -100,9 +119,11 @@ class AudioContentMainTabContentService(
rankSortTypeList = listOf("매출", "댓글", "좋아요"), rankSortTypeList = listOf("매출", "댓글", "좋아요"),
rankContentList = rankContentList, rankContentList = rankContentList,
contentRankCreatorList = contentRankCreatorList, contentRankCreatorList = contentRankCreatorList,
salesRankContentList = salesRankContentList,
salesCountRankContentList = salesCountRankContentList, salesCountRankContentList = salesCountRankContentList,
eventBannerList = eventBannerList eventBannerList = eventBannerList,
tagList = tagList,
tagCurationContentList = tagCurationContentList,
curationList = curationList
) )
} }
@ -144,20 +165,14 @@ class AudioContentMainTabContentService(
) )
} }
fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): GetPopularContentByCreatorResponse { fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> {
val salesRankContentList = rankingService.fetchCreatorContentBySalesTop2( return rankingService.fetchCreatorContentBySalesCountTop4(
creatorId = creatorId, creatorId = creatorId,
isAdult = isAdult isAdult = isAdult
) )
}
val salesCountRankContentList = rankingService.fetchCreatorContentBySalesCountTop2(
creatorId = creatorId, fun getRecommendedContentByTag(memberId: Long, tag: String): List<GetAudioContentMainItem> {
isAdult = isAdult return tagCurationService.getTagCurationContentList(memberId = memberId, tag = tag)
)
return GetPopularContentByCreatorResponse(
salesRankContentList = salesRankContentList,
salesCountRankContentList = salesCountRankContentList
)
} }
} }

View File

@ -0,0 +1,79 @@
package kr.co.vividnext.sodalive.content.main.tab.content
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.content.main.QGetAudioContentMainItem
import kr.co.vividnext.sodalive.content.main.curation.tag.QContentHashTagCuration.contentHashTagCuration
import kr.co.vividnext.sodalive.content.main.curation.tag.QContentHashTagCurationItem.contentHashTagCurationItem
import kr.co.vividnext.sodalive.member.QMember.member
import kr.co.vividnext.sodalive.member.block.QBlockMember.blockMember
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Repository
@Repository
class ContentMainTabTagCurationRepository(
private val queryFactory: JPAQueryFactory,
@Value("\${cloud.aws.cloud-front.host}")
private val imageHost: String
) {
fun getTagList(isAdult: Boolean): List<String> {
var where = contentHashTagCuration.isActive.isTrue
.and(audioContent.isActive.isTrue)
.and(audioContent.duration.isNotNull)
.and(audioContent.limited.isNull)
.and(contentHashTagCurationItem.isActive.isTrue)
if (!isAdult) {
where = where.and(contentHashTagCuration.isAdult.isFalse)
}
return queryFactory
.select(contentHashTagCuration.tag)
.from(contentHashTagCuration)
.innerJoin(contentHashTagCurationItem)
.on(contentHashTagCurationItem.curation.id.eq(contentHashTagCuration.id))
.innerJoin(contentHashTagCurationItem.content, audioContent)
.where(where)
.groupBy(contentHashTagCuration.id)
.having(contentHashTagCurationItem.id.countDistinct().gt(0))
.orderBy(contentHashTagCuration.orders.asc())
.fetch()
}
fun getTagCurationContentList(memberId: Long, tag: String): List<GetAudioContentMainItem> {
val blockMemberCondition = blockMember.member.id.eq(member.id)
.and(blockMember.isActive.isTrue)
.and(blockMember.blockedMember.id.eq(memberId))
val where = audioContent.isActive.isTrue
.and(audioContent.duration.isNotNull)
.and(audioContent.limited.isNull)
.and(blockMember.id.isNull)
.and(contentHashTagCurationItem.isActive.isTrue)
.and(contentHashTagCuration.tag.eq(tag))
return queryFactory
.select(
QGetAudioContentMainItem(
audioContent.id,
audioContent.coverImage.prepend("/").prepend(imageHost),
audioContent.title,
member.id,
member.profileImage.prepend("/").prepend(imageHost),
member.nickname,
audioContent.price,
audioContent.duration
)
)
.from(contentHashTagCurationItem)
.innerJoin(contentHashTagCurationItem.curation, contentHashTagCuration)
.innerJoin(contentHashTagCurationItem.content, audioContent)
.innerJoin(audioContent.member, member)
.leftJoin(blockMember).on(blockMemberCondition)
.where(where)
.orderBy(contentHashTagCurationItem.orders.asc())
.fetch()
}
}

View File

@ -0,0 +1,15 @@
package kr.co.vividnext.sodalive.content.main.tab.content
import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
import org.springframework.stereotype.Service
@Service
class ContentMainTabTagCurationService(private val repository: ContentMainTabTagCurationRepository) {
fun getTagList(isAdult: Boolean): List<String> {
return repository.getTagList(isAdult = isAdult)
}
fun getTagCurationContentList(memberId: Long, tag: String): List<GetAudioContentMainItem> {
return repository.getTagCurationContentList(memberId = memberId, tag = tag)
}
}

View File

@ -4,6 +4,7 @@ import kr.co.vividnext.sodalive.content.main.ContentCreatorResponse
import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse
import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
import kr.co.vividnext.sodalive.event.GetEventResponse import kr.co.vividnext.sodalive.event.GetEventResponse
data class GetContentMainTabContentResponse( data class GetContentMainTabContentResponse(
@ -14,7 +15,9 @@ data class GetContentMainTabContentResponse(
val rankSortTypeList: List<String>, val rankSortTypeList: List<String>,
val rankContentList: List<GetAudioContentRankingItem>, val rankContentList: List<GetAudioContentRankingItem>,
val contentRankCreatorList: List<ContentCreatorResponse>, val contentRankCreatorList: List<ContentCreatorResponse>,
val salesRankContentList: List<GetAudioContentRankingItem>,
val salesCountRankContentList: List<GetAudioContentRankingItem>, val salesCountRankContentList: List<GetAudioContentRankingItem>,
val eventBannerList: GetEventResponse val eventBannerList: GetEventResponse,
val tagList: List<String>,
val tagCurationContentList: List<GetAudioContentMainItem>,
val curationList: List<GetContentCurationResponse>
) )

View File

@ -60,4 +60,19 @@ class AudioContentMainTabFreeController(private val service: AudioContentMainTab
) )
) )
} }
@GetMapping("/popular-content-by-creator")
fun getPopularContentByCreator(
@RequestParam creatorId: Long,
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
ApiResponse.ok(
service.getPopularContentByCreator(
creatorId = creatorId,
isAdult = member.auth != null
)
)
}
} }

View File

@ -3,20 +3,25 @@ package kr.co.vividnext.sodalive.content.main.tab.free
import kr.co.vividnext.sodalive.content.AudioContentRepository import kr.co.vividnext.sodalive.content.AudioContentRepository
import kr.co.vividnext.sodalive.content.ContentType import kr.co.vividnext.sodalive.content.ContentType
import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService
import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository
import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTabRepository
import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
import kr.co.vividnext.sodalive.content.main.tab.RecommendSeriesRepository import kr.co.vividnext.sodalive.content.main.tab.RecommendSeriesRepository
import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository
import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.rank.RankingService
import org.springframework.stereotype.Service import org.springframework.stereotype.Service
@Service @Service
class AudioContentMainTabFreeService( class AudioContentMainTabFreeService(
private val repository: AudioContentMainTabRepository,
private val bannerService: AudioContentBannerService, private val bannerService: AudioContentBannerService,
private val recommendSeriesRepository: RecommendSeriesRepository, private val recommendSeriesRepository: RecommendSeriesRepository,
private val curationRepository: AudioContentCurationQueryRepository, private val curationRepository: AudioContentCurationQueryRepository,
private val contentRepository: AudioContentRepository, private val contentRepository: AudioContentRepository,
private val rankingService: RankingService,
private val audioContentRepository: AudioContentRepository, private val audioContentRepository: AudioContentRepository,
private val audioContentThemeRepository: AudioContentThemeQueryRepository private val audioContentThemeRepository: AudioContentThemeQueryRepository
) { ) {
@ -53,7 +58,6 @@ class AudioContentMainTabFreeService(
val newFreeContentList = if (themeList.isNotEmpty()) { val newFreeContentList = if (themeList.isNotEmpty()) {
audioContentRepository.findByTheme( audioContentRepository.findByTheme(
memberId = member.id!!, memberId = member.id!!,
theme = themeList[0],
isAdult = member.auth != null, isAdult = member.auth != null,
contentType = ContentType.ALL, contentType = ContentType.ALL,
offset = 0, offset = 0,
@ -64,6 +68,13 @@ class AudioContentMainTabFreeService(
emptyList() emptyList()
} }
val creatorList = repository.findCreatorWithHasFreeContent(memberId, 4)
val playCountRankContentList = if (creatorList.isNotEmpty()) {
rankingService.fetchFreeContentByCreatorIdTop4(creatorList[0].creatorId, isAdult)
} else {
emptyList()
}
val curationList = curationRepository.findByContentMainTabId(tabId = tabId, isAdult = isAdult) val curationList = curationRepository.findByContentMainTabId(tabId = tabId, isAdult = isAdult)
.filter { it.title != "크리에이터 소개" } .filter { it.title != "크리에이터 소개" }
.map { .map {
@ -87,6 +98,8 @@ class AudioContentMainTabFreeService(
recommendSeriesList = recommendSeriesList, recommendSeriesList = recommendSeriesList,
themeList = themeList, themeList = themeList,
newFreeContentList = newFreeContentList, newFreeContentList = newFreeContentList,
creatorList = creatorList,
playCountRankContentList = playCountRankContentList,
curationList = curationList curationList = curationList
) )
} }
@ -132,4 +145,8 @@ class AudioContentMainTabFreeService(
isFree = true isFree = true
) )
} }
fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> {
return rankingService.fetchFreeContentByCreatorIdTop4(creatorId, isAdult)
}
} }

View File

@ -1,6 +1,8 @@
package kr.co.vividnext.sodalive.content.main.tab.free package kr.co.vividnext.sodalive.content.main.tab.free
import kr.co.vividnext.sodalive.content.main.ContentCreatorResponse
import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem import kr.co.vividnext.sodalive.content.main.GetAudioContentMainItem
import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse import kr.co.vividnext.sodalive.content.main.banner.GetAudioContentBannerResponse
import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
import kr.co.vividnext.sodalive.content.main.tab.GetRecommendSeriesListResponse import kr.co.vividnext.sodalive.content.main.tab.GetRecommendSeriesListResponse
@ -12,5 +14,7 @@ data class GetContentMainTabFreeResponse(
val recommendSeriesList: List<GetRecommendSeriesListResponse>, val recommendSeriesList: List<GetRecommendSeriesListResponse>,
val themeList: List<String>, val themeList: List<String>,
val newFreeContentList: List<GetAudioContentMainItem>, val newFreeContentList: List<GetAudioContentMainItem>,
val creatorList: List<ContentCreatorResponse>,
val playCountRankContentList: List<GetAudioContentRankingItem>,
val curationList: List<GetContentCurationResponse> val curationList: List<GetContentCurationResponse>
) )

View File

@ -1,7 +1,7 @@
package kr.co.vividnext.sodalive.content.main.tab.home package kr.co.vividnext.sodalive.content.main.tab.home
import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService
import kr.co.vividnext.sodalive.content.main.tab.GetPopularContentByCreatorResponse
import kr.co.vividnext.sodalive.event.EventService import kr.co.vividnext.sodalive.event.EventService
import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.notice.ServiceNoticeService import kr.co.vividnext.sodalive.notice.ServiceNoticeService
@ -74,27 +74,17 @@ class AudioContentMainTabHomeService(
/* 채널별 인기 콘텐츠 /* 채널별 인기 콘텐츠
* - 콘텐츠를 4 이상 등록한 채널 * - 콘텐츠를 4 이상 등록한 채널
* - 주간 콘텐츠 매출 Top 20 채널 * - 주간 콘텐츠 판매 개수 Top 20 채널
* - 해당 채널의 누적 매출 Top 2 * - 해당 채널의 누적 판매 개수 Top 4
* - 해당 채널의 누적 판매 개수 Top 2
*/ */
val contentRankCreatorList = rankingService.fetchCreatorByContentRevenueRankTop20( val contentRankCreatorList = rankingService.fetchCreatorBySellContentCountRankTop20(
memberId = member.id!!, memberId = member.id!!,
startDate = startDate.minusDays(1), startDate = startDate.minusDays(1),
endDate = endDate endDate = endDate
) )
val salesRankContentList = if (contentRankCreatorList.isNotEmpty()) {
rankingService.fetchCreatorContentBySalesTop2(
creatorId = contentRankCreatorList[0].creatorId,
isAdult = member.auth != null
)
} else {
emptyList()
}
val salesCountRankContentList = if (contentRankCreatorList.isNotEmpty()) { val salesCountRankContentList = if (contentRankCreatorList.isNotEmpty()) {
rankingService.fetchCreatorContentBySalesCountTop2( rankingService.fetchCreatorContentBySalesCountTop4(
creatorId = contentRankCreatorList[0].creatorId, creatorId = contentRankCreatorList[0].creatorId,
isAdult = member.auth != null isAdult = member.auth != null
) )
@ -111,25 +101,14 @@ class AudioContentMainTabHomeService(
rankContentList = rankContentList, rankContentList = rankContentList,
eventBannerList = eventBannerList, eventBannerList = eventBannerList,
contentRankCreatorList = contentRankCreatorList, contentRankCreatorList = contentRankCreatorList,
salesRankContentList = salesRankContentList,
salesCountRankContentList = salesCountRankContentList salesCountRankContentList = salesCountRankContentList
) )
} }
fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): GetPopularContentByCreatorResponse { fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> {
val salesRankContentList = rankingService.fetchCreatorContentBySalesTop2( return rankingService.fetchCreatorContentBySalesCountTop4(
creatorId = creatorId, creatorId = creatorId,
isAdult = isAdult isAdult = isAdult
) )
val salesCountRankContentList = rankingService.fetchCreatorContentBySalesCountTop2(
creatorId = creatorId,
isAdult = isAdult
)
return GetPopularContentByCreatorResponse(
salesRankContentList = salesRankContentList,
salesCountRankContentList = salesCountRankContentList
)
} }
} }

View File

@ -18,6 +18,5 @@ data class GetContentMainTabHomeResponse(
val rankContentList: List<GetAudioContentRankingItem>, val rankContentList: List<GetAudioContentRankingItem>,
val eventBannerList: GetEventResponse, val eventBannerList: GetEventResponse,
val contentRankCreatorList: List<ContentCreatorResponse>, val contentRankCreatorList: List<ContentCreatorResponse>,
val salesRankContentList: List<GetAudioContentRankingItem>,
val salesCountRankContentList: List<GetAudioContentRankingItem> val salesCountRankContentList: List<GetAudioContentRankingItem>
) )

View File

@ -2,11 +2,11 @@ package kr.co.vividnext.sodalive.content.main.tab.replay
import kr.co.vividnext.sodalive.content.AudioContentRepository import kr.co.vividnext.sodalive.content.AudioContentRepository
import kr.co.vividnext.sodalive.content.ContentType import kr.co.vividnext.sodalive.content.ContentType
import kr.co.vividnext.sodalive.content.main.GetAudioContentRankingItem
import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerService
import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository import kr.co.vividnext.sodalive.content.main.curation.AudioContentCurationQueryRepository
import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTabRepository import kr.co.vividnext.sodalive.content.main.tab.AudioContentMainTabRepository
import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse import kr.co.vividnext.sodalive.content.main.tab.GetContentCurationResponse
import kr.co.vividnext.sodalive.content.main.tab.GetPopularContentByCreatorResponse
import kr.co.vividnext.sodalive.event.EventService import kr.co.vividnext.sodalive.event.EventService
import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.Member
import kr.co.vividnext.sodalive.rank.RankingService import kr.co.vividnext.sodalive.rank.RankingService
@ -68,18 +68,8 @@ class AudioContentMainTabLiveReplayService(
minCount = 4 minCount = 4
) )
val salesRankContentList = if (creatorList.isNotEmpty()) {
rankingService.fetchCreatorContentBySalesTop2(
creatorId = creatorList[0].creatorId,
isAdult = isAdult,
theme = theme
)
} else {
emptyList()
}
val salesCountRankContentList = if (creatorList.isNotEmpty()) { val salesCountRankContentList = if (creatorList.isNotEmpty()) {
rankingService.fetchCreatorContentBySalesCountTop2( rankingService.fetchCreatorContentBySalesCountTop4(
creatorId = creatorList[0].creatorId, creatorId = creatorList[0].creatorId,
isAdult = isAdult, isAdult = isAdult,
theme = theme theme = theme
@ -107,30 +97,17 @@ class AudioContentMainTabLiveReplayService(
newLiveReplayContentList = newLiveReplayContentList, newLiveReplayContentList = newLiveReplayContentList,
rankLiveReplayContentList = rankLiveReplayContentList, rankLiveReplayContentList = rankLiveReplayContentList,
creatorList = creatorList, creatorList = creatorList,
salesRankContentList = salesRankContentList,
salesCountRankContentList = salesCountRankContentList, salesCountRankContentList = salesCountRankContentList,
eventBannerList = eventBannerList, eventBannerList = eventBannerList,
curationList = curationList curationList = curationList
) )
} }
fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): GetPopularContentByCreatorResponse { fun getPopularContentByCreator(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> {
val theme = "다시듣기" return rankingService.fetchCreatorContentBySalesCountTop4(
val salesRankContentList = rankingService.fetchCreatorContentBySalesTop2(
creatorId = creatorId, creatorId = creatorId,
isAdult = isAdult, isAdult = isAdult,
theme = theme theme = "다시듣기"
)
val salesCountRankContentList = rankingService.fetchCreatorContentBySalesCountTop2(
creatorId = creatorId,
isAdult = isAdult,
theme = theme
)
return GetPopularContentByCreatorResponse(
salesRankContentList = salesRankContentList,
salesCountRankContentList = salesCountRankContentList
) )
} }
} }

View File

@ -13,7 +13,6 @@ data class GetContentMainTabLiveReplayResponse(
val newLiveReplayContentList: List<GetAudioContentMainItem>, val newLiveReplayContentList: List<GetAudioContentMainItem>,
val rankLiveReplayContentList: List<GetAudioContentRankingItem>, val rankLiveReplayContentList: List<GetAudioContentRankingItem>,
val creatorList: List<ContentCreatorResponse>, val creatorList: List<ContentCreatorResponse>,
val salesRankContentList: List<GetAudioContentRankingItem>,
val salesCountRankContentList: List<GetAudioContentRankingItem>, val salesCountRankContentList: List<GetAudioContentRankingItem>,
val eventBannerList: GetEventResponse, val eventBannerList: GetEventResponse,
val curationList: List<GetContentCurationResponse> val curationList: List<GetContentCurationResponse>

View File

@ -336,7 +336,7 @@ class RankingRepository(
.fetch() .fetch()
} }
fun fetchCreatorByContentRevenueRankTop20( fun fetchCreatorBySellContentCountRankTop20(
memberId: Long, memberId: Long,
startDate: LocalDateTime, startDate: LocalDateTime,
endDate: LocalDateTime endDate: LocalDateTime
@ -350,9 +350,12 @@ class RankingRepository(
.and(order.createdAt.goe(startDate)) .and(order.createdAt.goe(startDate))
.and(order.createdAt.lt(startDate)) .and(order.createdAt.lt(startDate))
val where = member.isActive.isTrue val memberCondition = member.isActive.isTrue
.and(member.role.eq(MemberRole.CREATOR)) .and(member.role.eq(MemberRole.CREATOR))
.and(audioContent.isActive.isTrue) .and(member.id.eq(audioContent.member.id))
val where = audioContent.isActive.isTrue
.and(audioContent.price.gt(0))
.and(audioContent.duration.isNotNull) .and(audioContent.duration.isNotNull)
.and(audioContent.limited.isNull) .and(audioContent.limited.isNull)
.and(blockMember.id.isNull) .and(blockMember.id.isNull)
@ -365,71 +368,20 @@ class RankingRepository(
member.profileImage.prepend("/").prepend(imageHost) member.profileImage.prepend("/").prepend(imageHost)
) )
) )
.from(member) .from(audioContent)
.innerJoin(audioContent).on(member.id.eq(audioContent.member.id)) .innerJoin(member).on(memberCondition)
.leftJoin(order).on(ordersCondition) .leftJoin(order).on(ordersCondition)
.leftJoin(blockMember).on(blockMemberCondition) .leftJoin(blockMember).on(blockMemberCondition)
.where(where) .where(where)
.groupBy(member.id) .groupBy(member.id)
.having(audioContent.id.count().goe(4)) .having(audioContent.id.count().goe(4))
.orderBy( .orderBy(order.id.count().desc(), member.id.desc())
order.can.sum().desc(),
Expressions.numberTemplate(Double::class.java, "function('rand')").asc()
)
.offset(0) .offset(0)
.limit(20) .limit(20)
.fetch() .fetch()
} }
fun fetchCreatorContentBySalesTop2( fun fetchCreatorContentBySalesCountTop4(
creatorId: Long,
isAdult: Boolean,
theme: String
): List<GetAudioContentRankingItem> {
var where = member.isActive.isTrue
.and(member.role.eq(MemberRole.CREATOR))
.and(audioContent.isActive.isTrue)
.and(audioContent.duration.isNotNull)
.and(audioContent.limited.isNull)
.and(audioContentTheme.isActive.isTrue)
.and(order.isActive.isTrue)
.and(member.id.eq(creatorId))
if (!isAdult) {
where = where.and(series.isAdult.isFalse)
}
if (theme.isNotBlank()) {
where = where.and(audioContentTheme.theme.eq(theme))
}
return queryFactory
.select(
QGetAudioContentRankingItem(
audioContent.id,
audioContent.title,
audioContent.coverImage.prepend("/").prepend(imageHost),
audioContentTheme.theme,
audioContent.price,
audioContent.duration,
member.id,
member.nickname,
member.profileImage.prepend("/").prepend(imageHost)
)
)
.from(order)
.innerJoin(order.audioContent, audioContent)
.innerJoin(audioContent.theme, audioContentTheme)
.innerJoin(audioContent.member, member)
.where(where)
.groupBy(audioContent.id)
.orderBy(order.can.sum().desc())
.offset(0)
.limit(2)
.fetch()
}
fun fetchCreatorContentBySalesCountTop2(
creatorId: Long, creatorId: Long,
isAdult: Boolean, isAdult: Boolean,
theme: String theme: String
@ -473,7 +425,7 @@ class RankingRepository(
.groupBy(audioContent.id) .groupBy(audioContent.id)
.orderBy(order.id.count().desc()) .orderBy(order.id.count().desc())
.offset(0) .offset(0)
.limit(2) .limit(4)
.fetch() .fetch()
} }
@ -556,4 +508,41 @@ class RankingRepository(
.limit(10) .limit(10)
.fetch() .fetch()
} }
fun fetchFreeContentByCreatorIdTop4(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> {
var where = member.isActive.isTrue
.and(member.id.eq(creatorId))
.and(member.role.eq(MemberRole.CREATOR))
.and(audioContent.isActive.isTrue)
.and(audioContent.duration.isNotNull)
.and(audioContent.limited.isNull)
.and(audioContent.price.loe(0))
if (!isAdult) {
where = where.and(series.isAdult.isFalse)
}
return queryFactory
.select(
QGetAudioContentRankingItem(
audioContent.id,
audioContent.title,
audioContent.coverImage.prepend("/").prepend(imageHost),
audioContentTheme.theme,
audioContent.price,
audioContent.duration,
member.id,
member.nickname,
member.profileImage.prepend("/").prepend(imageHost)
)
)
.from(audioContent)
.innerJoin(audioContent.theme, audioContentTheme)
.innerJoin(audioContent.member, member)
.where(where)
.orderBy(audioContent.playCount.desc())
.offset(0)
.limit(4)
.fetch()
}
} }

View File

@ -181,28 +181,20 @@ class RankingService(
} }
} }
fun fetchCreatorByContentRevenueRankTop20( fun fetchCreatorBySellContentCountRankTop20(
memberId: Long, memberId: Long,
startDate: LocalDateTime, startDate: LocalDateTime,
endDate: LocalDateTime endDate: LocalDateTime
): List<ContentCreatorResponse> { ): List<ContentCreatorResponse> {
return repository.fetchCreatorByContentRevenueRankTop20(memberId, startDate, endDate) return repository.fetchCreatorBySellContentCountRankTop20(memberId, startDate, endDate)
} }
fun fetchCreatorContentBySalesTop2( fun fetchCreatorContentBySalesCountTop4(
creatorId: Long, creatorId: Long,
isAdult: Boolean, isAdult: Boolean,
theme: String = "" theme: String = ""
): List<GetAudioContentRankingItem> { ): List<GetAudioContentRankingItem> {
return repository.fetchCreatorContentBySalesTop2(creatorId, isAdult, theme) return repository.fetchCreatorContentBySalesCountTop4(creatorId, isAdult, theme)
}
fun fetchCreatorContentBySalesCountTop2(
creatorId: Long,
isAdult: Boolean,
theme: String = ""
): List<GetAudioContentRankingItem> {
return repository.fetchCreatorContentBySalesCountTop2(creatorId, isAdult, theme)
} }
fun fetchCreatorBySeriesRevenueRankTop20( fun fetchCreatorBySeriesRevenueRankTop20(
@ -217,4 +209,8 @@ class RankingService(
val seriesList = repository.fetchCreatorSeriesBySales(creatorId = creatorId, isAdult = isAdult) val seriesList = repository.fetchCreatorSeriesBySales(creatorId = creatorId, isAdult = isAdult)
return seriesToSeriesListItem(seriesList, isAdult) return seriesToSeriesListItem(seriesList, isAdult)
} }
fun fetchFreeContentByCreatorIdTop4(creatorId: Long, isAdult: Boolean): List<GetAudioContentRankingItem> {
return repository.fetchFreeContentByCreatorIdTop4(creatorId, isAdult)
}
} }