Expose active temp card state on room responses
All checks were successful
build-and-deploy / build-deploy (push) Successful in 33s
All checks were successful
build-and-deploy / build-deploy (push) Successful in 33s
This commit is contained in:
@@ -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()
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -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(
|
||||||
|
|||||||
@@ -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>
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user