Files
TrisolarisServer/src/main/kotlin/com/android/trisolarisserver/component/RoomBoardEvents.kt
androidlover5842 ab7f02ddc6 add see update
2026-01-24 23:18:15 +05:30

87 lines
3.1 KiB
Kotlin

package com.android.trisolarisserver.component
import com.android.trisolarisserver.controller.dto.RoomBoardResponse
import com.android.trisolarisserver.controller.dto.RoomBoardStatus
import com.android.trisolarisserver.repo.RoomRepo
import com.android.trisolarisserver.repo.RoomStayRepo
import org.springframework.scheduling.annotation.Scheduled
import org.springframework.stereotype.Component
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter
import java.io.IOException
import java.util.UUID
import java.util.concurrent.ConcurrentHashMap
import java.util.concurrent.CopyOnWriteArrayList
@Component
class RoomBoardEvents(
private val roomRepo: RoomRepo,
private val roomStayRepo: RoomStayRepo
) {
private val emitters: MutableMap<UUID, CopyOnWriteArrayList<SseEmitter>> = ConcurrentHashMap()
fun subscribe(propertyId: UUID): SseEmitter {
val emitter = SseEmitter(0L)
emitters.computeIfAbsent(propertyId) { CopyOnWriteArrayList() }.add(emitter)
emitter.onCompletion { emitters[propertyId]?.remove(emitter) }
emitter.onTimeout { emitters[propertyId]?.remove(emitter) }
emitter.onError { emitters[propertyId]?.remove(emitter) }
try {
emitter.send(SseEmitter.event().name("room-board").data(buildSnapshot(propertyId)))
} catch (_: IOException) {
emitters[propertyId]?.remove(emitter)
}
return emitter
}
fun emit(propertyId: UUID) {
val data = buildSnapshot(propertyId)
val list = emitters[propertyId] ?: return
val dead = mutableListOf<SseEmitter>()
for (emitter in list) {
try {
emitter.send(SseEmitter.event().name("room-board").data(data))
} catch (_: IOException) {
dead.add(emitter)
}
}
if (dead.isNotEmpty()) {
list.removeAll(dead.toSet())
}
}
@Scheduled(fixedDelayString = "25000")
fun heartbeat() {
emitters.forEach { (_, list) ->
val dead = mutableListOf<SseEmitter>()
for (emitter in list) {
try {
emitter.send(SseEmitter.event().name("ping").data("ok"))
} catch (_: IOException) {
dead.add(emitter)
}
}
if (dead.isNotEmpty()) {
list.removeAll(dead.toSet())
}
}
}
private fun buildSnapshot(propertyId: UUID): List<RoomBoardResponse> {
val rooms = roomRepo.findByPropertyIdOrderByRoomNumber(propertyId)
val occupiedRoomIds = roomStayRepo.findOccupiedRoomIds(propertyId).toHashSet()
return rooms.map { room ->
val status = when {
room.maintenance -> RoomBoardStatus.MAINTENANCE
!room.active -> RoomBoardStatus.INACTIVE
occupiedRoomIds.contains(room.id) -> RoomBoardStatus.OCCUPIED
else -> RoomBoardStatus.FREE
}
RoomBoardResponse(
roomNumber = room.roomNumber,
roomTypeName = room.roomType.name,
status = status
)
}
}
}