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 9761c77..495d919 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 @@ -53,6 +53,8 @@ import java.time.LocalDateTime import java.time.ZoneId import java.time.format.DateTimeFormatter import java.util.Date +import java.util.concurrent.locks.ReentrantReadWriteLock +import kotlin.concurrent.write @Service @Transactional(readOnly = true) @@ -88,6 +90,8 @@ class LiveRoomService( @Value("\${cloud.aws.cloud-front.host}") private val cloudFrontHost: String ) { + private val tokenLocks: MutableMap = mutableMapOf() + fun getRoomList( dateString: String?, status: LiveRoomStatus, @@ -415,49 +419,52 @@ class LiveRoomService( throw SodaException("비밀번호가 일치하지 않습니다.\n다시 확인 후 입력해주세요.") } - var roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) - if (roomInfo == null) { - roomInfo = roomInfoRepository.save(LiveRoomInfo(roomId = request.roomId)) - } - - if (roomInfo.speakerCount + roomInfo.listenerCount + roomInfo.managerCount >= room.numberOfPeople) { - throw SodaException("방이 가득찼습니다.") - } - - if ( - room.price > 0 && - room.member!!.id!! != member.id!! && - canRepository.isExistPaidLiveRoom(memberId = member.id!!, roomId = request.roomId) == null - ) { - val findMember = memberRepository.findByIdOrNull(id = member.id!!) - ?: throw SodaException("로그인 정보를 확인해 주세요.") - - val totalCan = findMember.getChargeCan(request.container) + findMember.getRewardCan(request.container) - if (totalCan < room.price) { - throw SodaException("${room.price - totalCan}캔이 부족합니다. 충전 후 이용해 주세요.") + val lock = getOrCreateLock(memberId = member.id!!) + lock.write { + var roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) + if (roomInfo == null) { + roomInfo = roomInfoRepository.save(LiveRoomInfo(roomId = request.roomId)) } - canPaymentService.spendCan( - memberId = member.id!!, - needCan = room.price, - canUsage = CanUsage.LIVE, - liveRoom = room, - container = request.container - ) + if (roomInfo.speakerCount + roomInfo.listenerCount + roomInfo.managerCount >= room.numberOfPeople) { + throw SodaException("방이 가득찼습니다.") + } + + if ( + room.price > 0 && + room.member!!.id!! != member.id!! && + canRepository.isExistPaidLiveRoom(memberId = member.id!!, roomId = request.roomId) == null + ) { + val findMember = memberRepository.findByIdOrNull(id = member.id!!) + ?: throw SodaException("로그인 정보를 확인해 주세요.") + + val totalCan = findMember.getChargeCan(request.container) + findMember.getRewardCan(request.container) + if (totalCan < room.price) { + throw SodaException("${room.price - totalCan}캔이 부족합니다. 충전 후 이용해 주세요.") + } + + canPaymentService.spendCan( + memberId = member.id!!, + needCan = room.price, + canUsage = CanUsage.LIVE, + liveRoom = room, + container = request.container + ) + } + + roomInfo.removeListener(member) + roomInfo.removeSpeaker(member) + roomInfo.removeManager(member) + + if (room.member!!.id == member.id) { + roomInfo.addSpeaker(member, cloudFrontHost) + } else { + roomInfo.addListener(member, cloudFrontHost) + } + + roomInfoRepository.save(roomInfo) + roomVisitService.roomVisit(room, member) } - - roomInfo.removeListener(member) - roomInfo.removeSpeaker(member) - roomInfo.removeManager(member) - - if (room.member!!.id == member.id) { - roomInfo.addSpeaker(member, cloudFrontHost) - } else { - roomInfo.addListener(member, cloudFrontHost) - } - - roomInfoRepository.save(roomInfo) - roomVisitService.roomVisit(room, member) } fun getRecentRoomInfo(member: Member): GetRecentRoomInfoResponse { @@ -621,18 +628,21 @@ class LiveRoomService( } fun deleteDonationMessage(request: DeleteLiveRoomDonationMessage, member: Member) { - val room = repository.findByIdOrNull(request.roomId) - ?: throw SodaException("해당하는 라이브가 없습니다.") + val lock = getOrCreateLock(memberId = member.id!!) + lock.write { + val room = repository.findByIdOrNull(request.roomId) + ?: throw SodaException("해당하는 라이브가 없습니다.") - if (member.id!! != room.member!!.id!!) { - 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) } - - val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) - ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") - - roomInfo.removeDonationMessage(request.messageUUID) - roomInfoRepository.save(roomInfo) } fun getUserProfile(roomId: Long, userId: Long, member: Member): GetLiveRoomUserProfileResponse { @@ -701,64 +711,73 @@ class LiveRoomService( } fun setSpeaker(request: SetManagerOrSpeakerOrAudienceRequest) { - val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) - ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") + val lock = getOrCreateLock(memberId = request.memberId) + lock.write { + val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) + ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") - val account = memberRepository.findByIdOrNull(request.accountId) - ?: throw SodaException("로그인 정보를 확인해 주세요.") + val member = memberRepository.findByIdOrNull(request.memberId) + ?: throw SodaException("로그인 정보를 확인해 주세요.") - if (roomInfo.speakerCount > 9) { - throw SodaException("스피커 정원이 초과하였습니다.") + if (roomInfo.speakerCount > 9) { + throw SodaException("스피커 정원이 초과하였습니다.") + } + + roomInfo.removeListener(member) + roomInfo.removeManager(member) + roomInfo.addSpeaker(member, cloudFrontHost) + + roomInfoRepository.save(roomInfo) } - - roomInfo.removeListener(account) - roomInfo.removeManager(account) - roomInfo.addSpeaker(account, cloudFrontHost) - - roomInfoRepository.save(roomInfo) } fun setListener(request: SetManagerOrSpeakerOrAudienceRequest) { - val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) - ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") + val lock = getOrCreateLock(memberId = request.memberId) + lock.write { + val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) + ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") - val member = memberRepository.findByIdOrNull(request.accountId) - ?: throw SodaException("로그인 정보를 확인해 주세요.") + val member = memberRepository.findByIdOrNull(request.memberId) + ?: throw SodaException("로그인 정보를 확인해 주세요.") - roomInfo.removeSpeaker(member) - roomInfo.removeManager(member) - roomInfo.addListener(member, cloudFrontHost) + roomInfo.removeSpeaker(member) + roomInfo.removeManager(member) + roomInfo.addListener(member, cloudFrontHost) - roomInfoRepository.save(roomInfo) + 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 lock = getOrCreateLock(memberId = member.id!!) + lock.write { + val room = repository.getLiveRoom(request.roomId) ?: throw SodaException("잘못된 요청입니다.") + if (room.member!!.id!! != member.id!!) { + throw SodaException("권한이 없습니다.") + } + + val user = memberRepository.findByIdOrNull(request.memberId) ?: throw SodaException("해당하는 유저가 없습니다.") + val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) + ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") + + val roomAccountResponse = LiveRoomMember(member = user, cloudFrontHost) + 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, cloudFrontHost) + + roomInfoRepository.save(roomInfo) } - - val user = memberRepository.findByIdOrNull(request.accountId) ?: throw SodaException("해당하는 유저가 없습니다.") - val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) - ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") - - val roomAccountResponse = LiveRoomMember(member = user, cloudFrontHost) - 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, cloudFrontHost) - - roomInfoRepository.save(roomInfo) } @Transactional @@ -781,16 +800,19 @@ class LiveRoomService( ) if (request.message.isNotBlank()) { - val roomInfo = roomInfoRepository.findByIdOrNull(room.id!!) - ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") + val lock = getOrCreateLock(memberId = member.id!!) + lock.write { + val roomInfo = roomInfoRepository.findByIdOrNull(room.id!!) + ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") - roomInfo.addDonationMessage( - nickname = member.nickname, - can = request.can, - donationMessage = request.message - ) + roomInfo.addDonationMessage( + nickname = member.nickname, + can = request.can, + donationMessage = request.message + ) - roomInfoRepository.save(roomInfo) + roomInfoRepository.save(roomInfo) + } } } @@ -848,20 +870,27 @@ class LiveRoomService( val roomId = roomVisit?.room?.id if (roomId != null) { - val roomInfo = roomInfoRepository.findByIdOrNull(roomId) - if (roomInfo != null) { - val room = repository.getLiveRoom(roomId) ?: return - 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) + val lock = getOrCreateLock(memberId = member.id!!) + lock.write { + val roomInfo = roomInfoRepository.findByIdOrNull(roomId) + if (roomInfo != null) { + val room = repository.getLiveRoom(roomId) ?: return + 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) + } } } } } + + private fun getOrCreateLock(memberId: Long): ReentrantReadWriteLock { + return tokenLocks.computeIfAbsent(memberId) { ReentrantReadWriteLock() } + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/SetManagerOrSpeakerOrAudienceRequest.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/SetManagerOrSpeakerOrAudienceRequest.kt index e5afd3f..5d41045 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/SetManagerOrSpeakerOrAudienceRequest.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/SetManagerOrSpeakerOrAudienceRequest.kt @@ -2,5 +2,5 @@ package kr.co.vividnext.sodalive.live.room data class SetManagerOrSpeakerOrAudienceRequest( val roomId: Long, - val accountId: Long + val memberId: Long )