| @@ -75,7 +75,13 @@ class HomeService( | ||||
|             } | ||||
|             .map { | ||||
|                 val followerCount = explorerQueryRepository.getNotificationUserIds(it.id!!).size | ||||
|                 it.toExplorerSectionCreator(imageHost, followerCount) | ||||
|                 val follow = if (memberId != null) { | ||||
|                     explorerQueryRepository.isFollow(it.id!!, memberId = memberId) | ||||
|                 } else { | ||||
|                     false | ||||
|                 } | ||||
|  | ||||
|                 it.toExplorerSectionCreator(imageHost, follow, followerCount = followerCount) | ||||
|             } | ||||
|  | ||||
|         val latestContentThemeList = contentThemeService.getActiveThemeOfContent( | ||||
|   | ||||
| @@ -0,0 +1,33 @@ | ||||
| package kr.co.vividnext.sodalive.api.live | ||||
|  | ||||
| import kr.co.vividnext.sodalive.common.ApiResponse | ||||
| import kr.co.vividnext.sodalive.content.ContentType | ||||
| import kr.co.vividnext.sodalive.member.Member | ||||
| import org.springframework.security.core.annotation.AuthenticationPrincipal | ||||
| import org.springframework.web.bind.annotation.GetMapping | ||||
| import org.springframework.web.bind.annotation.RequestMapping | ||||
| import org.springframework.web.bind.annotation.RequestParam | ||||
| import org.springframework.web.bind.annotation.RestController | ||||
|  | ||||
| @RestController | ||||
| @RequestMapping("/api/live") | ||||
| class LiveApiController( | ||||
|     private val service: LiveApiService | ||||
| ) { | ||||
|     @GetMapping | ||||
|     fun fetchData( | ||||
|         @RequestParam timezone: String, | ||||
|         @RequestParam("contentType", required = false) contentType: ContentType? = null, | ||||
|         @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, | ||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? | ||||
|     ) = run { | ||||
|         ApiResponse.ok( | ||||
|             service.fetchData( | ||||
|                 isAdultContentVisible = isAdultContentVisible ?: true, | ||||
|                 contentType = contentType ?: ContentType.ALL, | ||||
|                 timezone = timezone, | ||||
|                 member = member | ||||
|             ) | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,94 @@ | ||||
| package kr.co.vividnext.sodalive.api.live | ||||
|  | ||||
| import kr.co.vividnext.sodalive.content.AudioContentService | ||||
| import kr.co.vividnext.sodalive.content.ContentType | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.CreatorCommunityService | ||||
| import kr.co.vividnext.sodalive.live.recommend.LiveRecommendService | ||||
| import kr.co.vividnext.sodalive.live.room.LiveRoomService | ||||
| import kr.co.vividnext.sodalive.live.room.LiveRoomStatus | ||||
| import kr.co.vividnext.sodalive.member.Member | ||||
| import kr.co.vividnext.sodalive.member.block.BlockMemberRepository | ||||
| import org.springframework.data.domain.Pageable | ||||
| import org.springframework.stereotype.Service | ||||
|  | ||||
| @Service | ||||
| class LiveApiService( | ||||
|     private val liveService: LiveRoomService, | ||||
|     private val contentService: AudioContentService, | ||||
|     private val recommendService: LiveRecommendService, | ||||
|     private val creatorCommunityService: CreatorCommunityService, | ||||
|  | ||||
|     private val blockMemberRepository: BlockMemberRepository | ||||
| ) { | ||||
|     fun fetchData( | ||||
|         isAdultContentVisible: Boolean, | ||||
|         contentType: ContentType, | ||||
|         timezone: String, | ||||
|         member: Member? | ||||
|     ): LiveMainResponse { | ||||
|         val memberId = member?.id | ||||
|         val isAdult = member?.auth != null && isAdultContentVisible | ||||
|  | ||||
|         val liveOnAirRoomList = liveService.getRoomList( | ||||
|             dateString = null, | ||||
|             status = LiveRoomStatus.NOW, | ||||
|             isAdultContentVisible = isAdultContentVisible, | ||||
|             pageable = Pageable.ofSize(20), | ||||
|             member = member, | ||||
|             timezone = timezone | ||||
|         ) | ||||
|  | ||||
|         val communityPostList = if (memberId != null) { | ||||
|             creatorCommunityService.getLatestPostListFromCreatorsYouFollow( | ||||
|                 timezone = timezone, | ||||
|                 memberId = memberId, | ||||
|                 isAdult = isAdult | ||||
|             ) | ||||
|         } else { | ||||
|             listOf() | ||||
|         } | ||||
|  | ||||
|         val recommendLiveList = recommendService.getRecommendLive(member) | ||||
|  | ||||
|         val latestFinishedLiveList = liveService.getLatestFinishedLive(member) | ||||
|  | ||||
|         val replayLive = contentService.getLatestContentByTheme( | ||||
|             theme = listOf("다시듣기"), | ||||
|             contentType = contentType, | ||||
|             isFree = false, | ||||
|             isAdult = isAdult | ||||
|         ) | ||||
|             .filter { content -> | ||||
|                 if (memberId != null) { | ||||
|                     !blockMemberRepository.isBlocked(blockedMemberId = memberId, memberId = content.creatorId) | ||||
|                 } else { | ||||
|                     true | ||||
|                 } | ||||
|             } | ||||
|  | ||||
|         val followingChannelList = if (memberId != null) { | ||||
|             recommendService.getFollowingChannelList(member) | ||||
|         } else { | ||||
|             listOf() | ||||
|         } | ||||
|  | ||||
|         val liveReservationRoomList = liveService.getRoomList( | ||||
|             dateString = null, | ||||
|             status = LiveRoomStatus.RESERVATION, | ||||
|             isAdultContentVisible = isAdultContentVisible, | ||||
|             pageable = Pageable.ofSize(10), | ||||
|             member = member, | ||||
|             timezone = timezone | ||||
|         ) | ||||
|  | ||||
|         return LiveMainResponse( | ||||
|             liveOnAirRoomList = liveOnAirRoomList, | ||||
|             communityPostList = communityPostList, | ||||
|             recommendLiveList = recommendLiveList, | ||||
|             latestFinishedLiveList = latestFinishedLiveList, | ||||
|             replayLive = replayLive, | ||||
|             followingChannelList = followingChannelList, | ||||
|             liveReservationRoomList = liveReservationRoomList | ||||
|         ) | ||||
|     } | ||||
| } | ||||
| @@ -0,0 +1,18 @@ | ||||
| package kr.co.vividnext.sodalive.api.live | ||||
|  | ||||
| import kr.co.vividnext.sodalive.content.AudioContentMainItem | ||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.GetCommunityPostListResponse | ||||
| import kr.co.vividnext.sodalive.live.recommend.GetRecommendChannelResponse | ||||
| import kr.co.vividnext.sodalive.live.recommend.GetRecommendLiveResponse | ||||
| import kr.co.vividnext.sodalive.live.room.GetLatestFinishedLiveResponse | ||||
| import kr.co.vividnext.sodalive.live.room.GetRoomListResponse | ||||
|  | ||||
| data class LiveMainResponse( | ||||
|     val liveOnAirRoomList: List<GetRoomListResponse>, | ||||
|     val communityPostList: List<GetCommunityPostListResponse>, | ||||
|     val recommendLiveList: List<GetRecommendLiveResponse>, | ||||
|     val latestFinishedLiveList: List<GetLatestFinishedLiveResponse>, | ||||
|     val replayLive: List<AudioContentMainItem>, | ||||
|     val followingChannelList: List<GetRecommendChannelResponse>, | ||||
|     val liveReservationRoomList: List<GetRoomListResponse> | ||||
| ) | ||||
| @@ -96,6 +96,33 @@ class RedisConfig( | ||||
|                 ) | ||||
|             ) | ||||
|  | ||||
|         cacheConfigMap["cache_ttl_10_minutes"] = RedisCacheConfiguration.defaultCacheConfig() | ||||
|             .entryTtl(Duration.ofMinutes(10)) | ||||
|             .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer())) | ||||
|             .serializeValuesWith( | ||||
|                 RedisSerializationContext.SerializationPair.fromSerializer( | ||||
|                     GenericJackson2JsonRedisSerializer() | ||||
|                 ) | ||||
|             ) | ||||
|  | ||||
|         cacheConfigMap["cache_ttl_5_minutes"] = RedisCacheConfiguration.defaultCacheConfig() | ||||
|             .entryTtl(Duration.ofMinutes(5)) | ||||
|             .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer())) | ||||
|             .serializeValuesWith( | ||||
|                 RedisSerializationContext.SerializationPair.fromSerializer( | ||||
|                     GenericJackson2JsonRedisSerializer() | ||||
|                 ) | ||||
|             ) | ||||
|  | ||||
|         cacheConfigMap["cache_ttl_3_minutes"] = RedisCacheConfiguration.defaultCacheConfig() | ||||
|             .entryTtl(Duration.ofMinutes(3)) | ||||
|             .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer())) | ||||
|             .serializeValuesWith( | ||||
|                 RedisSerializationContext.SerializationPair.fromSerializer( | ||||
|                     GenericJackson2JsonRedisSerializer() | ||||
|                 ) | ||||
|             ) | ||||
|  | ||||
|         return RedisCacheManager.builder(redisConnectionFactory) | ||||
|             .cacheDefaults(defaultCacheConfig) | ||||
|             .withInitialCacheConfigurations(cacheConfigMap) | ||||
|   | ||||
| @@ -83,6 +83,7 @@ class SecurityConfig( | ||||
|             .antMatchers("/api/home").permitAll() | ||||
|             .antMatchers("/api/home/latest-content").permitAll() | ||||
|             .antMatchers("/api/home/day-of-week-series").permitAll() | ||||
|             .antMatchers(HttpMethod.GET, "/api/live").permitAll() | ||||
|             .antMatchers(HttpMethod.GET, "/faq").permitAll() | ||||
|             .antMatchers(HttpMethod.GET, "/faq/category").permitAll() | ||||
|             .antMatchers("/audition").permitAll() | ||||
|   | ||||
| @@ -939,6 +939,7 @@ class AudioContentService( | ||||
|         return GenerateUrlResponse(contentUrl) | ||||
|     } | ||||
|  | ||||
|     @Transactional(readOnly = true) | ||||
|     fun getLatestContentByTheme( | ||||
|         theme: List<String>, | ||||
|         contentType: ContentType, | ||||
|   | ||||
| @@ -15,5 +15,6 @@ data class GetExplorerSectionCreatorResponse( | ||||
|     val nickname: String, | ||||
|     val tags: String, | ||||
|     val profileImageUrl: String, | ||||
|     val follow: Boolean, | ||||
|     val followerCount: Int | ||||
| ) | ||||
|   | ||||
| @@ -441,6 +441,7 @@ class CreatorCommunityService( | ||||
|         return GetCommunityPostCommentListResponse(totalCount = totalCount, items = commentList) | ||||
|     } | ||||
|  | ||||
|     @Transactional(readOnly = true) | ||||
|     fun getLatestPostListFromCreatorsYouFollow( | ||||
|         timezone: String, | ||||
|         memberId: Long, | ||||
|   | ||||
| @@ -3,15 +3,21 @@ package kr.co.vividnext.sodalive.live.recommend | ||||
| import kr.co.vividnext.sodalive.member.Member | ||||
| import kr.co.vividnext.sodalive.member.MemberRole | ||||
| import kr.co.vividnext.sodalive.member.block.BlockMemberRepository | ||||
| import org.springframework.cache.annotation.Cacheable | ||||
| import org.springframework.data.domain.Pageable | ||||
| import org.springframework.stereotype.Service | ||||
| import org.springframework.transaction.annotation.Transactional | ||||
|  | ||||
| @Service | ||||
| class LiveRecommendService( | ||||
|     private val repository: LiveRecommendRepository, | ||||
|     private val blockMemberRepository: BlockMemberRepository | ||||
| ) { | ||||
|  | ||||
|     @Transactional(readOnly = true) | ||||
|     @Cacheable( | ||||
|         cacheNames = ["cache_ttl_3_hours"], | ||||
|         key = "'getRecommendLive:' + (#member ?: 'guest')" | ||||
|     ) | ||||
|     fun getRecommendLive(member: Member?): List<GetRecommendLiveResponse> { | ||||
|         return repository.getRecommendLive( | ||||
|             isBlocked = { | ||||
|   | ||||
| @@ -1,5 +1,6 @@ | ||||
| package kr.co.vividnext.sodalive.live.room | ||||
|  | ||||
| import com.fasterxml.jackson.annotation.JsonProperty | ||||
| import com.querydsl.core.annotations.QueryProjection | ||||
| import kr.co.vividnext.sodalive.extensions.getTimeAgoString | ||||
| import java.time.LocalDateTime | ||||
| @@ -8,22 +9,19 @@ data class GetLatestFinishedLiveQueryResponse @QueryProjection constructor( | ||||
|     val memberId: Long, | ||||
|     val nickname: String, | ||||
|     val profileImageUrl: String, | ||||
|     val title: String, | ||||
|     val updatedAt: LocalDateTime | ||||
| ) | ||||
|  | ||||
| data class GetLatestFinishedLiveResponse( | ||||
|     val memberId: Long, | ||||
|     val nickname: String, | ||||
|     val profileImageUrl: String, | ||||
|     val title: String, | ||||
|     val timeAgo: String | ||||
|     @JsonProperty("memberId") val memberId: Long, | ||||
|     @JsonProperty("nickname") val nickname: String, | ||||
|     @JsonProperty("profileImageUrl") val profileImageUrl: String, | ||||
|     @JsonProperty("timeAgo") val timeAgo: String | ||||
| ) { | ||||
|     constructor(response: GetLatestFinishedLiveQueryResponse) : this( | ||||
|         response.memberId, | ||||
|         response.nickname, | ||||
|         response.profileImageUrl, | ||||
|         response.title, | ||||
|         response.updatedAt.getTimeAgoString() | ||||
|     ) | ||||
| } | ||||
|   | ||||
| @@ -404,7 +404,6 @@ class LiveRoomQueryRepositoryImpl( | ||||
|                     member.id, | ||||
|                     member.nickname, | ||||
|                     member.profileImage.prepend("/").prepend(cloudFrontHost), | ||||
|                     liveRoom.title, | ||||
|                     liveRoom.updatedAt | ||||
|                 ) | ||||
|             ) | ||||
|   | ||||
| @@ -56,6 +56,7 @@ import kr.co.vividnext.sodalive.member.MemberRole | ||||
| 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.cache.annotation.Cacheable | ||||
| import org.springframework.context.ApplicationEventPublisher | ||||
| import org.springframework.data.domain.Pageable | ||||
| import org.springframework.data.repository.findByIdOrNull | ||||
| @@ -113,6 +114,7 @@ class LiveRoomService( | ||||
| ) { | ||||
|     private val tokenLocks: MutableMap<Long, ReentrantReadWriteLock> = mutableMapOf() | ||||
|  | ||||
|     @Transactional(readOnly = true) | ||||
|     fun getRoomList( | ||||
|         dateString: String?, | ||||
|         status: LiveRoomStatus, | ||||
| @@ -1297,6 +1299,11 @@ class LiveRoomService( | ||||
|         ) | ||||
|     } | ||||
|  | ||||
|     @Transactional(readOnly = true) | ||||
|     @Cacheable( | ||||
|         cacheNames = ["cache_ttl_10_minutes"], | ||||
|         key = "'getLatestFinishedLive:' + (#member ?: 'guest')" | ||||
|     ) | ||||
|     fun getLatestFinishedLive(member: Member?): List<GetLatestFinishedLiveResponse> { | ||||
|         return repository.getLatestFinishedLive() | ||||
|             .filter { | ||||
|   | ||||
| @@ -123,7 +123,11 @@ data class Member( | ||||
|         } | ||||
|     } | ||||
|  | ||||
|     fun toExplorerSectionCreator(imageHost: String, followerCount: Int = 0): GetExplorerSectionCreatorResponse { | ||||
|     fun toExplorerSectionCreator( | ||||
|         imageHost: String, | ||||
|         follow: Boolean = false, | ||||
|         followerCount: Int = 0 | ||||
|     ): GetExplorerSectionCreatorResponse { | ||||
|         return GetExplorerSectionCreatorResponse( | ||||
|             id = id!!, | ||||
|             nickname = nickname, | ||||
| @@ -136,7 +140,8 @@ data class Member( | ||||
|             } else { | ||||
|                 "$imageHost/profile/default-profile.png" | ||||
|             }, | ||||
|             followerCount = followerCount | ||||
|             followerCount = followerCount, | ||||
|             follow = follow | ||||
|         ) | ||||
|     } | ||||
| } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user