87 lines
3.1 KiB
Kotlin
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
|
|
)
|
|
}
|
|
}
|
|
}
|