현재 사용하지 않는 API 제거, 캐시 전략 변경을 위해 @Cacheable 제거
This commit is contained in:
		| @@ -4,16 +4,10 @@ import org.springframework.beans.factory.annotation.Value | |||||||
| import org.springframework.cache.annotation.EnableCaching | import org.springframework.cache.annotation.EnableCaching | ||||||
| import org.springframework.context.annotation.Bean | import org.springframework.context.annotation.Bean | ||||||
| import org.springframework.context.annotation.Configuration | 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.RedisConnectionFactory | ||||||
| import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory | import org.springframework.data.redis.connection.lettuce.LettuceConnectionFactory | ||||||
| import org.springframework.data.redis.core.RedisTemplate | import org.springframework.data.redis.core.RedisTemplate | ||||||
| import org.springframework.data.redis.repository.configuration.EnableRedisRepositories | import org.springframework.data.redis.repository.configuration.EnableRedisRepositories | ||||||
| import org.springframework.data.redis.serializer.GenericJackson2JsonRedisSerializer |  | ||||||
| import org.springframework.data.redis.serializer.RedisSerializationContext |  | ||||||
| import org.springframework.data.redis.serializer.StringRedisSerializer |  | ||||||
| import java.time.Duration |  | ||||||
|  |  | ||||||
| @Configuration | @Configuration | ||||||
| @EnableCaching | @EnableCaching | ||||||
| @@ -35,31 +29,4 @@ class RedisConfig( | |||||||
|         redisTemplate.setConnectionFactory(redisConnectionFactory()) |         redisTemplate.setConnectionFactory(redisConnectionFactory()) | ||||||
|         return redisTemplate |         return redisTemplate | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Bean |  | ||||||
|     fun cacheManager(connectionFactory: RedisConnectionFactory): RedisCacheManager { |  | ||||||
|         val defaultConfig = RedisCacheConfiguration.defaultCacheConfig() |  | ||||||
|             .entryTtl(Duration.ofHours(1)) // Default TTL |  | ||||||
|             .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer())) |  | ||||||
|             .serializeValuesWith( |  | ||||||
|                 RedisSerializationContext.SerializationPair.fromSerializer( |  | ||||||
|                     GenericJackson2JsonRedisSerializer() |  | ||||||
|                 ) |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|         val weekLivedCacheConfig = RedisCacheConfiguration.defaultCacheConfig() |  | ||||||
|             .entryTtl(Duration.ofDays(3)) |  | ||||||
|             .serializeKeysWith(RedisSerializationContext.SerializationPair.fromSerializer(StringRedisSerializer())) |  | ||||||
|             .serializeValuesWith( |  | ||||||
|                 RedisSerializationContext.SerializationPair.fromSerializer( |  | ||||||
|                     GenericJackson2JsonRedisSerializer() |  | ||||||
|                 ) |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|         return RedisCacheManager.RedisCacheManagerBuilder |  | ||||||
|             .fromConnectionFactory(connectionFactory) |  | ||||||
|             .cacheDefaults(defaultConfig) |  | ||||||
|             .withCacheConfiguration("weekLivedCache", weekLivedCacheConfig) |  | ||||||
|             .build() |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -17,7 +17,6 @@ import kr.co.vividnext.sodalive.content.order.QOrder.order | |||||||
| import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme | import kr.co.vividnext.sodalive.content.theme.QAudioContentTheme.audioContentTheme | ||||||
| import kr.co.vividnext.sodalive.event.QEvent.event | import kr.co.vividnext.sodalive.event.QEvent.event | ||||||
| import kr.co.vividnext.sodalive.member.QMember.member | import kr.co.vividnext.sodalive.member.QMember.member | ||||||
| import org.springframework.cache.annotation.Cacheable |  | ||||||
| import org.springframework.data.jpa.repository.JpaRepository | import org.springframework.data.jpa.repository.JpaRepository | ||||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||||
| import java.time.LocalDateTime | import java.time.LocalDateTime | ||||||
| @@ -77,9 +76,6 @@ interface AudioContentQueryRepository { | |||||||
|  |  | ||||||
|     fun getAudioContentMainBannerList(isAdult: Boolean): List<AudioContentBanner> |     fun getAudioContentMainBannerList(isAdult: Boolean): List<AudioContentBanner> | ||||||
|     fun getAudioContentCurations(isAdult: Boolean): List<AudioContentCuration> |     fun getAudioContentCurations(isAdult: Boolean): List<AudioContentCuration> | ||||||
|  |  | ||||||
|     fun getAudioContentCurationList(isAdult: Boolean, offset: Long = 0, limit: Long = 10): List<AudioContentCuration> |  | ||||||
|  |  | ||||||
|     fun findAudioContentByCurationId( |     fun findAudioContentByCurationId( | ||||||
|         curationId: Long, |         curationId: Long, | ||||||
|         cloudfrontHost: String, |         cloudfrontHost: String, | ||||||
| @@ -390,10 +386,6 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) | |||||||
|             .fetch() |             .fetch() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Cacheable( |  | ||||||
|         value = ["getAudioContentCurations"], |  | ||||||
|         cacheManager = "cacheManager" |  | ||||||
|     ) |  | ||||||
|     override fun getAudioContentCurations(isAdult: Boolean): List<AudioContentCuration> { |     override fun getAudioContentCurations(isAdult: Boolean): List<AudioContentCuration> { | ||||||
|         var where = audioContentCuration.isActive.isTrue |         var where = audioContentCuration.isActive.isTrue | ||||||
|  |  | ||||||
| @@ -408,26 +400,6 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) | |||||||
|             .fetch() |             .fetch() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     override fun getAudioContentCurationList( |  | ||||||
|         isAdult: Boolean, |  | ||||||
|         offset: Long, |  | ||||||
|         limit: Long |  | ||||||
|     ): List<AudioContentCuration> { |  | ||||||
|         var where = audioContentCuration.isActive.isTrue |  | ||||||
|  |  | ||||||
|         if (!isAdult) { |  | ||||||
|             where = where.and(audioContentCuration.isAdult.isFalse) |  | ||||||
|         } |  | ||||||
|  |  | ||||||
|         return queryFactory |  | ||||||
|             .selectFrom(audioContentCuration) |  | ||||||
|             .where(where) |  | ||||||
|             .orderBy(audioContentCuration.orders.asc()) |  | ||||||
|             .offset(offset) |  | ||||||
|             .limit(limit) |  | ||||||
|             .fetch() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     override fun findAudioContentByCurationId( |     override fun findAudioContentByCurationId( | ||||||
|         curationId: Long, |         curationId: Long, | ||||||
|         cloudfrontHost: String, |         cloudfrontHost: String, | ||||||
| @@ -465,10 +437,6 @@ class AudioContentQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) | |||||||
|             .fetch() |             .fetch() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Cacheable( |  | ||||||
|         value = ["weekLivedCache"], |  | ||||||
|         cacheManager = "cacheManager" |  | ||||||
|     ) |  | ||||||
|     override fun getAudioContentRanking( |     override fun getAudioContentRanking( | ||||||
|         cloudfrontHost: String, |         cloudfrontHost: String, | ||||||
|         isAdult: Boolean, |         isAdult: Boolean, | ||||||
|   | |||||||
| @@ -23,33 +23,6 @@ class AudioContentMainController(private val service: AudioContentMainService) { | |||||||
|         ApiResponse.ok(service.getMain(member = member)) |         ApiResponse.ok(service.getMain(member = member)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @GetMapping("/new-content-upload-creator") |  | ||||||
|     fun getNewContentUploadCreatorList( |  | ||||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? |  | ||||||
|     ) = run { |  | ||||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") |  | ||||||
|  |  | ||||||
|         ApiResponse.ok(service.getNewContentUploadCreatorList(member = member)) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @GetMapping("/banner-list") |  | ||||||
|     fun getAudioContentMainBannerList( |  | ||||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? |  | ||||||
|     ) = run { |  | ||||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") |  | ||||||
|  |  | ||||||
|         ApiResponse.ok(service.getAudioContentMainBannerList(member = member)) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @GetMapping("/order-list") |  | ||||||
|     fun getAudioContentMainOrderList( |  | ||||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? |  | ||||||
|     ) = run { |  | ||||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") |  | ||||||
|  |  | ||||||
|         ApiResponse.ok(service.getAudioContentMainOrderList(member = member)) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @GetMapping("/new") |     @GetMapping("/new") | ||||||
|     fun getNewContentByTheme( |     fun getNewContentByTheme( | ||||||
|         @RequestParam("theme") theme: String, |         @RequestParam("theme") theme: String, | ||||||
| @@ -80,29 +53,4 @@ class AudioContentMainController(private val service: AudioContentMainService) { | |||||||
|  |  | ||||||
|         ApiResponse.ok(service.getNewContentFor2WeeksByTheme(theme, member, pageable)) |         ApiResponse.ok(service.getNewContentFor2WeeksByTheme(theme, member, pageable)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @GetMapping("/curation-list") |  | ||||||
|     fun getAudioContentMainCurationList( |  | ||||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?, |  | ||||||
|         pageable: Pageable |  | ||||||
|     ) = run { |  | ||||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") |  | ||||||
|  |  | ||||||
|         ApiResponse.ok( |  | ||||||
|             service.getAudioContentMainCurationList( |  | ||||||
|                 member, |  | ||||||
|                 offset = pageable.offset, |  | ||||||
|                 limit = pageable.pageSize.toLong() |  | ||||||
|             ) |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @GetMapping("/content-ranking") |  | ||||||
|     fun getAudioContentMainContentRanking( |  | ||||||
|         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? |  | ||||||
|     ) = run { |  | ||||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") |  | ||||||
|  |  | ||||||
|         ApiResponse.ok(service.getAudioContentMainContentRanking(member)) |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -10,7 +10,6 @@ import kr.co.vividnext.sodalive.event.EventItem | |||||||
| import kr.co.vividnext.sodalive.member.Member | import kr.co.vividnext.sodalive.member.Member | ||||||
| import kr.co.vividnext.sodalive.member.block.BlockMemberRepository | import kr.co.vividnext.sodalive.member.block.BlockMemberRepository | ||||||
| import org.springframework.beans.factory.annotation.Value | import org.springframework.beans.factory.annotation.Value | ||||||
| import org.springframework.cache.annotation.Cacheable |  | ||||||
| import org.springframework.data.domain.Pageable | import org.springframework.data.domain.Pageable | ||||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||||
| import java.time.DayOfWeek | import java.time.DayOfWeek | ||||||
| @@ -169,87 +168,6 @@ class AudioContentMainService( | |||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Cacheable( |  | ||||||
|         value = ["getNewContentUploadCreatorList"], |  | ||||||
|         cacheManager = "cacheManager" |  | ||||||
|     ) |  | ||||||
|     fun getNewContentUploadCreatorList(member: Member): List<GetNewContentUploadCreator> { |  | ||||||
|         val isAdult = member.auth != null |  | ||||||
|  |  | ||||||
|         // 2주일 이내에 콘텐츠를 올린 크리에이터 20명 조회 |  | ||||||
|         return repository.getNewContentUploadCreatorList( |  | ||||||
|             cloudfrontHost = imageHost, |  | ||||||
|             isAdult = isAdult |  | ||||||
|         ) |  | ||||||
|             .asSequence() |  | ||||||
|             .filter { !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.creatorId) } |  | ||||||
|             .toList() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Cacheable( |  | ||||||
|         value = ["getAudioContentMainBannerList"], |  | ||||||
|         cacheManager = "cacheManager" |  | ||||||
|     ) |  | ||||||
|     fun getAudioContentMainBannerList(member: Member): List<GetAudioContentBannerResponse> { |  | ||||||
|         val isAdult = member.auth != null |  | ||||||
|  |  | ||||||
|         return repository |  | ||||||
|             .getAudioContentMainBannerList(isAdult = isAdult) |  | ||||||
|             .asSequence() |  | ||||||
|             .filter { |  | ||||||
|                 if (it.type == AudioContentBannerType.CREATOR && it.creator != null) { |  | ||||||
|                     !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.creator!!.id!!) |  | ||||||
|                 } else { |  | ||||||
|                     true |  | ||||||
|                 } |  | ||||||
|             } |  | ||||||
|             .map { |  | ||||||
|                 GetAudioContentBannerResponse( |  | ||||||
|                     type = it.type, |  | ||||||
|                     thumbnailImageUrl = "$imageHost/${it.thumbnailImage}", |  | ||||||
|                     eventItem = if (it.type == AudioContentBannerType.EVENT && it.event != null) { |  | ||||||
|                         EventItem( |  | ||||||
|                             id = it.event!!.id!!, |  | ||||||
|                             thumbnailImageUrl = if (!it.event!!.thumbnailImage.startsWith("https://")) { |  | ||||||
|                                 "$imageHost/${it.event!!.thumbnailImage}" |  | ||||||
|                             } else { |  | ||||||
|                                 it.event!!.thumbnailImage |  | ||||||
|                             }, |  | ||||||
|                             detailImageUrl = if ( |  | ||||||
|                                 it.event!!.detailImage != null && |  | ||||||
|                                 !it.event!!.detailImage!!.startsWith("https://") |  | ||||||
|                             ) { |  | ||||||
|                                 "$imageHost/${it.event!!.detailImage}" |  | ||||||
|                             } else { |  | ||||||
|                                 it.event!!.detailImage |  | ||||||
|                             }, |  | ||||||
|                             popupImageUrl = null, |  | ||||||
|                             link = it.event!!.link, |  | ||||||
|                             title = it.event!!.title, |  | ||||||
|                             isPopup = false |  | ||||||
|                         ) |  | ||||||
|                     } else { |  | ||||||
|                         null |  | ||||||
|                     }, |  | ||||||
|                     creatorId = if (it.type == AudioContentBannerType.CREATOR && it.creator != null) { |  | ||||||
|                         it.creator!!.id |  | ||||||
|                     } else { |  | ||||||
|                         null |  | ||||||
|                     }, |  | ||||||
|                     link = it.link |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
|             .toList() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun getAudioContentMainOrderList(member: Member): List<GetAudioContentMainItem> { |  | ||||||
|         return orderService |  | ||||||
|             .getAudioContentMainOrderList( |  | ||||||
|                 member = member, |  | ||||||
|                 limit = 20 |  | ||||||
|             ) |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun getThemeList(member: Member): List<String> { |     fun getThemeList(member: Member): List<String> { | ||||||
|         return audioContentThemeRepository.getActiveThemeOfContent(isAdult = member.auth != null) |         return audioContentThemeRepository.getActiveThemeOfContent(isAdult = member.auth != null) | ||||||
|     } |     } | ||||||
| @@ -279,77 +197,4 @@ class AudioContentMainService( | |||||||
|  |  | ||||||
|         return GetNewContentAllResponse(totalCount, items) |         return GetNewContentAllResponse(totalCount, items) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @Cacheable( |  | ||||||
|         value = ["getAudioContentMainCurationList"], |  | ||||||
|         cacheManager = "cacheManager" |  | ||||||
|     ) |  | ||||||
|     fun getAudioContentMainCurationList( |  | ||||||
|         member: Member, |  | ||||||
|         offset: Long, |  | ||||||
|         limit: Long |  | ||||||
|     ): List<GetAudioContentCurationResponse> { |  | ||||||
|         val isAdult = member.auth != null |  | ||||||
|  |  | ||||||
|         return repository |  | ||||||
|             .getAudioContentCurationList( |  | ||||||
|                 isAdult = isAdult, |  | ||||||
|                 offset = offset, |  | ||||||
|                 limit = limit |  | ||||||
|             ) |  | ||||||
|             .asSequence() |  | ||||||
|             .map { |  | ||||||
|                 GetAudioContentCurationResponse( |  | ||||||
|                     curationId = it.id!!, |  | ||||||
|                     title = it.title, |  | ||||||
|                     description = it.description, |  | ||||||
|                     contents = repository.findAudioContentByCurationId( |  | ||||||
|                         curationId = it.id!!, |  | ||||||
|                         cloudfrontHost = imageHost, |  | ||||||
|                         isAdult = isAdult |  | ||||||
|                     ) |  | ||||||
|                         .asSequence() |  | ||||||
|                         .filter { content -> |  | ||||||
|                             !blockMemberRepository.isBlocked( |  | ||||||
|                                 blockedMemberId = member.id!!, |  | ||||||
|                                 memberId = content.creatorId |  | ||||||
|                             ) |  | ||||||
|                         } |  | ||||||
|                         .toList() |  | ||||||
|                 ) |  | ||||||
|             } |  | ||||||
|             .filter { it.contents.isNotEmpty() } |  | ||||||
|             .toList() |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     fun getAudioContentMainContentRanking(member: Member): GetAudioContentRanking { |  | ||||||
|         val isAdult = member.auth != null |  | ||||||
|  |  | ||||||
|         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 startDateFormatter = DateTimeFormatter.ofPattern("yyyy년 MM월 dd일") |  | ||||||
|         val endDateFormatter = DateTimeFormatter.ofPattern("MM월 dd일") |  | ||||||
|  |  | ||||||
|         val contentRankingItemList = repository |  | ||||||
|             .getAudioContentRanking( |  | ||||||
|                 cloudfrontHost = imageHost, |  | ||||||
|                 startDate = startDate.minusDays(1), |  | ||||||
|                 endDate = endDate.minusDays(1), |  | ||||||
|                 isAdult = isAdult |  | ||||||
|             ) |  | ||||||
|  |  | ||||||
|         return GetAudioContentRanking( |  | ||||||
|             startDate = startDate.format(startDateFormatter), |  | ||||||
|             endDate = endDate.minusDays(1).format(endDateFormatter), |  | ||||||
|             contentRankingItemList |  | ||||||
|         ) |  | ||||||
|     } |  | ||||||
| } | } | ||||||
|   | |||||||
| @@ -5,7 +5,6 @@ import kr.co.vividnext.sodalive.aws.s3.S3Uploader | |||||||
| import kr.co.vividnext.sodalive.common.SodaException | import kr.co.vividnext.sodalive.common.SodaException | ||||||
| import kr.co.vividnext.sodalive.utils.generateFileName | import kr.co.vividnext.sodalive.utils.generateFileName | ||||||
| import org.springframework.beans.factory.annotation.Value | import org.springframework.beans.factory.annotation.Value | ||||||
| import org.springframework.cache.annotation.Cacheable |  | ||||||
| import org.springframework.data.repository.findByIdOrNull | import org.springframework.data.repository.findByIdOrNull | ||||||
| import org.springframework.stereotype.Service | import org.springframework.stereotype.Service | ||||||
| import org.springframework.transaction.annotation.Transactional | import org.springframework.transaction.annotation.Transactional | ||||||
| @@ -21,10 +20,6 @@ class EventService( | |||||||
|     @Value("\${cloud.aws.cloud-front.host}") |     @Value("\${cloud.aws.cloud-front.host}") | ||||||
|     private val cloudFrontHost: String |     private val cloudFrontHost: String | ||||||
| ) { | ) { | ||||||
|     @Cacheable( |  | ||||||
|         value = ["getEventList"], |  | ||||||
|         cacheManager = "cacheManager" |  | ||||||
|     ) |  | ||||||
|     fun getEventList(): GetEventResponse { |     fun getEventList(): GetEventResponse { | ||||||
|         val eventList = repository.getEventList() |         val eventList = repository.getEventList() | ||||||
|             .asSequence() |             .asSequence() | ||||||
|   | |||||||
| @@ -9,7 +9,6 @@ import kr.co.vividnext.sodalive.member.MemberRole | |||||||
| import kr.co.vividnext.sodalive.member.QMember.member | import kr.co.vividnext.sodalive.member.QMember.member | ||||||
| import kr.co.vividnext.sodalive.member.following.QCreatorFollowing.creatorFollowing | import kr.co.vividnext.sodalive.member.following.QCreatorFollowing.creatorFollowing | ||||||
| import org.springframework.beans.factory.annotation.Value | import org.springframework.beans.factory.annotation.Value | ||||||
| import org.springframework.cache.annotation.Cacheable |  | ||||||
| import org.springframework.stereotype.Repository | import org.springframework.stereotype.Repository | ||||||
| import java.time.LocalDateTime | import java.time.LocalDateTime | ||||||
|  |  | ||||||
| @@ -20,10 +19,6 @@ class LiveRecommendRepository( | |||||||
|     @Value("\${cloud.aws.cloud-front.host}") |     @Value("\${cloud.aws.cloud-front.host}") | ||||||
|     private val cloudFrontHost: String |     private val cloudFrontHost: String | ||||||
| ) { | ) { | ||||||
|     @Cacheable( |  | ||||||
|         value = ["getRecommendLive"], |  | ||||||
|         cacheManager = "cacheManager" |  | ||||||
|     ) |  | ||||||
|     fun getRecommendLive( |     fun getRecommendLive( | ||||||
|         memberId: Long, |         memberId: Long, | ||||||
|         isBlocked: (Long) -> Boolean, |         isBlocked: (Long) -> Boolean, | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user