diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/admin/live/AdminLiveService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/admin/live/AdminLiveService.kt index f1fc065..6e672bb 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/admin/live/AdminLiveService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/admin/live/AdminLiveService.kt @@ -20,9 +20,9 @@ import kr.co.vividnext.sodalive.live.recommend.RecommendLiveCreatorBannerReposit import kr.co.vividnext.sodalive.live.reservation.LiveReservationRepository import kr.co.vividnext.sodalive.live.room.cancel.LiveRoomCancel import kr.co.vividnext.sodalive.live.room.cancel.LiveRoomCancelRepository -import kr.co.vividnext.sodalive.live.room.info.LiveRoomInfoRedisRepository import kr.co.vividnext.sodalive.member.MemberRepository import kr.co.vividnext.sodalive.utils.generateFileName +import kr.co.vividnext.sodalive.v2.LiveRoomInfoRedisRepositoryV2 import org.springframework.beans.factory.annotation.Value import org.springframework.context.ApplicationEventPublisher import org.springframework.data.domain.Pageable @@ -37,7 +37,7 @@ import java.time.format.DateTimeFormatter @Service class AdminLiveService( private val recommendCreatorBannerRepository: RecommendLiveCreatorBannerRepository, - private val roomInfoRepository: LiveRoomInfoRedisRepository, + private val roomInfoRepository: LiveRoomInfoRedisRepositoryV2, private val roomCancelRepository: LiveRoomCancelRepository, private val repository: AdminLiveRoomQueryRepository, private val memberRepository: MemberRepository, 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 37ecf7b..51090ac 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 @@ -35,7 +35,6 @@ 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.donation.LiveRoomDonationResponse 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 @@ -55,6 +54,8 @@ import kr.co.vividnext.sodalive.member.MemberRepository import kr.co.vividnext.sodalive.member.MemberRole import kr.co.vividnext.sodalive.member.block.BlockMemberRepository import kr.co.vividnext.sodalive.utils.generateFileName +import kr.co.vividnext.sodalive.v2.LiveRoomInfoRedisRepositoryV2 +import kr.co.vividnext.sodalive.v2.LiveRoomInfoV2 import org.springframework.beans.factory.annotation.Value import org.springframework.context.ApplicationEventPublisher import org.springframework.data.domain.Pageable @@ -79,6 +80,7 @@ class LiveRoomService( private val repository: LiveRoomRepository, private val rouletteRepository: NewRouletteRepository, private val roomInfoRepository: LiveRoomInfoRedisRepository, + private val roomInfoRepositoryV2: LiveRoomInfoRedisRepositoryV2, private val roomCancelRepository: LiveRoomCancelRepository, private val kickOutService: LiveRoomKickOutService, private val blockMemberRepository: BlockMemberRepository, @@ -113,6 +115,22 @@ class LiveRoomService( ) { private val tokenLocks: MutableMap<Long, ReentrantReadWriteLock> = mutableMapOf() + private fun getLiveRoomInfo(roomId: Long): LiveRoomInfoV2? { + var roomInfoV2 = roomInfoRepositoryV2.findByIdOrNull(roomId) + + if (roomInfoV2 == null) { + val roomInfoV1 = roomInfoRepository.findByIdOrNull(roomId) + + if (roomInfoV1 != null) { + roomInfoV2 = roomInfoV1.convertRoomInfoV2() + roomInfoRepositoryV2.save(roomInfoV2) + roomInfoRepository.delete(roomInfoV1) + } + } + + return roomInfoV2 + } + fun getRoomList( dateString: String?, status: LiveRoomStatus, @@ -150,7 +168,7 @@ class LiveRoomService( return roomList .filter { !blockMemberRepository.isBlocked(blockedMemberId = member.id!!, memberId = it.member!!.id!!) } .map { - val roomInfo = roomInfoRepository.findByIdOrNull(it.id!!) + val roomInfo = getLiveRoomInfo(it.id!!) val reservations = it.reservations .filter { reservation -> reservation.member!!.id!! == member.id!! && reservation.isActive } @@ -442,7 +460,7 @@ class LiveRoomService( response.manager = GetRoomDetailManager(room.member!!, cloudFrontHost = cloudFrontHost) if (!room.channelName.isNullOrBlank()) { - val roomInfo = roomInfoRepository.findByIdOrNull(roomId) + val roomInfo = getLiveRoomInfo(roomId) if (roomInfo != null) { response.isPaid = canRepository.isExistPaidLiveRoom( @@ -651,9 +669,9 @@ class LiveRoomService( val lock = getOrCreateLock(memberId = member.id!!) lock.write { - var roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) + var roomInfo = getLiveRoomInfo(request.roomId) if (roomInfo == null) { - roomInfo = roomInfoRepository.save(LiveRoomInfo(roomId = request.roomId)) + roomInfo = roomInfoRepositoryV2.save(LiveRoomInfoV2(roomId = request.roomId)) } if (roomInfo.speakerCount + roomInfo.listenerCount + roomInfo.managerCount >= room.numberOfPeople) { @@ -692,7 +710,7 @@ class LiveRoomService( roomInfo.addListener(member, cloudFrontHost) } - roomInfoRepository.save(roomInfo) + roomInfoRepositoryV2.save(roomInfo) roomVisitService.roomVisit(room, member) } } @@ -789,7 +807,7 @@ class LiveRoomService( } fun getRoomInfo(roomId: Long, member: Member): GetRoomInfoResponse { - val roomInfo = roomInfoRepository.findByIdOrNull(roomId) + val roomInfo = getLiveRoomInfo(roomId) ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") val room = repository.findByIdOrNull(roomId) @@ -889,7 +907,7 @@ class LiveRoomService( val liveRoomCreatorId = repository.getLiveRoomCreatorId(roomId) ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") - val roomInfo = roomInfoRepository.findByIdOrNull(roomId) + val roomInfo = getLiveRoomInfo(roomId) ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") return if (liveRoomCreatorId != member.id!!) { @@ -910,18 +928,18 @@ class LiveRoomService( throw SodaException("잘못된 요청입니다.") } - val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) + val roomInfo = getLiveRoomInfo(request.roomId) ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") roomInfo.removeDonationMessage(request.messageUUID) - roomInfoRepository.save(roomInfo) + roomInfoRepositoryV2.save(roomInfo) } } fun getUserProfile(roomId: Long, userId: Long, member: Member): GetLiveRoomUserProfileResponse { val room = repository.getLiveRoom(roomId) ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") - val roomInfo = roomInfoRepository.findByIdOrNull(roomId) + val roomInfo = getLiveRoomInfo(roomId) ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") val user = memberRepository.findByIdOrNull(userId) @@ -990,7 +1008,7 @@ class LiveRoomService( fun setSpeaker(request: SetManagerOrSpeakerOrAudienceRequest) { val lock = getOrCreateLock(memberId = request.memberId) lock.write { - val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) + val roomInfo = getLiveRoomInfo(request.roomId) ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") val member = memberRepository.findByIdOrNull(request.memberId) @@ -1004,14 +1022,14 @@ class LiveRoomService( roomInfo.removeManager(member) roomInfo.addSpeaker(member, cloudFrontHost) - roomInfoRepository.save(roomInfo) + roomInfoRepositoryV2.save(roomInfo) } } fun setListener(request: SetManagerOrSpeakerOrAudienceRequest) { val lock = getOrCreateLock(memberId = request.memberId) lock.write { - val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) + val roomInfo = getLiveRoomInfo(request.roomId) ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") val member = memberRepository.findByIdOrNull(request.memberId) @@ -1021,7 +1039,7 @@ class LiveRoomService( roomInfo.removeManager(member) roomInfo.addListener(member, cloudFrontHost) - roomInfoRepository.save(roomInfo) + roomInfoRepositoryV2.save(roomInfo) } } @@ -1034,7 +1052,7 @@ class LiveRoomService( } val user = memberRepository.findByIdOrNull(request.memberId) ?: throw SodaException("해당하는 유저가 없습니다.") - val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) + val roomInfo = getLiveRoomInfo(request.roomId) ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") val roomAccountResponse = LiveRoomMember(member = user, cloudFrontHost) @@ -1053,7 +1071,7 @@ class LiveRoomService( roomInfo.removeSpeaker(user) roomInfo.addManager(user, cloudFrontHost) - roomInfoRepository.save(roomInfo) + roomInfoRepositoryV2.save(roomInfo) } } @@ -1080,7 +1098,7 @@ class LiveRoomService( if (request.message.isNotBlank()) { val lock = getOrCreateLock(memberId = member.id!!) lock.write { - val roomInfo = roomInfoRepository.findByIdOrNull(room.id!!) + val roomInfo = getLiveRoomInfo(room.id!!) ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") roomInfo.addDonationMessage( @@ -1091,7 +1109,7 @@ class LiveRoomService( donationMessage = request.message ) - roomInfoRepository.save(roomInfo) + roomInfoRepositoryV2.save(roomInfo) } } @@ -1126,7 +1144,7 @@ class LiveRoomService( if (request.message.isNotBlank()) { val lock = getOrCreateLock(memberId = member.id!!) lock.write { - val roomInfo = roomInfoRepository.findByIdOrNull(room.id!!) + val roomInfo = getLiveRoomInfo(room.id!!) ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") roomInfo.addDonationMessage( @@ -1137,7 +1155,7 @@ class LiveRoomService( donationMessage = request.message ) - roomInfoRepository.save(roomInfo) + roomInfoRepositoryV2.save(roomInfo) } } @@ -1216,12 +1234,12 @@ class LiveRoomService( val room = repository.getLiveRoom(roomId) val lock = getOrCreateLock(memberId = member.id!!) lock.write { - val roomInfo = roomInfoRepository.findByIdOrNull(roomId) + val roomInfo = getLiveRoomInfo(roomId) if (roomInfo != null) { if (room?.member != null && room.member!! == member) { room.isActive = false kickOutService.deleteKickOutData(roomId = room.id!!) - roomInfoRepository.deleteById(roomInfo.roomId) + roomInfoRepositoryV2.deleteById(roomInfo.roomId) val rouletteList = rouletteRepository.findByCreatorId(creatorId = member.id!!) if (rouletteList.isNotEmpty()) { @@ -1236,7 +1254,7 @@ class LiveRoomService( roomInfo.removeSpeaker(member) roomInfo.removeListener(member) roomInfo.removeManager(member) - roomInfoRepository.save(roomInfo) + roomInfoRepositoryV2.save(roomInfo) } } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/info/LiveRoomInfo.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/info/LiveRoomInfo.kt index 6c366d8..d648b90 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/info/LiveRoomInfo.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/info/LiveRoomInfo.kt @@ -2,6 +2,7 @@ package kr.co.vividnext.sodalive.live.room.info import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessage import kr.co.vividnext.sodalive.member.Member +import kr.co.vividnext.sodalive.v2.LiveRoomInfoV2 import org.springframework.data.annotation.Id import org.springframework.data.redis.core.RedisHash @@ -118,4 +119,14 @@ data class LiveRoomInfo( fun removeDonationMessage(uuid: String) { (donationMessageList as MutableList).removeIf { it.uuid == uuid } } + + fun convertRoomInfoV2(): LiveRoomInfoV2 { + return LiveRoomInfoV2( + roomId = roomId, + speakerList = speakerList, + listenerList = listenerList, + managerList = managerList, + donationMessageList = donationMessageList + ) + } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/kickout/LiveRoomKickOutService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/kickout/LiveRoomKickOutService.kt index b193a82..a702818 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/room/kickout/LiveRoomKickOutService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/room/kickout/LiveRoomKickOutService.kt @@ -6,6 +6,8 @@ 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 kr.co.vividnext.sodalive.v2.LiveRoomInfoRedisRepositoryV2 +import kr.co.vividnext.sodalive.v2.LiveRoomInfoV2 import org.springframework.beans.factory.annotation.Value import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service @@ -13,6 +15,7 @@ import org.springframework.stereotype.Service @Service class LiveRoomKickOutService( private val roomInfoRepository: LiveRoomInfoRedisRepository, + private val roomInfoRepositoryV2: LiveRoomInfoRedisRepositoryV2, private val repository: LiveRoomKickOutRedisRepository, private val memberRepository: MemberRepository, private val roomRepository: LiveRoomRepository, @@ -20,11 +23,27 @@ class LiveRoomKickOutService( @Value("\${cloud.aws.cloud-front.host}") private val cloudFrontHost: String ) { + private fun getLiveRoomInfo(roomId: Long): LiveRoomInfoV2? { + var roomInfoV2 = roomInfoRepositoryV2.findByIdOrNull(roomId) + + if (roomInfoV2 == null) { + val roomInfoV1 = roomInfoRepository.findByIdOrNull(roomId) + + if (roomInfoV1 != null) { + roomInfoV2 = roomInfoV1.convertRoomInfoV2() + roomInfoRepositoryV2.save(roomInfoV2) + roomInfoRepository.delete(roomInfoV1) + } + } + + return roomInfoV2 + } + fun kickOut(request: LiveRoomKickOutRequest, member: Member) { val room = roomRepository.findByIdOrNull(request.roomId) ?: throw SodaException("해당하는 라이브가 없습니다.") - val roomInfo = roomInfoRepository.findByIdOrNull(request.roomId) + val roomInfo = getLiveRoomInfo(request.roomId) ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") if (room.member == null || room.member!!.id == null) { @@ -48,7 +67,7 @@ class LiveRoomKickOutService( roomInfo.removeSpeaker(kickOutUser) roomInfo.removeListener(kickOutUser) roomInfo.removeManager(kickOutUser) - roomInfoRepository.save(roomInfo) + roomInfoRepositoryV2.save(roomInfo) } } diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/live/roulette/v2/RouletteService.kt b/src/main/kotlin/kr/co/vividnext/sodalive/live/roulette/v2/RouletteService.kt index bcd5802..fabcc4b 100644 --- a/src/main/kotlin/kr/co/vividnext/sodalive/live/roulette/v2/RouletteService.kt +++ b/src/main/kotlin/kr/co/vividnext/sodalive/live/roulette/v2/RouletteService.kt @@ -20,6 +20,8 @@ import kr.co.vividnext.sodalive.live.roulette.RouletteItem 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.v2.LiveRoomInfoRedisRepositoryV2 +import kr.co.vividnext.sodalive.v2.LiveRoomInfoV2 import org.springframework.data.repository.findByIdOrNull import org.springframework.stereotype.Service import org.springframework.transaction.annotation.Transactional @@ -38,10 +40,27 @@ class RouletteService( private val memberRepository: MemberRepository, private val chargeRepository: ChargeRepository, private val roomInfoRepository: LiveRoomInfoRedisRepository, + private val roomInfoRepositoryV2: LiveRoomInfoRedisRepositoryV2, private val useCanCalculateRepository: UseCanCalculateRepository ) { private val tokenLocks: MutableMap<Long, ReentrantReadWriteLock> = mutableMapOf() + private fun getLiveRoomInfo(roomId: Long): LiveRoomInfoV2? { + var roomInfoV2 = roomInfoRepositoryV2.findByIdOrNull(roomId) + + if (roomInfoV2 == null) { + val roomInfoV1 = roomInfoRepository.findByIdOrNull(roomId) + + if (roomInfoV1 != null) { + roomInfoV2 = roomInfoV1.convertRoomInfoV2() + roomInfoRepositoryV2.save(roomInfoV2) + roomInfoRepository.delete(roomInfoV1) + } + } + + return roomInfoV2 + } + fun getAllRoulette(creatorId: Long, memberId: Long): List<GetRouletteResponseV2> { if (creatorId != memberId) throw SodaException("잘못된 요청입니다.") @@ -158,7 +177,7 @@ class RouletteService( val lock = getOrCreateLock(memberId = member.id!!) lock.write { - val roomInfo = roomInfoRepository.findByIdOrNull(room.id!!) + val roomInfo = getLiveRoomInfo(room.id!!) ?: throw SodaException("해당하는 라이브의 정보가 없습니다.") roomInfo.addRouletteMessage( @@ -167,7 +186,7 @@ class RouletteService( donationMessage = "[$result] 당첨!" ) - roomInfoRepository.save(roomInfo) + roomInfoRepositoryV2.save(roomInfo) } return SpinRouletteResponse(can = roulette.can, result = result, items = itemList) diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/LiveRoomInfoRedisRepositoryV2.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/LiveRoomInfoRedisRepositoryV2.kt new file mode 100644 index 0000000..c98c39a --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/LiveRoomInfoRedisRepositoryV2.kt @@ -0,0 +1,5 @@ +package kr.co.vividnext.sodalive.v2 + +import org.springframework.data.repository.CrudRepository + +interface LiveRoomInfoRedisRepositoryV2 : CrudRepository<LiveRoomInfoV2, Long> diff --git a/src/main/kotlin/kr/co/vividnext/sodalive/v2/LiveRoomInfoV2.kt b/src/main/kotlin/kr/co/vividnext/sodalive/v2/LiveRoomInfoV2.kt new file mode 100644 index 0000000..b46dfa6 --- /dev/null +++ b/src/main/kotlin/kr/co/vividnext/sodalive/v2/LiveRoomInfoV2.kt @@ -0,0 +1,123 @@ +package kr.co.vividnext.sodalive.v2 + +import kr.co.vividnext.sodalive.live.room.donation.LiveRoomDonationMessage +import kr.co.vividnext.sodalive.live.room.info.LiveRoomMember +import kr.co.vividnext.sodalive.live.room.info.LiveRoomMemberRole +import kr.co.vividnext.sodalive.member.Member +import org.springframework.data.annotation.Id +import org.springframework.data.redis.core.RedisHash + +@RedisHash("LiveRoomInfo") +data class LiveRoomInfoV2( + @Id + val roomId: Long, + var speakerList: List<LiveRoomMember> = mutableListOf(), + var listenerList: List<LiveRoomMember> = mutableListOf(), + var managerList: List<LiveRoomMember> = mutableListOf(), + var donationMessageList: List<LiveRoomDonationMessage> = mutableListOf() +) { + var speakerCount = 0 + private set + + var listenerCount = 0 + private set + + var managerCount = 0 + private set + + fun addSpeaker(member: Member, cloudFrontHost: String) { + val liveRoomMember = LiveRoomMember(member, cloudFrontHost) + liveRoomMember.role = LiveRoomMemberRole.SPEAKER + + val speakerSet = speakerList.toMutableSet() + speakerSet.add(liveRoomMember) + speakerList = speakerSet.toList() + + setSpeakerCount() + } + + fun removeSpeaker(member: Member) { + (speakerList as MutableList).removeIf { it.id == member.id!! } + setSpeakerCount() + } + + private fun setSpeakerCount() { + speakerCount = speakerList.size + } + + fun addListener(member: Member, cloudFrontHost: String) { + val liveRoomMember = LiveRoomMember(member, cloudFrontHost) + liveRoomMember.role = LiveRoomMemberRole.LISTENER + + val listenerSet = listenerList.toMutableSet() + listenerSet.add(liveRoomMember) + listenerList = listenerSet.toList() + + setListenerCount() + } + + fun removeListener(member: Member) { + (listenerList as MutableList).removeIf { it.id == member.id!! } + setListenerCount() + } + + private fun setListenerCount() { + listenerCount = listenerList.size + } + + fun addManager(member: Member, cloudFrontHost: String) { + val liveRoomMember = LiveRoomMember(member, cloudFrontHost) + liveRoomMember.role = LiveRoomMemberRole.MANAGER + + val managerSet = managerList.toMutableSet() + managerSet.add(liveRoomMember) + managerList = managerSet.toList() + + setManagerCount() + } + + fun removeManager(member: Member) { + (managerList as MutableList).removeIf { it.id == member.id!! } + setManagerCount() + } + + private fun setManagerCount() { + managerCount = managerList.size + } + + fun addDonationMessage(memberId: Long, nickname: String, isSecret: Boolean, can: Int, donationMessage: String) { + val donationMessageSet = donationMessageList.toMutableSet() + donationMessageSet.add( + LiveRoomDonationMessage( + memberId = memberId, + nickname = nickname, + isSecret = isSecret, + canMessage = if (isSecret) { + "${can}캔으로 비밀미션을 보냈습니다." + } else { + "${can}캔을 후원하셨습니다." + }, + donationMessage = donationMessage + ) + ) + donationMessageList = donationMessageSet.toList() + } + + fun addRouletteMessage(memberId: Long, nickname: String, donationMessage: String) { + val donationMessageSet = donationMessageList.toMutableSet() + donationMessageSet.add( + LiveRoomDonationMessage( + memberId = memberId, + nickname = nickname, + isSecret = false, + canMessage = "", + donationMessage = donationMessage + ) + ) + donationMessageList = donationMessageSet.toList() + } + + fun removeDonationMessage(uuid: String) { + (donationMessageList as MutableList).removeIf { it.uuid == uuid } + } +}