Deduplicate logic across controllers, auth, and schema fixes
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:
@@ -26,7 +26,6 @@ import org.springframework.web.bind.annotation.ResponseStatus
|
||||
import org.springframework.web.bind.annotation.RestController
|
||||
import org.springframework.web.server.ResponseStatusException
|
||||
import java.time.OffsetDateTime
|
||||
import java.time.ZoneId
|
||||
import java.util.UUID
|
||||
|
||||
@RestController
|
||||
@@ -50,15 +49,7 @@ class IssuedCards(
|
||||
@RequestBody request: CardPrepareRequest
|
||||
): CardPrepareResponse {
|
||||
val actor = requireIssueActor(propertyId, principal)
|
||||
val stay = roomStayRepo.findById(roomStayId).orElseThrow {
|
||||
ResponseStatusException(HttpStatus.NOT_FOUND, "Room stay not found")
|
||||
}
|
||||
if (stay.property.id != propertyId) {
|
||||
throw ResponseStatusException(HttpStatus.NOT_FOUND, "Room stay not found for property")
|
||||
}
|
||||
if (stay.toAt != null) {
|
||||
throw ResponseStatusException(HttpStatus.CONFLICT, "Room stay closed")
|
||||
}
|
||||
val stay = requireOpenRoomStayForProperty(roomStayRepo, propertyId, roomStayId, "Room stay closed")
|
||||
|
||||
val issuedAt = nowForProperty(stay.property.timezone)
|
||||
val expiresAt = request.expiresAt?.let { parseOffset(it) } ?: stay.toAt
|
||||
@@ -97,15 +88,7 @@ class IssuedCards(
|
||||
if (request.cardIndex <= 0) {
|
||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "cardIndex required")
|
||||
}
|
||||
val stay = roomStayRepo.findById(roomStayId).orElseThrow {
|
||||
ResponseStatusException(HttpStatus.NOT_FOUND, "Room stay not found")
|
||||
}
|
||||
if (stay.property.id != propertyId) {
|
||||
throw ResponseStatusException(HttpStatus.NOT_FOUND, "Room stay not found for property")
|
||||
}
|
||||
if (stay.toAt != null) {
|
||||
throw ResponseStatusException(HttpStatus.CONFLICT, "Room stay closed")
|
||||
}
|
||||
val stay = requireOpenRoomStayForProperty(roomStayRepo, propertyId, roomStayId, "Room stay closed")
|
||||
val issuedAt = parseOffset(request.issuedAt) ?: nowForProperty(stay.property.timezone)
|
||||
val expiresAt = parseOffset(request.expiresAt)
|
||||
?: throw ResponseStatusException(HttpStatus.BAD_REQUEST, "expiresAt required")
|
||||
@@ -140,12 +123,7 @@ class IssuedCards(
|
||||
@AuthenticationPrincipal principal: MyPrincipal?
|
||||
): List<IssuedCardResponse> {
|
||||
requireViewActor(propertyId, principal)
|
||||
val stay = roomStayRepo.findById(roomStayId).orElseThrow {
|
||||
ResponseStatusException(HttpStatus.NOT_FOUND, "Room stay not found")
|
||||
}
|
||||
if (stay.property.id != propertyId) {
|
||||
throw ResponseStatusException(HttpStatus.NOT_FOUND, "Room stay not found for property")
|
||||
}
|
||||
val stay = requireRoomStayForProperty(roomStayRepo, propertyId, roomStayId)
|
||||
return issuedCardRepo.findByRoomStayIdOrderByIssuedAtDesc(roomStayId)
|
||||
.map { it.toResponse() }
|
||||
}
|
||||
@@ -183,34 +161,6 @@ class IssuedCards(
|
||||
return card.toResponse()
|
||||
}
|
||||
|
||||
private fun parseOffset(value: String?): OffsetDateTime? {
|
||||
if (value.isNullOrBlank()) return null
|
||||
return try {
|
||||
OffsetDateTime.parse(value.trim())
|
||||
} catch (_: Exception) {
|
||||
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Invalid timestamp")
|
||||
}
|
||||
}
|
||||
|
||||
private fun nowForProperty(timezone: String?): OffsetDateTime {
|
||||
val zone = try {
|
||||
if (timezone.isNullOrBlank()) ZoneId.of("Asia/Kolkata") else ZoneId.of(timezone)
|
||||
} catch (_: Exception) {
|
||||
ZoneId.of("Asia/Kolkata")
|
||||
}
|
||||
return OffsetDateTime.now(zone)
|
||||
}
|
||||
|
||||
private fun encodeBlock(value: String?): String {
|
||||
val raw = (value ?: "").padEnd(16).take(16)
|
||||
val bytes = raw.toByteArray(Charsets.UTF_8)
|
||||
val sb = StringBuilder(bytes.size * 2)
|
||||
for (b in bytes) {
|
||||
sb.append(String.format("%02X", b))
|
||||
}
|
||||
return sb.toString()
|
||||
}
|
||||
|
||||
private fun requireMember(propertyId: UUID, principal: MyPrincipal?) {
|
||||
if (principal == null) {
|
||||
throw ResponseStatusException(HttpStatus.UNAUTHORIZED, "Missing principal")
|
||||
@@ -303,73 +253,9 @@ class IssuedCards(
|
||||
val checkSum = calculateChecksum((key ?: "") + newData)
|
||||
return newData + checkSum
|
||||
}
|
||||
|
||||
private fun buildSector0Block2(roomNumber: Int, cardID: Int): String {
|
||||
val guestID = cardID + 1
|
||||
val cardIdStr = cardID.toString().padStart(6, '0')
|
||||
val guestIdStr = guestID.toString().padStart(6, '0')
|
||||
val finalRoom = roomNumber.toString().padStart(2, '0')
|
||||
return "472F${cardIdStr}2F${guestIdStr}00010000${finalRoom}0000"
|
||||
}
|
||||
|
||||
private fun formatDateComponents(time: OffsetDateTime): String {
|
||||
val minute = time.minute.toString().padStart(2, '0')
|
||||
val hour = time.hour.toString().padStart(2, '0')
|
||||
val day = time.dayOfMonth.toString().padStart(2, '0')
|
||||
val month = time.monthValue.toString().padStart(2, '0')
|
||||
val year = time.year.toString().takeLast(2)
|
||||
return "${minute}${hour}${day}${month}${year}"
|
||||
}
|
||||
|
||||
private fun calculateChecksum(dataHex: String): String {
|
||||
val data = hexStringToByteArray(dataHex)
|
||||
var checksum = 0
|
||||
for (byte in data) {
|
||||
checksum = calculateByteChecksum(byte, checksum)
|
||||
}
|
||||
return String.format("%02X", checksum)
|
||||
}
|
||||
|
||||
private fun calculateByteChecksum(byte: Byte, currentChecksum: Int): Int {
|
||||
var checksum = currentChecksum
|
||||
var b = byte.toInt()
|
||||
for (i in 0 until 8) {
|
||||
checksum = if ((checksum xor b) and 1 != 0) {
|
||||
(checksum xor 0x18) shr 1 or 0x80
|
||||
} else {
|
||||
checksum shr 1
|
||||
}
|
||||
b = b shr 1
|
||||
}
|
||||
return checksum
|
||||
}
|
||||
|
||||
private fun hexStringToByteArray(hexString: String): ByteArray {
|
||||
val len = hexString.length
|
||||
val data = ByteArray(len / 2)
|
||||
for (i in 0 until len step 2) {
|
||||
data[i / 2] = ((Character.digit(hexString[i], 16) shl 4)
|
||||
+ Character.digit(hexString[i + 1], 16)).toByte()
|
||||
}
|
||||
return data
|
||||
}
|
||||
}
|
||||
|
||||
private data class Sector0Payload(
|
||||
val key: String,
|
||||
val timeData: String
|
||||
)
|
||||
private fun IssuedCard.toResponse(): IssuedCardResponse {
|
||||
return IssuedCardResponse(
|
||||
id = id!!,
|
||||
propertyId = property.id!!,
|
||||
roomId = room.id!!,
|
||||
roomStayId = roomStay?.id,
|
||||
cardId = cardId,
|
||||
cardIndex = cardIndex,
|
||||
issuedAt = issuedAt.toString(),
|
||||
expiresAt = expiresAt.toString(),
|
||||
issuedByUserId = issuedBy?.id,
|
||||
revokedAt = revokedAt?.toString()
|
||||
)
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user