feat: 메인 홈

- API 추가
This commit is contained in:
2025-07-10 15:31:41 +09:00
parent a8da17162a
commit 22fc8b22b8
19 changed files with 799 additions and 30 deletions

View File

@@ -0,0 +1,82 @@
package kr.co.vividnext.sodalive.query.recommend
import com.querydsl.core.types.dsl.Expressions
import com.querydsl.jpa.impl.JPAQueryFactory
import kr.co.vividnext.sodalive.content.ContentType
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
import kr.co.vividnext.sodalive.content.comment.QAudioContentComment.audioContentComment
import kr.co.vividnext.sodalive.content.like.QAudioContentLike.audioContentLike
import kr.co.vividnext.sodalive.member.MemberRole
import kr.co.vividnext.sodalive.member.QMember.member
import kr.co.vividnext.sodalive.member.auth.QAuth.auth
import org.springframework.beans.factory.annotation.Value
import org.springframework.stereotype.Repository
@Repository
class RecommendChannelQueryRepository(
private val queryFactory: JPAQueryFactory,
@Value("\${cloud.aws.cloud-front.host}")
private val imageHost: String
) {
fun getRecommendChannelList(isAdult: Boolean, contentType: ContentType): List<RecommendChannelResponse> {
var where = member.role.eq(MemberRole.CREATOR)
.and(audioContent.isActive.isTrue)
if (!isAdult) {
where = where.and(audioContent.isAdult.isFalse)
} else {
if (contentType != ContentType.ALL) {
where = where.and(
member.auth.gender.eq(
if (contentType == ContentType.MALE) {
0
} else {
1
}
)
)
}
}
return queryFactory
.select(
QRecommendChannelResponse(
member.id,
member.nickname,
member.profileImage.prepend("/").prepend(imageHost),
audioContent.id.count(),
Expressions.constant(emptyList<RecommendChannelContentItem>())
)
)
.from(member)
.innerJoin(auth).on(auth.member.id.eq(member.id))
.innerJoin(audioContent).on(audioContent.member.id.eq(member.id))
.where(where)
.groupBy(member.id)
.having(audioContent.id.count().goe(3))
.orderBy(Expressions.numberTemplate(Double::class.java, "function('rand')").asc())
.limit(6)
.fetch()
}
fun getContentsByCreatorIdLikeDesc(creatorId: Long): List<RecommendChannelContentItem> {
queryFactory
.select(
QRecommendChannelContentItem(
audioContent.id,
audioContent.title,
audioContent.coverImage.prepend("/").prepend(imageHost),
audioContentLike.id.countDistinct(),
audioContentComment.id.countDistinct()
)
)
.from(audioContent)
.leftJoin(audioContentLike).on(audioContentLike.audioContent.id.eq(audioContent.id))
.leftJoin(audioContentComment).on(audioContentComment.audioContent.id.eq(audioContent.id))
.where(audioContent.member.id.eq(creatorId))
.groupBy(audioContent.id)
.fetch()
return listOf()
}
}

View File

@@ -0,0 +1,30 @@
package kr.co.vividnext.sodalive.query.recommend
import kr.co.vividnext.sodalive.content.ContentType
import org.springframework.cache.annotation.Cacheable
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
@Service
@Transactional(readOnly = true)
class RecommendChannelQueryService(private val repository: RecommendChannelQueryRepository) {
@Cacheable(
cacheNames = ["default"],
key = "'recommendChannel:' + (#memberId ?: 'guest') + ':' + #isAdult + ':' + #contentType"
)
fun getRecommendChannel(
memberId: Long?,
isAdult: Boolean,
contentType: ContentType
): List<RecommendChannelResponse> {
val recommendChannelList = repository.getRecommendChannelList(
isAdult = isAdult,
contentType = contentType
)
return recommendChannelList.map {
it.contentList = repository.getContentsByCreatorIdLikeDesc(it.channelId)
it
}
}
}

View File

@@ -0,0 +1,20 @@
package kr.co.vividnext.sodalive.query.recommend
import com.fasterxml.jackson.annotation.JsonProperty
import com.querydsl.core.annotations.QueryProjection
data class RecommendChannelResponse @QueryProjection constructor(
@JsonProperty("channelId") val channelId: Long,
@JsonProperty("creatorNickname") val creatorNickname: String,
@JsonProperty("creatorProfileImageUrl") val creatorProfileImageUrl: String,
@JsonProperty("contentCount") val contentCount: Long,
@JsonProperty("contentList") var contentList: List<RecommendChannelContentItem>
)
data class RecommendChannelContentItem @QueryProjection constructor(
@JsonProperty("contentId") val contentId: Long,
@JsonProperty("title") val title: String,
@JsonProperty("thumbnailImageUrl") val thumbnailImageUrl: String,
@JsonProperty("likeCount") val likeCount: Long,
@JsonProperty("commentCount") val commentCount: Long
)