diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt index f5a529a..c9510ab 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentController.kt @@ -236,4 +236,24 @@ class AudioContentController(private val service: AudioContentService) { 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 + } + ) + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt index 1ee6a50..40038d3 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/content/AudioContentRepository.kt @@ -490,10 +490,8 @@ class AudioContentQueryRepositoryImpl( where = where.and(audioContentTheme.theme.`in`(theme)) } - where = if (isFree) { - where.and(audioContent.price.loe(0)) - } else { - where.and(audioContent.price.gt(0)) + if (isFree) { + where = where.and(audioContent.price.loe(0)) } return queryFactory diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityService.kt index a8deee9..a209742 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/explorer/profile/creatorCommunity/CreatorCommunityService.kt @@ -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.PostCommunityPostLikeRequest 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.FcmEventType 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.transaction.annotation.Transactional import org.springframework.web.multipart.MultipartFile -import java.time.Duration -import java.time.LocalDateTime @Service class CreatorCommunityService( @@ -243,7 +242,7 @@ class CreatorCommunityService( it.toCommunityPostListResponse( imageHost = imageHost, audioUrl = audioUrl, - date = getTimeAgoString(it.date), + date = it.date.getTimeAgoString(), isLike = isLike, memberId = memberId, existOrdered = if (memberId == it.creatorId) { @@ -314,7 +313,7 @@ class CreatorCommunityService( return post.toCommunityPostListResponse( imageHost = imageHost, audioUrl = audioUrl, - date = getTimeAgoString(post.date), + date = post.date.getTimeAgoString(), isLike = isLike, memberId = memberId, 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 fun communityPostLike(request: PostCommunityPostLikeRequest, member: Member): PostCommunityPostLikeResponse { var postLike = likeRepository.findByPostIdAndMemberId(postId = request.postId, memberId = member.id!!) @@ -506,7 +492,7 @@ class CreatorCommunityService( it.toCommunityPostListResponse( imageHost = imageHost, audioUrl = null, - date = getTimeAgoString(it.date), + date = it.date.getTimeAgoString(), isLike = isLike, memberId = memberId, existOrdered = if (memberId == it.creatorId) { @@ -590,7 +576,7 @@ class CreatorCommunityService( imageHost = imageHost, audioUrl = audioUrl, content = post.content, - date = getTimeAgoString(post.createdAt!!), + date = post.createdAt!!.getTimeAgoString(), isLike = isLike, existOrdered = true, likeCount = likeCount, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/extensions/LocalDateTimeExtensions.kt b/src/main/kotlin/kr/co/vividnext/sodalive/extensions/LocalDateTimeExtensions.kt new file mode 100644 index 0000000..061e19c --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/extensions/LocalDateTimeExtensions.kt @@ -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}년전" + } +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/GetLatestFinishedLiveResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/GetLatestFinishedLiveResponse.kt new file mode 100644 index 0000000..8955da6 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/GetLatestFinishedLiveResponse.kt @@ -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() + ) +} diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/GetRoomListResponse.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/GetRoomListResponse.kt index 2649133..01ed451 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/GetRoomListResponse.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/GetRoomListResponse.kt @@ -5,6 +5,7 @@ data class GetRoomListResponse( val title: String, val content: String, val beginDateTime: String, + val beginDateTimeUtc: String, val numberOfParticipate: Int, val numberOfPeople: Int, val coverImageUrl: String, diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomController.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomController.kt index 74b9bae..c33c5be 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomController.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomController.kt @@ -171,7 +171,7 @@ class LiveRoomController( ) = run { if (member == null) throw SodaException("로그인 정보를 확인해주세요.") - ApiResponse.ok(service.getDonationTotal(roomId, memberId = member.id!!)) + ApiResponse.ok(service.getDonationTotal(roomId)) } @PutMapping("/info/set/speaker") @@ -291,4 +291,11 @@ class LiveRoomController( 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)) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomRepository.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomRepository.kt index a648fe2..5967fb9 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomRepository.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomRepository.kt @@ -4,6 +4,7 @@ import com.querydsl.core.types.Projections import com.querydsl.core.types.dsl.CaseBuilder import com.querydsl.core.types.dsl.Expressions import com.querydsl.core.types.dsl.NumberExpression +import com.querydsl.jpa.JPAExpressions import com.querydsl.jpa.impl.JPAQueryFactory import kr.co.vividnext.sodalive.can.use.CanUsage 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.like.GetLiveRoomHeartListItem 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 org.springframework.beans.factory.annotation.Value import org.springframework.data.jpa.repository.JpaRepository @@ -60,6 +62,7 @@ interface LiveRoomQueryRepository { fun getTotalHeartCount(roomId: Long): Int? fun getLiveRoomCreatorId(roomId: Long): Long? fun getHeartList(roomId: Long): List + fun getLatestFinishedLive(): List } class LiveRoomQueryRepositoryImpl( @@ -380,4 +383,39 @@ class LiveRoomQueryRepositoryImpl( .orderBy(useCan.can.add(useCan.rewardCan).sum().desc()) .fetch() } + + override fun getLatestFinishedLive(): List { + 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() + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt index c066c1d..b8a6e27 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/LiveRoomService.kt @@ -176,11 +176,15 @@ class LiveRoomService( .withLocale(Locale.KOREAN) ) + val beginDateTimeUtc = it.beginDateTime + .format(DateTimeFormatter.ISO_LOCAL_DATE_TIME) + GetRoomListResponse( roomId = it.id!!, title = it.title, content = it.notice, beginDateTime = beginDateTime, + beginDateTimeUtc = beginDateTimeUtc, numberOfParticipate = (roomInfo?.listenerCount ?: 0) + (roomInfo?.speakerCount ?: 0) + (roomInfo?.managerCount ?: 0), @@ -993,7 +997,7 @@ class LiveRoomService( ) } - fun getDonationTotal(roomId: Long, memberId: Long): GetLiveRoomDonationTotalResponse { + fun getDonationTotal(roomId: Long): GetLiveRoomDonationTotalResponse { return GetLiveRoomDonationTotalResponse( totalDonationCan = repository.getDonationTotal(roomId = roomId) ?: 0 ) @@ -1292,4 +1296,18 @@ class LiveRoomService( totalHeart = heartList.sumOf { it.heart } ) } + + fun getLatestFinishedLive(member: Member?): List { + return repository.getLatestFinishedLive() + .filter { + if (member?.id != null) { + !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.memberId) + } else { + true + } + } + .map { + GetLatestFinishedLiveResponse(response = it) + } + } }