feat(explorer): 크리에이터 프로필에 최신/총/소장 콘텐츠 정보 추가

- ExplorerService.getCreatorProfile에서 다음 정보 계산/반환
  - 최신 오디오 콘텐츠 1개(`latestContent`)
  - 전체 콘텐츠 수(`totalContentCount`)
  - 조회 유저의 소장 콘텐츠 수(`ownedContentCount`)
- ExplorerQueryRepository.getOwnedContentCount 추가
  - 활성 KEEP 또는 유효한 RENTAL 주문 기준으로 distinct 카운트
- GetCreatorProfileResponse 스키마 확장
  - `latestContent`, `totalContentCount`, `ownedContentCount` 필드 추가
- AudioContentService.getLatestCreatorAudioContent 사용해 최신 콘텐츠 조회 로직 보강
  - 성인 콘텐츠 노출 여부 및 구매/대여 상태 반영
- OrderRepository의 주문 타입 조회 로직을 활용해 보유/대여 상태 표시

API 응답 필드가 추가되어 프로필 화면 구성 정보를 보강합니다. (호환성 유지)
This commit is contained in:
2025-10-14 15:35:15 +09:00
parent 88c3a84972
commit 59700493eb
6 changed files with 124 additions and 2 deletions

View File

@@ -10,6 +10,7 @@ import kr.co.vividnext.sodalive.can.use.QUseCanCalculate.useCanCalculate
import kr.co.vividnext.sodalive.can.use.UseCanCalculateStatus
import kr.co.vividnext.sodalive.common.SodaException
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
import kr.co.vividnext.sodalive.content.order.QOrder.order
import kr.co.vividnext.sodalive.explorer.QCreatorRanking.creatorRanking
import kr.co.vividnext.sodalive.explorer.follower.GetFollowerListDto
import kr.co.vividnext.sodalive.explorer.follower.QGetFollowerListDto
@@ -653,6 +654,28 @@ class ExplorerQueryRepository(
.fetchFirst()
}
fun getOwnedContentCount(creatorId: Long, memberId: Long): Long {
// 활성 주문 + 대여의 경우 유효기간 내 주문만 포함, 동일 콘텐츠 중복 구매는 1개로 카운트
return queryFactory
.select(audioContent.id)
.from(order)
.innerJoin(order.audioContent, audioContent)
.where(
order.isActive.isTrue,
order.member.id.eq(memberId),
audioContent.member.id.eq(creatorId),
order.type.eq(kr.co.vividnext.sodalive.content.order.OrderType.KEEP)
.or(
order.type.eq(kr.co.vividnext.sodalive.content.order.OrderType.RENTAL)
.and(order.endDate.after(LocalDateTime.now()))
)
)
.distinct()
.fetch()
.size
.toLong()
}
fun getVisibleDonationRank(creatorId: Long): Boolean {
return queryFactory
.select(member.isVisibleDonationRank)

View File

@@ -76,7 +76,7 @@ class ExplorerService(
)
}
fun getExplorer(member: Member, growthRankingCreatorsLimit: Long = 20): GetExplorerResponse {
fun getExplorer(member: Member): GetExplorerResponse {
val sections = mutableListOf<GetExplorerSectionResponse>()
// 인기 크리에이터
@@ -231,6 +231,27 @@ class ExplorerService(
listOf()
}
// 크리에이터의 최신 오디오 콘텐츠 1개
val latestContent = if (isCreator) {
audioContentService.getLatestCreatorAudioContent(creatorId, member, isAdultContentVisible)
} else {
null
}
// 크리에이터의 전체 콘텐츠 개수
val totalContentCount = if (isCreator) {
queryRepository.getContentCount(creatorId) ?: 0
} else {
0
}
// 조회하는 유저가 소장 중인 크리에이터의 콘텐츠 개수
val ownedContentCount = if (isCreator) {
queryRepository.getOwnedContentCount(creatorId, member.id!!)
} else {
0
}
// 공지사항
val notice = if (isCreator) {
queryRepository.getNoticeString(creatorId)
@@ -311,6 +332,9 @@ class ExplorerService(
similarCreatorList = similarCreatorList,
liveRoomList = liveRoomList,
contentList = contentList,
latestContent = latestContent,
totalContentCount = totalContentCount,
ownedContentCount = ownedContentCount,
notice = notice,
communityPostList = communityPostList,
cheers = cheers,

View File

@@ -10,6 +10,9 @@ data class GetCreatorProfileResponse(
val similarCreatorList: List<SimilarCreatorResponse>,
val liveRoomList: List<LiveRoomResponse>,
val contentList: List<GetAudioContentListItem>,
val latestContent: GetAudioContentListItem?,
val totalContentCount: Long,
val ownedContentCount: Long,
val notice: String,
val communityPostList: List<GetCommunityPostListResponse>,
val cheers: GetCheersResponse,