generated from pricelees/issue-pr-template
[#18] 코드 정리 및 일부 컨벤션 통일 #19
@ -26,7 +26,7 @@ enum class ErrorType(
|
||||
// 404 Not Found
|
||||
MEMBER_NOT_FOUND("회원(Member) 정보가 존재하지 않습니다."),
|
||||
RESERVATION_NOT_FOUND("예약(Reservation) 정보가 존재하지 않습니다."),
|
||||
RESERVATION_TIME_NOT_FOUND("예약 시간(ReservationTime) 정보가 존재하지 않습니다."),
|
||||
TIME_NOT_FOUND("예약 시간(Time) 정보가 존재하지 않습니다."),
|
||||
THEME_NOT_FOUND("테마(Theme) 정보가 존재하지 않습니다."),
|
||||
PAYMENT_NOT_FOUND("결제(Payment) 정보가 존재하지 않습니다."),
|
||||
|
||||
|
||||
@ -5,7 +5,7 @@ import com.fasterxml.jackson.databind.annotation.JsonDeserialize
|
||||
import roomescape.payment.infrastructure.client.PaymentCancelResponseDeserializer
|
||||
import roomescape.payment.infrastructure.persistence.PaymentEntity
|
||||
import roomescape.reservation.web.ReservationResponse
|
||||
import roomescape.reservation.web.toResponse
|
||||
import roomescape.reservation.web.toCreateResponse
|
||||
import java.time.OffsetDateTime
|
||||
|
||||
data class PaymentApproveRequest(
|
||||
@ -51,6 +51,6 @@ fun PaymentEntity.toReservationPaymentResponse(): ReservationPaymentResponse = R
|
||||
orderId = this.orderId,
|
||||
paymentKey = this.paymentKey,
|
||||
totalAmount = this.totalAmount,
|
||||
reservation = this.reservation.toResponse(),
|
||||
reservation = this.reservation.toCreateResponse(),
|
||||
approvedAt = this.approvedAt
|
||||
)
|
||||
@ -18,7 +18,7 @@ import java.time.LocalDateTime
|
||||
@Transactional
|
||||
class ReservationService(
|
||||
private val reservationRepository: ReservationRepository,
|
||||
private val reservationTimeService: ReservationTimeService,
|
||||
private val timeService: TimeService,
|
||||
private val memberService: MemberService,
|
||||
private val themeService: ThemeService,
|
||||
) {
|
||||
@ -43,7 +43,7 @@ class ReservationService(
|
||||
}
|
||||
|
||||
private fun findAllReservationByStatus(spec: Specification<ReservationEntity>): List<ReservationResponse> {
|
||||
return reservationRepository.findAll(spec).map { it.toResponse() }
|
||||
return reservationRepository.findAll(spec).map { it.toCreateResponse() }
|
||||
}
|
||||
|
||||
fun removeReservationById(reservationId: Long, memberId: Long) {
|
||||
@ -96,7 +96,7 @@ class ReservationService(
|
||||
): ReservationResponse = getReservationForSave(timeId, themeId, date, memberId, status)
|
||||
.also {
|
||||
reservationRepository.save(it)
|
||||
}.toResponse()
|
||||
}.toCreateResponse()
|
||||
|
||||
|
||||
private fun validateMemberAlreadyReserve(themeId: Long?, timeId: Long?, date: LocalDate?, memberId: Long?) {
|
||||
@ -127,10 +127,10 @@ class ReservationService(
|
||||
|
||||
private fun validateDateAndTime(
|
||||
requestDate: LocalDate,
|
||||
requestReservationTime: ReservationTimeEntity
|
||||
requestTime: TimeEntity
|
||||
) {
|
||||
val now = LocalDateTime.now()
|
||||
val request = LocalDateTime.of(requestDate, requestReservationTime.startAt)
|
||||
val request = LocalDateTime.of(requestDate, requestTime.startAt)
|
||||
|
||||
if (request.isBefore(now)) {
|
||||
throw RoomescapeException(
|
||||
@ -148,7 +148,7 @@ class ReservationService(
|
||||
memberId: Long,
|
||||
status: ReservationStatus
|
||||
): ReservationEntity {
|
||||
val time = reservationTimeService.findTimeById(timeId)
|
||||
val time = timeService.findById(timeId)
|
||||
val theme = themeService.findById(themeId)
|
||||
val member = memberService.findById(memberId)
|
||||
|
||||
@ -156,7 +156,7 @@ class ReservationService(
|
||||
|
||||
return ReservationEntity(
|
||||
date = date,
|
||||
reservationTime = time,
|
||||
time = time,
|
||||
theme = theme,
|
||||
member = member,
|
||||
reservationStatus = status
|
||||
|
||||
@ -1,74 +0,0 @@
|
||||
package roomescape.reservation.business
|
||||
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import roomescape.common.exception.ErrorType
|
||||
import roomescape.common.exception.RoomescapeException
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationRepository
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationTimeRepository
|
||||
import roomescape.reservation.web.*
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
|
||||
@Service
|
||||
class ReservationTimeService(
|
||||
private val reservationTimeRepository: ReservationTimeRepository,
|
||||
private val reservationRepository: ReservationRepository
|
||||
) {
|
||||
@Transactional(readOnly = true)
|
||||
fun findTimeById(id: Long): ReservationTimeEntity = reservationTimeRepository.findByIdOrNull(id)
|
||||
?: throw RoomescapeException(
|
||||
ErrorType.RESERVATION_TIME_NOT_FOUND,
|
||||
"[reservationTimeId: $id]",
|
||||
HttpStatus.BAD_REQUEST
|
||||
)
|
||||
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
fun findAllTimes(): ReservationTimesResponse = reservationTimeRepository.findAll()
|
||||
.toResponses()
|
||||
|
||||
@Transactional
|
||||
fun addTime(reservationTimeRequest: ReservationTimeRequest): ReservationTimeResponse {
|
||||
val startAt: LocalTime = reservationTimeRequest.startAt
|
||||
|
||||
if (reservationTimeRepository.existsByStartAt(startAt)) {
|
||||
throw RoomescapeException(
|
||||
ErrorType.TIME_DUPLICATED, "[startAt: $startAt]", HttpStatus.CONFLICT
|
||||
)
|
||||
}
|
||||
|
||||
return ReservationTimeEntity(startAt = startAt)
|
||||
.also { reservationTimeRepository.save(it) }
|
||||
.toResponse()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun removeTimeById(id: Long) {
|
||||
val reservationTime: ReservationTimeEntity = findTimeById(id)
|
||||
reservationRepository.findByReservationTime(reservationTime)
|
||||
.also {
|
||||
if (it.isNotEmpty()) {
|
||||
throw RoomescapeException(
|
||||
ErrorType.TIME_IS_USED_CONFLICT, "[timeId: $id]", HttpStatus.CONFLICT
|
||||
)
|
||||
}
|
||||
reservationTimeRepository.deleteById(id)
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
fun findAllAvailableTimesByDateAndTheme(date: LocalDate, themeId: Long): ReservationTimeInfosResponse {
|
||||
val allTimes = reservationTimeRepository.findAll()
|
||||
val reservations: List<ReservationEntity> = reservationRepository.findByDateAndThemeId(date, themeId)
|
||||
|
||||
return ReservationTimeInfosResponse(allTimes.map { time ->
|
||||
val alreadyBooked: Boolean = reservations.any { reservation -> reservation.reservationTime.id == time.id }
|
||||
time.toInfoResponse(alreadyBooked)
|
||||
})
|
||||
}
|
||||
}
|
||||
74
src/main/java/roomescape/reservation/business/TimeService.kt
Normal file
74
src/main/java/roomescape/reservation/business/TimeService.kt
Normal file
@ -0,0 +1,74 @@
|
||||
package roomescape.reservation.business
|
||||
|
||||
import org.springframework.data.repository.findByIdOrNull
|
||||
import org.springframework.http.HttpStatus
|
||||
import org.springframework.stereotype.Service
|
||||
import org.springframework.transaction.annotation.Transactional
|
||||
import roomescape.common.exception.ErrorType
|
||||
import roomescape.common.exception.RoomescapeException
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationRepository
|
||||
import roomescape.reservation.infrastructure.persistence.TimeEntity
|
||||
import roomescape.reservation.infrastructure.persistence.TimeRepository
|
||||
import roomescape.reservation.web.*
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
|
||||
@Service
|
||||
class TimeService(
|
||||
private val timeRepository: TimeRepository,
|
||||
private val reservationRepository: ReservationRepository
|
||||
) {
|
||||
@Transactional(readOnly = true)
|
||||
fun findById(id: Long): TimeEntity = timeRepository.findByIdOrNull(id)
|
||||
?: throw RoomescapeException(
|
||||
ErrorType.TIME_NOT_FOUND,
|
||||
"[timeId: $id]",
|
||||
HttpStatus.BAD_REQUEST
|
||||
)
|
||||
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
fun findAll(): TimeRetrieveListResponse = timeRepository.findAll().toRetrieveListResponse()
|
||||
|
||||
@Transactional
|
||||
fun create(timeCreateRequest: TimeCreateRequest): TimeCreateResponse {
|
||||
val startAt: LocalTime = timeCreateRequest.startAt
|
||||
|
||||
if (timeRepository.existsByStartAt(startAt)) {
|
||||
throw RoomescapeException(
|
||||
ErrorType.TIME_DUPLICATED, "[startAt: $startAt]", HttpStatus.CONFLICT
|
||||
)
|
||||
}
|
||||
|
||||
return TimeEntity(startAt = startAt)
|
||||
.also { timeRepository.save(it) }
|
||||
.toCreateResponse()
|
||||
}
|
||||
|
||||
@Transactional
|
||||
fun deleteById(id: Long) {
|
||||
val time: TimeEntity = findById(id)
|
||||
reservationRepository.findByTime(time)
|
||||
.also {
|
||||
if (it.isNotEmpty()) {
|
||||
throw RoomescapeException(
|
||||
ErrorType.TIME_IS_USED_CONFLICT, "[timeId: $id]", HttpStatus.CONFLICT
|
||||
)
|
||||
}
|
||||
timeRepository.deleteById(id)
|
||||
}
|
||||
}
|
||||
|
||||
@Transactional(readOnly = true)
|
||||
fun findAllWithAvailability(date: LocalDate, themeId: Long): TimeWithAvailabilityListResponse {
|
||||
val allTimes = timeRepository.findAll()
|
||||
val reservations: List<ReservationEntity> = reservationRepository.findByDateAndThemeId(date, themeId)
|
||||
|
||||
return TimeWithAvailabilityListResponse(allTimes.map { time ->
|
||||
val isAvailable: Boolean = reservations.none { reservation -> reservation.time.id == time.id }
|
||||
|
||||
TimeWithAvailabilityResponse(time.id!!, time.startAt, isAvailable)
|
||||
})
|
||||
}
|
||||
}
|
||||
@ -12,39 +12,39 @@ import org.springframework.web.bind.annotation.RequestParam
|
||||
import roomescape.auth.web.support.Admin
|
||||
import roomescape.auth.web.support.LoginRequired
|
||||
import roomescape.common.dto.response.CommonApiResponse
|
||||
import roomescape.reservation.web.ReservationTimeInfosResponse
|
||||
import roomescape.reservation.web.ReservationTimeRequest
|
||||
import roomescape.reservation.web.ReservationTimeResponse
|
||||
import roomescape.reservation.web.ReservationTimesResponse
|
||||
import roomescape.reservation.web.TimeWithAvailabilityListResponse
|
||||
import roomescape.reservation.web.TimeCreateRequest
|
||||
import roomescape.reservation.web.TimeCreateResponse
|
||||
import roomescape.reservation.web.TimeRetrieveListResponse
|
||||
import java.time.LocalDate
|
||||
|
||||
@Tag(name = "4. 예약 시간 API", description = "예약 시간을 조회 / 추가 / 삭제할 때 사용합니다.")
|
||||
interface ReservationTimeAPI {
|
||||
interface TimeAPI {
|
||||
|
||||
@Admin
|
||||
@Operation(summary = "모든 시간 조회", tags = ["관리자 로그인이 필요한 API"])
|
||||
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
||||
fun getAllTimes(): ResponseEntity<CommonApiResponse<ReservationTimesResponse>>
|
||||
fun findAll(): ResponseEntity<CommonApiResponse<TimeRetrieveListResponse>>
|
||||
|
||||
@Admin
|
||||
@Operation(summary = "시간 추가", tags = ["관리자 로그인이 필요한 API"])
|
||||
@ApiResponses(ApiResponse(responseCode = "201", description = "성공", useReturnTypeSchema = true))
|
||||
fun saveTime(
|
||||
@Valid @RequestBody reservationTimeRequest: ReservationTimeRequest,
|
||||
): ResponseEntity<CommonApiResponse<ReservationTimeResponse>>
|
||||
fun create(
|
||||
@Valid @RequestBody timeCreateRequest: TimeCreateRequest,
|
||||
): ResponseEntity<CommonApiResponse<TimeCreateResponse>>
|
||||
|
||||
@Admin
|
||||
@Operation(summary = "시간 삭제", tags = ["관리자 로그인이 필요한 API"])
|
||||
@ApiResponses(ApiResponse(responseCode = "204", description = "성공", useReturnTypeSchema = true))
|
||||
fun removeTime(
|
||||
fun deleteById(
|
||||
@PathVariable id: Long
|
||||
): ResponseEntity<CommonApiResponse<Unit>>
|
||||
|
||||
@LoginRequired
|
||||
@Operation(summary = "예약 가능 여부를 포함한 모든 시간 조회", tags = ["로그인이 필요한 API"])
|
||||
@ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true))
|
||||
fun findAllAvailableReservationTimes(
|
||||
fun findAllWithAvailability(
|
||||
@RequestParam date: LocalDate,
|
||||
@RequestParam themeId: Long
|
||||
): ResponseEntity<CommonApiResponse<ReservationTimeInfosResponse>>
|
||||
): ResponseEntity<CommonApiResponse<TimeWithAvailabilityListResponse>>
|
||||
}
|
||||
@ -18,7 +18,7 @@ class ReservationEntity(
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "time_id", nullable = false)
|
||||
var reservationTime: ReservationTimeEntity,
|
||||
var time: TimeEntity,
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "theme_id", nullable = false)
|
||||
@ -40,14 +40,8 @@ class ReservationEntity(
|
||||
}
|
||||
}
|
||||
|
||||
@Schema(description = "예약 상태를 나타냅니다.", allowableValues = ["CONFIRMED", "CONFIRMED_PAYMENT_REQUIRED", "WAITING"])
|
||||
enum class ReservationStatus {
|
||||
@Schema(description = "결제가 완료된 예약")
|
||||
CONFIRMED,
|
||||
|
||||
@Schema(description = "결제가 필요한 예약")
|
||||
CONFIRMED_PAYMENT_REQUIRED,
|
||||
|
||||
@Schema(description = "대기 중인 예약")
|
||||
WAITING
|
||||
}
|
||||
|
||||
@ -10,7 +10,7 @@ import java.time.LocalDate
|
||||
|
||||
interface ReservationRepository
|
||||
: JpaRepository<ReservationEntity, Long>, JpaSpecificationExecutor<ReservationEntity> {
|
||||
fun findByReservationTime(reservationTime: ReservationTimeEntity): List<ReservationEntity>
|
||||
fun findByTime(time: TimeEntity): List<ReservationEntity>
|
||||
|
||||
fun findByDateAndThemeId(date: LocalDate, themeId: Long): List<ReservationEntity>
|
||||
|
||||
@ -33,7 +33,7 @@ interface ReservationRepository
|
||||
AND EXISTS (
|
||||
SELECT 1 FROM ReservationEntity r
|
||||
WHERE r.theme.id = r2.theme.id
|
||||
AND r.reservationTime.id = r2.reservationTime.id
|
||||
AND r.time.id = r2.time.id
|
||||
AND r.date = r2.date
|
||||
AND r.reservationStatus != 'WAITING'
|
||||
)
|
||||
@ -46,9 +46,9 @@ interface ReservationRepository
|
||||
r.id,
|
||||
t.name,
|
||||
r.date,
|
||||
r.reservationTime.startAt,
|
||||
r.time.startAt,
|
||||
r.reservationStatus,
|
||||
(SELECT COUNT (r2) * 1L FROM ReservationEntity r2 WHERE r2.theme = r.theme AND r2.date = r.date AND r2.reservationTime = r.reservationTime AND r2.id < r.id),
|
||||
(SELECT COUNT (r2) * 1L FROM ReservationEntity r2 WHERE r2.theme = r.theme AND r2.date = r.date AND r2.time = r.time AND r2.id < r.id),
|
||||
p.paymentKey,
|
||||
p.totalAmount
|
||||
)
|
||||
|
||||
@ -22,7 +22,7 @@ class ReservationSearchSpecification(
|
||||
|
||||
fun sameTimeId(timeId: Long?): ReservationSearchSpecification = andIfNotNull(timeId?.let {
|
||||
Specification { root, _, cb ->
|
||||
cb.equal(root.get<ReservationTimeEntity>("reservationTime").get<Long>("id"), timeId)
|
||||
cb.equal(root.get<TimeEntity>("time").get<Long>("id"), timeId)
|
||||
}
|
||||
})
|
||||
|
||||
|
||||
@ -4,8 +4,8 @@ import jakarta.persistence.*
|
||||
import java.time.LocalTime
|
||||
|
||||
@Entity
|
||||
@Table(name = "reservation_time")
|
||||
class ReservationTimeEntity(
|
||||
@Table(name = "times")
|
||||
class TimeEntity(
|
||||
@Id
|
||||
@GeneratedValue(strategy = GenerationType.IDENTITY)
|
||||
var id: Long? = null,
|
||||
@ -3,6 +3,6 @@ package roomescape.reservation.infrastructure.persistence
|
||||
import org.springframework.data.jpa.repository.JpaRepository
|
||||
import java.time.LocalTime
|
||||
|
||||
interface ReservationTimeRepository : JpaRepository<ReservationTimeEntity, Long> {
|
||||
interface TimeRepository : JpaRepository<TimeEntity, Long> {
|
||||
fun existsByStartAt(startAt: LocalTime): Boolean
|
||||
}
|
||||
@ -46,7 +46,7 @@ data class MyReservationsResponse(
|
||||
|
||||
@Schema(name = "예약 정보", description = "예약 저장 및 조회 응답에 사용됩니다.")
|
||||
data class ReservationResponse(
|
||||
|
||||
|
||||
@field:Schema(description = "예약 번호. 예약을 식별할 때 사용합니다.")
|
||||
val id: Long,
|
||||
|
||||
@ -59,7 +59,7 @@ data class ReservationResponse(
|
||||
|
||||
@field:Schema(description = "예약 시간 정보")
|
||||
@field:JsonProperty("time")
|
||||
val time: ReservationTimeResponse,
|
||||
val time: TimeCreateResponse,
|
||||
|
||||
@field:Schema(description = "예약한 테마 정보")
|
||||
@field:JsonProperty("theme")
|
||||
@ -69,11 +69,11 @@ data class ReservationResponse(
|
||||
val status: ReservationStatus
|
||||
)
|
||||
|
||||
fun ReservationEntity.toResponse(): ReservationResponse = ReservationResponse(
|
||||
fun ReservationEntity.toCreateResponse(): ReservationResponse = ReservationResponse(
|
||||
id = this.id!!,
|
||||
date = this.date,
|
||||
member = this.member.toResponse(),
|
||||
time = this.reservationTime.toResponse(),
|
||||
time = this.time.toCreateResponse(),
|
||||
theme = this.theme.toResponse(),
|
||||
status = this.reservationStatus
|
||||
)
|
||||
|
||||
@ -1,51 +0,0 @@
|
||||
package roomescape.reservation.web
|
||||
|
||||
import jakarta.validation.Valid
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.*
|
||||
import roomescape.common.dto.response.CommonApiResponse
|
||||
import roomescape.reservation.business.ReservationTimeService
|
||||
import roomescape.reservation.docs.ReservationTimeAPI
|
||||
import java.net.URI
|
||||
import java.time.LocalDate
|
||||
|
||||
@RestController
|
||||
class ReservationTimeController(
|
||||
private val reservationTimeService: ReservationTimeService
|
||||
) : ReservationTimeAPI {
|
||||
|
||||
@GetMapping("/times")
|
||||
override fun getAllTimes(): ResponseEntity<CommonApiResponse<ReservationTimesResponse>> {
|
||||
val response: ReservationTimesResponse = reservationTimeService.findAllTimes()
|
||||
|
||||
return ResponseEntity.ok(CommonApiResponse(response))
|
||||
}
|
||||
|
||||
@PostMapping("/times")
|
||||
override fun saveTime(
|
||||
@Valid @RequestBody reservationTimeRequest: ReservationTimeRequest,
|
||||
): ResponseEntity<CommonApiResponse<ReservationTimeResponse>> {
|
||||
val response: ReservationTimeResponse = reservationTimeService.addTime(reservationTimeRequest)
|
||||
|
||||
return ResponseEntity
|
||||
.created(URI.create("/times/${response.id}"))
|
||||
.body(CommonApiResponse(response))
|
||||
}
|
||||
|
||||
@DeleteMapping("/times/{id}")
|
||||
override fun removeTime(@PathVariable id: Long): ResponseEntity<CommonApiResponse<Unit>> {
|
||||
reservationTimeService.removeTimeById(id)
|
||||
|
||||
return ResponseEntity.noContent().build()
|
||||
}
|
||||
|
||||
@GetMapping("/times/filter")
|
||||
override fun findAllAvailableReservationTimes(
|
||||
@RequestParam date: LocalDate,
|
||||
@RequestParam themeId: Long
|
||||
): ResponseEntity<CommonApiResponse<ReservationTimeInfosResponse>> {
|
||||
val response: ReservationTimeInfosResponse = reservationTimeService.findAllAvailableTimesByDateAndTheme(date, themeId)
|
||||
|
||||
return ResponseEntity.ok(CommonApiResponse(response))
|
||||
}
|
||||
}
|
||||
@ -1,58 +0,0 @@
|
||||
package roomescape.reservation.web
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity
|
||||
import java.time.LocalTime
|
||||
|
||||
@Schema(name = "예약 시간 저장 요청", description = "예약 시간 저장 요청시 사용됩니다.")
|
||||
data class ReservationTimeRequest(
|
||||
@field:Schema(description = "예약 시간. HH:mm 형식으로 입력해야 합니다.", type = "string", example = "09:00")
|
||||
val startAt: LocalTime
|
||||
)
|
||||
|
||||
@Schema(name = "예약 시간 정보", description = "예약 시간 추가 및 조회 응답시 사용됩니다.")
|
||||
data class ReservationTimeResponse(
|
||||
@field:Schema(description = "예약 시간 번호. 예약 시간을 식별할 때 사용합니다.")
|
||||
val id: Long,
|
||||
|
||||
@field:Schema(description = "예약 시간", type = "string", example = "09:00")
|
||||
val startAt: LocalTime
|
||||
)
|
||||
|
||||
fun ReservationTimeEntity.toResponse(): ReservationTimeResponse = ReservationTimeResponse(
|
||||
this.id!!, this.startAt
|
||||
)
|
||||
|
||||
@Schema(name = "예약 시간 정보 목록 응답", description = "모든 예약 시간 조회 응답시 사용됩니다.")
|
||||
data class ReservationTimesResponse(
|
||||
@field:Schema(description = "모든 시간 목록")
|
||||
val times: List<ReservationTimeResponse>
|
||||
)
|
||||
|
||||
fun List<ReservationTimeEntity>.toResponses(): ReservationTimesResponse = ReservationTimesResponse(
|
||||
this.map { it.toResponse() }
|
||||
)
|
||||
|
||||
@Schema(name = "특정 테마, 날짜에 대한 시간 정보 응답", description = "특정 날짜와 테마에 대해, 예약 가능 여부를 포함한 시간 정보를 저장합니다.")
|
||||
data class ReservationTimeInfoResponse(
|
||||
@field:Schema(description = "예약 시간 번호. 예약 시간을 식별할 때 사용합니다.")
|
||||
val id: Long,
|
||||
|
||||
@field:Schema(description = "예약 시간", type = "string", example = "09:00")
|
||||
val startAt: LocalTime,
|
||||
|
||||
@field:Schema(description = "이미 예약이 완료된 시간인지 여부")
|
||||
val alreadyBooked: Boolean
|
||||
)
|
||||
|
||||
fun ReservationTimeEntity.toInfoResponse(alreadyBooked: Boolean): ReservationTimeInfoResponse = ReservationTimeInfoResponse(
|
||||
id = this.id!!,
|
||||
startAt = this.startAt,
|
||||
alreadyBooked = alreadyBooked
|
||||
)
|
||||
|
||||
@Schema(name = "예약 시간 정보 목록 응답", description = "특정 테마, 날짜에 대한 모든 예약 가능 시간 정보를 저장합니다.")
|
||||
data class ReservationTimeInfosResponse(
|
||||
@field:Schema(description = "특정 테마, 날짜에 대한 예약 가능 여부를 포함한 시간 목록")
|
||||
val times: List<ReservationTimeInfoResponse>
|
||||
)
|
||||
51
src/main/java/roomescape/reservation/web/TimeController.kt
Normal file
51
src/main/java/roomescape/reservation/web/TimeController.kt
Normal file
@ -0,0 +1,51 @@
|
||||
package roomescape.reservation.web
|
||||
|
||||
import jakarta.validation.Valid
|
||||
import org.springframework.http.ResponseEntity
|
||||
import org.springframework.web.bind.annotation.*
|
||||
import roomescape.common.dto.response.CommonApiResponse
|
||||
import roomescape.reservation.business.TimeService
|
||||
import roomescape.reservation.docs.TimeAPI
|
||||
import java.net.URI
|
||||
import java.time.LocalDate
|
||||
|
||||
@RestController
|
||||
class TimeController(
|
||||
private val timeService: TimeService
|
||||
) : TimeAPI {
|
||||
|
||||
@GetMapping("/times")
|
||||
override fun findAll(): ResponseEntity<CommonApiResponse<TimeRetrieveListResponse>> {
|
||||
val response: TimeRetrieveListResponse = timeService.findAll()
|
||||
|
||||
return ResponseEntity.ok(CommonApiResponse(response))
|
||||
}
|
||||
|
||||
@PostMapping("/times")
|
||||
override fun create(
|
||||
@Valid @RequestBody timeCreateRequest: TimeCreateRequest,
|
||||
): ResponseEntity<CommonApiResponse<TimeCreateResponse>> {
|
||||
val response: TimeCreateResponse = timeService.create(timeCreateRequest)
|
||||
|
||||
return ResponseEntity
|
||||
.created(URI.create("/times/${response.id}"))
|
||||
.body(CommonApiResponse(response))
|
||||
}
|
||||
|
||||
@DeleteMapping("/times/{id}")
|
||||
override fun deleteById(@PathVariable id: Long): ResponseEntity<CommonApiResponse<Unit>> {
|
||||
timeService.deleteById(id)
|
||||
|
||||
return ResponseEntity.noContent().build()
|
||||
}
|
||||
|
||||
@GetMapping("/times/filter")
|
||||
override fun findAllWithAvailability(
|
||||
@RequestParam date: LocalDate,
|
||||
@RequestParam themeId: Long
|
||||
): ResponseEntity<CommonApiResponse<TimeWithAvailabilityListResponse>> {
|
||||
val response: TimeWithAvailabilityListResponse = timeService.findAllWithAvailability(date, themeId)
|
||||
|
||||
return ResponseEntity.ok(CommonApiResponse(response))
|
||||
}
|
||||
}
|
||||
55
src/main/java/roomescape/reservation/web/TimeDTO.kt
Normal file
55
src/main/java/roomescape/reservation/web/TimeDTO.kt
Normal file
@ -0,0 +1,55 @@
|
||||
package roomescape.reservation.web
|
||||
|
||||
import io.swagger.v3.oas.annotations.media.Schema
|
||||
import roomescape.reservation.infrastructure.persistence.TimeEntity
|
||||
import java.time.LocalTime
|
||||
|
||||
@Schema(name = "예약 시간 저장 요청", description = "예약 시간 저장 요청시 사용됩니다.")
|
||||
data class TimeCreateRequest(
|
||||
@field:Schema(description = "시간", type = "string", example = "09:00")
|
||||
val startAt: LocalTime
|
||||
)
|
||||
|
||||
@Schema(name = "예약 시간 정보", description = "예약 시간 추가 및 조회 응답시 사용됩니다.")
|
||||
data class TimeCreateResponse(
|
||||
@field:Schema(description = "시간 식별자")
|
||||
val id: Long,
|
||||
|
||||
@field:Schema(description = "시간")
|
||||
val startAt: LocalTime
|
||||
)
|
||||
|
||||
fun TimeEntity.toCreateResponse(): TimeCreateResponse = TimeCreateResponse(this.id!!, this.startAt)
|
||||
|
||||
data class TimeRetrieveResponse(
|
||||
@field:Schema(description = "시간 식별자.")
|
||||
val id: Long,
|
||||
|
||||
@field:Schema(description = "시간")
|
||||
val startAt: LocalTime
|
||||
)
|
||||
|
||||
fun TimeEntity.toRetrieveResponse(): TimeRetrieveResponse = TimeRetrieveResponse(this.id!!, this.startAt)
|
||||
|
||||
data class TimeRetrieveListResponse(
|
||||
val times: List<TimeRetrieveResponse>
|
||||
)
|
||||
|
||||
fun List<TimeEntity>.toRetrieveListResponse(): TimeRetrieveListResponse = TimeRetrieveListResponse(
|
||||
this.map { it.toRetrieveResponse() }
|
||||
)
|
||||
|
||||
data class TimeWithAvailabilityResponse(
|
||||
@field:Schema(description = "시간 식별자")
|
||||
val id: Long,
|
||||
|
||||
@field:Schema(description = "시간")
|
||||
val startAt: LocalTime,
|
||||
|
||||
@field:Schema(description = "예약 가능 여부")
|
||||
val isAvailable: Boolean
|
||||
)
|
||||
|
||||
data class TimeWithAvailabilityListResponse(
|
||||
val times: List<TimeWithAvailabilityResponse>
|
||||
)
|
||||
@ -1,10 +1,10 @@
|
||||
insert into reservation_time(start_at)
|
||||
insert into times(start_at)
|
||||
values ('15:00');
|
||||
insert into reservation_time(start_at)
|
||||
insert into times(start_at)
|
||||
values ('16:00');
|
||||
insert into reservation_time(start_at)
|
||||
insert into times(start_at)
|
||||
values ('17:00');
|
||||
insert into reservation_time(start_at)
|
||||
insert into times(start_at)
|
||||
values ('18:00');
|
||||
|
||||
insert into theme(name, description, thumbnail)
|
||||
|
||||
@ -124,9 +124,9 @@ function renderAvailableTimes(times) {
|
||||
times.data.times.forEach(time => {
|
||||
const startAt = time.startAt;
|
||||
const timeId = time.id;
|
||||
const alreadyBooked = time.alreadyBooked;
|
||||
const isAvailable = time.isAvailable;
|
||||
|
||||
const div = createSlot('time', startAt, timeId, alreadyBooked); // createSlot('time', 시작 시간, time id, 예약 여부)
|
||||
const div = createSlot('time', startAt, timeId, isAvailable); // createSlot('time', 시작 시간, time id, 예약 여부)
|
||||
timeSlots.appendChild(div);
|
||||
});
|
||||
}
|
||||
@ -139,7 +139,7 @@ function checkDateAndThemeAndTime() {
|
||||
const waitButton = document.getElementById("wait-button");
|
||||
|
||||
if (selectedDate && selectedThemeElement && selectedTimeElement) {
|
||||
if (selectedTimeElement.getAttribute('data-time-booked') === 'true') {
|
||||
if (selectedTimeElement.getAttribute('data-time-booked') === 'false') {
|
||||
// 선택된 시간이 이미 예약된 경우
|
||||
reserveButton.classList.add("disabled");
|
||||
waitButton.classList.remove("disabled"); // 예약 대기 버튼 활성화
|
||||
|
||||
@ -94,7 +94,7 @@ class PaymentRepositoryTest(
|
||||
return ReservationFixture.create().also {
|
||||
entityManager.persist(it.member)
|
||||
entityManager.persist(it.theme)
|
||||
entityManager.persist(it.reservationTime)
|
||||
entityManager.persist(it.time)
|
||||
entityManager.persist(it)
|
||||
|
||||
entityManager.flush()
|
||||
|
||||
@ -13,19 +13,19 @@ import roomescape.reservation.infrastructure.persistence.ReservationRepository
|
||||
import roomescape.theme.business.ThemeService
|
||||
import roomescape.util.MemberFixture
|
||||
import roomescape.util.ReservationFixture
|
||||
import roomescape.util.ReservationTimeFixture
|
||||
import roomescape.util.TimeFixture
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
|
||||
class ReservationServiteTest : FunSpec({
|
||||
|
||||
val reservationRepository: ReservationRepository = mockk()
|
||||
val reservationTimeService: ReservationTimeService = mockk()
|
||||
val timeService: TimeService = mockk()
|
||||
val memberService: MemberService = mockk()
|
||||
val themeService: ThemeService = mockk()
|
||||
val reservationService = ReservationService(
|
||||
reservationRepository,
|
||||
reservationTimeService,
|
||||
timeService,
|
||||
memberService,
|
||||
themeService
|
||||
)
|
||||
@ -65,8 +65,8 @@ class ReservationServiteTest : FunSpec({
|
||||
)
|
||||
|
||||
every {
|
||||
reservationTimeService.findTimeById(any())
|
||||
} returns ReservationTimeFixture.create()
|
||||
timeService.findById(any())
|
||||
} returns TimeFixture.create()
|
||||
|
||||
shouldThrow<RoomescapeException> {
|
||||
reservationService.addReservation(reservationRequest, 1L)
|
||||
@ -81,8 +81,8 @@ class ReservationServiteTest : FunSpec({
|
||||
)
|
||||
|
||||
every {
|
||||
reservationTimeService.findTimeById(reservationRequest.timeId)
|
||||
} returns ReservationTimeFixture.create(
|
||||
timeService.findById(reservationRequest.timeId)
|
||||
} returns TimeFixture.create(
|
||||
startAt = LocalTime.now().minusMinutes(1)
|
||||
)
|
||||
|
||||
|
||||
@ -32,7 +32,7 @@ class ReservationWithPaymentServiceTest : FunSpec({
|
||||
val reservationEntity: ReservationEntity = ReservationFixture.create(
|
||||
id = 1L,
|
||||
date = reservationRequest.date,
|
||||
reservationTime = ReservationTimeFixture.create(id = reservationRequest.timeId),
|
||||
time = TimeFixture.create(id = reservationRequest.timeId),
|
||||
theme = ThemeFixture.create(id = reservationRequest.themeId),
|
||||
member = MemberFixture.create(id = memberId),
|
||||
status = ReservationStatus.CONFIRMED
|
||||
@ -65,7 +65,7 @@ class ReservationWithPaymentServiceTest : FunSpec({
|
||||
this.id shouldBe reservationEntity.id
|
||||
this.date shouldBe reservationEntity.date
|
||||
this.member.id shouldBe reservationEntity.member.id
|
||||
this.time.id shouldBe reservationEntity.reservationTime.id
|
||||
this.time.id shouldBe reservationEntity.time.id
|
||||
this.theme.id shouldBe reservationEntity.theme.id
|
||||
this.status shouldBe ReservationStatus.CONFIRMED
|
||||
}
|
||||
|
||||
@ -10,17 +10,17 @@ import org.springframework.http.HttpStatus
|
||||
import roomescape.common.exception.ErrorType
|
||||
import roomescape.common.exception.RoomescapeException
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationRepository
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationTimeRepository
|
||||
import roomescape.reservation.web.ReservationTimeRequest
|
||||
import roomescape.util.ReservationTimeFixture
|
||||
import roomescape.reservation.infrastructure.persistence.TimeRepository
|
||||
import roomescape.reservation.web.TimeCreateRequest
|
||||
import roomescape.util.TimeFixture
|
||||
import java.time.LocalTime
|
||||
|
||||
class ReservationTimeServiceTest : FunSpec({
|
||||
val reservationTimeRepository: ReservationTimeRepository = mockk()
|
||||
class TimeServiceTest : FunSpec({
|
||||
val timeRepository: TimeRepository = mockk()
|
||||
val reservationRepository: ReservationRepository = mockk()
|
||||
|
||||
val reservationTimeService = ReservationTimeService(
|
||||
reservationTimeRepository = reservationTimeRepository,
|
||||
val timeService = TimeService(
|
||||
timeRepository = timeRepository,
|
||||
reservationRepository = reservationRepository
|
||||
)
|
||||
|
||||
@ -28,13 +28,12 @@ class ReservationTimeServiceTest : FunSpec({
|
||||
test("시간을 찾을 수 없으면 400 에러를 던진다.") {
|
||||
val id = 1L
|
||||
|
||||
// Mocking the behavior of reservationTimeRepository.findByIdOrNull
|
||||
every { reservationTimeRepository.findByIdOrNull(id) } returns null
|
||||
every { timeRepository.findByIdOrNull(id) } returns null
|
||||
|
||||
shouldThrow<RoomescapeException> {
|
||||
reservationTimeService.findTimeById(id)
|
||||
timeService.findById(id)
|
||||
}.apply {
|
||||
errorType shouldBe ErrorType.RESERVATION_TIME_NOT_FOUND
|
||||
errorType shouldBe ErrorType.TIME_NOT_FOUND
|
||||
httpStatus shouldBe HttpStatus.BAD_REQUEST
|
||||
}
|
||||
}
|
||||
@ -42,13 +41,12 @@ class ReservationTimeServiceTest : FunSpec({
|
||||
|
||||
context("addTime") {
|
||||
test("중복된 시간이 있으면 409 에러를 던진다.") {
|
||||
val request = ReservationTimeRequest(startAt = LocalTime.of(10, 0))
|
||||
val request = TimeCreateRequest(startAt = LocalTime.of(10, 0))
|
||||
|
||||
// Mocking the behavior of reservationTimeRepository.findByStartAt
|
||||
every { reservationTimeRepository.existsByStartAt(request.startAt) } returns true
|
||||
every { timeRepository.existsByStartAt(request.startAt) } returns true
|
||||
|
||||
shouldThrow<RoomescapeException> {
|
||||
reservationTimeService.addTime(request)
|
||||
timeService.create(request)
|
||||
}.apply {
|
||||
errorType shouldBe ErrorType.TIME_DUPLICATED
|
||||
httpStatus shouldBe HttpStatus.CONFLICT
|
||||
@ -60,29 +58,26 @@ class ReservationTimeServiceTest : FunSpec({
|
||||
test("시간을 찾을 수 없으면 400 에러를 던진다.") {
|
||||
val id = 1L
|
||||
|
||||
// Mocking the behavior of reservationTimeRepository.findByIdOrNull
|
||||
every { reservationTimeRepository.findByIdOrNull(id) } returns null
|
||||
every { timeRepository.findByIdOrNull(id) } returns null
|
||||
|
||||
shouldThrow<RoomescapeException> {
|
||||
reservationTimeService.removeTimeById(id)
|
||||
timeService.deleteById(id)
|
||||
}.apply {
|
||||
errorType shouldBe ErrorType.RESERVATION_TIME_NOT_FOUND
|
||||
errorType shouldBe ErrorType.TIME_NOT_FOUND
|
||||
httpStatus shouldBe HttpStatus.BAD_REQUEST
|
||||
}
|
||||
}
|
||||
|
||||
test("예약이 있는 시간이면 409 에러를 던진다.") {
|
||||
val id = 1L
|
||||
val reservationTime = ReservationTimeFixture.create()
|
||||
val time = TimeFixture.create()
|
||||
|
||||
// Mocking the behavior of reservationTimeRepository.findByIdOrNull
|
||||
every { reservationTimeRepository.findByIdOrNull(id) } returns reservationTime
|
||||
|
||||
// Mocking the behavior of reservationRepository.findByReservationTime
|
||||
every { reservationRepository.findByReservationTime(reservationTime) } returns listOf(mockk())
|
||||
every { timeRepository.findByIdOrNull(id) } returns time
|
||||
|
||||
every { reservationRepository.findByTime(time) } returns listOf(mockk())
|
||||
|
||||
shouldThrow<RoomescapeException> {
|
||||
reservationTimeService.removeTimeById(id)
|
||||
timeService.deleteById(id)
|
||||
}.apply {
|
||||
errorType shouldBe ErrorType.TIME_IS_USED_CONFLICT
|
||||
httpStatus shouldBe HttpStatus.CONFLICT
|
||||
@ -12,7 +12,7 @@ import roomescape.reservation.web.MyReservationResponse
|
||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||
import roomescape.util.PaymentFixture
|
||||
import roomescape.util.ReservationFixture
|
||||
import roomescape.util.ReservationTimeFixture
|
||||
import roomescape.util.TimeFixture
|
||||
import roomescape.util.ThemeFixture
|
||||
|
||||
@DataJpaTest
|
||||
@ -21,13 +21,13 @@ class ReservationRepositoryTest(
|
||||
val reservationRepository: ReservationRepository,
|
||||
) : FunSpec() {
|
||||
init {
|
||||
context("findByReservationTime") {
|
||||
val time = ReservationTimeFixture.create()
|
||||
context("findByTime") {
|
||||
val time = TimeFixture.create()
|
||||
|
||||
beforeTest {
|
||||
listOf(
|
||||
ReservationFixture.create(reservationTime = time),
|
||||
ReservationFixture.create(reservationTime = ReservationTimeFixture.create(
|
||||
ReservationFixture.create(time = time),
|
||||
ReservationFixture.create(time = TimeFixture.create(
|
||||
startAt = time.startAt.plusSeconds(1)
|
||||
))
|
||||
).forEach {
|
||||
@ -39,9 +39,9 @@ class ReservationRepositoryTest(
|
||||
}
|
||||
|
||||
test("입력된 시간과 일치하는 예약을 반환한다.") {
|
||||
assertSoftly(reservationRepository.findByReservationTime(time)) {
|
||||
assertSoftly(reservationRepository.findByTime(time)) {
|
||||
it shouldHaveSize 1
|
||||
assertSoftly(it.first().reservationTime.startAt) { result ->
|
||||
assertSoftly(it.first().time.startAt) { result ->
|
||||
result.hour shouldBe time.startAt.hour
|
||||
result.minute shouldBe time.startAt.minute
|
||||
}
|
||||
@ -68,7 +68,7 @@ class ReservationRepositoryTest(
|
||||
ReservationFixture.create(date = date.plusDays(1), theme = theme1),
|
||||
ReservationFixture.create(date = date, theme = theme2),
|
||||
).forEach {
|
||||
entityManager.persist(it.reservationTime)
|
||||
entityManager.persist(it.time)
|
||||
entityManager.persist(it.member)
|
||||
entityManager.persist(it)
|
||||
}
|
||||
@ -192,7 +192,7 @@ class ReservationRepositoryTest(
|
||||
}
|
||||
|
||||
fun persistReservation(reservation: ReservationEntity) {
|
||||
entityManager.persist(reservation.reservationTime)
|
||||
entityManager.persist(reservation.time)
|
||||
entityManager.persist(reservation.theme)
|
||||
entityManager.persist(reservation.member)
|
||||
entityManager.persist(reservation)
|
||||
|
||||
@ -10,7 +10,7 @@ import roomescape.member.infrastructure.persistence.MemberEntity
|
||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||
import roomescape.util.MemberFixture
|
||||
import roomescape.util.ReservationFixture
|
||||
import roomescape.util.ReservationTimeFixture
|
||||
import roomescape.util.TimeFixture
|
||||
import roomescape.util.ThemeFixture
|
||||
import java.time.LocalDate
|
||||
|
||||
@ -25,7 +25,7 @@ class ReservationSearchSpecificationTest(
|
||||
lateinit var confirmedNotPaidYesterday: ReservationEntity
|
||||
lateinit var waitingTomorrow: ReservationEntity
|
||||
lateinit var member: MemberEntity
|
||||
lateinit var reservationTime: ReservationTimeEntity
|
||||
lateinit var time: TimeEntity
|
||||
lateinit var theme: ThemeEntity
|
||||
|
||||
"동일한 테마의 예약을 조회한다" {
|
||||
@ -56,7 +56,7 @@ class ReservationSearchSpecificationTest(
|
||||
|
||||
"동일한 예약 시간의 예약을 조회한다" {
|
||||
val spec = ReservationSearchSpecification()
|
||||
.sameTimeId(reservationTime.id)
|
||||
.sameTimeId(time.id)
|
||||
.build()
|
||||
|
||||
val results: List<ReservationEntity> = reservationRepository.findAll(spec)
|
||||
@ -136,7 +136,7 @@ class ReservationSearchSpecificationTest(
|
||||
member = MemberFixture.create().also {
|
||||
entityManager.persist(it)
|
||||
}
|
||||
reservationTime = ReservationTimeFixture.create().also {
|
||||
time = TimeFixture.create().also {
|
||||
entityManager.persist(it)
|
||||
}
|
||||
theme = ThemeFixture.create().also {
|
||||
@ -144,7 +144,7 @@ class ReservationSearchSpecificationTest(
|
||||
}
|
||||
|
||||
confirmedNow = ReservationFixture.create(
|
||||
reservationTime = reservationTime,
|
||||
time = time,
|
||||
member = member,
|
||||
theme = theme,
|
||||
date = LocalDate.now(),
|
||||
@ -154,7 +154,7 @@ class ReservationSearchSpecificationTest(
|
||||
}
|
||||
|
||||
confirmedNotPaidYesterday = ReservationFixture.create(
|
||||
reservationTime = reservationTime,
|
||||
time = time,
|
||||
member = member,
|
||||
theme = theme,
|
||||
date = LocalDate.now().minusDays(1),
|
||||
@ -164,7 +164,7 @@ class ReservationSearchSpecificationTest(
|
||||
}
|
||||
|
||||
waitingTomorrow = ReservationFixture.create(
|
||||
reservationTime = reservationTime,
|
||||
time = time,
|
||||
member = member,
|
||||
theme = theme,
|
||||
date = LocalDate.now().plusDays(1),
|
||||
|
||||
@ -4,30 +4,30 @@ import io.kotest.core.spec.style.FunSpec
|
||||
import io.kotest.matchers.shouldBe
|
||||
import jakarta.persistence.EntityManager
|
||||
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest
|
||||
import roomescape.util.ReservationTimeFixture
|
||||
import roomescape.util.TimeFixture
|
||||
import java.time.LocalTime
|
||||
|
||||
@DataJpaTest
|
||||
class ReservationTimeRepositoryTest(
|
||||
class TimeRepositoryTest(
|
||||
val entityManager: EntityManager,
|
||||
val reservationTimeRepository: ReservationTimeRepository,
|
||||
val timeRepository: TimeRepository,
|
||||
) : FunSpec({
|
||||
|
||||
context("existsByStartAt") {
|
||||
val startAt = LocalTime.of(10, 0)
|
||||
|
||||
beforeTest {
|
||||
entityManager.persist(ReservationTimeFixture.create(startAt = startAt))
|
||||
entityManager.persist(TimeFixture.create(startAt = startAt))
|
||||
entityManager.flush()
|
||||
entityManager.clear()
|
||||
}
|
||||
|
||||
test("동일한 시간이 있으면 true 반환") {
|
||||
reservationTimeRepository.existsByStartAt(startAt) shouldBe true
|
||||
timeRepository.existsByStartAt(startAt) shouldBe true
|
||||
}
|
||||
|
||||
test("동일한 시간이 없으면 false 반환") {
|
||||
reservationTimeRepository.existsByStartAt(startAt.plusSeconds(1)) shouldBe false
|
||||
timeRepository.existsByStartAt(startAt.plusSeconds(1)) shouldBe false
|
||||
}
|
||||
}
|
||||
})
|
||||
@ -28,7 +28,7 @@ import roomescape.payment.infrastructure.client.TossPaymentClient
|
||||
import roomescape.payment.infrastructure.persistence.PaymentEntity
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity
|
||||
import roomescape.reservation.infrastructure.persistence.TimeEntity
|
||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||
import roomescape.util.*
|
||||
import java.time.LocalDate
|
||||
@ -503,7 +503,7 @@ class ReservationControllerTest(
|
||||
val reservation = ReservationFixture.create(
|
||||
date = reservationRequest.date,
|
||||
theme = entityManager.find(ThemeEntity::class.java, reservationRequest.themeId),
|
||||
reservationTime = entityManager.find(ReservationTimeEntity::class.java, reservationRequest.timeId),
|
||||
time = entityManager.find(TimeEntity::class.java, reservationRequest.timeId),
|
||||
member = member,
|
||||
status = ReservationStatus.WAITING
|
||||
)
|
||||
@ -675,7 +675,7 @@ class ReservationControllerTest(
|
||||
return ReservationFixture.create(
|
||||
date = date,
|
||||
theme = ThemeFixture.create(name = themeName),
|
||||
reservationTime = ReservationTimeFixture.create(startAt = time),
|
||||
time = TimeFixture.create(startAt = time),
|
||||
member = member,
|
||||
status = status
|
||||
).also { it ->
|
||||
@ -683,7 +683,7 @@ class ReservationControllerTest(
|
||||
if (member.id == null) {
|
||||
entityManager.persist(member)
|
||||
}
|
||||
entityManager.persist(it.reservationTime)
|
||||
entityManager.persist(it.time)
|
||||
entityManager.persist(it.theme)
|
||||
entityManager.persist(it)
|
||||
entityManager.flush()
|
||||
@ -710,14 +710,14 @@ class ReservationControllerTest(
|
||||
transactionTemplate.executeWithoutResult {
|
||||
repeat(10) { index ->
|
||||
val theme = ThemeFixture.create(name = "theme$index")
|
||||
val time = ReservationTimeFixture.create(startAt = LocalTime.now().plusMinutes(index.toLong()))
|
||||
val time = TimeFixture.create(startAt = LocalTime.now().plusMinutes(index.toLong()))
|
||||
entityManager.persist(theme)
|
||||
entityManager.persist(time)
|
||||
|
||||
val reservation = ReservationFixture.create(
|
||||
date = LocalDate.now().plusDays(index.toLong()),
|
||||
theme = theme,
|
||||
reservationTime = time,
|
||||
time = time,
|
||||
member = members[index % members.size],
|
||||
status = ReservationStatus.CONFIRMED
|
||||
)
|
||||
@ -733,7 +733,7 @@ class ReservationControllerTest(
|
||||
|
||||
fun createRequest(
|
||||
theme: ThemeEntity = ThemeFixture.create(),
|
||||
time: ReservationTimeEntity = ReservationTimeFixture.create(),
|
||||
time: TimeEntity = TimeFixture.create(),
|
||||
): ReservationRequest {
|
||||
lateinit var reservationRequest: ReservationRequest
|
||||
|
||||
|
||||
@ -13,28 +13,28 @@ import org.springframework.http.MediaType
|
||||
import org.springframework.test.web.servlet.MockMvc
|
||||
import roomescape.common.config.JacksonConfig
|
||||
import roomescape.common.exception.ErrorType
|
||||
import roomescape.reservation.business.ReservationTimeService
|
||||
import roomescape.reservation.business.TimeService
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationRepository
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationTimeRepository
|
||||
import roomescape.reservation.infrastructure.persistence.TimeEntity
|
||||
import roomescape.reservation.infrastructure.persistence.TimeRepository
|
||||
import roomescape.util.ReservationFixture
|
||||
import roomescape.util.ReservationTimeFixture
|
||||
import roomescape.util.TimeFixture
|
||||
import roomescape.util.RoomescapeApiTest
|
||||
import roomescape.util.ThemeFixture
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
|
||||
@WebMvcTest(ReservationTimeController::class)
|
||||
@WebMvcTest(TimeController::class)
|
||||
@Import(JacksonConfig::class)
|
||||
class ReservationTimeControllerTest(
|
||||
class TimeControllerTest(
|
||||
val mockMvc: MockMvc,
|
||||
) : RoomescapeApiTest() {
|
||||
|
||||
@SpykBean
|
||||
private lateinit var reservationTimeService: ReservationTimeService
|
||||
private lateinit var timeService: TimeService
|
||||
|
||||
@MockkBean
|
||||
private lateinit var reservationTimeRepository: ReservationTimeRepository
|
||||
private lateinit var timeRepository: TimeRepository
|
||||
|
||||
@MockkBean
|
||||
private lateinit var reservationRepository: ReservationRepository
|
||||
@ -50,10 +50,10 @@ class ReservationTimeControllerTest(
|
||||
|
||||
Then("정상 응답") {
|
||||
every {
|
||||
reservationTimeRepository.findAll()
|
||||
timeRepository.findAll()
|
||||
} returns listOf(
|
||||
ReservationTimeFixture.create(id = 1L),
|
||||
ReservationTimeFixture.create(id = 2L)
|
||||
TimeFixture.create(id = 1L),
|
||||
TimeFixture.create(id = 2L)
|
||||
)
|
||||
|
||||
runGetTest(
|
||||
@ -95,7 +95,7 @@ class ReservationTimeControllerTest(
|
||||
loginAsAdmin()
|
||||
}
|
||||
val time = LocalTime.of(10, 0)
|
||||
val request = ReservationTimeRequest(startAt = time)
|
||||
val request = TimeCreateRequest(startAt = time)
|
||||
|
||||
Then("시간 형식이 HH:mm이 아니거나, 범위를 벗어나면 400 응답") {
|
||||
listOf(
|
||||
@ -115,8 +115,8 @@ class ReservationTimeControllerTest(
|
||||
|
||||
Then("정상 응답") {
|
||||
every {
|
||||
reservationTimeService.addTime(request)
|
||||
} returns ReservationTimeResponse(id = 1, startAt = time)
|
||||
timeService.create(request)
|
||||
} returns TimeCreateResponse(id = 1, startAt = time)
|
||||
|
||||
runPostTest(
|
||||
mockMvc = mockMvc,
|
||||
@ -135,7 +135,7 @@ class ReservationTimeControllerTest(
|
||||
|
||||
Then("동일한 시간이 존재하면 409 응답") {
|
||||
every {
|
||||
reservationTimeRepository.existsByStartAt(time)
|
||||
timeRepository.existsByStartAt(time)
|
||||
} returns true
|
||||
|
||||
runPostTest(
|
||||
@ -160,7 +160,7 @@ class ReservationTimeControllerTest(
|
||||
runPostTest(
|
||||
mockMvc = mockMvc,
|
||||
endpoint = endpoint,
|
||||
body = ReservationTimeFixture.create(),
|
||||
body = TimeFixture.create(),
|
||||
log = true
|
||||
) {
|
||||
status { is3xxRedirection() }
|
||||
@ -180,7 +180,7 @@ class ReservationTimeControllerTest(
|
||||
|
||||
Then("정상 응답") {
|
||||
every {
|
||||
reservationTimeService.removeTimeById(1L)
|
||||
timeService.deleteById(1L)
|
||||
} returns Unit
|
||||
|
||||
runDeleteTest(
|
||||
@ -195,7 +195,7 @@ class ReservationTimeControllerTest(
|
||||
Then("없는 시간을 조회하면 400 응답") {
|
||||
val id = 1L
|
||||
every {
|
||||
reservationTimeRepository.findByIdOrNull(id)
|
||||
timeRepository.findByIdOrNull(id)
|
||||
} returns null
|
||||
|
||||
runDeleteTest(
|
||||
@ -206,7 +206,7 @@ class ReservationTimeControllerTest(
|
||||
status { isBadRequest() }
|
||||
content {
|
||||
contentType(MediaType.APPLICATION_JSON)
|
||||
jsonPath("$.errorType") { value(ErrorType.RESERVATION_TIME_NOT_FOUND.name) }
|
||||
jsonPath("$.errorType") { value(ErrorType.TIME_NOT_FOUND.name) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -214,11 +214,11 @@ class ReservationTimeControllerTest(
|
||||
Then("예약이 있는 시간을 삭제하면 409 응답") {
|
||||
val id = 1L
|
||||
every {
|
||||
reservationTimeRepository.findByIdOrNull(id)
|
||||
} returns ReservationTimeFixture.create(id = id)
|
||||
timeRepository.findByIdOrNull(id)
|
||||
} returns TimeFixture.create(id = id)
|
||||
|
||||
every {
|
||||
reservationRepository.findByReservationTime(any())
|
||||
reservationRepository.findByTime(any())
|
||||
} returns listOf(ReservationFixture.create())
|
||||
|
||||
runDeleteTest(
|
||||
@ -258,13 +258,13 @@ class ReservationTimeControllerTest(
|
||||
val themeId = 1L
|
||||
|
||||
When("저장된 예약 시간이 있으면") {
|
||||
val times: List<ReservationTimeEntity> = listOf(
|
||||
ReservationTimeFixture.create(id = 1L, startAt = LocalTime.of(10, 0)),
|
||||
ReservationTimeFixture.create(id = 2L, startAt = LocalTime.of(11, 0))
|
||||
val times: List<TimeEntity> = listOf(
|
||||
TimeFixture.create(id = 1L, startAt = LocalTime.of(10, 0)),
|
||||
TimeFixture.create(id = 2L, startAt = LocalTime.of(11, 0))
|
||||
)
|
||||
|
||||
every {
|
||||
reservationTimeRepository.findAll()
|
||||
timeRepository.findAll()
|
||||
} returns times
|
||||
|
||||
Then("그 시간과, 해당 날짜와 테마에 대한 예약 여부가 담긴 목록을 응답") {
|
||||
@ -276,7 +276,7 @@ class ReservationTimeControllerTest(
|
||||
id = 1L,
|
||||
date = date,
|
||||
theme = ThemeFixture.create(id = themeId),
|
||||
reservationTime = times[0]
|
||||
time = times[0]
|
||||
)
|
||||
)
|
||||
|
||||
@ -289,15 +289,15 @@ class ReservationTimeControllerTest(
|
||||
content {
|
||||
contentType(MediaType.APPLICATION_JSON)
|
||||
}
|
||||
}.andReturn().readValue(ReservationTimeInfosResponse::class.java)
|
||||
}.andReturn().readValue(TimeWithAvailabilityListResponse::class.java)
|
||||
|
||||
assertSoftly(response.times) {
|
||||
this shouldHaveSize times.size
|
||||
this[0].id shouldBe times[0].id
|
||||
this[0].alreadyBooked shouldBe true
|
||||
this[0].isAvailable shouldBe false
|
||||
|
||||
this[1].id shouldBe times[1].id
|
||||
this[1].alreadyBooked shouldBe false
|
||||
this[1].isAvailable shouldBe true
|
||||
}
|
||||
}
|
||||
}
|
||||
@ -3,11 +3,11 @@ package roomescape.theme.util
|
||||
import jakarta.persistence.EntityManager
|
||||
import roomescape.member.infrastructure.persistence.MemberEntity
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity
|
||||
import roomescape.reservation.infrastructure.persistence.TimeEntity
|
||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||
import roomescape.util.MemberFixture
|
||||
import roomescape.util.ReservationFixture
|
||||
import roomescape.util.ReservationTimeFixture
|
||||
import roomescape.util.TimeFixture
|
||||
import roomescape.util.ThemeFixture
|
||||
import java.time.LocalDate
|
||||
import java.time.LocalTime
|
||||
@ -23,7 +23,7 @@ object TestThemeCreateUtil {
|
||||
val member: MemberEntity = MemberFixture.create().also { entityManager.persist(it) }
|
||||
|
||||
for (i in 1..reservedCount) {
|
||||
val time: ReservationTimeEntity = ReservationTimeFixture.create(
|
||||
val time: TimeEntity = TimeFixture.create(
|
||||
startAt = LocalTime.now().plusMinutes(i.toLong())
|
||||
).also { entityManager.persist(it) }
|
||||
|
||||
@ -31,7 +31,7 @@ object TestThemeCreateUtil {
|
||||
date = date,
|
||||
theme = themeEntity,
|
||||
member = member,
|
||||
reservationTime = time,
|
||||
time = time,
|
||||
status = ReservationStatus.CONFIRMED
|
||||
).also { entityManager.persist(it) }
|
||||
}
|
||||
|
||||
@ -12,7 +12,7 @@ import roomescape.payment.web.PaymentCancelRequest
|
||||
import roomescape.payment.web.PaymentCancelResponse
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationEntity
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationStatus
|
||||
import roomescape.reservation.infrastructure.persistence.ReservationTimeEntity
|
||||
import roomescape.reservation.infrastructure.persistence.TimeEntity
|
||||
import roomescape.reservation.web.ReservationRequest
|
||||
import roomescape.reservation.web.WaitingRequest
|
||||
import roomescape.theme.infrastructure.persistence.ThemeEntity
|
||||
@ -54,11 +54,11 @@ object MemberFixture {
|
||||
)
|
||||
}
|
||||
|
||||
object ReservationTimeFixture {
|
||||
object TimeFixture {
|
||||
fun create(
|
||||
id: Long? = null,
|
||||
startAt: LocalTime = LocalTime.now().plusHours(1),
|
||||
): ReservationTimeEntity = ReservationTimeEntity(id, startAt)
|
||||
): TimeEntity = TimeEntity(id, startAt)
|
||||
}
|
||||
|
||||
object ThemeFixture {
|
||||
@ -75,10 +75,10 @@ object ReservationFixture {
|
||||
id: Long? = null,
|
||||
date: LocalDate = LocalDate.now().plusWeeks(1),
|
||||
theme: ThemeEntity = ThemeFixture.create(),
|
||||
reservationTime: ReservationTimeEntity = ReservationTimeFixture.create(),
|
||||
time: TimeEntity = TimeFixture.create(),
|
||||
member: MemberEntity = MemberFixture.create(),
|
||||
status: ReservationStatus = ReservationStatus.CONFIRMED_PAYMENT_REQUIRED
|
||||
): ReservationEntity = ReservationEntity(id, date, reservationTime, theme, member, status)
|
||||
): ReservationEntity = ReservationEntity(id, date, time, theme, member, status)
|
||||
|
||||
fun createRequest(
|
||||
date: LocalDate = LocalDate.now().plusWeeks(1),
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user