콘텐츠 메인 API - 코루틴 적용

This commit is contained in:
Klaus 2023-11-07 16:47:25 +09:00
parent 0e786f46cb
commit ce15025c8d
5 changed files with 116 additions and 48 deletions

View File

@ -55,6 +55,8 @@ dependencies {
implementation("org.json:json:20230227") implementation("org.json:json:20230227")
implementation("com.google.code.findbugs:jsr305:3.0.2") implementation("com.google.code.findbugs:jsr305:3.0.2")
implementation("org.jetbrains.kotlinx:kotlinx-coroutines-core:1.7.3")
// firebase admin sdk // firebase admin sdk
implementation("com.google.firebase:firebase-admin:9.2.0") implementation("com.google.firebase:firebase-admin:9.2.0")

View File

@ -0,0 +1,10 @@
package kr.co.vividnext.sodalive.common
import kotlinx.coroutines.sync.Semaphore
import kotlinx.coroutines.sync.withPermit
class SemaphoreManager(private val semaphore: Semaphore) {
suspend fun <T> withPermit(block: suspend () -> T): T {
return semaphore.withPermit { block() }
}
}

View File

@ -0,0 +1,14 @@
package kr.co.vividnext.sodalive.configs
import kotlinx.coroutines.sync.Semaphore
import kr.co.vividnext.sodalive.common.SemaphoreManager
import org.springframework.context.annotation.Bean
import org.springframework.context.annotation.Configuration
@Configuration
class SemaphoreConfig {
@Bean
fun semaphoreManager(): SemaphoreManager {
return SemaphoreManager(Semaphore(4))
}
}

View File

@ -15,7 +15,7 @@ import org.springframework.web.bind.annotation.RestController
class AudioContentMainController(private val service: AudioContentMainService) { class AudioContentMainController(private val service: AudioContentMainService) {
@GetMapping @GetMapping
fun getMain( suspend fun getMain(
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
) = run { ) = run {
if (member == null) throw SodaException("로그인 정보를 확인해주세요.") if (member == null) throw SodaException("로그인 정보를 확인해주세요.")

View File

@ -1,5 +1,8 @@
package kr.co.vividnext.sodalive.content.main package kr.co.vividnext.sodalive.content.main
import kotlinx.coroutines.async
import kotlinx.coroutines.coroutineScope
import kr.co.vividnext.sodalive.common.SemaphoreManager
import kr.co.vividnext.sodalive.content.AudioContentRepository import kr.co.vividnext.sodalive.content.AudioContentRepository
import kr.co.vividnext.sodalive.content.AudioContentService import kr.co.vividnext.sodalive.content.AudioContentService
import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerType import kr.co.vividnext.sodalive.content.main.banner.AudioContentBannerType
@ -26,64 +29,100 @@ class AudioContentMainService(
private val orderService: OrderService, private val orderService: OrderService,
private val audioContentThemeRepository: AudioContentThemeQueryRepository, private val audioContentThemeRepository: AudioContentThemeQueryRepository,
private val semaphoreManager: SemaphoreManager,
@Value("\${cloud.aws.cloud-front.host}") @Value("\${cloud.aws.cloud-front.host}")
private val imageHost: String private val imageHost: String
) { ) {
@Cacheable(cacheNames = ["default"], key = "'contentMain:' + #memberId + ':' + #isAdult") suspend fun getMain(memberId: Long, isAdult: Boolean): GetAudioContentMainResponse {
fun getMain(memberId: Long, isAdult: Boolean): GetAudioContentMainResponse { return coroutineScope {
// 2주일 이내에 콘텐츠를 올린 크리에이터 20명 조회 // 2주일 이내에 콘텐츠를 올린 크리에이터 20명 조회
val newContentUploadCreatorList = getNewContentUploadCreatorList(memberId = memberId, isAdult = isAdult) val newContentUploadCreatorList = async {
semaphoreManager.withPermit {
getNewContentUploadCreatorList(memberId = memberId, isAdult = isAdult)
}
}
val bannerList = getAudioContentMainBannerList(memberId = memberId, isAdult = isAdult) val bannerList = async {
semaphoreManager.withPermit {
getAudioContentMainBannerList(memberId = memberId, isAdult = isAdult)
}
}
// 구매목록 20개 // 구매목록 20개
val orderList = orderService.getAudioContentMainOrderList( val orderList = async {
memberId = memberId, semaphoreManager.withPermit {
limit = 20 orderService.getAudioContentMainOrderList(
) memberId = memberId,
limit = 20
)
}
}
// 콘텐츠 테마 // 콘텐츠 테마
val themeList = audioContentThemeRepository.getActiveThemeOfContent(isAdult = isAdult) val themeList = async {
semaphoreManager.withPermit {
audioContentThemeRepository.getActiveThemeOfContent(isAdult = isAdult)
}
}
// 새 콘텐츠 20개 - 시간 내림차순 정렬 // 새 콘텐츠 20개 - 시간 내림차순 정렬
val newContentList = repository.findByTheme( val newContentList = async {
cloudfrontHost = imageHost, semaphoreManager.withPermit {
isAdult = isAdult repository.findByTheme(
) cloudfrontHost = imageHost,
.asSequence() isAdult = isAdult
.filter { !blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = it.creatorId) } )
.toList() .asSequence()
.filter {
!blockMemberRepository.isBlocked(
blockedMemberId = memberId,
memberId = it.creatorId
)
}
.toList()
}
}
val curationList = getAudioContentCurationList(memberId = memberId, isAdult = isAdult) val curationList = async {
semaphoreManager.withPermit {
getAudioContentCurationList(memberId = memberId, isAdult = isAdult)
}
}
val currentDateTime = LocalDateTime.now() val currentDateTime = LocalDateTime.now()
val startDate = currentDateTime val startDate = currentDateTime
.minusWeeks(1) .minusWeeks(1)
.with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY))
.withHour(15) .withHour(15)
.withMinute(0) .withMinute(0)
.withSecond(0) .withSecond(0)
val endDate = startDate.plusDays(7) val endDate = startDate.plusDays(7)
val contentRankingSortTypeList = audioContentService.getContentRankingSortTypeList() val contentRankingSortTypeList = audioContentService.getContentRankingSortTypeList()
val contentRanking = audioContentService.getAudioContentRanking( val contentRanking = async {
isAdult = isAdult, semaphoreManager.withPermit {
startDate = startDate, audioContentService.getAudioContentRanking(
endDate = endDate, isAdult = isAdult,
offset = 0, startDate = startDate,
limit = 12 endDate = endDate,
) offset = 0,
limit = 12
)
}
}
return GetAudioContentMainResponse( GetAudioContentMainResponse(
newContentUploadCreatorList = newContentUploadCreatorList, newContentUploadCreatorList = newContentUploadCreatorList.await(),
bannerList = bannerList, bannerList = bannerList.await(),
orderList = orderList, orderList = orderList.await(),
themeList = themeList, themeList = themeList.await(),
newContentList = newContentList, newContentList = newContentList.await(),
curationList = curationList, curationList = curationList.await(),
contentRankingSortTypeList = contentRankingSortTypeList, contentRankingSortTypeList = contentRankingSortTypeList,
contentRanking = contentRanking contentRanking = contentRanking.await()
) )
}
} }
fun getThemeList(member: Member): List<String> { fun getThemeList(member: Member): List<String> {
@ -116,6 +155,7 @@ class AudioContentMainService(
return GetNewContentAllResponse(totalCount, items) return GetNewContentAllResponse(totalCount, items)
} }
@Cacheable(cacheNames = ["default"], key = "'newContentUploadCreatorList:' + #memberId + ':' + #isAdult")
fun getNewContentUploadCreatorList(memberId: Long, isAdult: Boolean): List<GetNewContentUploadCreator> { fun getNewContentUploadCreatorList(memberId: Long, isAdult: Boolean): List<GetNewContentUploadCreator> {
return repository.getNewContentUploadCreatorList( return repository.getNewContentUploadCreatorList(
cloudfrontHost = imageHost, cloudfrontHost = imageHost,
@ -126,6 +166,7 @@ class AudioContentMainService(
.toList() .toList()
} }
@Cacheable(cacheNames = ["default"], key = "'contentMainBannerList:' + #memberId + ':' + #isAdult")
fun getAudioContentMainBannerList(memberId: Long, isAdult: Boolean) = fun getAudioContentMainBannerList(memberId: Long, isAdult: Boolean) =
repository.getAudioContentMainBannerList(isAdult = isAdult) repository.getAudioContentMainBannerList(isAdult = isAdult)
.asSequence() .asSequence()
@ -174,6 +215,7 @@ class AudioContentMainService(
} }
.toList() .toList()
@Cacheable(cacheNames = ["default"], key = "'contentCurationList:' + #memberId + ':' + #isAdult")
fun getAudioContentCurationList(memberId: Long, isAdult: Boolean) = fun getAudioContentCurationList(memberId: Long, isAdult: Boolean) =
repository.getAudioContentCurations(isAdult = isAdult) repository.getAudioContentCurations(isAdult = isAdult)
.asSequence() .asSequence()