616 lines
24 KiB
Kotlin
616 lines
24 KiB
Kotlin
package kr.co.vividnext.sodalive.content
|
|
|
|
import com.amazonaws.services.s3.model.ObjectMetadata
|
|
import com.fasterxml.jackson.databind.ObjectMapper
|
|
import kr.co.vividnext.sodalive.aws.cloudfront.AudioContentCloudFront
|
|
import kr.co.vividnext.sodalive.aws.s3.S3Uploader
|
|
import kr.co.vividnext.sodalive.common.SodaException
|
|
import kr.co.vividnext.sodalive.content.comment.AudioContentCommentRepository
|
|
import kr.co.vividnext.sodalive.content.hashtag.AudioContentHashTag
|
|
import kr.co.vividnext.sodalive.content.hashtag.HashTag
|
|
import kr.co.vividnext.sodalive.content.hashtag.HashTagRepository
|
|
import kr.co.vividnext.sodalive.content.like.AudioContentLike
|
|
import kr.co.vividnext.sodalive.content.like.AudioContentLikeRepository
|
|
import kr.co.vividnext.sodalive.content.like.PutAudioContentLikeRequest
|
|
import kr.co.vividnext.sodalive.content.like.PutAudioContentLikeResponse
|
|
import kr.co.vividnext.sodalive.content.main.GetAudioContentRanking
|
|
import kr.co.vividnext.sodalive.content.order.OrderRepository
|
|
import kr.co.vividnext.sodalive.content.order.OrderType
|
|
import kr.co.vividnext.sodalive.content.theme.AudioContentThemeQueryRepository
|
|
import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository
|
|
import kr.co.vividnext.sodalive.fcm.FcmEvent
|
|
import kr.co.vividnext.sodalive.fcm.FcmEventType
|
|
import kr.co.vividnext.sodalive.member.Member
|
|
import kr.co.vividnext.sodalive.member.block.BlockMemberRepository
|
|
import kr.co.vividnext.sodalive.utils.generateFileName
|
|
import org.springframework.beans.factory.annotation.Value
|
|
import org.springframework.context.ApplicationEventPublisher
|
|
import org.springframework.data.repository.findByIdOrNull
|
|
import org.springframework.stereotype.Service
|
|
import org.springframework.transaction.annotation.Transactional
|
|
import org.springframework.web.multipart.MultipartFile
|
|
import java.text.SimpleDateFormat
|
|
import java.time.DayOfWeek
|
|
import java.time.LocalDateTime
|
|
import java.time.ZoneId
|
|
import java.time.format.DateTimeFormatter
|
|
import java.time.temporal.TemporalAdjusters
|
|
import java.util.Locale
|
|
|
|
@Service
|
|
@Transactional(readOnly = true)
|
|
class AudioContentService(
|
|
private val repository: AudioContentRepository,
|
|
private val explorerQueryRepository: ExplorerQueryRepository,
|
|
private val blockMemberRepository: BlockMemberRepository,
|
|
private val hashTagRepository: HashTagRepository,
|
|
private val orderRepository: OrderRepository,
|
|
private val themeQueryRepository: AudioContentThemeQueryRepository,
|
|
private val playbackTrackingRepository: PlaybackTrackingRepository,
|
|
private val commentRepository: AudioContentCommentRepository,
|
|
private val audioContentLikeRepository: AudioContentLikeRepository,
|
|
|
|
private val s3Uploader: S3Uploader,
|
|
private val objectMapper: ObjectMapper,
|
|
private val audioContentCloudFront: AudioContentCloudFront,
|
|
private val applicationEventPublisher: ApplicationEventPublisher,
|
|
|
|
@Value("\${cloud.aws.s3.content-bucket}")
|
|
private val audioContentBucket: String,
|
|
|
|
@Value("\${cloud.aws.s3.bucket}")
|
|
private val coverImageBucket: String,
|
|
|
|
@Value("\${cloud.aws.cloud-front.host}")
|
|
private val coverImageHost: String
|
|
) {
|
|
@Transactional
|
|
fun audioContentLike(request: PutAudioContentLikeRequest, member: Member): PutAudioContentLikeResponse {
|
|
var audioContentLike = audioContentLikeRepository.findByMemberIdAndContentId(
|
|
memberId = member.id!!,
|
|
contentId = request.contentId
|
|
)
|
|
|
|
if (audioContentLike == null) {
|
|
audioContentLike = AudioContentLike(
|
|
memberId = member.id!!,
|
|
contentId = request.contentId
|
|
)
|
|
|
|
audioContentLikeRepository.save(audioContentLike)
|
|
} else {
|
|
audioContentLike.isActive = !audioContentLike.isActive
|
|
}
|
|
|
|
return PutAudioContentLikeResponse(like = audioContentLike.isActive)
|
|
}
|
|
|
|
@Transactional
|
|
fun modifyAudioContent(
|
|
coverImage: MultipartFile?,
|
|
requestString: String,
|
|
member: Member
|
|
) {
|
|
// request 내용 파싱
|
|
val request = objectMapper.readValue(requestString, ModifyAudioContentRequest::class.java)
|
|
|
|
val audioContent = repository.findByIdAndCreatorId(request.contentId, member.id!!)
|
|
?: throw SodaException("잘못된 콘텐츠 입니다.\n다시 시도해 주세요.")
|
|
|
|
if (request.title != null) audioContent.title = request.title
|
|
if (request.detail != null) audioContent.detail = request.detail
|
|
audioContent.isCommentAvailable = request.isCommentAvailable
|
|
audioContent.isAdult = request.isAdult
|
|
|
|
if (coverImage != null) {
|
|
val metadata = ObjectMetadata()
|
|
metadata.contentLength = coverImage.size
|
|
|
|
// 커버 이미지 파일명 생성
|
|
val coverImageFileName = generateFileName(prefix = "${audioContent.id}-cover")
|
|
|
|
// 커버 이미지 업로드
|
|
val coverImagePath = s3Uploader.upload(
|
|
inputStream = coverImage.inputStream,
|
|
bucket = coverImageBucket,
|
|
filePath = "audio_content_cover/${audioContent.id}/$coverImageFileName",
|
|
metadata = metadata
|
|
)
|
|
|
|
audioContent.coverImage = coverImagePath
|
|
}
|
|
}
|
|
|
|
@Transactional
|
|
fun deleteAudioContent(audioContentId: Long, member: Member) {
|
|
val audioContent = repository.findByIdAndCreatorId(audioContentId, member.id!!)
|
|
?: throw SodaException("잘못된 콘텐츠 입니다.\n다시 시도해 주세요.")
|
|
|
|
audioContent.isActive = false
|
|
}
|
|
|
|
@Transactional
|
|
fun createAudioContent(
|
|
contentFile: MultipartFile?,
|
|
coverImage: MultipartFile?,
|
|
requestString: String,
|
|
member: Member
|
|
): CreateAudioContentResponse {
|
|
// coverImage 체크
|
|
if (coverImage == null) throw SodaException("커버이미지를 선택해 주세요.")
|
|
|
|
// request 내용 파싱
|
|
val request = objectMapper.readValue(requestString, CreateAudioContentRequest::class.java)
|
|
|
|
// 미리듣기 시간 체크
|
|
validatePreviewTime(request.previewStartTime, request.previewEndTime)
|
|
|
|
// contentFile 체크
|
|
if (contentFile == null && request.type == AudioContentType.INDIVIDUAL) {
|
|
throw SodaException("콘텐츠를 선택해 주세요.")
|
|
}
|
|
|
|
if (request.type == AudioContentType.BUNDLE && request.childIds == null) {
|
|
throw SodaException("묶음상품의 하위상품을 선택해 주세요.")
|
|
}
|
|
|
|
// 테마 체크
|
|
val theme = themeQueryRepository.findThemeByIdAndActive(id = request.themeId)
|
|
?: throw SodaException("잘못된 테마입니다. 다시 선택해 주세요.")
|
|
|
|
if (request.price in 1..4) throw SodaException("콘텐츠의 최소금액은 5캔 입니다.")
|
|
|
|
// DB에 값 추가
|
|
val audioContent = AudioContent(
|
|
title = request.title,
|
|
detail = request.detail,
|
|
type = request.type,
|
|
price = if (request.price > 0) {
|
|
request.price
|
|
} else {
|
|
0
|
|
},
|
|
isAdult = request.isAdult,
|
|
isGeneratePreview = if (request.type == AudioContentType.INDIVIDUAL) {
|
|
request.isGeneratePreview
|
|
} else {
|
|
false
|
|
},
|
|
isCommentAvailable = request.isCommentAvailable
|
|
)
|
|
audioContent.theme = theme
|
|
audioContent.member = member
|
|
audioContent.isActive = request.type == AudioContentType.BUNDLE
|
|
|
|
repository.save(audioContent)
|
|
|
|
// 태그 분리, #추가, 등록
|
|
if (request.tags.isNotBlank()) {
|
|
val tags = request.tags
|
|
.split(" ")
|
|
.asSequence()
|
|
.map { it.trim() }
|
|
.filter { it.isNotEmpty() }
|
|
.map {
|
|
val tag = if (!it.startsWith("#")) {
|
|
"#$it"
|
|
} else {
|
|
it
|
|
}
|
|
|
|
val hashTag = hashTagRepository.findByTag(tag)
|
|
?: hashTagRepository.save(HashTag(tag))
|
|
|
|
val audioContentHashTag = AudioContentHashTag()
|
|
audioContentHashTag.audioContent = audioContent
|
|
audioContentHashTag.hashTag = hashTag
|
|
|
|
audioContentHashTag
|
|
}.toList()
|
|
|
|
audioContent.audioContentHashTags.addAll(tags)
|
|
}
|
|
|
|
var metadata = ObjectMetadata()
|
|
metadata.contentLength = coverImage.size
|
|
|
|
// 커버 이미지 파일명 생성
|
|
val coverImageFileName = generateFileName(prefix = "${audioContent.id}-cover")
|
|
|
|
// 커버 이미지 업로드
|
|
val coverImagePath = s3Uploader.upload(
|
|
inputStream = coverImage.inputStream,
|
|
bucket = coverImageBucket,
|
|
filePath = "audio_content_cover/${audioContent.id}/$coverImageFileName",
|
|
metadata = metadata
|
|
)
|
|
|
|
audioContent.coverImage = coverImagePath
|
|
|
|
if (contentFile != null && request.type == AudioContentType.INDIVIDUAL) {
|
|
// 콘텐츠 파일명 생성
|
|
val contentFileName = generateFileName(prefix = "${audioContent.id}-content")
|
|
|
|
// 콘텐츠 파일 업로드
|
|
metadata = ObjectMetadata()
|
|
metadata.contentLength = contentFile.size
|
|
metadata.addUserMetadata("generate_preview", "true")
|
|
|
|
if (request.previewStartTime != null && request.previewEndTime != null) {
|
|
metadata.addUserMetadata("preview_start_time", request.previewStartTime)
|
|
metadata.addUserMetadata("preview_end_time", request.previewEndTime)
|
|
}
|
|
|
|
val contentPath = s3Uploader.upload(
|
|
inputStream = contentFile.inputStream,
|
|
bucket = audioContentBucket,
|
|
filePath = "input/${audioContent.id}/$contentFileName",
|
|
metadata = metadata
|
|
)
|
|
|
|
audioContent.content = contentPath
|
|
}
|
|
|
|
if (request.childIds != null && request.type == AudioContentType.BUNDLE) {
|
|
for (childId in request.childIds) {
|
|
val childContent = repository.findByIdAndActive(childId)
|
|
?: continue
|
|
|
|
val bundleAudioContent = BundleAudioContent()
|
|
bundleAudioContent.parent = audioContent
|
|
bundleAudioContent.child = childContent
|
|
audioContent.children.add(bundleAudioContent)
|
|
}
|
|
}
|
|
|
|
return CreateAudioContentResponse(contentId = audioContent.id!!)
|
|
}
|
|
|
|
private fun validatePreviewTime(previewStartTime: String?, previewEndTime: String?) {
|
|
if (previewStartTime != null && previewEndTime != null) {
|
|
val startTimeArray = previewStartTime.split(":")
|
|
if (startTimeArray.size != 3) {
|
|
throw SodaException("미리 듣기 시간 형식은 00:30:00 과 같아야 합니다")
|
|
}
|
|
|
|
for (time in startTimeArray) {
|
|
if (time.length != 2) {
|
|
throw SodaException("미리 듣기 시간 형식은 00:30:00 과 같아야 합니다")
|
|
}
|
|
}
|
|
|
|
val endTimeArray = previewEndTime.split(":")
|
|
if (endTimeArray.size != 3) {
|
|
throw SodaException("미리 듣기 시간 형식은 00:30:00 과 같아야 합니다")
|
|
}
|
|
|
|
for (time in endTimeArray) {
|
|
if (time.length != 2) {
|
|
throw SodaException("미리 듣기 시간 형식은 00:30:00 과 같아야 합니다")
|
|
}
|
|
}
|
|
|
|
val timeDifference = timeDifference(previewStartTime, previewEndTime)
|
|
|
|
if (timeDifference < 30000) {
|
|
throw SodaException("미리 듣기의 최소 시간은 30초 입니다.")
|
|
}
|
|
} else {
|
|
if (previewStartTime != null || previewEndTime != null) {
|
|
throw SodaException("미리 듣기 시작 시간과 종료 시간 둘 다 입력을 하거나 둘 다 입력 하지 않아야 합니다.")
|
|
}
|
|
}
|
|
}
|
|
|
|
private fun timeDifference(startTime: String, endTime: String): Long {
|
|
try {
|
|
// Define a date format for parsing the times
|
|
val dateFormat = SimpleDateFormat("HH:mm:ss", Locale.KOREAN)
|
|
|
|
// Parse the input times into Date objects
|
|
val date1 = dateFormat.parse(startTime)
|
|
val date2 = dateFormat.parse(endTime)
|
|
|
|
// Check if either date is null
|
|
if (date1 == null || date2 == null) {
|
|
return 0
|
|
}
|
|
|
|
// Check if the time difference is greater than 30 seconds (30000 milliseconds)
|
|
return date2.time - date1.time
|
|
} catch (e: Exception) {
|
|
// Handle invalid time formats or parsing errors
|
|
return 0
|
|
}
|
|
}
|
|
|
|
@Transactional
|
|
fun uploadComplete(contentId: Long, content: String, duration: String) {
|
|
val keyFileName = content.split("/").last()
|
|
if (!keyFileName.startsWith(contentId.toString())) throw SodaException("잘못된 요청입니다.")
|
|
|
|
val audioContent = repository.findByIdOrNull(contentId)
|
|
?: throw SodaException("잘못된 요청입니다.")
|
|
|
|
audioContent.isActive = true
|
|
audioContent.content = content
|
|
audioContent.duration = duration
|
|
|
|
applicationEventPublisher.publishEvent(
|
|
FcmEvent(
|
|
type = FcmEventType.INDIVIDUAL,
|
|
title = "콘텐츠 등록완료",
|
|
message = audioContent.title,
|
|
recipients = listOf(audioContent.member!!.id!!),
|
|
isAuth = audioContent.isAdult,
|
|
contentId = contentId
|
|
)
|
|
)
|
|
|
|
applicationEventPublisher.publishEvent(
|
|
FcmEvent(
|
|
type = FcmEventType.UPLOAD_CONTENT,
|
|
title = audioContent.member!!.nickname,
|
|
message = "콘텐츠를 업로드 하였습니다. - ${audioContent.title}",
|
|
isAuth = audioContent.isAdult,
|
|
contentId = contentId,
|
|
creatorId = audioContent.member!!.id,
|
|
container = "ios"
|
|
)
|
|
)
|
|
|
|
applicationEventPublisher.publishEvent(
|
|
FcmEvent(
|
|
type = FcmEventType.UPLOAD_CONTENT,
|
|
title = audioContent.member!!.nickname,
|
|
message = "콘텐츠를 업로드 하였습니다. - ${audioContent.title}",
|
|
isAuth = audioContent.isAdult,
|
|
contentId = contentId,
|
|
creatorId = audioContent.member!!.id,
|
|
container = "aos"
|
|
)
|
|
)
|
|
}
|
|
|
|
fun getDetail(id: Long, member: Member, timezone: String): GetAudioContentDetailResponse {
|
|
// 묶음 콘텐츠 조회
|
|
val bundleAudioContentList = repository.findBundleByContentId(contentId = id)
|
|
|
|
// 오디오 콘텐츠 조회 (content_id, 제목, 내용, 테마, 태그, 19여부, 이미지, 콘텐츠 PATH)
|
|
val audioContent = repository.findByIdOrNull(id)
|
|
?: throw SodaException("잘못된 콘텐츠 입니다.\n다시 시도해 주세요.")
|
|
|
|
// 크리에이터(유저) 정보
|
|
val creatorId = audioContent.member!!.id!!
|
|
val creator = explorerQueryRepository.getMember(creatorId)
|
|
?: throw SodaException("없는 사용자 입니다.")
|
|
|
|
val notificationUserIds = explorerQueryRepository.getNotificationUserIds(creatorId)
|
|
val isFollowing = notificationUserIds.contains(member.id)
|
|
|
|
// 차단된 사용자 체크
|
|
val isBlocked = blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = creatorId)
|
|
if (isBlocked) throw SodaException("${creator.nickname}님의 요청으로 콘텐츠 접근이 제한됩니다.")
|
|
|
|
// 구매 여부 확인
|
|
val isExistsBundleAudioContent = bundleAudioContentList
|
|
.map { orderRepository.isExistOrdered(memberId = member.id!!, contentId = it.id!!) }
|
|
.contains(true)
|
|
|
|
val (isExistsAudioContent, orderType) = orderRepository.isExistOrderedAndOrderType(
|
|
memberId = member.id!!,
|
|
contentId = audioContent.id!!
|
|
)
|
|
|
|
if (!isExistsAudioContent && !isExistsBundleAudioContent && !audioContent.isActive) {
|
|
throw SodaException("잘못된 콘텐츠 입니다.\n다시 시도해 주세요.")
|
|
}
|
|
|
|
// 댓글
|
|
val commentList = if (audioContent.isCommentAvailable) {
|
|
commentRepository.findByContentId(
|
|
cloudFrontHost = coverImageHost,
|
|
contentId = audioContent.id!!,
|
|
timezone = timezone,
|
|
offset = 0,
|
|
limit = 1
|
|
)
|
|
} else {
|
|
listOf()
|
|
}
|
|
|
|
// 댓글 수
|
|
val commentCount = if (audioContent.isCommentAvailable) {
|
|
commentRepository.totalCountCommentByContentId(contentId = audioContent.id!!)
|
|
} else {
|
|
0
|
|
}
|
|
|
|
val audioContentUrl = audioContentCloudFront.generateSignedURL(
|
|
resourcePath = if (
|
|
isExistsAudioContent ||
|
|
isExistsBundleAudioContent ||
|
|
audioContent.member!!.id!! == member.id!! ||
|
|
audioContent.price <= 0
|
|
) {
|
|
audioContent.content!!
|
|
} else {
|
|
audioContent.content!!.replace("output/", "preview/")
|
|
},
|
|
expirationTime = 1000 * 60 * 60 * (audioContent.duration!!.split(":")[0].toLong() + 2)
|
|
)
|
|
|
|
val tag = audioContent.audioContentHashTags
|
|
.map { it.hashTag!!.tag }
|
|
.joinToString(" ") { it }
|
|
|
|
val creatorOtherContentList = repository.getCreatorOtherContentList(
|
|
cloudfrontHost = coverImageHost,
|
|
contentId = audioContent.id!!,
|
|
creatorId = creatorId,
|
|
isAdult = member.auth != null
|
|
)
|
|
|
|
val sameThemeOtherContentList = repository.getSameThemeOtherContentList(
|
|
cloudfrontHost = coverImageHost,
|
|
contentId = audioContent.id!!,
|
|
themeId = audioContent.theme!!.id!!,
|
|
isAdult = member.auth != null
|
|
)
|
|
|
|
val likeCount = audioContentLikeRepository.totalCountAudioContentLike(contentId = id)
|
|
val isLike = audioContentLikeRepository.findByMemberIdAndContentId(
|
|
memberId = member.id!!,
|
|
contentId = id
|
|
)?.isActive ?: false
|
|
|
|
val remainingTime = if (orderType == OrderType.RENTAL) {
|
|
orderRepository.getAudioContentRemainingTime(
|
|
memberId = member.id!!,
|
|
contentId = audioContent.id!!,
|
|
timezone = timezone
|
|
)
|
|
} else {
|
|
null
|
|
}
|
|
|
|
return GetAudioContentDetailResponse(
|
|
contentId = audioContent.id!!,
|
|
title = audioContent.title,
|
|
detail = audioContent.detail,
|
|
coverImageUrl = "$coverImageHost/${audioContent.coverImage!!}",
|
|
contentUrl = audioContentUrl,
|
|
themeStr = audioContent.theme!!.theme,
|
|
tag = tag,
|
|
price = audioContent.price,
|
|
duration = audioContent.duration ?: "",
|
|
isAdult = audioContent.isAdult,
|
|
isMosaic = audioContent.isAdult && member.auth == null,
|
|
isOnlyRental = audioContent.isOnlyRental,
|
|
existOrdered = isExistsBundleAudioContent || isExistsAudioContent,
|
|
orderType = orderType,
|
|
remainingTime = remainingTime,
|
|
creatorOtherContentList = creatorOtherContentList,
|
|
sameThemeOtherContentList = sameThemeOtherContentList,
|
|
isCommentAvailable = audioContent.isCommentAvailable,
|
|
isLike = isLike,
|
|
likeCount = likeCount,
|
|
commentList = commentList,
|
|
commentCount = commentCount,
|
|
creator = AudioContentCreator(
|
|
creatorId = creatorId,
|
|
nickname = creator.nickname,
|
|
profileImageUrl = if (creator.profileImage != null) {
|
|
"$coverImageHost/${creator.profileImage}"
|
|
} else {
|
|
"$coverImageHost/profile/default-profile.png"
|
|
},
|
|
isFollowing = isFollowing
|
|
)
|
|
)
|
|
}
|
|
|
|
fun getAudioContentList(
|
|
creatorId: Long,
|
|
sortType: SortType,
|
|
member: Member,
|
|
offset: Long,
|
|
limit: Long
|
|
): GetAudioContentListResponse {
|
|
val totalCount = repository.findTotalCountByCreatorId(
|
|
creatorId = creatorId,
|
|
isAdult = member.auth != null
|
|
)
|
|
|
|
val audioContentList = repository.findByCreatorId(
|
|
creatorId = creatorId,
|
|
isAdult = member.auth != null,
|
|
sortType = sortType,
|
|
offset = offset,
|
|
limit = limit
|
|
)
|
|
|
|
val items = audioContentList
|
|
.map {
|
|
val commentCount = commentRepository
|
|
.totalCountCommentByContentId(it.id!!)
|
|
|
|
val likeCount = audioContentLikeRepository
|
|
.totalCountAudioContentLike(it.id!!)
|
|
|
|
GetAudioContentListItem(
|
|
contentId = it.id!!,
|
|
coverImageUrl = "$coverImageHost/${it.coverImage!!}",
|
|
title = it.title,
|
|
price = it.price,
|
|
themeStr = it.theme!!.theme,
|
|
duration = it.duration,
|
|
likeCount = likeCount,
|
|
commentCount = commentCount,
|
|
isAdult = it.isAdult
|
|
)
|
|
}
|
|
|
|
return GetAudioContentListResponse(
|
|
totalCount = totalCount,
|
|
items = items
|
|
)
|
|
}
|
|
|
|
@Transactional
|
|
fun addAllPlaybackTracking(request: AddAllPlaybackTrackingRequest, member: Member) {
|
|
val dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss")
|
|
|
|
for (trackingData in request.trackingDataList) {
|
|
val playDate = LocalDateTime.parse(trackingData.playDateTime, dateTimeFormatter)
|
|
.atZone(ZoneId.of(request.timezone))
|
|
.withZoneSameInstant(ZoneId.of("UTC"))
|
|
.toLocalDateTime()
|
|
|
|
playbackTrackingRepository.save(
|
|
PlaybackTracking(
|
|
memberId = member.id!!,
|
|
contentId = trackingData.contentId,
|
|
playDate = playDate,
|
|
isPreview = trackingData.isPreview
|
|
)
|
|
)
|
|
}
|
|
}
|
|
|
|
fun getAudioContentRanking(
|
|
member: Member,
|
|
offset: Long,
|
|
limit: Long
|
|
): GetAudioContentRanking {
|
|
val currentDateTime = LocalDateTime.now()
|
|
val startDate = currentDateTime
|
|
.withHour(15)
|
|
.withMinute(0)
|
|
.withSecond(0)
|
|
.minusWeeks(1)
|
|
.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
|
|
val endDate = startDate
|
|
.plusDays(7)
|
|
|
|
val startDateFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일")
|
|
val endDateFormatter = DateTimeFormatter.ofPattern("MM월 dd일")
|
|
|
|
val contentRankingItemList = repository
|
|
.getAudioContentRanking(
|
|
cloudfrontHost = coverImageHost,
|
|
startDate = startDate.minusDays(1),
|
|
endDate = endDate.minusDays(1),
|
|
isAdult = member.auth != null,
|
|
offset = offset,
|
|
limit = limit
|
|
)
|
|
|
|
return GetAudioContentRanking(
|
|
startDate = startDate.format(startDateFormatter),
|
|
endDate = endDate.minusDays(1).format(endDateFormatter),
|
|
items = contentRankingItemList
|
|
)
|
|
}
|
|
}
|