diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/configs/RedisConfig.kt b/src/main/kotlin/kr/co/vividnext/sodalive/configs/RedisConfig.kt index 3fa5d11..015324c 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/configs/RedisConfig.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/configs/RedisConfig.kt @@ -4,10 +4,13 @@ import org.springframework.beans.factory.annotation.Value import org.springframework.cache.annotation.EnableCaching import org.springframework.context.annotation.Bean import org.springframework.context.annotation.Configuration +import org.springframework.data.redis.cache.RedisCacheConfiguration +import org.springframework.data.redis.cache.RedisCacheManager import org.springframework.data.redis.connection.RedisConnectionFactory import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory import org.springframework.data.redis.core.RedisTemplate import org.springframework.data.redis.repository.configuration.EnableRedisRepositories +import java.time.Duration @Configuration @EnableCaching @@ -29,4 +32,21 @@ class RedisConfig( redisTemplate.setConnectionFactory(redisConnectionFactory()) return redisTemplate } + + @Bean + fun cacheManager(redisConnectionFactory: RedisConnectionFactory): RedisCacheManager { + val defaultCacheConfig = RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofMinutes(30)) + + val cacheConfigMap = mutableMapOf<String, RedisCacheConfiguration>() + cacheConfigMap["default"] = RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofMinutes(30)) + cacheConfigMap["cache_ttl_3_days"] = RedisCacheConfiguration.defaultCacheConfig() + .entryTtl(Duration.ofDays(3)) + + return RedisCacheManager.builder(redisConnectionFactory) + .cacheDefaults(defaultCacheConfig) + .withInitialCacheConfigurations(cacheConfigMap) + .build() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt index 5b6b050..c2d80d0 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/main/AudioContentMainService.kt @@ -10,6 +10,7 @@ import kr.co.vividnext.sodalive.event.EventItem import kr.co.vividnext.sodalive.member.Member import kr.co.vividnext.sodalive.member.block.BlockMemberRepository import org.springframework.beans.factory.annotation.Value +import org.springframework.cache.annotation.Cacheable import org.springframework.data.domain.Pageable import org.springframework.stereotype.Service import java.time.DayOfWeek @@ -29,21 +30,103 @@ class AudioContentMainService( ) { fun getMain(member: Member): GetAudioContentMainResponse { val isAdult = member.auth != null + val memberId = member.id!! // 2주일 이내에 콘텐츠를 올린 크리에이터 20명 조회 - val newContentUploadCreatorList = repository.getNewContentUploadCreatorList( + val newContentUploadCreatorList = getNewContentUploadCreatorList(memberId = memberId, isAdult = isAdult) + + val bannerList = getAudioContentMainBannerList(memberId = memberId, isAdult = isAdult) + + // 구매목록 20개 + val orderList = orderService.getAudioContentMainOrderList( + member = member, + limit = 20 + ) + + // 콘텐츠 테마 + val themeList = audioContentThemeRepository.getActiveThemeOfContent(isAdult = isAdult) + + // 새 콘텐츠 20개 - 시간 내림차순 정렬 + val newContentList = repository.findByTheme( cloudfrontHost = imageHost, isAdult = isAdult ) .asSequence() - .filter { !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.creatorId) } + .filter { !blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = it.creatorId) } .toList() - val bannerList = repository.getAudioContentMainBannerList(isAdult = isAdult) + val curationList = getAudioContentCurationList(memberId = memberId, isAdult = isAdult) + + val currentDateTime = LocalDateTime.now() + val startDate = currentDateTime + .minusWeeks(1) + .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) + .withHour(15) + .withMinute(0) + .withSecond(0) + val endDate = startDate.plusDays(7) + + val contentRanking = getContentRanking(isAdult = isAdult, startDate = startDate, endDate = endDate) + + return GetAudioContentMainResponse( + newContentUploadCreatorList = newContentUploadCreatorList, + bannerList = bannerList, + orderList = orderList, + themeList = themeList, + newContentList = newContentList, + curationList = curationList, + contentRanking = contentRanking + ) + } + + fun getThemeList(member: Member): List<String> { + return audioContentThemeRepository.getActiveThemeOfContent(isAdult = member.auth != null) + } + + fun getNewContentByTheme(theme: String, member: Member, pageable: Pageable): List<GetAudioContentMainItem> { + return repository.findByTheme( + cloudfrontHost = imageHost, + theme = theme, + isAdult = member.auth != null, + offset = pageable.offset, + limit = pageable.pageSize.toLong() + ) + .asSequence() + .filter { !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.creatorId) } + .toList() + } + + fun getNewContentFor2WeeksByTheme(theme: String, member: Member, pageable: Pageable): GetNewContentAllResponse { + val totalCount = repository.totalCountNewContentFor2Weeks(theme, isAdult = member.auth != null) + val items = repository.findByThemeFor2Weeks( + cloudfrontHost = imageHost, + theme = theme, + isAdult = member.auth != null, + offset = pageable.offset, + limit = pageable.pageSize.toLong() + ) + + return GetNewContentAllResponse(totalCount, items) + } + + @Cacheable(cacheNames = ["default"], key = "'getNewContentUploadCreatorList:' + #memberId + ':' + #isAdult") + fun getNewContentUploadCreatorList(memberId: Long, isAdult: Boolean): List<GetNewContentUploadCreator> { + return repository.getNewContentUploadCreatorList( + cloudfrontHost = imageHost, + isAdult = isAdult + ) + .asSequence() + .filter { !blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = it.creatorId) } + .toList() + } + + @Cacheable(cacheNames = ["default"], key = "'getAudioContentMainBannerList:' + #memberId + ':' + #isAdult") + fun getAudioContentMainBannerList(memberId: Long, isAdult: Boolean) = + repository.getAudioContentMainBannerList(isAdult = isAdult) .asSequence() .filter { if (it.type == AudioContentBannerType.CREATOR && it.creator != null) { - !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.creator!!.id!!) + !blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = it.creator!!.id!!) } else { true } @@ -86,26 +169,9 @@ class AudioContentMainService( } .toList() - // 구매목록 20개 - val orderList = orderService.getAudioContentMainOrderList( - member = member, - limit = 20 - ) - - // 콘텐츠 테마 - val themeList = audioContentThemeRepository.getActiveThemeOfContent(isAdult = isAdult) - - // 새 콘텐츠 20개 - 시간 내림차순 정렬 - val newContentList = repository.findByTheme( - cloudfrontHost = imageHost, - isAdult = isAdult - ) - .asSequence() - .filter { !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.creatorId) } - .toList() - - val curationList = repository - .getAudioContentCurations(isAdult = isAdult) + @Cacheable(cacheNames = ["default"], key = "'getAudioContentCurationList:' + #memberId + ':' + #isAdult") + fun getAudioContentCurationList(memberId: Long, isAdult: Boolean) = + repository.getAudioContentCurations(isAdult = isAdult) .asSequence() .map { GetAudioContentCurationResponse( @@ -119,10 +185,7 @@ class AudioContentMainService( ) .asSequence() .filter { content -> - !blockMemberRepository.isBlocked( - blockedMemberId = member.id!!, - memberId = content.creatorId - ) + !blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = content.creatorId) } .toList() ) @@ -130,16 +193,11 @@ class AudioContentMainService( .filter { it.contents.isNotEmpty() } .toList() - val currentDateTime = LocalDateTime.now() - val startDate = currentDateTime - .minusWeeks(1) - .with(TemporalAdjusters.previousOrSame(DayOfWeek.MONDAY)) - .withHour(15) - .withMinute(0) - .withSecond(0) - val endDate = startDate - .plusDays(7) - + @Cacheable( + cacheNames = ["cache_ttl_3_days"], + key = "'getAudioContentCurationList:' + ':' + #isAdult + ':' + #startDate + ':' + #endDate" + ) + fun getContentRanking(isAdult: Boolean, startDate: LocalDateTime, endDate: LocalDateTime): GetAudioContentRanking { val startDateFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일") val endDateFormatter = DateTimeFormatter.ofPattern("MM월 dd일") @@ -151,50 +209,10 @@ class AudioContentMainService( isAdult = isAdult ) - val contentRanking = GetAudioContentRanking( + return GetAudioContentRanking( startDate = startDate.format(startDateFormatter), endDate = endDate.minusDays(1).format(endDateFormatter), contentRankingItemList ) - - return GetAudioContentMainResponse( - newContentUploadCreatorList = newContentUploadCreatorList, - bannerList = bannerList, - orderList = orderList, - themeList = themeList, - newContentList = newContentList, - curationList = curationList, - contentRanking = contentRanking - ) - } - - fun getThemeList(member: Member): List<String> { - return audioContentThemeRepository.getActiveThemeOfContent(isAdult = member.auth != null) - } - - fun getNewContentByTheme(theme: String, member: Member, pageable: Pageable): List<GetAudioContentMainItem> { - return repository.findByTheme( - cloudfrontHost = imageHost, - theme = theme, - isAdult = member.auth != null, - offset = pageable.offset, - limit = pageable.pageSize.toLong() - ) - .asSequence() - .filter { !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.creatorId) } - .toList() - } - - fun getNewContentFor2WeeksByTheme(theme: String, member: Member, pageable: Pageable): GetNewContentAllResponse { - val totalCount = repository.totalCountNewContentFor2Weeks(theme, isAdult = member.auth != null) - val items = repository.findByThemeFor2Weeks( - cloudfrontHost = imageHost, - theme = theme, - isAdult = member.auth != null, - offset = pageable.offset, - limit = pageable.pageSize.toLong() - ) - - return GetNewContentAllResponse(totalCount, items) } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentThemeQueryRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentThemeQueryRepository.kt index e5cba07..bf7bfc1 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentThemeQueryRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/theme/AudioContentThemeQueryRepository.kt @@ -4,6 +4,7 @@ import com.querydsl.jpa.impl.JPAQueryFactory import kr.co.vividnext.sodalive.content.QAudioContent.audioContent import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme import org.springframework.beans.factory.annotation.Value +import org.springframework.cache.annotation.Cacheable import org.springframework.stereotype.Repository @Repository @@ -27,6 +28,7 @@ class AudioContentThemeQueryRepository( .fetch() } + @Cacheable(cacheNames = ["default"], key = "'getActiveThemeOfContent:' + ':' + #isAdult") fun getActiveThemeOfContent(isAdult: Boolean = false): List<String> { var where = audioContent.isActive.isTrue .and(audioContentTheme.isActive.isTrue)