diff --git a/src/main/kotlin/com/android/trisolarisserver/controller/RoomImages.kt b/src/main/kotlin/com/android/trisolarisserver/controller/RoomImages.kt index c62c74f..44d183d 100644 --- a/src/main/kotlin/com/android/trisolarisserver/controller/RoomImages.kt +++ b/src/main/kotlin/com/android/trisolarisserver/controller/RoomImages.kt @@ -3,6 +3,7 @@ package com.android.trisolarisserver.controller import com.android.trisolarisserver.component.PropertyAccess import com.android.trisolarisserver.component.RoomImageStorage import com.android.trisolarisserver.controller.dto.RoomImageResponse +import com.android.trisolarisserver.controller.dto.RoomImageReorderRequest import com.android.trisolarisserver.models.room.RoomImage import com.android.trisolarisserver.models.property.Role import com.android.trisolarisserver.repo.RoomImageRepo @@ -19,6 +20,7 @@ import org.springframework.web.bind.annotation.DeleteMapping 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.PutMapping import org.springframework.web.bind.annotation.RequestMapping import org.springframework.web.bind.annotation.RequestParam import org.springframework.web.bind.annotation.ResponseStatus @@ -29,6 +31,7 @@ import java.nio.file.Files import java.nio.file.Paths import java.security.MessageDigest import java.util.UUID +import org.springframework.web.bind.annotation.RequestBody @RestController @RequestMapping("/properties/{propertyId}/rooms/{roomId}/images") @@ -153,6 +156,72 @@ class RoomImages( } } + @PutMapping("/reorder-room") + @ResponseStatus(HttpStatus.NO_CONTENT) + @Transactional + fun reorderRoomImages( + @PathVariable propertyId: UUID, + @PathVariable roomId: UUID, + @AuthenticationPrincipal principal: MyPrincipal?, + @RequestBody request: RoomImageReorderRequest + ) { + requirePrincipal(principal) + propertyAccess.requireMember(propertyId, principal!!.userId) + propertyAccess.requireAnyRole(propertyId, principal.userId, Role.ADMIN, Role.MANAGER) + ensureRoom(propertyId, roomId) + + if (request.imageIds.isEmpty()) { + return + } + val images = roomImageRepo.findByIdIn(request.imageIds).toMutableList() + if (images.size != request.imageIds.size) { + throw ResponseStatusException(HttpStatus.NOT_FOUND, "Image not found") + } + if (images.any { it.room.id != roomId || it.property.id != propertyId }) { + throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Images do not belong to room") + } + val orderMap = request.imageIds.withIndex().associate { it.value to it.index + 1 } + for (img in images) { + img.roomSortOrder = orderMap[img.id] ?: img.roomSortOrder + } + roomImageRepo.saveAll(images) + } + + @PutMapping("/reorder-room-type") + @ResponseStatus(HttpStatus.NO_CONTENT) + @Transactional + fun reorderRoomTypeImages( + @PathVariable propertyId: UUID, + @PathVariable roomId: UUID, + @AuthenticationPrincipal principal: MyPrincipal?, + @RequestBody request: RoomImageReorderRequest + ) { + requirePrincipal(principal) + propertyAccess.requireMember(propertyId, principal!!.userId) + propertyAccess.requireAnyRole(propertyId, principal.userId, Role.ADMIN, Role.MANAGER) + val room = ensureRoom(propertyId, roomId) + + if (request.imageIds.isEmpty()) { + return + } + val images = roomImageRepo.findByIdIn(request.imageIds).toMutableList() + if (images.size != request.imageIds.size) { + throw ResponseStatusException(HttpStatus.NOT_FOUND, "Image not found") + } + val roomTypeCode = room.roomType.code + if (images.any { it.roomTypeCode != roomTypeCode }) { + throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Images do not belong to room type") + } + if (images.any { it.property.id != propertyId }) { + throw ResponseStatusException(HttpStatus.BAD_REQUEST, "Images do not belong to property") + } + val orderMap = request.imageIds.withIndex().associate { it.value to it.index + 1 } + for (img in images) { + img.roomTypeSortOrder = orderMap[img.id] ?: img.roomTypeSortOrder + } + roomImageRepo.saveAll(images) + } + @GetMapping("/{imageId}/file") fun file( @PathVariable propertyId: UUID, diff --git a/src/main/kotlin/com/android/trisolarisserver/controller/dto/RoomDtos.kt b/src/main/kotlin/com/android/trisolarisserver/controller/dto/RoomDtos.kt index 7741fe8..689a4f2 100644 --- a/src/main/kotlin/com/android/trisolarisserver/controller/dto/RoomDtos.kt +++ b/src/main/kotlin/com/android/trisolarisserver/controller/dto/RoomDtos.kt @@ -61,3 +61,7 @@ data class RoomUpsertRequest( val maintenance: Boolean, val notes: String? ) + +data class RoomImageReorderRequest( + val imageIds: List +) diff --git a/src/main/kotlin/com/android/trisolarisserver/repo/RoomImageRepo.kt b/src/main/kotlin/com/android/trisolarisserver/repo/RoomImageRepo.kt index 2a99069..6b89f45 100644 --- a/src/main/kotlin/com/android/trisolarisserver/repo/RoomImageRepo.kt +++ b/src/main/kotlin/com/android/trisolarisserver/repo/RoomImageRepo.kt @@ -37,6 +37,7 @@ interface RoomImageRepo : JpaRepository { ) fun findByRoomTypeCodeForReorder(@Param("roomTypeCode") roomTypeCode: String): List fun findByIdAndRoomIdAndPropertyId(id: UUID, roomId: UUID, propertyId: UUID): RoomImage? + fun findByIdIn(ids: Collection): List fun existsByRoomIdAndContentHash(roomId: UUID, contentHash: String): Boolean @Query("select coalesce(max(ri.roomSortOrder), 0) from RoomImage ri where ri.room.id = :roomId")