diff --git a/src/main/kotlin/roomescape/reservation/docs/ReservationAPI.kt b/src/main/kotlin/roomescape/reservation/docs/ReservationAPI.kt index fb5b7dd3..cce392f3 100644 --- a/src/main/kotlin/roomescape/reservation/docs/ReservationAPI.kt +++ b/src/main/kotlin/roomescape/reservation/docs/ReservationAPI.kt @@ -2,150 +2,59 @@ package roomescape.reservation.docs import io.swagger.v3.oas.annotations.Operation import io.swagger.v3.oas.annotations.Parameter -import io.swagger.v3.oas.annotations.headers.Header -import io.swagger.v3.oas.annotations.media.Schema import io.swagger.v3.oas.annotations.responses.ApiResponse import io.swagger.v3.oas.annotations.responses.ApiResponses -import io.swagger.v3.oas.annotations.tags.Tag import jakarta.validation.Valid -import org.springframework.http.HttpHeaders import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.PathVariable import org.springframework.web.bind.annotation.RequestBody -import org.springframework.web.bind.annotation.RequestParam -import roomescape.auth.web.support.Admin import roomescape.auth.web.support.LoginRequired import roomescape.auth.web.support.MemberId import roomescape.common.dto.response.CommonApiResponse -import roomescape.reservation.web.* -import java.time.LocalDate +import roomescape.reservation.web.PendingReservationCreateRequest +import roomescape.reservation.web.PendingReservationCreateResponse +import roomescape.reservation.web.ReservationCancelRequest +import roomescape.reservation.web.ReservationDetailRetrieveResponse +import roomescape.reservation.web.ReservationSummaryRetrieveListResponse -@Tag(name = "3. 예약 API", description = "예약 및 대기 정보를 추가 / 조회 / 삭제할 때 사용합니다.") interface ReservationAPI { - @Admin - @Operation(summary = "모든 예약 정보 조회", tags = ["관리자 로그인이 필요한 API"]) + @LoginRequired + @Operation(summary = "결제 대기 예약 저장", tags = ["로그인이 필요한 API"]) @ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true)) - fun findReservations(): ResponseEntity> + fun createPendingReservation( + @MemberId @Parameter(hidden = true) memberId: Long, + @Valid @RequestBody request: PendingReservationCreateRequest + ): ResponseEntity> @LoginRequired - @Operation(summary = "자신의 예약 및 대기 조회", tags = ["로그인이 필요한 API"]) + @Operation(summary = "예약 확정", tags = ["로그인이 필요한 API"]) @ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true)) - fun findReservationsByMemberId( - @MemberId @Parameter(hidden = true) memberId: Long - ): ResponseEntity> - - @Admin - @Operation(summary = "관리자의 예약 검색", description = "특정 조건에 해당되는 예약 검색", tags = ["관리자 로그인이 필요한 API"]) - @ApiResponses( - ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true) - ) - fun searchReservations( - @RequestParam(required = false) themeId: Long?, - @RequestParam(required = false) memberId: Long?, - @RequestParam(required = false) dateFrom: LocalDate?, - @RequestParam(required = false) dateTo: LocalDate? - ): ResponseEntity> - - @Admin - @Operation(summary = "관리자의 예약 취소", tags = ["관리자 로그인이 필요한 API"]) - @ApiResponses( - ApiResponse(responseCode = "204", description = "성공"), - ) - fun cancelReservationByAdmin( - @MemberId @Parameter(hidden = true) memberId: Long, - @PathVariable("id") reservationId: Long + fun confirmReservation( + @PathVariable("id") id: Long ): ResponseEntity> @LoginRequired - @Operation(summary = "예약 추가", tags = ["로그인이 필요한 API"]) - @ApiResponses( - ApiResponse( - responseCode = "201", - description = "성공", - useReturnTypeSchema = true, - headers = [Header( - name = HttpHeaders.LOCATION, - description = "생성된 예약 정보 URL", - schema = Schema(example = "/reservations/1") - )] - ) - ) - fun createReservationWithPayment( - @Valid @RequestBody reservationCreateWithPaymentRequest: ReservationCreateWithPaymentRequest, - @MemberId @Parameter(hidden = true) memberId: Long - ): ResponseEntity> - - @Admin - @Operation(summary = "관리자 예약 추가", tags = ["관리자 로그인이 필요한 API"]) - @ApiResponses( - ApiResponse( - responseCode = "201", - description = "성공", - useReturnTypeSchema = true, - headers = [Header( - name = HttpHeaders.LOCATION, - description = "생성된 예약 정보 URL", - schema = Schema(example = "/reservations/1") - )], - ) - ) - fun createReservationByAdmin( - @Valid @RequestBody adminReservationRequest: AdminReservationCreateRequest, - @MemberId @Parameter(hidden = true) memberId: Long - ): ResponseEntity> - - @Admin - @Operation(summary = "모든 예약 대기 조회", tags = ["관리자 로그인이 필요한 API"]) + @Operation(summary = "예약 취소", tags = ["로그인이 필요한 API"]) @ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true)) - fun findAllWaiting(): ResponseEntity> + fun cancelReservation( + @MemberId @Parameter(hidden = true) memberId: Long, + @PathVariable reservationId: Long, + @Valid @RequestBody request: ReservationCancelRequest + ): ResponseEntity> @LoginRequired - @Operation(summary = "예약 대기 신청", tags = ["로그인이 필요한 API"]) - @ApiResponses( - ApiResponse( - responseCode = "201", - description = "성공", - useReturnTypeSchema = true, - headers = [Header( - name = HttpHeaders.LOCATION, - description = "생성된 예약 정보 URL", - schema = Schema(example = "/reservations/1") - )] - ) - ) - fun createWaiting( - @Valid @RequestBody waitingCreateRequest: WaitingCreateRequest, - @MemberId @Parameter(hidden = true) memberId: Long, - ): ResponseEntity> + @Operation(summary = "회원별 예약 요약 목록 조회", tags = ["로그인이 필요한 API"]) + @ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true)) + fun findSummaryByMemberId( + @MemberId @Parameter(hidden = true) memberId: Long + ): ResponseEntity> @LoginRequired - @Operation(summary = "예약 대기 취소", tags = ["로그인이 필요한 API"]) - @ApiResponses( - ApiResponse(responseCode = "204", description = "성공"), - ) - fun cancelWaitingByMember( - @MemberId @Parameter(hidden = true) memberId: Long, - @PathVariable("id") @Parameter(description = "예약 ID") reservationId: Long - ): ResponseEntity> + @Operation(summary = "특정 예약에 대한 상세 조회", tags = ["로그인이 필요한 API"]) + @ApiResponses(ApiResponse(responseCode = "200", description = "성공", useReturnTypeSchema = true)) + fun findDetailById( + @PathVariable("id") id: Long + ): ResponseEntity> - @Admin - @Operation(summary = "대기 중인 예약 승인", tags = ["관리자 로그인이 필요한 API"]) - @ApiResponses( - ApiResponse(responseCode = "200", description = "성공"), - ) - fun confirmWaiting( - @MemberId @Parameter(hidden = true) memberId: Long, - @PathVariable("id") @Parameter(description = "예약 ID") reservationId: Long - ): ResponseEntity> - - @Admin - @Operation(summary = "대기 중인 예약 거절", tags = ["관리자 로그인이 필요한 API"]) - @ApiResponses( - ApiResponse(responseCode = "204", description = "대기 중인 예약 거절 성공"), - ) - fun rejectWaiting( - @MemberId @Parameter(hidden = true) memberId: Long, - @PathVariable("id") @Parameter(description = "예약 ID") reservationId: Long - ): ResponseEntity> -} +} \ No newline at end of file diff --git a/src/main/kotlin/roomescape/reservation/web/ReservationController.kt b/src/main/kotlin/roomescape/reservation/web/ReservationController.kt index 431a57be..f4317e5e 100644 --- a/src/main/kotlin/roomescape/reservation/web/ReservationController.kt +++ b/src/main/kotlin/roomescape/reservation/web/ReservationController.kt @@ -6,170 +6,59 @@ import org.springframework.http.ResponseEntity import org.springframework.web.bind.annotation.* import roomescape.auth.web.support.MemberId import roomescape.common.dto.response.CommonApiResponse -import roomescape.payment.infrastructure.client.PaymentApproveRequest -import roomescape.payment.infrastructure.client.PaymentApproveResponse -import roomescape.payment.infrastructure.client.TossPaymentClient -import roomescape.payment.web.PaymentCancelRequest -import roomescape.reservation.business.ReservationWriteService -import roomescape.reservation.business.ReservationFindService -import roomescape.reservation.business.ReservationWithPaymentService +import roomescape.reservation.business.ReservationService import roomescape.reservation.docs.ReservationAPI -import java.net.URI -import java.time.LocalDate @RestController class ReservationController( - private val reservationWithPaymentService: ReservationWithPaymentService, - private val reservationFindService: ReservationFindService, - private val reservationWriteService: ReservationWriteService, - private val paymentClient: TossPaymentClient + private val reservationService: ReservationService ) : ReservationAPI { - @GetMapping("/reservations") - override fun findReservations(): ResponseEntity> { - val response: ReservationRetrieveListResponse = reservationFindService.findReservations() + + @PostMapping("/reservations/pending") + override fun createPendingReservation( + @MemberId @Parameter(hidden = true) memberId: Long, + @Valid @RequestBody request: PendingReservationCreateRequest + ): ResponseEntity> { + val response = reservationService.createPendingReservation(memberId, request) return ResponseEntity.ok(CommonApiResponse(response)) } - @GetMapping("/reservations-mine") - override fun findReservationsByMemberId( + @PatchMapping("/reservations/{id}/confirm") + override fun confirmReservation( + @PathVariable("id") id: Long + ): ResponseEntity> { + reservationService.confirmReservation(id) + + return ResponseEntity.ok().body(CommonApiResponse()) + } + + @PostMapping("/reservations/{reservationId}/cancel") + override fun cancelReservation( + @MemberId @Parameter(hidden = true) memberId: Long, + @PathVariable reservationId: Long, + @Valid @RequestBody request: ReservationCancelRequest + ): ResponseEntity> { + reservationService.cancelReservation(memberId, reservationId, request) + + return ResponseEntity.noContent().build() + } + + @GetMapping("/reservations/summary") + override fun findSummaryByMemberId( @MemberId @Parameter(hidden = true) memberId: Long - ): ResponseEntity> { - val response: MyReservationRetrieveListResponse = reservationFindService.findReservationsByMemberId(memberId) + ): ResponseEntity> { + val response = reservationService.findSummaryByMemberId(memberId) return ResponseEntity.ok(CommonApiResponse(response)) } - @GetMapping("/reservations/search") - override fun searchReservations( - @RequestParam(required = false) themeId: Long?, - @RequestParam(required = false) memberId: Long?, - @RequestParam(required = false) dateFrom: LocalDate?, - @RequestParam(required = false) dateTo: LocalDate? - ): ResponseEntity> { - val response: ReservationRetrieveListResponse = reservationFindService.searchReservations( - themeId, - memberId, - dateFrom, - dateTo - ) + @GetMapping("/reservations/{id}/detail") + override fun findDetailById( + @PathVariable("id") id: Long + ): ResponseEntity> { + val response = reservationService.findDetailById(id) return ResponseEntity.ok(CommonApiResponse(response)) } - - @DeleteMapping("/reservations/{id}") - override fun cancelReservationByAdmin( - @MemberId @Parameter(hidden = true) memberId: Long, - @PathVariable("id") reservationId: Long - ): ResponseEntity> { - if (reservationWithPaymentService.isNotPaidReservation(reservationId)) { - reservationWriteService.deleteReservation(reservationId, memberId) - return ResponseEntity.noContent().build() - } - - val paymentCancelRequest = reservationWithPaymentService.deleteReservationAndPayment(reservationId, memberId) - val paymentCancelResponse = paymentClient.cancel(paymentCancelRequest) - reservationWithPaymentService.updateCanceledTime( - paymentCancelRequest.paymentKey, - paymentCancelResponse.canceledAt - ) - - return ResponseEntity.noContent().build() - } - - @PostMapping("/reservations") - override fun createReservationWithPayment( - @Valid @RequestBody reservationCreateWithPaymentRequest: ReservationCreateWithPaymentRequest, - @MemberId @Parameter(hidden = true) memberId: Long - ): ResponseEntity> { - val paymentRequest: PaymentApproveRequest = reservationCreateWithPaymentRequest.toPaymentApproveRequest() - val paymentResponse: PaymentApproveResponse = paymentClient.confirm(paymentRequest) - - try { - val response: ReservationCreateResponse = - reservationWithPaymentService.createReservationAndPayment( - reservationCreateWithPaymentRequest, - paymentResponse, - memberId - ) - return ResponseEntity.created(URI.create("/reservations/${response.id}")) - .body(CommonApiResponse(response)) - } catch (e: Exception) { - val cancelRequest = PaymentCancelRequest( - paymentRequest.paymentKey, - paymentRequest.amount, - e.message!! - ) - val paymentCancelResponse = paymentClient.cancel(cancelRequest) - reservationWithPaymentService.createCanceledPayment( - paymentCancelResponse, - paymentResponse.approvedAt, - paymentRequest.paymentKey - ) - throw e - } - } - - @PostMapping("/reservations/admin") - override fun createReservationByAdmin( - @Valid @RequestBody adminReservationRequest: AdminReservationCreateRequest, - @MemberId @Parameter(hidden = true) memberId: Long, - ): ResponseEntity> { - val response: ReservationCreateResponse = - reservationWriteService.createReservationByAdmin(adminReservationRequest, memberId) - - return ResponseEntity.created(URI.create("/reservations/${response.id}")) - .body(CommonApiResponse(response)) - } - - @GetMapping("/reservations/waiting") - override fun findAllWaiting(): ResponseEntity> { - val response: ReservationRetrieveListResponse = reservationFindService.findAllWaiting() - - return ResponseEntity.ok(CommonApiResponse(response)) - } - - @PostMapping("/reservations/waiting") - override fun createWaiting( - @Valid @RequestBody waitingCreateRequest: WaitingCreateRequest, - @MemberId @Parameter(hidden = true) memberId: Long, - ): ResponseEntity> { - val response: ReservationCreateResponse = reservationWriteService.createWaiting( - waitingCreateRequest, - memberId - ) - - return ResponseEntity.created(URI.create("/reservations/${response.id}")) - .body(CommonApiResponse(response)) - } - - @DeleteMapping("/reservations/waiting/{id}") - override fun cancelWaitingByMember( - @MemberId @Parameter(hidden = true) memberId: Long, - @PathVariable("id") reservationId: Long - ): ResponseEntity> { - reservationWriteService.deleteWaiting(reservationId, memberId) - - return ResponseEntity.noContent().build() - } - - @PostMapping("/reservations/waiting/{id}/confirm") - override fun confirmWaiting( - @MemberId @Parameter(hidden = true) memberId: Long, - @PathVariable("id") reservationId: Long - ): ResponseEntity> { - reservationWriteService.confirmWaiting(reservationId, memberId) - - return ResponseEntity.ok().build() - } - - @PostMapping("/reservations/waiting/{id}/reject") - override fun rejectWaiting( - @MemberId @Parameter(hidden = true) memberId: Long, - @PathVariable("id") reservationId: Long - ): ResponseEntity> { - reservationWriteService.deleteWaiting(reservationId, memberId) - - return ResponseEntity.noContent().build() - } }