라이브 방 - 아고라 설정 및 라이브 방 관련 API
This commit is contained in:
@@ -0,0 +1,18 @@
|
||||
package kr.co.vividnext.sodalive.live.room
|
||||
|
||||
data class GetLiveRoomUserProfileResponse(
|
||||
val userId: Long,
|
||||
val nickname: String,
|
||||
val profileUrl: String,
|
||||
val gender: String,
|
||||
val instagramUrl: String,
|
||||
val youtubeUrl: String,
|
||||
val websiteUrl: String,
|
||||
val blogUrl: String,
|
||||
val introduce: String,
|
||||
val tags: String,
|
||||
val isSpeaker: Boolean?,
|
||||
val isManager: Boolean?,
|
||||
val isFollowing: Boolean?,
|
||||
val isBlock: Boolean
|
||||
)
|
@@ -3,9 +3,12 @@ package kr.co.vividnext.sodalive.live.room
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.common.SodaException
|
||||
import kr.co.vividnext.sodalive.live.room.cancel.CancelLiveRequest
|
||||
import kr.co.vividnext.sodalive.live.room.donation.DeleteLiveRoomDonationMessage
|
||||
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationRequest
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import org.springframework.data.domain.Pageable
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||
import org.springframework.web.bind.annotation.DeleteMapping
|
||||
import org.springframework.web.bind.annotation.GetMapping
|
||||
import org.springframework.web.bind.annotation.PathVariable
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
@@ -106,4 +109,124 @@ class LiveRoomController(private val service: LiveRoomService) {
|
||||
|
||||
ApiResponse.ok(service.editLiveRoomInfo(roomId, coverImage, requestString, member))
|
||||
}
|
||||
|
||||
@GetMapping("/info/{id}")
|
||||
fun getRoomInfo(
|
||||
@PathVariable id: Long,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(service.getRoomInfo(roomId = id, member))
|
||||
}
|
||||
|
||||
@GetMapping("/donation-message")
|
||||
fun getDonationMessageList(
|
||||
@RequestParam roomId: Long,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(service.getDonationMessageList(roomId, member))
|
||||
}
|
||||
|
||||
@DeleteMapping("/donation-message")
|
||||
fun removeDonationMessage(
|
||||
@RequestBody request: DeleteLiveRoomDonationMessage,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(service.deleteDonationMessage(request, member))
|
||||
}
|
||||
|
||||
@GetMapping("/{room_id}/profile/{user_id}")
|
||||
fun getUserProfile(
|
||||
@PathVariable("room_id") roomId: Long,
|
||||
@PathVariable("user_id") userId: Long,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(service.getUserProfile(roomId, userId, member))
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/donation-total")
|
||||
fun donationTotal(
|
||||
@PathVariable("id") roomId: Long,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(service.getDonationTotal(roomId))
|
||||
}
|
||||
|
||||
@PutMapping("/info/set/speaker")
|
||||
fun setSpeaker(
|
||||
@RequestBody request: SetManagerOrSpeakerOrAudienceRequest,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(service.setSpeaker(request))
|
||||
}
|
||||
|
||||
@PutMapping("/info/set/listener")
|
||||
fun setListener(
|
||||
@RequestBody request: SetManagerOrSpeakerOrAudienceRequest,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(service.setListener(request))
|
||||
}
|
||||
|
||||
@PutMapping("/info/set/manager")
|
||||
fun setManager(
|
||||
@RequestBody request: SetManagerOrSpeakerOrAudienceRequest,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(service.setManager(request, member))
|
||||
}
|
||||
|
||||
@PostMapping("/donation")
|
||||
fun donation(
|
||||
@RequestBody request: LiveRoomDonationRequest,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(service.donation(request, member))
|
||||
}
|
||||
|
||||
@PostMapping("/donation/refund/{id}")
|
||||
fun refundDonation(
|
||||
@PathVariable id: Long,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(service.refundDonation(id, member))
|
||||
}
|
||||
|
||||
@GetMapping("/{id}/donation-list")
|
||||
fun donationList(
|
||||
@PathVariable("id") roomId: Long,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(service.getDonationStatus(roomId, member))
|
||||
}
|
||||
|
||||
@PostMapping("/quit")
|
||||
fun quitRoom(
|
||||
@RequestParam("id") roomId: Long,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
ApiResponse.ok(service.quitRoom(roomId, member))
|
||||
}
|
||||
}
|
||||
|
@@ -6,7 +6,12 @@ import com.querydsl.core.types.Projections
|
||||
import com.querydsl.core.types.dsl.CaseBuilder
|
||||
import com.querydsl.core.types.dsl.Expressions
|
||||
import com.querydsl.jpa.impl.JPAQueryFactory
|
||||
import kr.co.vividnext.sodalive.can.use.CanUsage
|
||||
import kr.co.vividnext.sodalive.can.use.QUseCan.useCan
|
||||
import kr.co.vividnext.sodalive.can.use.QUseCanCalculate.useCanCalculate
|
||||
import kr.co.vividnext.sodalive.live.room.QLiveRoom.liveRoom
|
||||
import kr.co.vividnext.sodalive.live.room.donation.GetLiveRoomDonationItem
|
||||
import kr.co.vividnext.sodalive.live.room.donation.QGetLiveRoomDonationItem
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.QMember
|
||||
import kr.co.vividnext.sodalive.member.QMember.member
|
||||
@@ -34,6 +39,8 @@ interface LiveRoomQueryRepository {
|
||||
fun getLiveRoom(id: Long): LiveRoom?
|
||||
fun getLiveRoomAndAccountId(roomId: Long, memberId: Long): LiveRoom?
|
||||
fun getRecentRoomInfo(memberId: Long, cloudFrontHost: String): GetRecentRoomInfoResponse?
|
||||
fun getDonationTotal(roomId: Long): Int?
|
||||
fun getDonationList(roomId: Long, cloudFrontHost: String): List<GetLiveRoomDonationItem>
|
||||
}
|
||||
|
||||
class LiveRoomQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : LiveRoomQueryRepository {
|
||||
@@ -143,6 +150,45 @@ class LiveRoomQueryRepositoryImpl(private val queryFactory: JPAQueryFactory) : L
|
||||
.fetchFirst()
|
||||
}
|
||||
|
||||
override fun getDonationTotal(roomId: Long): Int? {
|
||||
return queryFactory
|
||||
.select(useCanCalculate.can.sum())
|
||||
.from(useCanCalculate)
|
||||
.innerJoin(useCanCalculate.useCan, useCan)
|
||||
.innerJoin(useCan.room, liveRoom)
|
||||
.where(
|
||||
liveRoom.id.eq(roomId)
|
||||
.and(useCan.canUsage.eq(CanUsage.DONATION))
|
||||
.and(useCan.isRefund.isFalse)
|
||||
)
|
||||
.fetchOne()
|
||||
}
|
||||
|
||||
override fun getDonationList(roomId: Long, cloudFrontHost: String): List<GetLiveRoomDonationItem> {
|
||||
return queryFactory
|
||||
.select(
|
||||
QGetLiveRoomDonationItem(
|
||||
member.profileImage
|
||||
.coalesce("profile/default-profile.png")
|
||||
.prepend("/")
|
||||
.prepend(cloudFrontHost),
|
||||
member.nickname,
|
||||
member.id.coalesce(0),
|
||||
useCan.can.sum().add(useCan.rewardCan.sum())
|
||||
)
|
||||
)
|
||||
.from(useCan)
|
||||
.join(useCan.member, member)
|
||||
.groupBy(useCan.member)
|
||||
.where(
|
||||
useCan.room.id.eq(roomId)
|
||||
.and(useCan.canUsage.eq(CanUsage.DONATION))
|
||||
.and(useCan.isRefund.isFalse)
|
||||
)
|
||||
.orderBy(useCan.can.sum().add(useCan.rewardCan.sum()).desc())
|
||||
.fetch()
|
||||
}
|
||||
|
||||
private fun orderByFieldAccountId(
|
||||
memberId: Long,
|
||||
status: LiveRoomStatus,
|
||||
|
@@ -2,6 +2,8 @@ package kr.co.vividnext.sodalive.live.room
|
||||
|
||||
import com.amazonaws.services.s3.model.ObjectMetadata
|
||||
import com.fasterxml.jackson.databind.ObjectMapper
|
||||
import kr.co.vividnext.sodalive.agora.RtcTokenBuilder
|
||||
import kr.co.vividnext.sodalive.agora.RtmTokenBuilder
|
||||
import kr.co.vividnext.sodalive.aws.s3.S3Uploader
|
||||
import kr.co.vividnext.sodalive.can.CanRepository
|
||||
import kr.co.vividnext.sodalive.can.charge.Charge
|
||||
@@ -15,6 +17,7 @@ import kr.co.vividnext.sodalive.can.use.CanUsage
|
||||
import kr.co.vividnext.sodalive.can.use.UseCanCalculateRepository
|
||||
import kr.co.vividnext.sodalive.can.use.UseCanCalculateStatus
|
||||
import kr.co.vividnext.sodalive.common.SodaException
|
||||
import kr.co.vividnext.sodalive.explorer.ExplorerQueryRepository
|
||||
import kr.co.vividnext.sodalive.extensions.convertLocalDateTime
|
||||
import kr.co.vividnext.sodalive.live.reservation.LiveReservationRepository
|
||||
import kr.co.vividnext.sodalive.live.room.cancel.CancelLiveRequest
|
||||
@@ -23,12 +26,22 @@ import kr.co.vividnext.sodalive.live.room.cancel.LiveRoomCancelRepository
|
||||
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailManager
|
||||
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailResponse
|
||||
import kr.co.vividnext.sodalive.live.room.detail.GetRoomDetailUser
|
||||
import kr.co.vividnext.sodalive.live.room.donation.DeleteLiveRoomDonationMessage
|
||||
import kr.co.vividnext.sodalive.live.room.donation.GetLiveRoomDonationStatusResponse
|
||||
import kr.co.vividnext.sodalive.live.room.donation.GetLiveRoomDonationTotalResponse
|
||||
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessage
|
||||
import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationRequest
|
||||
import kr.co.vividnext.sodalive.live.room.info.GetRoomInfoResponse
|
||||
import kr.co.vividnext.sodalive.live.room.info.LiveRoomInfo
|
||||
import kr.co.vividnext.sodalive.live.room.info.LiveRoomInfoRedisRepository
|
||||
import kr.co.vividnext.sodalive.live.room.info.LiveRoomMember
|
||||
import kr.co.vividnext.sodalive.live.room.kickout.LiveRoomKickOutService
|
||||
import kr.co.vividnext.sodalive.live.room.visit.LiveRoomVisitService
|
||||
import kr.co.vividnext.sodalive.live.tag.LiveTagRepository
|
||||
import kr.co.vividnext.sodalive.member.Gender
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||
import kr.co.vividnext.sodalive.member.MemberRole
|
||||
import kr.co.vividnext.sodalive.utils.generateFileName
|
||||
import org.springframework.beans.factory.annotation.Value
|
||||
import org.springframework.data.domain.Pageable
|
||||
@@ -39,6 +52,7 @@ import org.springframework.web.multipart.MultipartFile
|
||||
import java.time.LocalDateTime
|
||||
import java.time.ZoneId
|
||||
import java.time.format.DateTimeFormatter
|
||||
import java.util.Date
|
||||
|
||||
@Service
|
||||
@Transactional(readOnly = true)
|
||||
@@ -46,9 +60,11 @@ class LiveRoomService(
|
||||
private val repository: LiveRoomRepository,
|
||||
private val roomInfoRepository: LiveRoomInfoRedisRepository,
|
||||
private val roomCancelRepository: LiveRoomCancelRepository,
|
||||
private val kickOutService: LiveRoomKickOutService,
|
||||
|
||||
private val useCanCalculateRepository: UseCanCalculateRepository,
|
||||
private val reservationRepository: LiveReservationRepository,
|
||||
private val explorerQueryRepository: ExplorerQueryRepository,
|
||||
private val roomVisitService: LiveRoomVisitService,
|
||||
private val canPaymentService: CanPaymentService,
|
||||
private val chargeRepository: ChargeRepository,
|
||||
@@ -58,6 +74,15 @@ class LiveRoomService(
|
||||
private val objectMapper: ObjectMapper,
|
||||
private val s3Uploader: S3Uploader,
|
||||
|
||||
private val rtcTokenBuilder: RtcTokenBuilder,
|
||||
private val rtmTokenBuilder: RtmTokenBuilder,
|
||||
|
||||
@Value("\${agora.app-id}")
|
||||
private val agoraAppId: String,
|
||||
|
||||
@Value("\${agora.app-certificate}")
|
||||
private val agoraAppCertificate: String,
|
||||
|
||||
@Value("\${cloud.aws.s3.bucket}")
|
||||
private val coverImageBucket: String,
|
||||
@Value("\${cloud.aws.cloud-front.host}")
|
||||
@@ -322,6 +347,7 @@ class LiveRoomService(
|
||||
"${dateTime.hour}_${dateTime.minute}"
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun cancelLive(request: CancelLiveRequest, member: Member) {
|
||||
val room = repository.getLiveRoomAndAccountId(request.roomId, member.id!!)
|
||||
?: throw SodaException("해당하는 라이브가 없습니다.")
|
||||
@@ -339,8 +365,11 @@ class LiveRoomService(
|
||||
if (room.price > 0) {
|
||||
val bookerList = reservationRepository.getReservationBookerList(roomId = room.id!!)
|
||||
for (booker in bookerList) {
|
||||
val useCan = canRepository.getCanUsedForLiveRoomNotRefund(memberId = booker.id!!, roomId = room.id!!)
|
||||
?: continue
|
||||
val useCan = canRepository.getCanUsedForLiveRoomNotRefund(
|
||||
memberId = booker.id!!,
|
||||
roomId = room.id!!,
|
||||
canUsage = CanUsage.LIVE
|
||||
) ?: continue
|
||||
useCan.isRefund = true
|
||||
|
||||
val useCanCalculate = useCanCalculateRepository.findByUseCanIdAndStatus(useCanId = useCan.id!!)
|
||||
@@ -373,6 +402,7 @@ class LiveRoomService(
|
||||
reservationRepository.cancelReservation(roomId = room.id!!)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun enterLive(request: EnterOrQuitLiveRoomRequest, member: Member) {
|
||||
val room = repository.getLiveRoom(id = request.roomId)
|
||||
?: throw SodaException("해당하는 라이브가 없습니다.")
|
||||
@@ -491,4 +521,342 @@ class LiveRoomService(
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun getRoomInfo(roomId: Long, member: Member): GetRoomInfoResponse {
|
||||
val roomInfo = roomInfoRepository.findByIdOrNull(roomId)
|
||||
?: throw SodaException("해당하는 라이브의 정보가 없습니다.")
|
||||
|
||||
val room = repository.findByIdOrNull(roomId)
|
||||
?: throw SodaException("해당하는 라이브의 정보가 없습니다.")
|
||||
|
||||
val currentTimeStamp = Date().time
|
||||
val expireTimestamp = (currentTimeStamp + (60 * 60 * 24 * 1000)) / 1000
|
||||
|
||||
val rtcToken = rtcTokenBuilder.buildTokenWithUid(
|
||||
agoraAppId,
|
||||
agoraAppCertificate,
|
||||
room.channelName!!,
|
||||
member.id!!.toInt(),
|
||||
expireTimestamp.toInt()
|
||||
)
|
||||
|
||||
val rtmToken = rtmTokenBuilder.buildToken(
|
||||
agoraAppId,
|
||||
agoraAppCertificate,
|
||||
member.id!!.toString(),
|
||||
expireTimestamp.toInt()
|
||||
)
|
||||
|
||||
val tags = room.tags.asSequence().filter { it.tag.isActive }.map { it.tag.tag }.toList()
|
||||
val isRadioMode = tags.contains("라디오") or tags.contains("콘서트")
|
||||
val isAvailableDonation = room.member!!.id!! != member.id!! &&
|
||||
room.member!!.role == MemberRole.CREATOR
|
||||
val isFollowingManager = explorerQueryRepository
|
||||
.getNotificationUserIds(room.member!!.id!!)
|
||||
.contains(member.id)
|
||||
|
||||
val donationRankingTop3UserIds = explorerQueryRepository
|
||||
.getMemberDonationRanking(
|
||||
room.member!!.id!!,
|
||||
3,
|
||||
withDonationCoin = false
|
||||
)
|
||||
.asSequence()
|
||||
.map { it.userId }
|
||||
.toList()
|
||||
|
||||
return GetRoomInfoResponse(
|
||||
roomId = roomId,
|
||||
title = room.title,
|
||||
notice = room.notice,
|
||||
coverImageUrl = if (room.bgImage != null) {
|
||||
if (room.bgImage!!.startsWith("https://")) {
|
||||
room.bgImage!!
|
||||
} else {
|
||||
"$cloudFrontHost/${room.bgImage!!}"
|
||||
}
|
||||
} else {
|
||||
if (room.coverImage!!.startsWith("https://")) {
|
||||
room.coverImage!!
|
||||
} else {
|
||||
"$cloudFrontHost/${room.coverImage!!}"
|
||||
}
|
||||
},
|
||||
channelName = room.channelName!!,
|
||||
rtcToken = rtcToken,
|
||||
rtmToken = rtmToken,
|
||||
managerId = room.member!!.id!!,
|
||||
managerNickname = room.member!!.nickname,
|
||||
managerProfileUrl = if (room.member!!.profileImage != null) {
|
||||
"$cloudFrontHost/${room.member!!.profileImage}"
|
||||
} else {
|
||||
"$cloudFrontHost/profile/default-profile.png"
|
||||
},
|
||||
isFollowingManager = isFollowingManager,
|
||||
participantsCount = roomInfo.listenerCount + roomInfo.speakerCount + roomInfo.managerCount,
|
||||
totalAvailableParticipantsCount = room.numberOfPeople,
|
||||
speakerList = roomInfo.speakerList,
|
||||
listenerList = roomInfo.listenerList,
|
||||
managerList = roomInfo.managerList,
|
||||
donationRankingTop3UserIds = donationRankingTop3UserIds,
|
||||
isRadioMode = isRadioMode,
|
||||
isAvailableDonation = isAvailableDonation,
|
||||
isPrivateRoom = room.type == LiveRoomType.PRIVATE,
|
||||
password = room.password
|
||||
)
|
||||
}
|
||||
|
||||
fun getDonationMessageList(roomId: Long, member: Member): List<LiveRoomDonationMessage> {
|
||||
val room = repository.findByIdOrNull(roomId)
|
||||
?: throw SodaException("해당하는 라이브가 없습니다.")
|
||||
|
||||
if (member.id!! != room.member!!.id!!) {
|
||||
throw SodaException("잘못된 요청입니다.")
|
||||
}
|
||||
|
||||
val roomInfo = roomInfoRepository.findByIdOrNull(roomId)
|
||||
?: throw SodaException("해당하는 라이브의 정보가 없습니다.")
|
||||
|
||||
return roomInfo.donationMessageList
|
||||
}
|
||||
|
||||
fun deleteDonationMessage(request: DeleteLiveRoomDonationMessage, member: Member) {
|
||||
val room = repository.findByIdOrNull(request.roomId)
|
||||
?: throw SodaException("해당하는 라이브가 없습니다.")
|
||||
|
||||
if (member.id!! != room.member!!.id!!) {
|
||||
throw SodaException("잘못된 요청입니다.")
|
||||
}
|
||||
|
||||
val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId)
|
||||
?: throw SodaException("해당하는 라이브의 정보가 없습니다.")
|
||||
|
||||
roomInfo.removeDonationMessage(request.messageUUID)
|
||||
roomInfoRepository.save(roomInfo)
|
||||
}
|
||||
|
||||
fun getUserProfile(roomId: Long, userId: Long, member: Member): GetLiveRoomUserProfileResponse {
|
||||
val room = repository.getLiveRoom(roomId)
|
||||
?: throw SodaException("해당하는 라이브의 정보가 없습니다.")
|
||||
val roomInfo = roomInfoRepository.findByIdOrNull(roomId)
|
||||
?: throw SodaException("해당하는 라이브의 정보가 없습니다.")
|
||||
|
||||
val user = memberRepository.findByIdOrNull(userId)
|
||||
?: throw SodaException("잘못된 요청입니다.")
|
||||
|
||||
val isFollowing = if (user.role == MemberRole.CREATOR) {
|
||||
explorerQueryRepository
|
||||
.getNotificationUserIds(userId)
|
||||
.contains(member.id!!)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
// 조회 하는 유저
|
||||
val memberResponse = LiveRoomMember(member)
|
||||
// 조회 당하는 유저
|
||||
val userResponse = LiveRoomMember(user)
|
||||
|
||||
val isSpeaker = if (
|
||||
room.member!!.id!! != userId &&
|
||||
(room.member!!.id!! == member.id!! || roomInfo.managerList.contains(memberResponse))
|
||||
) {
|
||||
roomInfo.speakerList.contains(userResponse)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
val isManager = if (room.member!!.id!! != userId && room.member!!.id!! == member.id!!) {
|
||||
roomInfo.managerList.contains(userResponse)
|
||||
} else {
|
||||
null
|
||||
}
|
||||
|
||||
return GetLiveRoomUserProfileResponse(
|
||||
userId = user.id!!,
|
||||
nickname = user.nickname,
|
||||
profileUrl = if (user.profileImage != null) {
|
||||
"$cloudFrontHost/${user.profileImage}"
|
||||
} else {
|
||||
"$cloudFrontHost/profile/default-profile.png"
|
||||
},
|
||||
gender = if (user.gender == Gender.FEMALE) "여" else if (user.gender == Gender.MALE) "남" else "미",
|
||||
instagramUrl = user.instagramUrl,
|
||||
youtubeUrl = user.youtubeUrl,
|
||||
websiteUrl = user.websiteUrl,
|
||||
blogUrl = user.blogUrl,
|
||||
introduce = user.introduce,
|
||||
tags = "",
|
||||
isSpeaker = isSpeaker,
|
||||
isManager = isManager,
|
||||
isFollowing = isFollowing,
|
||||
isBlock = false
|
||||
)
|
||||
}
|
||||
|
||||
fun getDonationTotal(roomId: Long): GetLiveRoomDonationTotalResponse {
|
||||
return GetLiveRoomDonationTotalResponse(
|
||||
totalDonationCoin = repository.getDonationTotal(roomId = roomId) ?: 0
|
||||
)
|
||||
}
|
||||
|
||||
fun setSpeaker(request: SetManagerOrSpeakerOrAudienceRequest) {
|
||||
val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId)
|
||||
?: throw SodaException("해당하는 라이브의 정보가 없습니다.")
|
||||
|
||||
val account = memberRepository.findByIdOrNull(request.accountId)
|
||||
?: throw SodaException("로그인 정보를 확인해 주세요.")
|
||||
|
||||
if (roomInfo.speakerCount > 9) {
|
||||
throw SodaException("스피커 정원이 초과하였습니다.")
|
||||
}
|
||||
|
||||
roomInfo.removeListener(account)
|
||||
roomInfo.removeManager(account)
|
||||
roomInfo.addSpeaker(account)
|
||||
|
||||
roomInfoRepository.save(roomInfo)
|
||||
}
|
||||
|
||||
fun setListener(request: SetManagerOrSpeakerOrAudienceRequest) {
|
||||
val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId)
|
||||
?: throw SodaException("해당하는 라이브의 정보가 없습니다.")
|
||||
|
||||
val member = memberRepository.findByIdOrNull(request.accountId)
|
||||
?: throw SodaException("로그인 정보를 확인해 주세요.")
|
||||
|
||||
roomInfo.removeSpeaker(member)
|
||||
roomInfo.removeManager(member)
|
||||
roomInfo.addListener(member)
|
||||
|
||||
roomInfoRepository.save(roomInfo)
|
||||
}
|
||||
|
||||
fun setManager(request: SetManagerOrSpeakerOrAudienceRequest, member: Member) {
|
||||
val room = repository.getLiveRoom(request.roomId) ?: throw SodaException("잘못된 요청입니다.")
|
||||
if (room.member!!.id!! != member.id!!) {
|
||||
throw SodaException("권한이 없습니다.")
|
||||
}
|
||||
|
||||
val user = memberRepository.findByIdOrNull(request.accountId) ?: throw SodaException("해당하는 유저가 없습니다.")
|
||||
val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId)
|
||||
?: throw SodaException("해당하는 라이브의 정보가 없습니다.")
|
||||
|
||||
val roomAccountResponse = LiveRoomMember(member = user)
|
||||
if (roomInfo.managerList.contains(roomAccountResponse)) {
|
||||
throw SodaException("이미 매니저 입니다.")
|
||||
}
|
||||
|
||||
if (
|
||||
!roomInfo.speakerList.contains(roomAccountResponse) &&
|
||||
!roomInfo.listenerList.contains(roomAccountResponse)
|
||||
) {
|
||||
throw SodaException("해당하는 유저가 없습니다.")
|
||||
}
|
||||
|
||||
roomInfo.removeListener(user)
|
||||
roomInfo.removeSpeaker(user)
|
||||
roomInfo.addManager(user)
|
||||
|
||||
roomInfoRepository.save(roomInfo)
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun donation(request: LiveRoomDonationRequest, member: Member) {
|
||||
val room = repository.findByIdOrNull(request.roomId)
|
||||
?: throw SodaException("해당하는 라이브가 없습니다.")
|
||||
|
||||
val host = room.member ?: throw SodaException("잘못된 요청입니다.")
|
||||
|
||||
if (host.role != MemberRole.CREATOR) {
|
||||
throw SodaException("비비드넥스트와 계약한\n요즘친구에게만 후원을 하실 수 있습니다.")
|
||||
}
|
||||
|
||||
canPaymentService.spendCan(
|
||||
memberId = member.id!!,
|
||||
needCan = request.can,
|
||||
canUsage = CanUsage.DONATION,
|
||||
liveRoom = room,
|
||||
container = request.container
|
||||
)
|
||||
|
||||
if (request.message.isNotBlank()) {
|
||||
val roomInfo = roomInfoRepository.findByIdOrNull(room.id!!)
|
||||
?: throw SodaException("해당하는 라이브의 정보가 없습니다.")
|
||||
|
||||
roomInfo.addDonationMessage(
|
||||
nickname = member.nickname,
|
||||
can = request.can,
|
||||
donationMessage = request.message
|
||||
)
|
||||
|
||||
roomInfoRepository.save(roomInfo)
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun refundDonation(roomId: Long, member: Member) {
|
||||
val donator = memberRepository.findByIdOrNull(member.id)
|
||||
?: throw SodaException("후원에 실패한 코인이 환불되지 않았습니다\n고객센터로 문의해주세요.")
|
||||
|
||||
val useCan = canRepository.getCanUsedForLiveRoomNotRefund(
|
||||
memberId = member.id!!,
|
||||
roomId = roomId,
|
||||
canUsage = CanUsage.DONATION
|
||||
) ?: throw SodaException("후원에 실패한 코인이 환불되지 않았습니다\n고객센터로 문의해주세요.")
|
||||
useCan.isRefund = true
|
||||
|
||||
val useCoinCalculates = useCanCalculateRepository.findByUseCanIdAndStatus(useCan.id!!)
|
||||
useCoinCalculates.forEach {
|
||||
it.status = UseCanCalculateStatus.REFUND
|
||||
val charge = Charge(0, it.can, status = ChargeStatus.REFUND_CHARGE)
|
||||
charge.title = "${it.can} 코인"
|
||||
charge.useCan = useCan
|
||||
|
||||
when (it.paymentGateway) {
|
||||
PaymentGateway.PG -> donator.pgRewardCan += charge.rewardCan
|
||||
PaymentGateway.GOOGLE_IAP -> donator.googleRewardCan += charge.rewardCan
|
||||
PaymentGateway.APPLE_IAP -> donator.appleRewardCan += charge.rewardCan
|
||||
}
|
||||
charge.member = donator
|
||||
|
||||
val payment = Payment(
|
||||
status = PaymentStatus.COMPLETE,
|
||||
paymentGateway = it.paymentGateway
|
||||
)
|
||||
payment.method = "환불"
|
||||
charge.payment = payment
|
||||
|
||||
chargeRepository.save(charge)
|
||||
}
|
||||
}
|
||||
|
||||
fun getDonationStatus(roomId: Long, member: Member): GetLiveRoomDonationStatusResponse {
|
||||
val room = repository.getLiveRoom(roomId) ?: throw SodaException("잘못된 요청입니다.")
|
||||
val donationList = repository.getDonationList(roomId = room.id!!, cloudFrontHost = cloudFrontHost)
|
||||
val totalCan = donationList.sumOf { it.can }
|
||||
|
||||
return GetLiveRoomDonationStatusResponse(
|
||||
donationList = donationList,
|
||||
totalCount = donationList.size,
|
||||
totalCan = totalCan
|
||||
)
|
||||
}
|
||||
|
||||
fun quitRoom(roomId: Long, member: Member) {
|
||||
val room = repository.getLiveRoom(roomId)
|
||||
val roomInfo = roomInfoRepository.findByIdOrNull(roomId)
|
||||
if (roomInfo != null) {
|
||||
if (room?.member != null && room.member!! == member) {
|
||||
room.isActive = false
|
||||
kickOutService.deleteKickOutData(roomId = room.id!!)
|
||||
roomInfoRepository.deleteById(roomInfo.roomId)
|
||||
} else {
|
||||
roomInfo.removeSpeaker(member)
|
||||
roomInfo.removeListener(member)
|
||||
roomInfo.removeManager(member)
|
||||
roomInfoRepository.save(roomInfo)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,6 @@
|
||||
package kr.co.vividnext.sodalive.live.room
|
||||
|
||||
data class SetManagerOrSpeakerOrAudienceRequest(
|
||||
val roomId: Long,
|
||||
val accountId: Long
|
||||
)
|
@@ -0,0 +1,6 @@
|
||||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
data class DeleteLiveRoomDonationMessage(
|
||||
val roomId: Long,
|
||||
val messageUUID: String
|
||||
)
|
@@ -0,0 +1,16 @@
|
||||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
import com.querydsl.core.annotations.QueryProjection
|
||||
|
||||
data class GetLiveRoomDonationStatusResponse(
|
||||
val donationList: List<GetLiveRoomDonationItem>,
|
||||
val totalCount: Int,
|
||||
val totalCan: Int
|
||||
)
|
||||
|
||||
data class GetLiveRoomDonationItem @QueryProjection constructor(
|
||||
val profileImage: String,
|
||||
val nickname: String,
|
||||
val userId: Long,
|
||||
val can: Int
|
||||
)
|
@@ -0,0 +1,3 @@
|
||||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
data class GetLiveRoomDonationTotalResponse(val totalDonationCoin: Int)
|
@@ -0,0 +1,8 @@
|
||||
package kr.co.vividnext.sodalive.live.room.donation
|
||||
|
||||
data class LiveRoomDonationRequest(
|
||||
val roomId: Long,
|
||||
val can: Int,
|
||||
val container: String,
|
||||
val message: String = ""
|
||||
)
|
@@ -0,0 +1,25 @@
|
||||
package kr.co.vividnext.sodalive.live.room.info
|
||||
|
||||
data class GetRoomInfoResponse(
|
||||
val roomId: Long,
|
||||
val title: String,
|
||||
val notice: String,
|
||||
val coverImageUrl: String,
|
||||
val channelName: String,
|
||||
val rtcToken: String,
|
||||
val rtmToken: String,
|
||||
val managerId: Long,
|
||||
val managerNickname: String,
|
||||
val managerProfileUrl: String,
|
||||
val isFollowingManager: Boolean,
|
||||
val participantsCount: Int,
|
||||
val totalAvailableParticipantsCount: Int,
|
||||
val speakerList: List<LiveRoomMember>,
|
||||
val listenerList: List<LiveRoomMember>,
|
||||
val managerList: List<LiveRoomMember>,
|
||||
val donationRankingTop3UserIds: List<Long>,
|
||||
val isRadioMode: Boolean = false,
|
||||
val isAvailableDonation: Boolean = false,
|
||||
val isPrivateRoom: Boolean = false,
|
||||
val password: String? = null
|
||||
)
|
@@ -0,0 +1,32 @@
|
||||
package kr.co.vividnext.sodalive.live.room.kickout
|
||||
|
||||
import org.springframework.data.annotation.Id
|
||||
import org.springframework.data.redis.core.RedisHash
|
||||
|
||||
@RedisHash("live_room_kick_out")
|
||||
data class LiveRoomKickOut(
|
||||
@Id
|
||||
val roomId: Long,
|
||||
var userList: MutableList<LiveRoomKickOutUser> = mutableListOf()
|
||||
) {
|
||||
fun kickOut(userId: Long) {
|
||||
var liveRoomKickOutUser = userList.find { it.userId == userId }
|
||||
if (liveRoomKickOutUser == null) {
|
||||
liveRoomKickOutUser = LiveRoomKickOutUser(userId)
|
||||
} else {
|
||||
liveRoomKickOutUser.plusCount()
|
||||
}
|
||||
|
||||
userList.removeIf { it.userId == userId }
|
||||
userList.add(liveRoomKickOutUser)
|
||||
}
|
||||
}
|
||||
|
||||
data class LiveRoomKickOutUser(
|
||||
val userId: Long,
|
||||
var count: Int = 1
|
||||
) {
|
||||
fun plusCount() {
|
||||
count += 1
|
||||
}
|
||||
}
|
@@ -0,0 +1,25 @@
|
||||
package kr.co.vividnext.sodalive.live.room.kickout
|
||||
|
||||
import kr.co.vividnext.sodalive.common.ApiResponse
|
||||
import kr.co.vividnext.sodalive.common.SodaException
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
||||
import org.springframework.web.bind.annotation.PostMapping
|
||||
import org.springframework.web.bind.annotation.RequestBody
|
||||
import org.springframework.web.bind.annotation.RequestMapping
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
|
||||
@RestController
|
||||
@RequestMapping("/live/room/kick-out")
|
||||
class LiveRoomKickOutController(private val service: LiveRoomKickOutService) {
|
||||
|
||||
@PostMapping
|
||||
fun liveRoomKickOut(
|
||||
@RequestBody request: LiveRoomKickOutRequest,
|
||||
@AuthenticationPrincipal(expression = "#this == 'anonymousUser' ? null : member") member: Member?
|
||||
) = run {
|
||||
if (member == null) throw SodaException("로그인 정보를 확인해주세요.")
|
||||
|
||||
ApiResponse.ok(service.kickOut(request = request, member = member))
|
||||
}
|
||||
}
|
@@ -0,0 +1,5 @@
|
||||
package kr.co.vividnext.sodalive.live.room.kickout
|
||||
|
||||
import org.springframework.data.repository.CrudRepository
|
||||
|
||||
interface LiveRoomKickOutRedisRepository : CrudRepository<LiveRoomKickOut, Long>
|
@@ -0,0 +1,6 @@
|
||||
package kr.co.vividnext.sodalive.live.room.kickout
|
||||
|
||||
data class LiveRoomKickOutRequest(
|
||||
val roomId: Long,
|
||||
val userId: Long
|
||||
)
|
@@ -0,0 +1,61 @@
|
||||
package kr.co.vividnext.sodalive.live.room.kickout
|
||||
|
||||
import kr.co.vividnext.sodalive.common.SodaException
|
||||
import kr.co.vividnext.sodalive.live.room.LiveRoomRepository
|
||||
import kr.co.vividnext.sodalive.live.room.info.LiveRoomInfoRedisRepository
|
||||
import kr.co.vividnext.sodalive.live.room.info.LiveRoomMember
|
||||
import kr.co.vividnext.sodalive.member.Member
|
||||
import kr.co.vividnext.sodalive.member.MemberRepository
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.stereotype.Service
|
||||
|
||||
@Service
|
||||
class LiveRoomKickOutService(
|
||||
private val roomInfoRepository: LiveRoomInfoRedisRepository,
|
||||
private val repository: LiveRoomKickOutRedisRepository,
|
||||
private val accountRepository: MemberRepository,
|
||||
private val roomRepository: LiveRoomRepository
|
||||
) {
|
||||
fun kickOut(request: LiveRoomKickOutRequest, member: Member) {
|
||||
val room = roomRepository.findByIdOrNull(request.roomId)
|
||||
?: throw SodaException("해당하는 라이브가 없습니다.")
|
||||
|
||||
val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId)
|
||||
?: throw SodaException("해당하는 라이브의 정보가 없습니다.")
|
||||
|
||||
if (room.member == null || room.member!!.id == null) {
|
||||
throw SodaException("해당하는 라이브가 없습니다.")
|
||||
}
|
||||
|
||||
if (!roomInfo.managerList.contains(LiveRoomMember(member)) && room.member!!.id != member.id) {
|
||||
throw SodaException("권한이 없습니다.")
|
||||
}
|
||||
|
||||
var liveRoomKickOut = repository.findByIdOrNull(request.roomId)
|
||||
if (liveRoomKickOut == null) {
|
||||
liveRoomKickOut = repository.save(LiveRoomKickOut(roomId = request.roomId))
|
||||
}
|
||||
|
||||
liveRoomKickOut.kickOut(request.userId)
|
||||
repository.save(liveRoomKickOut)
|
||||
|
||||
val kickOutUser = accountRepository.findByIdOrNull(request.userId)
|
||||
if (kickOutUser != null) {
|
||||
roomInfo.removeSpeaker(kickOutUser)
|
||||
roomInfo.removeListener(kickOutUser)
|
||||
roomInfo.removeManager(kickOutUser)
|
||||
roomInfoRepository.save(roomInfo)
|
||||
}
|
||||
}
|
||||
|
||||
fun getKickOutCount(roomId: Long, userId: Long): Int {
|
||||
val liveRoomKickOut = repository.findByIdOrNull(roomId) ?: return 0
|
||||
|
||||
val findUser = liveRoomKickOut.userList.find { it.userId == userId } ?: return 0
|
||||
return findUser.count
|
||||
}
|
||||
|
||||
fun deleteKickOutData(roomId: Long) {
|
||||
repository.deleteById(roomId)
|
||||
}
|
||||
}
|
Reference in New Issue
Block a user