| @@ -236,4 +236,24 @@ class AudioContentController(private val service: AudioContentService) { | |||||||
|  |  | ||||||
|         ApiResponse.ok(service.unpinAtTheTop(contentId = id, member = member)) |         ApiResponse.ok(service.unpinAtTheTop(contentId = id, member = member)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @GetMapping("/replay-live") | ||||||
|  |     fun replayLive( | ||||||
|  |         @RequestParam("isAdultContentVisible", required = false) isAdultContentVisible: Boolean? = null, | ||||||
|  |         @RequestParam("contentType", required = false) contentType: ContentType? = null, | ||||||
|  |         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? | ||||||
|  |     ) = run { | ||||||
|  |         ApiResponse.ok( | ||||||
|  |             service.getLatestContentByTheme( | ||||||
|  |                 theme = listOf("다시듣기"), | ||||||
|  |                 contentType = contentType ?: ContentType.ALL, | ||||||
|  |                 isFree = false, | ||||||
|  |                 isAdult = if (member != null) { | ||||||
|  |                     (isAdultContentVisible ?: true) && member.auth != null | ||||||
|  |                 } else { | ||||||
|  |                     false | ||||||
|  |                 } | ||||||
|  |             ) | ||||||
|  |         ) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -490,10 +490,8 @@ class AudioContentQueryRepositoryImpl( | |||||||
|             where = where.and(audioContentTheme.theme.`in`(theme)) |             where = where.and(audioContentTheme.theme.`in`(theme)) | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         where = if (isFree) { |         if (isFree) { | ||||||
|             where.and(audioContent.price.loe(0)) |             where = where.and(audioContent.price.loe(0)) | ||||||
|         } else { |  | ||||||
|             where.and(audioContent.price.gt(0)) |  | ||||||
|         } |         } | ||||||
|  |  | ||||||
|         return queryFactory |         return queryFactory | ||||||
|   | |||||||
| @@ -16,6 +16,7 @@ import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.CreatorCo | |||||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.CreatorCommunityLikeRepository | import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.CreatorCommunityLikeRepository | ||||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.PostCommunityPostLikeRequest | import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.PostCommunityPostLikeRequest | ||||||
| import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.PostCommunityPostLikeResponse | import kr.co.vividnext.sodalive.explorer.profile.creatorCommunity.like.PostCommunityPostLikeResponse | ||||||
|  | import kr.co.vividnext.sodalive.extensions.getTimeAgoString | ||||||
| import kr.co.vividnext.sodalive.fcm.FcmEvent | import kr.co.vividnext.sodalive.fcm.FcmEvent | ||||||
| import kr.co.vividnext.sodalive.fcm.FcmEventType | import kr.co.vividnext.sodalive.fcm.FcmEventType | ||||||
| import kr.co.vividnext.sodalive.member.Member | import kr.co.vividnext.sodalive.member.Member | ||||||
| @@ -28,8 +29,6 @@ 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 | ||||||
| import org.springframework.web.multipart.MultipartFile | import org.springframework.web.multipart.MultipartFile | ||||||
| import java.time.Duration |  | ||||||
| import java.time.LocalDateTime |  | ||||||
|  |  | ||||||
| @Service | @Service | ||||||
| class CreatorCommunityService( | class CreatorCommunityService( | ||||||
| @@ -243,7 +242,7 @@ class CreatorCommunityService( | |||||||
|                 it.toCommunityPostListResponse( |                 it.toCommunityPostListResponse( | ||||||
|                     imageHost = imageHost, |                     imageHost = imageHost, | ||||||
|                     audioUrl = audioUrl, |                     audioUrl = audioUrl, | ||||||
|                     date = getTimeAgoString(it.date), |                     date = it.date.getTimeAgoString(), | ||||||
|                     isLike = isLike, |                     isLike = isLike, | ||||||
|                     memberId = memberId, |                     memberId = memberId, | ||||||
|                     existOrdered = if (memberId == it.creatorId) { |                     existOrdered = if (memberId == it.creatorId) { | ||||||
| @@ -314,7 +313,7 @@ class CreatorCommunityService( | |||||||
|         return post.toCommunityPostListResponse( |         return post.toCommunityPostListResponse( | ||||||
|             imageHost = imageHost, |             imageHost = imageHost, | ||||||
|             audioUrl = audioUrl, |             audioUrl = audioUrl, | ||||||
|             date = getTimeAgoString(post.date), |             date = post.date.getTimeAgoString(), | ||||||
|             isLike = isLike, |             isLike = isLike, | ||||||
|             memberId = memberId, |             memberId = memberId, | ||||||
|             existOrdered = if (memberId == post.creatorId) { |             existOrdered = if (memberId == post.creatorId) { | ||||||
| @@ -328,19 +327,6 @@ class CreatorCommunityService( | |||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     private fun getTimeAgoString(dateTime: LocalDateTime): String { |  | ||||||
|         val now = LocalDateTime.now() |  | ||||||
|         val duration = Duration.between(dateTime, now) |  | ||||||
|  |  | ||||||
|         return when { |  | ||||||
|             duration.toMinutes() < 60 -> "${duration.toMinutes()}분전" |  | ||||||
|             duration.toHours() < 24 -> "${duration.toHours()}시간전" |  | ||||||
|             duration.toDays() < 30 -> "${duration.toDays()}일전" |  | ||||||
|             duration.toDays() < 365 -> "${duration.toDays() / 30}개월전" |  | ||||||
|             else -> "${duration.toDays() / 365}년전" |  | ||||||
|         } |  | ||||||
|     } |  | ||||||
|  |  | ||||||
|     @Transactional |     @Transactional | ||||||
|     fun communityPostLike(request: PostCommunityPostLikeRequest, member: Member): PostCommunityPostLikeResponse { |     fun communityPostLike(request: PostCommunityPostLikeRequest, member: Member): PostCommunityPostLikeResponse { | ||||||
|         var postLike = likeRepository.findByPostIdAndMemberId(postId = request.postId, memberId = member.id!!) |         var postLike = likeRepository.findByPostIdAndMemberId(postId = request.postId, memberId = member.id!!) | ||||||
| @@ -506,7 +492,7 @@ class CreatorCommunityService( | |||||||
|                 it.toCommunityPostListResponse( |                 it.toCommunityPostListResponse( | ||||||
|                     imageHost = imageHost, |                     imageHost = imageHost, | ||||||
|                     audioUrl = null, |                     audioUrl = null, | ||||||
|                     date = getTimeAgoString(it.date), |                     date = it.date.getTimeAgoString(), | ||||||
|                     isLike = isLike, |                     isLike = isLike, | ||||||
|                     memberId = memberId, |                     memberId = memberId, | ||||||
|                     existOrdered = if (memberId == it.creatorId) { |                     existOrdered = if (memberId == it.creatorId) { | ||||||
| @@ -590,7 +576,7 @@ class CreatorCommunityService( | |||||||
|             imageHost = imageHost, |             imageHost = imageHost, | ||||||
|             audioUrl = audioUrl, |             audioUrl = audioUrl, | ||||||
|             content = post.content, |             content = post.content, | ||||||
|             date = getTimeAgoString(post.createdAt!!), |             date = post.createdAt!!.getTimeAgoString(), | ||||||
|             isLike = isLike, |             isLike = isLike, | ||||||
|             existOrdered = true, |             existOrdered = true, | ||||||
|             likeCount = likeCount, |             likeCount = likeCount, | ||||||
|   | |||||||
| @@ -0,0 +1,18 @@ | |||||||
|  | package kr.co.vividnext.sodalive.extensions | ||||||
|  |  | ||||||
|  | import java.time.Duration | ||||||
|  | import java.time.LocalDateTime | ||||||
|  |  | ||||||
|  | fun LocalDateTime.getTimeAgoString(): String { | ||||||
|  |     val now = LocalDateTime.now() | ||||||
|  |     val duration = Duration.between(this, now) | ||||||
|  |  | ||||||
|  |     return when { | ||||||
|  |         duration.toMinutes() < 1 -> "방금 전" | ||||||
|  |         duration.toMinutes() < 60 -> "${duration.toMinutes()}분전" | ||||||
|  |         duration.toHours() < 24 -> "${duration.toHours()}시간전" | ||||||
|  |         duration.toDays() < 30 -> "${duration.toDays()}일전" | ||||||
|  |         duration.toDays() < 365 -> "${duration.toDays() / 30}개월전" | ||||||
|  |         else -> "${duration.toDays() / 365}년전" | ||||||
|  |     } | ||||||
|  | } | ||||||
| @@ -0,0 +1,29 @@ | |||||||
|  | package kr.co.vividnext.sodalive.live.room | ||||||
|  |  | ||||||
|  | import com.querydsl.core.annotations.QueryProjection | ||||||
|  | import kr.co.vividnext.sodalive.extensions.getTimeAgoString | ||||||
|  | import java.time.LocalDateTime | ||||||
|  |  | ||||||
|  | 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 | ||||||
|  | ) { | ||||||
|  |     constructor(response: GetLatestFinishedLiveQueryResponse) : this( | ||||||
|  |         response.memberId, | ||||||
|  |         response.nickname, | ||||||
|  |         response.profileImageUrl, | ||||||
|  |         response.title, | ||||||
|  |         response.updatedAt.getTimeAgoString() | ||||||
|  |     ) | ||||||
|  | } | ||||||
| @@ -5,6 +5,7 @@ data class GetRoomListResponse( | |||||||
|     val title: String, |     val title: String, | ||||||
|     val content: String, |     val content: String, | ||||||
|     val beginDateTime: String, |     val beginDateTime: String, | ||||||
|  |     val beginDateTimeUtc: String, | ||||||
|     val numberOfParticipate: Int, |     val numberOfParticipate: Int, | ||||||
|     val numberOfPeople: Int, |     val numberOfPeople: Int, | ||||||
|     val coverImageUrl: String, |     val coverImageUrl: String, | ||||||
|   | |||||||
| @@ -171,7 +171,7 @@ class LiveRoomController( | |||||||
|     ) = run { |     ) = run { | ||||||
|         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") |         if (member == null) throw SodaException("로그인 정보를 확인해주세요.") | ||||||
|  |  | ||||||
|         ApiResponse.ok(service.getDonationTotal(roomId, memberId = member.id!!)) |         ApiResponse.ok(service.getDonationTotal(roomId)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     @PutMapping("/info/set/speaker") |     @PutMapping("/info/set/speaker") | ||||||
| @@ -291,4 +291,11 @@ class LiveRoomController( | |||||||
|  |  | ||||||
|         ApiResponse.ok(service.getHeartList(roomId)) |         ApiResponse.ok(service.getHeartList(roomId)) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     @GetMapping("/latest-finished-live") | ||||||
|  |     fun getLatestFinishedLive( | ||||||
|  |         @AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member? | ||||||
|  |     ) = run { | ||||||
|  |         ApiResponse.ok(service.getLatestFinishedLive(member)) | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -4,6 +4,7 @@ import com.querydsl.core.types.Projections | |||||||
| import com.querydsl.core.types.dsl.CaseBuilder | import com.querydsl.core.types.dsl.CaseBuilder | ||||||
| import com.querydsl.core.types.dsl.Expressions | import com.querydsl.core.types.dsl.Expressions | ||||||
| import com.querydsl.core.types.dsl.NumberExpression | import com.querydsl.core.types.dsl.NumberExpression | ||||||
|  | import com.querydsl.jpa.JPAExpressions | ||||||
| import com.querydsl.jpa.impl.JPAQueryFactory | import com.querydsl.jpa.impl.JPAQueryFactory | ||||||
| import kr.co.vividnext.sodalive.can.use.CanUsage | import kr.co.vividnext.sodalive.can.use.CanUsage | ||||||
| import kr.co.vividnext.sodalive.can.use.QUseCan.useCan | import kr.co.vividnext.sodalive.can.use.QUseCan.useCan | ||||||
| @@ -14,6 +15,7 @@ import kr.co.vividnext.sodalive.live.room.donation.GetLiveRoomDonationItem | |||||||
| import kr.co.vividnext.sodalive.live.room.donation.QGetLiveRoomDonationItem | import kr.co.vividnext.sodalive.live.room.donation.QGetLiveRoomDonationItem | ||||||
| import kr.co.vividnext.sodalive.live.room.like.GetLiveRoomHeartListItem | import kr.co.vividnext.sodalive.live.room.like.GetLiveRoomHeartListItem | ||||||
| import kr.co.vividnext.sodalive.live.room.like.QGetLiveRoomHeartListItem | import kr.co.vividnext.sodalive.live.room.like.QGetLiveRoomHeartListItem | ||||||
|  | import kr.co.vividnext.sodalive.member.MemberRole | ||||||
| import kr.co.vividnext.sodalive.member.QMember.member | import kr.co.vividnext.sodalive.member.QMember.member | ||||||
| import org.springframework.beans.factory.annotation.Value | import org.springframework.beans.factory.annotation.Value | ||||||
| import org.springframework.data.jpa.repository.JpaRepository | import org.springframework.data.jpa.repository.JpaRepository | ||||||
| @@ -60,6 +62,7 @@ interface LiveRoomQueryRepository { | |||||||
|     fun getTotalHeartCount(roomId: Long): Int? |     fun getTotalHeartCount(roomId: Long): Int? | ||||||
|     fun getLiveRoomCreatorId(roomId: Long): Long? |     fun getLiveRoomCreatorId(roomId: Long): Long? | ||||||
|     fun getHeartList(roomId: Long): List<GetLiveRoomHeartListItem> |     fun getHeartList(roomId: Long): List<GetLiveRoomHeartListItem> | ||||||
|  |     fun getLatestFinishedLive(): List<GetLatestFinishedLiveQueryResponse> | ||||||
| } | } | ||||||
|  |  | ||||||
| class LiveRoomQueryRepositoryImpl( | class LiveRoomQueryRepositoryImpl( | ||||||
| @@ -380,4 +383,39 @@ class LiveRoomQueryRepositoryImpl( | |||||||
|             .orderBy(useCan.can.add(useCan.rewardCan).sum().desc()) |             .orderBy(useCan.can.add(useCan.rewardCan).sum().desc()) | ||||||
|             .fetch() |             .fetch() | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     override fun getLatestFinishedLive(): List<GetLatestFinishedLiveQueryResponse> { | ||||||
|  |         val liveRoom = liveRoom | ||||||
|  |         val subLiveRoom = QLiveRoom.liveRoom | ||||||
|  |  | ||||||
|  |         val subQuery = JPAExpressions | ||||||
|  |             .select(subLiveRoom.member.id, subLiveRoom.updatedAt.max()) | ||||||
|  |             .from(subLiveRoom) | ||||||
|  |             .where( | ||||||
|  |                 subLiveRoom.isActive.isFalse | ||||||
|  |                     .and(subLiveRoom.channelName.isNotNull) | ||||||
|  |                     .and(subLiveRoom.member.role.eq(MemberRole.CREATOR)) | ||||||
|  |             ) | ||||||
|  |             .groupBy(subLiveRoom.member.id) | ||||||
|  |  | ||||||
|  |         return queryFactory | ||||||
|  |             .select( | ||||||
|  |                 QGetLatestFinishedLiveQueryResponse( | ||||||
|  |                     member.id, | ||||||
|  |                     member.nickname, | ||||||
|  |                     member.profileImage.prepend("/").prepend(cloudFrontHost), | ||||||
|  |                     liveRoom.title, | ||||||
|  |                     liveRoom.updatedAt | ||||||
|  |                 ) | ||||||
|  |             ) | ||||||
|  |             .from(liveRoom) | ||||||
|  |             .innerJoin(liveRoom.member, member) | ||||||
|  |             .where( | ||||||
|  |                 Expressions.list(liveRoom.member.id, liveRoom.updatedAt) | ||||||
|  |                     .`in`(subQuery) | ||||||
|  |             ) | ||||||
|  |             .orderBy(liveRoom.updatedAt.desc()) | ||||||
|  |             .limit(20) | ||||||
|  |             .fetch() | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
| @@ -176,11 +176,15 @@ class LiveRoomService( | |||||||
|                             .withLocale(Locale.KOREAN) |                             .withLocale(Locale.KOREAN) | ||||||
|                     ) |                     ) | ||||||
|  |  | ||||||
|  |                 val beginDateTimeUtc = it.beginDateTime | ||||||
|  |                     .format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) | ||||||
|  |  | ||||||
|                 GetRoomListResponse( |                 GetRoomListResponse( | ||||||
|                     roomId = it.id!!, |                     roomId = it.id!!, | ||||||
|                     title = it.title, |                     title = it.title, | ||||||
|                     content = it.notice, |                     content = it.notice, | ||||||
|                     beginDateTime = beginDateTime, |                     beginDateTime = beginDateTime, | ||||||
|  |                     beginDateTimeUtc = beginDateTimeUtc, | ||||||
|                     numberOfParticipate = (roomInfo?.listenerCount ?: 0) + |                     numberOfParticipate = (roomInfo?.listenerCount ?: 0) + | ||||||
|                         (roomInfo?.speakerCount ?: 0) + |                         (roomInfo?.speakerCount ?: 0) + | ||||||
|                         (roomInfo?.managerCount ?: 0), |                         (roomInfo?.managerCount ?: 0), | ||||||
| @@ -993,7 +997,7 @@ class LiveRoomService( | |||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|     fun getDonationTotal(roomId: Long, memberId: Long): GetLiveRoomDonationTotalResponse { |     fun getDonationTotal(roomId: Long): GetLiveRoomDonationTotalResponse { | ||||||
|         return GetLiveRoomDonationTotalResponse( |         return GetLiveRoomDonationTotalResponse( | ||||||
|             totalDonationCan = repository.getDonationTotal(roomId = roomId) ?: 0 |             totalDonationCan = repository.getDonationTotal(roomId = roomId) ?: 0 | ||||||
|         ) |         ) | ||||||
| @@ -1292,4 +1296,18 @@ class LiveRoomService( | |||||||
|             totalHeart = heartList.sumOf { it.heart } |             totalHeart = heartList.sumOf { it.heart } | ||||||
|         ) |         ) | ||||||
|     } |     } | ||||||
|  |  | ||||||
|  |     fun getLatestFinishedLive(member: Member?): List<GetLatestFinishedLiveResponse> { | ||||||
|  |         return repository.getLatestFinishedLive() | ||||||
|  |             .filter { | ||||||
|  |                 if (member?.id != null) { | ||||||
|  |                     !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.memberId) | ||||||
|  |                 } else { | ||||||
|  |                     true | ||||||
|  |                 } | ||||||
|  |             } | ||||||
|  |             .map { | ||||||
|  |                 GetLatestFinishedLiveResponse(response = it) | ||||||
|  |             } | ||||||
|  |     } | ||||||
| } | } | ||||||
|   | |||||||
		Reference in New Issue
	
	Block a user