Expose active temp card state on room responses
All checks were successful
build-and-deploy / build-deploy (push) Successful in 33s

This commit is contained in:
androidlover5842
2026-01-28 17:56:59 +05:30
parent 2591768efb
commit b52cb1a88d
3 changed files with 44 additions and 10 deletions

View File

@@ -10,6 +10,7 @@ import com.android.trisolarisserver.controller.dto.RoomResponse
import com.android.trisolarisserver.controller.dto.RoomUpsertRequest import com.android.trisolarisserver.controller.dto.RoomUpsertRequest
import com.android.trisolarisserver.repo.PropertyRepo import com.android.trisolarisserver.repo.PropertyRepo
import com.android.trisolarisserver.repo.PropertyUserRepo import com.android.trisolarisserver.repo.PropertyUserRepo
import com.android.trisolarisserver.repo.IssuedCardRepo
import com.android.trisolarisserver.repo.RoomImageRepo import com.android.trisolarisserver.repo.RoomImageRepo
import com.android.trisolarisserver.repo.RoomRepo import com.android.trisolarisserver.repo.RoomRepo
import com.android.trisolarisserver.repo.RoomStayRepo import com.android.trisolarisserver.repo.RoomStayRepo
@@ -31,6 +32,7 @@ import org.springframework.web.bind.annotation.RestController
import org.springframework.web.server.ResponseStatusException import org.springframework.web.server.ResponseStatusException
import org.springframework.web.servlet.mvc.method.annotation.SseEmitter import org.springframework.web.servlet.mvc.method.annotation.SseEmitter
import java.time.LocalDate import java.time.LocalDate
import java.time.OffsetDateTime
import java.time.ZoneId import java.time.ZoneId
import java.nio.file.Files import java.nio.file.Files
import java.nio.file.Paths import java.nio.file.Paths
@@ -45,6 +47,7 @@ class Rooms(
private val roomStayRepo: RoomStayRepo, private val roomStayRepo: RoomStayRepo,
private val propertyRepo: PropertyRepo, private val propertyRepo: PropertyRepo,
private val roomTypeRepo: RoomTypeRepo, private val roomTypeRepo: RoomTypeRepo,
private val issuedCardRepo: IssuedCardRepo,
private val propertyUserRepo: PropertyUserRepo, private val propertyUserRepo: PropertyUserRepo,
private val roomBoardEvents: RoomBoardEvents private val roomBoardEvents: RoomBoardEvents
) { ) {
@@ -58,14 +61,15 @@ class Rooms(
propertyAccess.requireMember(propertyId, principal!!.userId) propertyAccess.requireMember(propertyId, principal!!.userId)
val roles = propertyUserRepo.findRolesByPropertyAndUser(propertyId, principal.userId) val roles = propertyUserRepo.findRolesByPropertyAndUser(propertyId, principal.userId)
val rooms = roomRepo.findByPropertyIdOrderByRoomNumber(propertyId) val rooms = roomRepo.findByPropertyIdOrderByRoomNumber(propertyId)
val tempCardsByRoom = loadActiveTempCardsByRoom(propertyId)
if (isAgentOnly(roles)) { if (isAgentOnly(roles)) {
val occupiedRoomIds = roomStayRepo.findOccupiedRoomIds(propertyId).toHashSet() val occupiedRoomIds = roomStayRepo.findOccupiedRoomIds(propertyId).toHashSet()
return rooms return rooms
.filter { it.active && !it.maintenance && !occupiedRoomIds.contains(it.id) } .filter { it.active && !it.maintenance && !occupiedRoomIds.contains(it.id) }
.map { it.toRoomResponse() } .map { it.toRoomResponse(tempCardsByRoom[it.id]) }
} }
return rooms return rooms
.map { it.toRoomResponse() } .map { it.toRoomResponse(tempCardsByRoom[it.id]) }
} }
@GetMapping("/board") @GetMapping("/board")
@@ -134,9 +138,10 @@ class Rooms(
): List<RoomResponse> { ): List<RoomResponse> {
val rooms = roomRepo.findByPropertyIdOrderByRoomNumber(propertyId) val rooms = roomRepo.findByPropertyIdOrderByRoomNumber(propertyId)
val occupiedRoomIds = roomStayRepo.findOccupiedRoomIds(propertyId).toHashSet() val occupiedRoomIds = roomStayRepo.findOccupiedRoomIds(propertyId).toHashSet()
val tempCardsByRoom = loadActiveTempCardsByRoom(propertyId)
return rooms return rooms
.filter { it.active && !it.maintenance && !occupiedRoomIds.contains(it.id) } .filter { it.active && !it.maintenance && !occupiedRoomIds.contains(it.id) }
.map { it.toRoomResponse() } .map { it.toRoomResponse(tempCardsByRoom[it.id]) }
} }
@GetMapping("/by-type/{roomTypeCode}") @GetMapping("/by-type/{roomTypeCode}")
@@ -152,14 +157,15 @@ class Rooms(
val rooms = roomRepo.findByPropertyIdOrderByRoomNumber(propertyId) val rooms = roomRepo.findByPropertyIdOrderByRoomNumber(propertyId)
.filter { it.roomType.id == roomType.id } .filter { it.roomType.id == roomType.id }
val tempCardsByRoom = loadActiveTempCardsByRoom(propertyId)
if (availableOnly || (principal != null && isAgentOnly(propertyUserRepo.findRolesByPropertyAndUser(propertyId, principal.userId)))) { if (availableOnly || (principal != null && isAgentOnly(propertyUserRepo.findRolesByPropertyAndUser(propertyId, principal.userId)))) {
val occupiedRoomIds = roomStayRepo.findOccupiedRoomIds(propertyId).toHashSet() val occupiedRoomIds = roomStayRepo.findOccupiedRoomIds(propertyId).toHashSet()
return rooms return rooms
.filter { it.active && !it.maintenance && !occupiedRoomIds.contains(it.id) } .filter { it.active && !it.maintenance && !occupiedRoomIds.contains(it.id) }
.map { it.toRoomResponse() } .map { it.toRoomResponse(tempCardsByRoom[it.id]) }
} }
return rooms.map { it.toRoomResponse() } return rooms.map { it.toRoomResponse(tempCardsByRoom[it.id]) }
} }
@GetMapping("/availability-range") @GetMapping("/availability-range")
@@ -238,7 +244,9 @@ class Rooms(
hasNfc = saved.hasNfc, hasNfc = saved.hasNfc,
active = saved.active, active = saved.active,
maintenance = saved.maintenance, maintenance = saved.maintenance,
notes = saved.notes notes = saved.notes,
tempCardActive = false,
tempCardExpiresAt = null
) )
roomBoardEvents.emit(propertyId) roomBoardEvents.emit(propertyId)
return response return response
@@ -280,7 +288,9 @@ class Rooms(
hasNfc = saved.hasNfc, hasNfc = saved.hasNfc,
active = saved.active, active = saved.active,
maintenance = saved.maintenance, maintenance = saved.maintenance,
notes = saved.notes notes = saved.notes,
tempCardActive = false,
tempCardExpiresAt = null
) )
roomBoardEvents.emit(propertyId) roomBoardEvents.emit(propertyId)
return response return response
@@ -348,9 +358,16 @@ class Rooms(
return roomTypeRepo.findByPropertyIdAndCodeIgnoreCase(propertyId, code) return roomTypeRepo.findByPropertyIdAndCodeIgnoreCase(propertyId, code)
?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Room type not found") ?: throw ResponseStatusException(HttpStatus.NOT_FOUND, "Room type not found")
} }
private fun loadActiveTempCardsByRoom(propertyId: UUID): Map<UUID, OffsetDateTime> {
val now = OffsetDateTime.now()
return issuedCardRepo.findActiveTempCardsForProperty(propertyId, now)
.groupBy { it.room.id ?: throw IllegalStateException("Room id is null") }
.mapValues { (_, cards) -> cards.maxBy { it.expiresAt }.expiresAt }
}
} }
private fun Room.toRoomResponse(): RoomResponse { private fun Room.toRoomResponse(tempCardExpiresAt: OffsetDateTime? = null): RoomResponse {
val roomId = id ?: throw IllegalStateException("Room id is null") val roomId = id ?: throw IllegalStateException("Room id is null")
return RoomResponse( return RoomResponse(
id = roomId, id = roomId,
@@ -360,6 +377,8 @@ private fun Room.toRoomResponse(): RoomResponse {
hasNfc = hasNfc, hasNfc = hasNfc,
active = active, active = active,
maintenance = maintenance, maintenance = maintenance,
notes = notes notes = notes,
tempCardActive = tempCardExpiresAt != null,
tempCardExpiresAt = tempCardExpiresAt?.toString()
) )
} }

View File

@@ -10,7 +10,9 @@ data class RoomResponse(
val hasNfc: Boolean, val hasNfc: Boolean,
val active: Boolean, val active: Boolean,
val maintenance: Boolean, val maintenance: Boolean,
val notes: String? val notes: String?,
val tempCardActive: Boolean = false,
val tempCardExpiresAt: String? = null
) )
data class RoomBoardResponse( data class RoomBoardResponse(

View File

@@ -33,4 +33,17 @@ interface IssuedCardRepo : JpaRepository<IssuedCard, UUID> {
@org.springframework.data.repository.query.Param("roomStayId") roomStayId: UUID, @org.springframework.data.repository.query.Param("roomStayId") roomStayId: UUID,
@org.springframework.data.repository.query.Param("now") now: java.time.OffsetDateTime @org.springframework.data.repository.query.Param("now") now: java.time.OffsetDateTime
): Boolean ): Boolean
@org.springframework.data.jpa.repository.Query("""
select c
from IssuedCard c
where c.property.id = :propertyId
and c.roomStay is null
and c.revokedAt is null
and c.expiresAt > :now
""")
fun findActiveTempCardsForProperty(
@org.springframework.data.repository.query.Param("propertyId") propertyId: UUID,
@org.springframework.data.repository.query.Param("now") now: java.time.OffsetDateTime
): List<IssuedCard>
} }