112 lines
4.5 KiB
Kotlin
112 lines
4.5 KiB
Kotlin
package com.android.trisolarisserver.controller
|
|
|
|
import com.android.trisolarisserver.component.PropertyAccess
|
|
import com.android.trisolarisserver.controller.dto.GuestRatingCreateRequest
|
|
import com.android.trisolarisserver.controller.dto.GuestRatingResponse
|
|
import com.android.trisolarisserver.db.repo.BookingRepo
|
|
import com.android.trisolarisserver.db.repo.GuestRatingRepo
|
|
import com.android.trisolarisserver.db.repo.GuestRepo
|
|
import com.android.trisolarisserver.repo.AppUserRepo
|
|
import com.android.trisolarisserver.repo.PropertyRepo
|
|
import com.android.trisolarisserver.models.booking.GuestRating
|
|
import com.android.trisolarisserver.models.booking.GuestRatingScore
|
|
import com.android.trisolarisserver.security.MyPrincipal
|
|
import org.springframework.http.HttpStatus
|
|
import org.springframework.security.core.annotation.AuthenticationPrincipal
|
|
import org.springframework.web.bind.annotation.GetMapping
|
|
import org.springframework.web.bind.annotation.PathVariable
|
|
import org.springframework.web.bind.annotation.PostMapping
|
|
import org.springframework.web.bind.annotation.RequestBody
|
|
import org.springframework.web.bind.annotation.RequestMapping
|
|
import org.springframework.web.bind.annotation.ResponseStatus
|
|
import org.springframework.web.bind.annotation.RestController
|
|
import org.springframework.web.server.ResponseStatusException
|
|
import java.util.UUID
|
|
|
|
@RestController
|
|
@RequestMapping("/properties/{propertyId}/guests/{guestId}/ratings")
|
|
class GuestRatings(
|
|
private val propertyAccess: PropertyAccess,
|
|
private val propertyRepo: PropertyRepo,
|
|
private val guestRepo: GuestRepo,
|
|
private val bookingRepo: BookingRepo,
|
|
private val guestRatingRepo: GuestRatingRepo,
|
|
private val appUserRepo: AppUserRepo
|
|
) {
|
|
|
|
@PostMapping
|
|
@ResponseStatus(HttpStatus.CREATED)
|
|
fun create(
|
|
@PathVariable propertyId: UUID,
|
|
@PathVariable guestId: UUID,
|
|
@AuthenticationPrincipal principal: MyPrincipal?,
|
|
@RequestBody request: GuestRatingCreateRequest
|
|
): GuestRatingResponse {
|
|
val resolved = requireMember(propertyAccess, propertyId, principal)
|
|
|
|
val (property, guest) = requirePropertyGuest(propertyRepo, guestRepo, propertyId, guestId)
|
|
|
|
val booking = bookingRepo.findById(request.bookingId).orElseThrow {
|
|
ResponseStatusException(HttpStatus.NOT_FOUND, "Booking not found")
|
|
}
|
|
if (booking.property.id != property.id) {
|
|
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Booking not in property")
|
|
}
|
|
if (booking.primaryGuest?.id != guest.id) {
|
|
throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Booking not linked to guest")
|
|
}
|
|
if (guestRatingRepo.existsByGuestIdAndBookingId(guest.id!!, booking.id!!)) {
|
|
throw ResponseStatusException(HttpStatus.CONFLICT, "Rating already exists for booking")
|
|
}
|
|
|
|
val score = parseScore(request.score)
|
|
val rating = GuestRating(
|
|
property = property,
|
|
guest = guest,
|
|
booking = booking,
|
|
score = score,
|
|
notes = request.notes?.trim(),
|
|
createdBy = appUserRepo.findById(resolved.userId).orElse(null)
|
|
)
|
|
guestRatingRepo.save(rating)
|
|
return rating.toResponse()
|
|
}
|
|
|
|
@GetMapping
|
|
fun list(
|
|
@PathVariable propertyId: UUID,
|
|
@PathVariable guestId: UUID,
|
|
@AuthenticationPrincipal principal: MyPrincipal?
|
|
): List<GuestRatingResponse> {
|
|
requireMember(propertyAccess, propertyId, principal)
|
|
|
|
val (_, guest) = requirePropertyGuest(propertyRepo, guestRepo, propertyId, guestId)
|
|
|
|
return guestRatingRepo.findByGuestIdOrderByCreatedAtDesc(guestId).map { it.toResponse() }
|
|
}
|
|
|
|
private fun parseScore(raw: String): GuestRatingScore {
|
|
val normalized = raw.trim().uppercase()
|
|
return when (normalized) {
|
|
"1", "GOOD" -> GuestRatingScore.GOOD
|
|
"2", "OK" -> GuestRatingScore.OK
|
|
"3", "TROUBLE", "TROUBLEMAKER", "TROUBLE-MAKER" -> GuestRatingScore.TROUBLE
|
|
else -> throw ResponseStatusException(HttpStatus.BAD_REQUEST, "score must be GOOD/OK/TROUBLE or 1/2/3")
|
|
}
|
|
}
|
|
|
|
private fun GuestRating.toResponse(): GuestRatingResponse {
|
|
return GuestRatingResponse(
|
|
id = id!!,
|
|
propertyId = property.id!!,
|
|
guestId = guest.id!!,
|
|
bookingId = booking.id!!,
|
|
score = score.name,
|
|
notes = notes,
|
|
createdAt = createdAt.toString(),
|
|
createdByUserId = createdBy?.id
|
|
)
|
|
}
|
|
|
|
}
|