시리즈 리스트 상세 API
This commit is contained in:
parent
b10af9d9f1
commit
6db8013e34
|
@ -7,6 +7,7 @@ import kr.co.vividnext.sodalive.member.Member
|
|||
import org.springframework.data.domain.Pageable
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.PathVariable
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RequestParam
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
@ -33,4 +34,16 @@ class ContentSeriesController(private val service: ContentSeriesService) {
|
|||
)
|
||||
)
|
||||
}
|
||||
|
||||
@GetMapping("/{id}")
|
||||
fun getSeriesDetail(
|
||||
@PathVariable id: Long,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(
|
||||
service.getSeriesDetail(seriesId = id, member = member)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,20 @@
|
|||
package kr.co.vividnext.sodalive.content.series
|
||||
|
||||
import com.querydsl.core.types.dsl.Expressions
|
||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||
import kr.co.vividnext.sodalive.admin.content.series.genre.QSeriesGenre.seriesGenre
|
||||
import kr.co.vividnext.sodalive.content.QAudioContent.audioContent
|
||||
import kr.co.vividnext.sodalive.content.hashtag.QHashTag.hashTag
|
||||
import kr.co.vividnext.sodalive.content.series.content.GetSeriesContentMinMaxPriceResponse
|
||||
import kr.co.vividnext.sodalive.content.series.content.QGetSeriesContentMinMaxPriceResponse
|
||||
import kr.co.vividnext.sodalive.creator.admin.content.series.QSeries.series
|
||||
import kr.co.vividnext.sodalive.creator.admin.content.series.QSeriesContent.seriesContent
|
||||
import kr.co.vividnext.sodalive.creator.admin.content.series.Series
|
||||
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek
|
||||
import kr.co.vividnext.sodalive.creator.admin.content.series.keyword.QSeriesKeyword.seriesKeyword
|
||||
import kr.co.vividnext.sodalive.member.QMember
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import java.time.LocalDateTime
|
||||
|
||||
interface ContentSeriesRepository : JpaRepository<Series, Long>, ContentSeriesQueryRepository
|
||||
|
||||
|
@ -16,6 +27,11 @@ interface ContentSeriesQueryRepository {
|
|||
offset: Long,
|
||||
limit: Long
|
||||
): List<Series>
|
||||
|
||||
fun getSeriesDetail(seriesId: Long, isAuth: Boolean, imageHost: String): GetSeriesDetailResponse?
|
||||
fun getPublishedDaysOfWeek(seriesId: Long): Set<SeriesPublishedDaysOfWeek>
|
||||
fun getKeywordList(seriesId: Long): List<String>
|
||||
fun getSeriesContentMinMaxPrice(seriesId: Long): GetSeriesContentMinMaxPriceResponse
|
||||
}
|
||||
|
||||
class ContentSeriesQueryRepositoryImpl(
|
||||
|
@ -58,4 +74,92 @@ class ContentSeriesQueryRepositoryImpl(
|
|||
.limit(limit)
|
||||
.fetch()
|
||||
}
|
||||
|
||||
override fun getSeriesDetail(seriesId: Long, isAuth: Boolean, imageHost: String): GetSeriesDetailResponse? {
|
||||
val qCreator = QMember.member
|
||||
var where = series.id.eq(seriesId)
|
||||
.and(series.isActive.isTrue)
|
||||
|
||||
if (!isAuth) {
|
||||
where = where.and(series.isAdult.isFalse)
|
||||
}
|
||||
|
||||
val formattedDate = Expressions.stringTemplate(
|
||||
"DATE_FORMAT({0}, {1})",
|
||||
Expressions.dateTimeTemplate(
|
||||
LocalDateTime::class.java,
|
||||
"CONVERT_TZ({0},{1},{2})",
|
||||
series.createdAt,
|
||||
"UTC",
|
||||
"Asia/Seoul"
|
||||
),
|
||||
"%Y-%m-%d"
|
||||
)
|
||||
|
||||
return queryFactory
|
||||
.select(
|
||||
QGetSeriesDetailResponse(
|
||||
series.id,
|
||||
series.title,
|
||||
series.coverImage.prepend("/").prepend(imageHost),
|
||||
series.introduction,
|
||||
seriesGenre.genre,
|
||||
series.isAdult,
|
||||
series.writer,
|
||||
series.studio,
|
||||
formattedDate,
|
||||
QGetSeriesDetailResponse_GetSeriesDetailCreator(
|
||||
qCreator.id,
|
||||
qCreator.nickname,
|
||||
qCreator.profileImage.prepend("/").prepend(imageHost),
|
||||
Expressions.constant(false)
|
||||
),
|
||||
Expressions.constant(0),
|
||||
Expressions.constant(0),
|
||||
Expressions.constant(15),
|
||||
Expressions.constant(0),
|
||||
Expressions.constant(0),
|
||||
Expressions.constant<List<String>>(emptyList()),
|
||||
Expressions.constant(null)
|
||||
)
|
||||
)
|
||||
.from(series)
|
||||
.innerJoin(series.member, qCreator)
|
||||
.innerJoin(series.genre, seriesGenre)
|
||||
.where(where)
|
||||
.fetchFirst()
|
||||
}
|
||||
|
||||
override fun getPublishedDaysOfWeek(seriesId: Long): Set<SeriesPublishedDaysOfWeek> {
|
||||
return queryFactory
|
||||
.select(series.publishedDaysOfWeek)
|
||||
.from(series)
|
||||
.where(series.id.eq(seriesId))
|
||||
.fetchFirst()
|
||||
}
|
||||
|
||||
override fun getKeywordList(seriesId: Long): List<String> {
|
||||
return queryFactory
|
||||
.select(hashTag.tag)
|
||||
.from(series)
|
||||
.innerJoin(series.keywordList, seriesKeyword).fetchJoin()
|
||||
.innerJoin(seriesKeyword.keyword, hashTag)
|
||||
.where(series.id.eq(seriesId))
|
||||
.fetch()
|
||||
}
|
||||
|
||||
override fun getSeriesContentMinMaxPrice(seriesId: Long): GetSeriesContentMinMaxPriceResponse {
|
||||
return queryFactory
|
||||
.select(
|
||||
QGetSeriesContentMinMaxPriceResponse(
|
||||
audioContent.price.min(),
|
||||
audioContent.price.max()
|
||||
)
|
||||
)
|
||||
.from(series)
|
||||
.innerJoin(series.contentList, seriesContent).fetchJoin()
|
||||
.innerJoin(seriesContent.content, audioContent)
|
||||
.where(series.id.eq(seriesId))
|
||||
.fetchFirst()
|
||||
}
|
||||
}
|
||||
|
|
|
@ -1,9 +1,11 @@
|
|||
package kr.co.vividnext.sodalive.content.series
|
||||
|
||||
import kr.co.vividnext.sodalive.common.SodaException
|
||||
import kr.co.vividnext.sodalive.content.series.content.ContentSeriesContentRepository
|
||||
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesPublishedDaysOfWeek
|
||||
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesSortType
|
||||
import kr.co.vividnext.sodalive.creator.admin.content.series.SeriesState
|
||||
import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.stereotype.Service
|
||||
|
@ -12,6 +14,7 @@ import java.time.LocalDateTime
|
|||
@Service
|
||||
class ContentSeriesService(
|
||||
private val repository: ContentSeriesRepository,
|
||||
private val explorerQueryRepository: ExplorerQueryRepository,
|
||||
private val seriesContentRepository: ContentSeriesContentRepository,
|
||||
|
||||
@Value("\${cloud.aws.cloud-front.host}")
|
||||
|
@ -72,6 +75,35 @@ class ContentSeriesService(
|
|||
return GetSeriesListResponse(totalCount, items)
|
||||
}
|
||||
|
||||
fun getSeriesDetail(seriesId: Long, member: Member): GetSeriesDetailResponse {
|
||||
val seriesDetail = repository.getSeriesDetail(
|
||||
seriesId = seriesId,
|
||||
isAuth = member.auth != null,
|
||||
imageHost = coverImageHost
|
||||
) ?: throw SodaException("잘못된 시리즈 입니다.\n다시 시도해 주세요")
|
||||
|
||||
seriesDetail.creator.isFollow = explorerQueryRepository.isFollow(
|
||||
creatorId = seriesDetail.creator.creatorId,
|
||||
memberId = member.id!!
|
||||
)
|
||||
|
||||
seriesDetail.publishedDaysOfWeek = publishedDaysOfWeekText(
|
||||
repository.getPublishedDaysOfWeek(seriesId = seriesId)
|
||||
)
|
||||
|
||||
seriesDetail.keywordList = repository.getKeywordList(seriesId = seriesId)
|
||||
.filter { it.isNotBlank() }
|
||||
|
||||
val minMaxPrice = repository.getSeriesContentMinMaxPrice(seriesId = seriesId)
|
||||
seriesDetail.minPrice = minMaxPrice.minPrice
|
||||
seriesDetail.maxPrice = minMaxPrice.maxPrice
|
||||
seriesDetail.rentalMinPrice = (minMaxPrice.minPrice * 0.7).toInt()
|
||||
seriesDetail.rentalMaxPrice = (minMaxPrice.maxPrice * 0.7).toInt()
|
||||
|
||||
if (!seriesDetail.validate()) throw SodaException("잘못된 시리즈 입니다.\n다시 시도해 주세요")
|
||||
return seriesDetail
|
||||
}
|
||||
|
||||
private fun publishedDaysOfWeekText(publishedDaysOfWeek: Set<SeriesPublishedDaysOfWeek>): String {
|
||||
val dayOfWeekText = publishedDaysOfWeek.toList().sortedBy { it.ordinal }
|
||||
.map {
|
||||
|
@ -91,7 +123,7 @@ class ContentSeriesService(
|
|||
return if (publishedDaysOfWeek.contains(SeriesPublishedDaysOfWeek.RANDOM)) {
|
||||
dayOfWeekText
|
||||
} else {
|
||||
"매주 ${dayOfWeekText}요일"
|
||||
"매주 $dayOfWeekText"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,34 @@
|
|||
package kr.co.vividnext.sodalive.content.series
|
||||
|
||||
import com.querydsl.core.annotations.QueryProjection
|
||||
|
||||
data class GetSeriesDetailResponse @QueryProjection constructor(
|
||||
val seriesId: Long,
|
||||
val title: String,
|
||||
val coverImage: String,
|
||||
val introduction: String,
|
||||
val genre: String,
|
||||
val isAdult: Boolean,
|
||||
val writer: String?,
|
||||
val studio: String?,
|
||||
val publishedDate: String,
|
||||
val creator: GetSeriesDetailCreator,
|
||||
var rentalMinPrice: Int,
|
||||
var rentalMaxPrice: Int,
|
||||
val rentalPeriod: Int,
|
||||
var minPrice: Int,
|
||||
var maxPrice: Int,
|
||||
var keywordList: List<String>,
|
||||
var publishedDaysOfWeek: String?
|
||||
) {
|
||||
data class GetSeriesDetailCreator @QueryProjection constructor(
|
||||
val creatorId: Long,
|
||||
val nickname: String,
|
||||
val coverImage: String,
|
||||
var isFollow: Boolean
|
||||
)
|
||||
|
||||
fun validate(): Boolean {
|
||||
return keywordList.isNotEmpty() && !publishedDaysOfWeek.isNullOrBlank()
|
||||
}
|
||||
}
|
|
@ -0,0 +1,8 @@
|
|||
package kr.co.vividnext.sodalive.content.series.content
|
||||
|
||||
import com.querydsl.core.annotations.QueryProjection
|
||||
|
||||
data class GetSeriesContentMinMaxPriceResponse @QueryProjection constructor(
|
||||
val minPrice: Int,
|
||||
val maxPrice: Int
|
||||
)
|
Loading…
Reference in New Issue