Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Reservation, Room 관련 엔티티 / DTO / API 구현 #39

Merged
merged 14 commits into from
Jan 7, 2025
2 changes: 2 additions & 0 deletions src/main/kotlin/com/example/toyTeam6Airbnb/WebConfig.kt
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,8 @@ class WebConfig {
registry.addMapping("/**")
.allowedOrigins("*") // 허용할 도메인
.allowedMethods("GET", "POST", "PUT", "DELETE")
.allowedHeaders("*")
.allowCredentials(true)
}
}
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.example.toyTeam6Airbnb.reservation

import com.example.toyTeam6Airbnb.DomainException
import org.springframework.http.HttpStatus
import org.springframework.http.HttpStatusCode

sealed class ReservationException(
errorCode: Int,
httpStatusCode: HttpStatusCode,
msg: String,
cause: Throwable? = null
) : DomainException(errorCode, httpStatusCode, msg, cause)

class ReservationUnavailable : ReservationException(
errorCode = 1009,
httpStatusCode = HttpStatus.CONFLICT,
msg = "Reservation is not Available"
)

class ReservationNotFound : ReservationException(
errorCode = 1010,
httpStatusCode = HttpStatus.NOT_FOUND,
msg = "Reservation doesn't exist"
)

class ReservationPermissionDenied : ReservationException(
errorCode = 1011,
httpStatusCode = HttpStatus.FORBIDDEN,
msg = "Permission Denied"
)
Original file line number Diff line number Diff line change
@@ -1,3 +1,46 @@
package com.example.toyTeam6Airbnb.reservation.controller

class Reservation
import com.example.toyTeam6Airbnb.reservation.persistence.ReservationEntity
import com.example.toyTeam6Airbnb.review.controller.Review
import com.example.toyTeam6Airbnb.room.controller.Room
import com.example.toyTeam6Airbnb.user.controller.User
import java.time.Instant
import java.time.LocalDate

data class Reservation(
val id: Long,
val user: User,
val room: Room,
val review: Review?,
val startDate: LocalDate,
val endDate: LocalDate,
val totalPrice: Double,
val createdAt: Instant,
val updatedAt: Instant
) {
companion object {
fun fromEntity(entity: ReservationEntity): Reservation {
return Reservation(
id = entity.id!!,
user = User.fromEntity(entity.user),
room = Room.fromEntity(entity.room),
review = entity.review?.let { Review.fromEntity(it) },
startDate = entity.startDate,
endDate = entity.endDate,
totalPrice = entity.totalPrice,
createdAt = entity.createdAt,
updatedAt = entity.updatedAt
)
}
}

fun toDTO(): ReservationDTO {
return ReservationDTO(
id = this.id,
userId = this.user.id,
roomId = this.room.id,
startDate = this.startDate,
endDate = this.endDate
)
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,165 @@
package com.example.toyTeam6Airbnb.reservation.controller

import com.example.toyTeam6Airbnb.reservation.service.ReservationService
import com.example.toyTeam6Airbnb.user.controller.PrincipalDetails
import com.example.toyTeam6Airbnb.user.controller.User
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.http.HttpStatus
import org.springframework.http.ResponseEntity
import org.springframework.security.core.annotation.AuthenticationPrincipal
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.RequestBody
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController
import java.time.LocalDate
import java.time.YearMonth

@RestController
@RequestMapping("/api/v1")
class ReservationController
@RequestMapping("/api/v1/reservations")
@Tag(name = "Reservation Controller", description = "Reservation Controller API")
class ReservationController(
private val reservationService: ReservationService
) {

@PostMapping
@Operation(summary = "create Reservation", description = "create Reservation")
fun createReservation(
@AuthenticationPrincipal principalDetails: PrincipalDetails,
@RequestBody request: CreateReservationRequest
): ResponseEntity<ReservationDTO> {
val reservation = reservationService.createReservation(
User.fromEntity(principalDetails.getUser()),
request.roomId,
request.startDate,
request.endDate
)

return ResponseEntity.status(HttpStatus.CREATED).body(reservation.toDTO())
}

@DeleteMapping("/{reservationId}")
@Operation(summary = "delete Reservation", description = "delete Reservation")
fun deleteReservation(
@AuthenticationPrincipal principalDetails: PrincipalDetails,
@PathVariable reservationId: Long
): ResponseEntity<Unit> {
reservationService.deleteReservation(
User.fromEntity(principalDetails.getUser()),
reservationId
)

return ResponseEntity.noContent().build()
}

@PutMapping("/{reservationId}")
@Operation(summary = "update Reservation", description = "update Reservation")
fun updateReservation(
@AuthenticationPrincipal principalDetails: PrincipalDetails,
@PathVariable reservationId: Long,
@RequestBody request: UpdateReservationRequest
): ResponseEntity<ReservationDTO> {
val reservation = reservationService.updateReservation(
User.fromEntity(principalDetails.getUser()),
reservationId,
request.startDate,
request.endDate
)

return ResponseEntity.ok().body(reservation.toDTO())
}

// 특정 reservation을 가져오는 API
@GetMapping("/{reservationId}")
fun getReservation(
@PathVariable reservationId: Long
): ResponseEntity<ReservationDTO> {
val reservation = reservationService.getReservation(reservationId)

return ResponseEntity.ok(reservation.toDTO())
}

// 특정 user의 reservation을 모두 가져오는 API
@GetMapping
fun getReservationsByUser(
@AuthenticationPrincipal principalDetails: PrincipalDetails
): ResponseEntity<List<ReservationDTO>> {
val reservations = reservationService.getReservationsByUser(
User.fromEntity(principalDetails.getUser())
).map { it.toDTO() }

return ResponseEntity.ok(reservations)
}

// 특정 room의 reservation을 모두 가져오는 API
@GetMapping("/room/{roomId}")
fun getReservationsByRoom(
@PathVariable roomId: Long
): ResponseEntity<List<ReservationDTO>> {
val reservations = reservationService.getReservationsByRoom(roomId).map { it.toDTO() }

return ResponseEntity.ok().body(reservations)
}

// 특정 date range의 reservation을 모두 가져오는 API
@GetMapping("/date")
fun getReservationsByDate(
@RequestParam startDate: String,
@RequestParam endDate: String
): ResponseEntity<List<ReservationDTO>> {
val reservations = reservationService.getReservationsByDate(
LocalDate.parse(startDate),
LocalDate.parse(endDate)
).map { it.toDTO() }

return ResponseEntity.ok().body(reservations)
}

// 특정 room의 특정 month의 available/unavailable date를 가져오는 API
@GetMapping("/availability/{roomId}")
@Operation(summary = "get Date Available by month", description = "get Date Available by month")
fun getRoomAvailabilityByMonth(
@PathVariable roomId: Long,
@RequestParam year: Int,
@RequestParam month: Int
): ResponseEntity<RoomAvailabilityResponse> {
val roomAvailability = reservationService.getAvailabilityByMonth(
roomId,
YearMonth.of(year, month)
)

return ResponseEntity.ok().body(roomAvailability)
}
}

// Reservation DTO
// 추후, 가격이나 특정 프로퍼티 추가할 수 있음.
data class ReservationDTO(
val id: Long,
val roomId: Long,
val userId: Long,
val startDate: LocalDate,
val endDate: LocalDate
)

class CreateReservationRequest(
val roomId: Long,
val startDate: LocalDate,
val endDate: LocalDate
)

class UpdateReservationRequest(
val startDate: LocalDate,
val endDate: LocalDate
)

// 특정 방의 예약 가능날짜와 불가능한 날짜 반환 DTO
data class RoomAvailabilityResponse(
val availableDates: List<LocalDate>,
val unavailableDates: List<LocalDate>
)
Original file line number Diff line number Diff line change
Expand Up @@ -13,15 +13,18 @@ import jakarta.persistence.Id
import jakarta.persistence.JoinColumn
import jakarta.persistence.ManyToOne
import jakarta.persistence.OneToOne
import jakarta.persistence.PrePersist
import jakarta.persistence.PreUpdate
import jakarta.persistence.Table
import java.time.Instant
import java.time.LocalDate

@Entity
@Table(name = "reservations")
class ReservationEntity(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = 0,
val id: Long? = null,

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
Expand All @@ -32,14 +35,31 @@ class ReservationEntity(
val room: RoomEntity,

@OneToOne(mappedBy = "reservation", cascade = [CascadeType.ALL], orphanRemoval = true)
val review: ReviewEntity,
val review: ReviewEntity?,

@Column(nullable = false)
val startDate: LocalDate,
var startDate: LocalDate,

@Column(nullable = false)
val endDate: LocalDate,
var endDate: LocalDate,

@Column(nullable = false)
val totalPrice: Double
)
var totalPrice: Double,

@Column(nullable = false)
var createdAt: Instant = Instant.now(),

@Column(nullable = false)
var updatedAt: Instant = Instant.now()
) {
@PrePersist
fun onPrePersist() {
createdAt = Instant.now()
updatedAt = Instant.now()
}

@PreUpdate
fun onPreUpdate() {
updatedAt = Instant.now()
}
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,11 @@
package com.example.toyTeam6Airbnb.reservation.persistence

interface ReservationRepository
import com.example.toyTeam6Airbnb.room.persistence.RoomEntity
import com.example.toyTeam6Airbnb.user.persistence.UserEntity
import org.springframework.data.jpa.repository.JpaRepository

interface ReservationRepository : JpaRepository<ReservationEntity, Long> {
fun findAllByRoom(room: RoomEntity): List<ReservationEntity>

fun findAllByUser(user: UserEntity): List<ReservationEntity>
}
Original file line number Diff line number Diff line change
@@ -1,3 +1,48 @@
package com.example.toyTeam6Airbnb.reservation.service

interface ReservationService
import com.example.toyTeam6Airbnb.reservation.controller.Reservation
import com.example.toyTeam6Airbnb.reservation.controller.RoomAvailabilityResponse
import com.example.toyTeam6Airbnb.user.controller.User
import java.time.LocalDate
import java.time.YearMonth

interface ReservationService {

fun createReservation(
user: User,
roomId: Long,
startDate: LocalDate,
endDate: LocalDate
): Reservation

fun deleteReservation(
user: User,
reservationId: Long
)

fun updateReservation(
user: User,
reservationId: Long,
startDate: LocalDate,
endDate: LocalDate
): Reservation

fun getReservation(
reservationId: Long
): Reservation

fun getReservationsByUser(
user: User
): List<Reservation>

fun getReservationsByRoom(
roomId: Long
): List<Reservation>

fun getReservationsByDate(
startDate: LocalDate,
endDate: LocalDate
): List<Reservation>

fun getAvailabilityByMonth(roomId: Long, yearMonth: YearMonth): RoomAvailabilityResponse
}
Loading
Loading