From ab36031fe94ea748276fa79e35aa29cbbf3b8686 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 19 Jan 2026 13:26:39 +0900 Subject: [PATCH 01/30] =?UTF-8?q?[FEAT]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=B7=A8=EC=86=8C=20DTO=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/booking/dto/request/BookingRequestDTO.java | 4 ++++ .../booking/dto/response/BookingResponseDTO.java | 10 ++++++++++ 2 files changed, 14 insertions(+) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/request/BookingRequestDTO.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/request/BookingRequestDTO.java index 6616d469..7ec9ca0d 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/request/BookingRequestDTO.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/request/BookingRequestDTO.java @@ -43,4 +43,8 @@ public record PaymentConfirmDTO( @NotNull Integer amount //실제 결제 금액 ){} + public record CancelBookingDTO( + @NotBlank String reason //예약 취소 사유 + ){} + } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java index ad1bf084..055a1b8f 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java @@ -1,5 +1,6 @@ package com.eatsfine.eatsfine.domain.booking.dto.response; +import com.eatsfine.eatsfine.domain.booking.enums.BookingStatus; import lombok.Builder; import java.time.LocalDate; @@ -63,4 +64,13 @@ public record ConfirmPaymentResultDTO( String paymentKey, // PG사 결제 키 Integer amount // 최종 결제 금액 ){} + + @Builder + public record CancelBookingResultDTO( + Long bookingId, + BookingStatus status, // CANCELED + String cancelReason, // 취소 사유 + LocalDateTime canceledAt, // 취소 시간 + Integer refundAmount // 환불 금액 + ){} } From 635fe9302eadeafdcf79c9d753df03fba248c374 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 19 Jan 2026 13:42:46 +0900 Subject: [PATCH 02/30] =?UTF-8?q?[FEAT]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=B7=A8=EC=86=8C=20DTO=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/domain/booking/dto/request/BookingRequestDTO.java | 1 + .../domain/booking/dto/response/BookingResponseDTO.java | 2 +- 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/request/BookingRequestDTO.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/request/BookingRequestDTO.java index 7ec9ca0d..de52b9f5 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/request/BookingRequestDTO.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/request/BookingRequestDTO.java @@ -45,6 +45,7 @@ public record PaymentConfirmDTO( public record CancelBookingDTO( @NotBlank String reason //예약 취소 사유 + ){} } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java index 055a1b8f..9a188645 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java @@ -68,7 +68,7 @@ public record ConfirmPaymentResultDTO( @Builder public record CancelBookingResultDTO( Long bookingId, - BookingStatus status, // CANCELED + String status, // CANCELED String cancelReason, // 취소 사유 LocalDateTime canceledAt, // 취소 시간 Integer refundAmount // 환불 금액 From 366a69f03fe478a49154fa94ff44f616aade35f0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 19 Jan 2026 13:43:19 +0900 Subject: [PATCH 03/30] =?UTF-8?q?[FEAT]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=B7=A8=EC=86=8C=20=EC=84=B1=EA=B3=B5,=EC=8B=A4=ED=8C=A8=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/domain/booking/status/BookingErrorStatus.java | 5 +++-- .../domain/booking/status/BookingSuccessStatus.java | 6 ++++++ 2 files changed, 9 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/status/BookingErrorStatus.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/status/BookingErrorStatus.java index b5690932..7ebe586c 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/status/BookingErrorStatus.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/status/BookingErrorStatus.java @@ -17,8 +17,9 @@ public enum BookingErrorStatus implements BaseErrorCode { _INVALID_PARTY_SIZE(HttpStatus.BAD_REQUEST, "BOOKING4001", "인원 설정이 잘못되었습니다."), _ALREADY_RESERVED_TABLE(HttpStatus.CONFLICT, "BOOKING4091", "선택하신 테이블 중 이미 예약된 테이블이 포함되어 있습니다."), _ALREADY_CONFIRMED(HttpStatus.BAD_REQUEST,"BOOKING4002", "이미 확정된 예약입니다."), - _PAYMENT_AMOUNT_MISMATCH(HttpStatus.BAD_REQUEST, "BOOKING4003", "결제 금액이 일치하지 않습니다."); - + _PAYMENT_AMOUNT_MISMATCH(HttpStatus.BAD_REQUEST, "BOOKING4003", "결제 금액이 일치하지 않습니다."), + _ALREADY_CANCELED(HttpStatus.BAD_REQUEST,"BOOKING4004", "이미 취소된 예약입니다."), + ; private final HttpStatus httpStatus; private final String code; diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/status/BookingSuccessStatus.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/status/BookingSuccessStatus.java index 1e6b1e3e..0454cec3 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/status/BookingSuccessStatus.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/status/BookingSuccessStatus.java @@ -13,6 +13,12 @@ public enum BookingSuccessStatus implements BaseCode { _BOOKING_FOUND(HttpStatus.OK, "BOOKING200", "성공적으로 예약을 조회 했습니다."), _BOOKING_DETAIL_FOUND(HttpStatus.FOUND, "BOOKING_DETAIL200", "성공적으로 예약 상세 내역을 조회했습니다."), + + _BOOKING_CREATED(HttpStatus.CREATED, "BOOKING201", "성공적으로 예약이 생성되었습니다."), + + _BOOKING_CONFIRMED(HttpStatus.OK, "BOOKING2001", "성공적으로 예약이 확정되었습니다."), + + _BOOKING_CANCELED(HttpStatus.OK, "BOOKING2002", "성공적으로 예약이 취소되었습니다.") ; private final HttpStatus httpStatus; From a7926e59db6bb2c012db3c017c1ff6db9ddcf6eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 19 Jan 2026 13:43:37 +0900 Subject: [PATCH 04/30] =?UTF-8?q?[FIX]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=83=81=ED=83=9C=20=EC=9D=B4=EB=A6=84=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/eatsfine/domain/booking/enums/BookingStatus.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/enums/BookingStatus.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/enums/BookingStatus.java index 404fafc2..c0a94af6 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/enums/BookingStatus.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/enums/BookingStatus.java @@ -2,5 +2,5 @@ public enum BookingStatus { - PENDING, CONFIRMED, COMPLETED, CANCELLED, NOSHOW + PENDING, CONFIRMED, COMPLETED, CANCELED, NOSHOW } From 26820c4c59c290c7ea0967a154f4927f0e7fc12f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 19 Jan 2026 13:44:14 +0900 Subject: [PATCH 05/30] =?UTF-8?q?[FEAT]=20:=20Booking=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=EC=97=90=20cancel=20=EB=B9=84=EC=A7=80=EB=8B=88?= =?UTF-8?q?=EC=8A=A4=20=EB=A9=94=EC=84=9C=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/eatsfine/eatsfine/domain/booking/entity/Booking.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java index 7b9f6f90..82f8ae3c 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java @@ -54,6 +54,7 @@ public class Booking extends BaseEntity { @Column(name = "booking_time", nullable = false) private LocalTime bookingTime; + private String cancelReason; @Enumerated(EnumType.STRING) private BookingStatus status; @@ -72,4 +73,8 @@ public void confirm() { this.status = BookingStatus.CONFIRMED; } + public void cancel(String reason) { + this.status = BookingStatus.CANCELED; + this.cancelReason = reason; + } } From ae305c1b5a1560cfef949c146241f4252b0725eb Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 19 Jan 2026 13:44:37 +0900 Subject: [PATCH 06/30] =?UTF-8?q?[FEAT]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=B7=A8=EC=86=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../booking/controller/BookingController.java | 12 ++++++++++ .../service/BookingCommandService.java | 3 +++ .../service/BookingCommandServiceImpl.java | 23 +++++++++++++++++++ 3 files changed, 38 insertions(+) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java index bc91aa8d..44a8a67a 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java @@ -4,6 +4,7 @@ import com.eatsfine.eatsfine.domain.booking.dto.response.BookingResponseDTO; import com.eatsfine.eatsfine.domain.booking.service.BookingCommandService; import com.eatsfine.eatsfine.domain.booking.service.BookingQueryService; +import com.eatsfine.eatsfine.domain.booking.status.BookingSuccessStatus; import com.eatsfine.eatsfine.domain.user.entity.User; import com.eatsfine.eatsfine.domain.user.repository.UserRepository; import com.eatsfine.eatsfine.global.apiPayload.ApiResponse; @@ -71,4 +72,15 @@ public ApiResponse confirmPayment( return ApiResponse.onSuccess(bookingCommandService.confirmPayment(bookingId,dto)); } + @Operation(summary = "예약 취소", + description = "예약을 취소하고 환불을 진행합니다.") + @PatchMapping("/bookings/{bookingId}/cancel") + public ApiResponse cancelBooking( + @PathVariable Long bookingId, + @RequestBody @Valid BookingRequestDTO.CancelBookingDTO dto + ) { + return ApiResponse.of(BookingSuccessStatus._BOOKING_CANCELED, + bookingCommandService.cancelBooking(bookingId, dto)); + } + } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandService.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandService.java index 5eacc66b..0928674a 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandService.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandService.java @@ -14,4 +14,7 @@ public interface BookingCommandService { BookingResponseDTO.CreateBookingResultDTO createBooking(User user, Long storeId, BookingRequestDTO.CreateBookingDTO dto); BookingResponseDTO.ConfirmPaymentResultDTO confirmPayment(Long BookingId, BookingRequestDTO.PaymentConfirmDTO dto); + + BookingResponseDTO.CancelBookingResultDTO cancelBooking(Long bookingId, BookingRequestDTO.CancelBookingDTO dto); + } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java index e41b4371..ed7b98ac 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java @@ -121,4 +121,27 @@ public BookingResponseDTO.ConfirmPaymentResultDTO confirmPayment(Long bookingId, .amount(booking.getDepositAmount()) .build(); } + + @Override + @Transactional + public BookingResponseDTO.CancelBookingResultDTO cancelBooking(Long bookingId, BookingRequestDTO.CancelBookingDTO dto) { + Booking booking = bookingRepository.findById(bookingId) + .orElseThrow(() -> new BookingException(BookingErrorStatus._BOOKING_NOT_FOUND)); + + + // 이미 취소된 예약인지 최종 확인 + if(booking.getStatus().equals(BookingStatus.CANCELED)) { + throw new BookingException(BookingErrorStatus._ALREADY_CONFIRMED); + } + + // TODO 환불 로직 + + booking.cancel(dto.reason()); + + return BookingResponseDTO.CancelBookingResultDTO.builder() + .bookingId(booking.getId()) + .status(booking.getStatus().name()) + .refundAmount(booking.getDepositAmount()) + .build(); + } } From dc7f6c7a81ab99f23d0baabf8727cf07300ab2c2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 26 Jan 2026 18:12:39 +0900 Subject: [PATCH 07/30] =?UTF-8?q?[FEAT]=20:=20Booking=20=EC=97=94=ED=8B=B0?= =?UTF-8?q?=ED=8B=B0=20=EC=B7=A8=EC=86=8C=20=EC=9D=B4=EC=9C=A0=20=ED=95=84?= =?UTF-8?q?=EB=93=9C=EC=99=80=20=EC=B7=A8=EC=86=8C=20=EB=A9=94=EC=84=9C?= =?UTF-8?q?=EB=93=9C=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/eatsfine/domain/booking/entity/Booking.java | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java index 386d7644..f3a7b71f 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java @@ -60,6 +60,7 @@ public class Booking extends BaseEntity { private LocalTime bookingTime; @Enumerated(EnumType.STRING) + @Column(name = "status", length = 20, nullable = false) private BookingStatus status; public void addBookingTable(StoreTable storeTable) { @@ -72,8 +73,16 @@ public void addBookingTable(StoreTable storeTable) { private Integer depositAmount; + private String cancelReason; + public void confirm() { this.status = BookingStatus.CONFIRMED; } + public void cancel(String cancelReason) + { + this.status = BookingStatus.CANCELED; + this.cancelReason = cancelReason; + } + } From 8fbe74d78c70586298cf71a0edeb99d721a4b948 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 26 Jan 2026 18:12:56 +0900 Subject: [PATCH 08/30] =?UTF-8?q?[FEAT]=20:=20BookingConverter=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../booking/converter/BookingConverter.java | 27 +++++++++++++++++++ 1 file changed, 27 insertions(+) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/converter/BookingConverter.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/converter/BookingConverter.java index cfd9e42b..6eac1265 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/converter/BookingConverter.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/converter/BookingConverter.java @@ -1,4 +1,31 @@ package com.eatsfine.eatsfine.domain.booking.converter; +import com.eatsfine.eatsfine.domain.booking.dto.response.BookingResponseDTO; +import com.eatsfine.eatsfine.domain.booking.entity.Booking; +import com.eatsfine.eatsfine.domain.payment.dto.response.PaymentResponseDTO; +import com.eatsfine.eatsfine.domain.store.entity.Store; + +import java.util.List; + public class BookingConverter { + + public static BookingResponseDTO.CreateBookingResultDTO toCreateBookingResultDTO( + Booking booking, Store store, int totalDeposit, + List resultTableDTOS, + PaymentResponseDTO.PaymentRequestResultDTO paymentInfo) { + + return BookingResponseDTO.CreateBookingResultDTO.builder() + .bookingId(booking.getId()) + .storeName(store.getStoreName()) + .date(booking.getBookingDate()) + .time(booking.getBookingTime()) + .partySize(booking.getPartySize()) + .status(booking.getStatus().name()) + .totalDeposit(totalDeposit) + .createdAt(booking.getCreatedAt()) + .tables(resultTableDTOS) + .paymentId(paymentInfo.paymentId()) + .orderId(paymentInfo.orderId()) + .build(); + } } From bc7d8c943f523ab1a681bb993b14e3dcdb8daa75 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 26 Jan 2026 18:13:55 +0900 Subject: [PATCH 09/30] =?UTF-8?q?[FEAT]=20:=20=EC=98=88=EC=95=BD=EC=83=9D?= =?UTF-8?q?=EC=84=B1=20=EC=9D=91=EB=8B=B5=20DTO=EC=97=90=20paymentID,=20or?= =?UTF-8?q?derID=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/booking/dto/response/BookingResponseDTO.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java index 9a188645..eb4cc2e5 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java @@ -45,7 +45,9 @@ public record CreateBookingResultDTO( Integer partySize, Integer totalDeposit, List tables, - LocalDateTime createdAt // 예약 생성 시간 + LocalDateTime createdAt, // 예약 생성 시간 + Long paymentId, // 결제 ID + String orderId // 주문 ID ){} @Builder From 495800455c4b0d087f6096d9d5a62b0129fbb646 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 26 Jan 2026 18:14:22 +0900 Subject: [PATCH 10/30] =?UTF-8?q?[CHORE]=20:=20=EA=B2=B0=EC=A0=9C=20?= =?UTF-8?q?=EC=9A=94=EC=B2=AD=20API=20=EC=8A=A4=EC=9B=A8=EA=B1=B0=20?= =?UTF-8?q?=EC=84=A4=EB=AA=85=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/domain/payment/controller/PaymentController.java | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/payment/controller/PaymentController.java b/src/main/java/com/eatsfine/eatsfine/domain/payment/controller/PaymentController.java index dc3d4a16..81179adf 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/payment/controller/PaymentController.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/payment/controller/PaymentController.java @@ -22,7 +22,9 @@ public class PaymentController { private final PaymentService paymentService; - @Operation(summary = "결제 요청", description = "예약 ID를 받아 주문 ID를 생성하고 결제 정보를 초기화합니다.") + @Operation(summary = "결제 요청", description = "예약 ID를 받아 주문 ID를 생성하고 결제 정보를 초기화합니다." + + "주의) 외부에서 이 API를 호출하지 않고 POST /api/v1/stores/{storeId}/bookings API 호출 후 " + + "내부적으로 이 API의 로직을 실행합니다.") @PostMapping("/request") public ApiResponse requestPayment( @RequestBody @Valid PaymentRequestDTO.RequestPaymentDTO dto) { From 02dd443602a16dbd7f7b06057e6a66140b15175a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 26 Jan 2026 18:15:27 +0900 Subject: [PATCH 11/30] =?UTF-8?q?[REFACTOR]=20:=20=EA=B2=B0=EC=A0=9C=20?= =?UTF-8?q?=EC=8A=B9=EC=9D=B8=20API=EC=97=90=EC=84=9C=20=EC=98=88=EC=95=BD?= =?UTF-8?q?=20=EC=83=81=ED=83=9C=EB=8F=84=20=EB=B3=80=EA=B2=BD=ED=95=98?= =?UTF-8?q?=EB=8F=84=EB=A1=9D=20=EC=BD=94=EB=93=9C=20=EA=B5=AC=EC=A1=B0=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/domain/payment/service/PaymentService.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java b/src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java index 4414afe2..8ac3d8c5 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java @@ -100,6 +100,10 @@ public PaymentResponseDTO.PaymentRequestResultDTO confirmPayment(PaymentConfirmD provider ); + // 예약 상태 확정으로 변경 + Booking booking = payment.getBooking(); + booking.confirm(); + return new PaymentResponseDTO.PaymentRequestResultDTO( payment.getId(), payment.getBooking().getId(), From fe1f3f1216c07c02e25d7a92b98a508cfd7350d3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 26 Jan 2026 18:15:56 +0900 Subject: [PATCH 12/30] =?UTF-8?q?[CHORE]=20:=20=EA=B2=B0=EC=A0=9C=20?= =?UTF-8?q?=EC=99=84=EB=A3=8C=20=EC=B2=98=EB=A6=AC=20api=20=EC=8A=A4?= =?UTF-8?q?=EC=9B=A8=EA=B1=B0=20=EC=84=A4=EC=A0=95=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/booking/controller/BookingController.java | 5 +++-- 1 file changed, 3 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java index 44a8a67a..dc7aa29a 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java @@ -61,8 +61,9 @@ public ApiResponse createBooking( return ApiResponse.onSuccess(bookingCommandService.createBooking(user, storeId, dto)); } - @Operation(summary = "결제 완료 처리", - description = "결제 완료 후 결제 정보를 입력받아 예약 상태를 업데이트합니다.") + @Operation(summary = "예약 완료 처리", + description = "결제 완료 후 결제 정보를 입력받아 예약 상태를 업데이트합니다. 주의) 외부에서 이 API를 호출하지 않고 " + + "POST /api/v1/payments/confirm API 호출 후 내부적으로 이 API의 로직을 실행합니다.") @PatchMapping("/bookings/{bookingId}/payments-confirm") public ApiResponse confirmPayment( @PathVariable Long bookingId, From b5fda314117c09bfa8ec595dc0c183705fddb30f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 26 Jan 2026 18:18:05 +0900 Subject: [PATCH 13/30] =?UTF-8?q?[REFACTOR]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20api=EC=97=90=EC=84=9C=20=EB=82=B4=EB=B6=80?= =?UTF-8?q?=EC=A0=81=EC=9C=BC=EB=A1=9C=20=EA=B2=B0=EC=A0=9C=20=EC=9A=94?= =?UTF-8?q?=EC=B2=AD=20=EB=A1=9C=EC=A7=81=EA=B9=8C=EC=A7=80=20=EC=A7=84?= =?UTF-8?q?=ED=96=89=ED=95=98=EB=8F=84=EB=A1=9D=20=EC=BD=94=EB=93=9C=20?= =?UTF-8?q?=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/BookingCommandServiceImpl.java | 27 ++++++++++--------- 1 file changed, 14 insertions(+), 13 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java index ed7b98ac..65e81686 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java @@ -1,5 +1,6 @@ package com.eatsfine.eatsfine.domain.booking.service; +import com.eatsfine.eatsfine.domain.booking.converter.BookingConverter; import com.eatsfine.eatsfine.domain.booking.dto.request.BookingRequestDTO; import com.eatsfine.eatsfine.domain.booking.dto.response.BookingResponseDTO; import com.eatsfine.eatsfine.domain.booking.entity.Booking; @@ -8,6 +9,9 @@ import com.eatsfine.eatsfine.domain.booking.exception.BookingException; import com.eatsfine.eatsfine.domain.booking.repository.BookingRepository; import com.eatsfine.eatsfine.domain.booking.status.BookingErrorStatus; +import com.eatsfine.eatsfine.domain.payment.dto.request.PaymentRequestDTO; +import com.eatsfine.eatsfine.domain.payment.dto.response.PaymentResponseDTO; +import com.eatsfine.eatsfine.domain.payment.service.PaymentService; import com.eatsfine.eatsfine.domain.store.entity.Store; import com.eatsfine.eatsfine.domain.store.exception.StoreException; import com.eatsfine.eatsfine.domain.store.repository.StoreRepository; @@ -32,6 +36,7 @@ public class BookingCommandServiceImpl implements BookingCommandService{ private final StoreRepository storeRepository; private final StoreTableRepository storeTableRepository; private final BookingRepository bookingRepository; + private final PaymentService paymentService; @Override @Transactional @@ -50,7 +55,7 @@ public BookingResponseDTO.CreateBookingResultDTO createBooking(User user, Long s } } - int totalDeposit = store.getMinPrice() * dto.partySize(); // 자세한 예약금 로직은 추후 수정 + int totalDeposit = store.getMinPrice() * store.getDepositRate().getPercent() / 100; // 자세한 예약금 로직은 추후 수정 Booking booking = Booking.builder() @@ -68,6 +73,10 @@ public BookingResponseDTO.CreateBookingResultDTO createBooking(User user, Long s Booking savedBooking = bookingRepository.save(booking); + // 결제 대기 데이터 생성 (내부 서비스 호출) + PaymentRequestDTO.RequestPaymentDTO paymentRequest = new PaymentRequestDTO.RequestPaymentDTO(savedBooking.getId()); + PaymentResponseDTO.PaymentRequestResultDTO paymentInfo = paymentService.requestPayment(paymentRequest); + //BookingResponseDTO.BookingResultTableDTO로 변환 List resultTableDTOS = savedBooking.getBookingTables().stream() @@ -80,17 +89,8 @@ public BookingResponseDTO.CreateBookingResultDTO createBooking(User user, Long s .build()) .toList(); - return BookingResponseDTO.CreateBookingResultDTO.builder() - .bookingId(savedBooking.getId()) - .storeName(store.getStoreName()) - .date(savedBooking.getBookingDate()) - .time(savedBooking.getBookingTime()) - .partySize(savedBooking.getPartySize()) - .status(savedBooking.getStatus().name()) - .totalDeposit(totalDeposit) - .createdAt(savedBooking.getCreatedAt()) - .tables(resultTableDTOS) - .build(); + + return BookingConverter.toCreateBookingResultDTO(savedBooking,store,totalDeposit, resultTableDTOS,paymentInfo); } @Override @@ -113,7 +113,6 @@ public BookingResponseDTO.ConfirmPaymentResultDTO confirmPayment(Long bookingId, //예약 상태 확정으로 변경 booking.confirm(); - return BookingResponseDTO.ConfirmPaymentResultDTO.builder() .bookingId(booking.getId()) .status(booking.getStatus().name()) @@ -136,6 +135,8 @@ public BookingResponseDTO.CancelBookingResultDTO cancelBooking(Long bookingId, B // TODO 환불 로직 + + //예약 상태 변경 booking.cancel(dto.reason()); return BookingResponseDTO.CancelBookingResultDTO.builder() From 03f56a0371f3db56ccd225b9bc77eae194854339 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 26 Jan 2026 19:13:40 +0900 Subject: [PATCH 14/30] =?UTF-8?q?[FEAT]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=97=94=ED=8B=B0=ED=8B=B0=EC=97=90=EC=84=9C=20=EA=B2=B0?= =?UTF-8?q?=EC=A0=9C=20=EC=99=84=EB=A3=8C=EB=90=9C=20=EA=B2=B0=EC=A0=9C?= =?UTF-8?q?=ED=82=A4=EB=A5=BC=20=EC=B0=BE=EB=8A=94=20=ED=8E=B8=EC=9D=98=20?= =?UTF-8?q?=EB=A9=94=EC=84=9C=EB=93=9C=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/domain/booking/entity/Booking.java | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java index f3a7b71f..2cbd3973 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java @@ -3,6 +3,9 @@ import com.eatsfine.eatsfine.domain.booking.entity.mapping.BookingTable; import com.eatsfine.eatsfine.domain.booking.enums.BookingStatus; import com.eatsfine.eatsfine.domain.payment.entity.Payment; +import com.eatsfine.eatsfine.domain.payment.enums.PaymentStatus; +import com.eatsfine.eatsfine.domain.payment.exception.PaymentException; +import com.eatsfine.eatsfine.domain.payment.status.PaymentErrorStatus; import com.eatsfine.eatsfine.domain.store.entity.Store; import com.eatsfine.eatsfine.domain.storetable.entity.StoreTable; import com.eatsfine.eatsfine.domain.user.entity.User; @@ -85,4 +88,13 @@ public void cancel(String cancelReason) this.cancelReason = cancelReason; } + //예약과 관련된 결제 중 결제 완료된 결제키 조회 + public String getSuccessPaymentKey() { + return this.payments.stream() + .filter(p -> p.getPaymentStatus() == PaymentStatus.COMPLETED) + .map(Payment::getPaymentKey) + .findFirst() + .orElseThrow(() -> new PaymentException(PaymentErrorStatus._PAYMENT_NOT_FOUND)); + } + } From 950a46f19fc8d04077b5c7900eebd4c3ec1c346e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Mon, 26 Jan 2026 19:14:26 +0900 Subject: [PATCH 15/30] =?UTF-8?q?[FEAT]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=B7=A8=EC=86=8C=EC=8B=9C=20=ED=99=98=EB=B6=88=EA=B9=8C?= =?UTF-8?q?=EC=A7=80=20=EC=97=B0=EB=8F=99=EB=90=98=EB=8A=94=20=EB=B9=84?= =?UTF-8?q?=EC=A6=88=EB=8B=88=EC=8A=A4=EB=A1=9C=EC=A7=81=20=EA=B0=9C?= =?UTF-8?q?=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/BookingCommandServiceImpl.java | 17 ++++++++--------- 1 file changed, 8 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java index 65e81686..be96563d 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java @@ -11,7 +11,11 @@ import com.eatsfine.eatsfine.domain.booking.status.BookingErrorStatus; import com.eatsfine.eatsfine.domain.payment.dto.request.PaymentRequestDTO; import com.eatsfine.eatsfine.domain.payment.dto.response.PaymentResponseDTO; +import com.eatsfine.eatsfine.domain.payment.entity.Payment; +import com.eatsfine.eatsfine.domain.payment.enums.PaymentStatus; +import com.eatsfine.eatsfine.domain.payment.exception.PaymentException; import com.eatsfine.eatsfine.domain.payment.service.PaymentService; +import com.eatsfine.eatsfine.domain.payment.status.PaymentErrorStatus; import com.eatsfine.eatsfine.domain.store.entity.Store; import com.eatsfine.eatsfine.domain.store.exception.StoreException; import com.eatsfine.eatsfine.domain.store.repository.StoreRepository; @@ -127,16 +131,11 @@ public BookingResponseDTO.CancelBookingResultDTO cancelBooking(Long bookingId, B Booking booking = bookingRepository.findById(bookingId) .orElseThrow(() -> new BookingException(BookingErrorStatus._BOOKING_NOT_FOUND)); + // 예약 중 결제 완료된 결제의 결제키 이용 환불 로직 진행 + PaymentRequestDTO.CancelPaymentDTO cancelDto = new PaymentRequestDTO.CancelPaymentDTO(dto.reason()); + paymentService.cancelPayment(booking.getSuccessPaymentKey(), cancelDto); - // 이미 취소된 예약인지 최종 확인 - if(booking.getStatus().equals(BookingStatus.CANCELED)) { - throw new BookingException(BookingErrorStatus._ALREADY_CONFIRMED); - } - - // TODO 환불 로직 - - - //예약 상태 변경 + //예약 상태 취소로 변경 booking.cancel(dto.reason()); return BookingResponseDTO.CancelBookingResultDTO.builder() From 893ddb059922e792acc39c7570c1e14ba3a445ee Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Wed, 28 Jan 2026 15:56:35 +0900 Subject: [PATCH 16/30] =?UTF-8?q?[FEAT]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EC=9D=91=EB=8B=B5=20DTO=20=EA=B0=9C?= =?UTF-8?q?=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/BookingResponseDTO.java | 25 +++++++++++++++++++ 1 file changed, 25 insertions(+) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java index eb4cc2e5..f79b0323 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java @@ -75,4 +75,29 @@ public record CancelBookingResultDTO( LocalDateTime canceledAt, // 취소 시간 Integer refundAmount // 환불 금액 ){} + + @Builder + public record BookingPreviewListDTO( + List bookingList, + Integer listSize, + Integer totalPage, + Long totalElements, + Boolean isFirst, + Boolean isLast + + ){} + + @Builder + public record BookingPreviewDTO( + Long bookingId, + String storeName, + String storeAddress, + LocalDate bookingDate, + LocalTime bookingTime, + Integer partySize, + String tableNumbers, + Integer amount, + String paymentMethod, + String status + ){} } From ccda430ba9ba5dd9a918d754908e1a2895c0e6ae Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Wed, 28 Jan 2026 15:57:11 +0900 Subject: [PATCH 17/30] =?UTF-8?q?[FEAT]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20Repository=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../booking/repository/BookingRepository.java | 14 ++++++++++++++ 1 file changed, 14 insertions(+) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/repository/BookingRepository.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/repository/BookingRepository.java index acb5b0a5..43829436 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/repository/BookingRepository.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/repository/BookingRepository.java @@ -1,14 +1,20 @@ package com.eatsfine.eatsfine.domain.booking.repository; import com.eatsfine.eatsfine.domain.booking.entity.Booking; +import com.eatsfine.eatsfine.domain.booking.enums.BookingStatus; +import com.eatsfine.eatsfine.domain.user.entity.User; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.data.repository.query.Param; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; +import org.springframework.stereotype.Repository; import java.time.LocalDate; import java.time.LocalTime; import java.util.List; +@Repository public interface BookingRepository extends JpaRepository { @@ -34,4 +40,12 @@ public interface BookingRepository extends JpaRepository { "AND b.bookingTime = :time " + "AND b.status IN ('CONFIRMED', 'PENDING')") boolean existsBookingByTableAndDateTime(@Param("tableId") Long tableId, @Param("date") LocalDate date, @Param("time") LocalTime time); + + + // 1. 특정 유저의 모든 예약을 최신순으로 페이징 조회 + @Query("select b from Booking b join fetch b.store where b.user = :user") + Page findAllByUser(@Param("user") User user, Pageable pageable); + + @Query("Select b from Booking b join fetch b.store where b.user = :user and b.status = :status") + Page findAllByUserAndStatus(@Param("user") User user, @Param("status") BookingStatus status, Pageable pageable); } From e53a8922ddfcaeb60a73e663073a70f68089ddb2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Wed, 28 Jan 2026 15:58:14 +0900 Subject: [PATCH 18/30] =?UTF-8?q?[FEAT]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20Service=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../booking/service/BookingQueryService.java | 3 + .../service/BookingQueryServiceImpl.java | 60 +++++++++++++++++++ 2 files changed, 63 insertions(+) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingQueryService.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingQueryService.java index cc9df066..02d05e27 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingQueryService.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingQueryService.java @@ -2,6 +2,7 @@ import com.eatsfine.eatsfine.domain.booking.dto.request.BookingRequestDTO; import com.eatsfine.eatsfine.domain.booking.dto.response.BookingResponseDTO; +import com.eatsfine.eatsfine.domain.user.entity.User; import java.time.LocalDate; import java.time.LocalTime; @@ -11,4 +12,6 @@ public interface BookingQueryService { BookingResponseDTO.TimeSlotListDTO getAvailableTimeSlots(Long storeId, BookingRequestDTO.GetAvailableTimeDTO dto); BookingResponseDTO.AvailableTableListDTO getAvailableTables(Long storeId, BookingRequestDTO.GetAvailableTableDTO dto); + + BookingResponseDTO.BookingPreviewListDTO getBookingList(User user, String status, Integer page); } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingQueryServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingQueryServiceImpl.java index 62192a5d..f522e724 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingQueryServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingQueryServiceImpl.java @@ -2,17 +2,25 @@ import com.eatsfine.eatsfine.domain.booking.dto.request.BookingRequestDTO; import com.eatsfine.eatsfine.domain.booking.dto.response.BookingResponseDTO; +import com.eatsfine.eatsfine.domain.booking.entity.Booking; +import com.eatsfine.eatsfine.domain.booking.enums.BookingStatus; import com.eatsfine.eatsfine.domain.booking.exception.BookingException; import com.eatsfine.eatsfine.domain.booking.repository.BookingRepository; import com.eatsfine.eatsfine.domain.booking.status.BookingErrorStatus; import com.eatsfine.eatsfine.domain.businesshours.entity.BusinessHours; +import com.eatsfine.eatsfine.domain.payment.entity.Payment; +import com.eatsfine.eatsfine.domain.payment.enums.PaymentStatus; import com.eatsfine.eatsfine.domain.store.entity.Store; import com.eatsfine.eatsfine.domain.store.repository.StoreRepository; import com.eatsfine.eatsfine.domain.store.status.StoreErrorStatus; import com.eatsfine.eatsfine.domain.storetable.entity.StoreTable; import com.eatsfine.eatsfine.domain.table_layout.entity.TableLayout; import com.eatsfine.eatsfine.domain.table_layout.repository.TableLayoutRepository; +import com.eatsfine.eatsfine.domain.user.entity.User; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Sort; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -20,6 +28,7 @@ import java.time.LocalTime; import java.util.ArrayList; import java.util.List; +import java.util.stream.Collectors; @Service @RequiredArgsConstructor @@ -127,4 +136,55 @@ public BookingResponseDTO.AvailableTableListDTO getAvailableTables(Long storeId, .tables(availableTables) .build(); } + + @Override + public BookingResponseDTO.BookingPreviewListDTO getBookingList(User user, String status, Integer page) { + PageRequest pageRequest = PageRequest.of(page, 10, Sort.by("bookingDate").descending()); + + Page bookingPage; + + if(status == null || status.equals("ALL")) { + bookingPage = bookingRepository.findAllByUser(user, pageRequest); + } else { + BookingStatus bookingStatus = BookingStatus.valueOf(status); + bookingPage = bookingRepository.findAllByUserAndStatus(user, bookingStatus, pageRequest); + } + + List bookingPreviewDTOList = bookingPage.getContent().stream() + .map(booking -> { + + // 성공한 결제 정보 추출 (1:N 대응) + Payment successPayment = booking.getPayments().stream() + .filter(p -> p.getPaymentStatus() == PaymentStatus.COMPLETED || p.getPaymentStatus() == PaymentStatus.REFUNDED) + .findFirst() + .orElse(null); + + // 테이블 번호들을 하나의 문자열로 합치기 + String tableNumbers = booking.getBookingTables().stream() + .map(bt -> bt.getStoreTable().getTableNumber().toString()) + .collect(Collectors.joining(", ")); + + return BookingResponseDTO.BookingPreviewDTO.builder() + .bookingId(booking.getId()) + .storeName(booking.getStore().getStoreName()) + .storeAddress(booking.getStore().getAddress()) + .bookingDate(booking.getBookingDate()) + .bookingTime(booking.getBookingTime()) + .partySize(booking.getPartySize()) + .tableNumbers(tableNumbers + "번") + .amount(successPayment != null ? successPayment.getAmount() : booking.getDepositAmount()) + .paymentMethod(successPayment != null ? successPayment.getPaymentMethod().name() : "미결제") + .status(booking.getStatus().name()) + .build(); + }).collect(Collectors.toList()); + + return BookingResponseDTO.BookingPreviewListDTO.builder() + .isLast(bookingPage.isLast()) + .isFirst(bookingPage.isFirst()) + .totalPage(bookingPage.getTotalPages()) + .totalElements(bookingPage.getTotalElements()) + .listSize(bookingPreviewDTOList.size()) + .bookingList(bookingPreviewDTOList) + .build(); + } } From 70bd0f7047d9c7d41e0f866d6d8e50276fde8b3e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Wed, 28 Jan 2026 15:58:41 +0900 Subject: [PATCH 19/30] =?UTF-8?q?[FEAT]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20Controller=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../booking/controller/BookingController.java | 13 +++++++++++++ 1 file changed, 13 insertions(+) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java index dc7aa29a..861eac96 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java @@ -84,4 +84,17 @@ public ApiResponse cancelBooking( bookingCommandService.cancelBooking(bookingId, dto)); } + + @Operation(summary = "예약 내역 조회", + description = "마이페이지에서 나의 예약 내역을 조회합니다.") + @GetMapping("/users/bookings") + public ApiResponse getMyBookings( + @RequestParam(name = "status", required = false) String status, + @RequestParam(name = "page", defaultValue = "1") Integer page + ) { + User user = userRepository.findById(1L).orElseThrow(); // 임시로 임의의 유저 사용 + + return ApiResponse.of(BookingSuccessStatus._BOOKING_FOUND, + bookingQueryService.getBookingList(user, status, page)); + } } From a69fbb79290a93a50d4428b0b6d22b8bff4eeb54 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Wed, 28 Jan 2026 15:59:24 +0900 Subject: [PATCH 20/30] =?UTF-8?q?[CHORE]=20:=20application-local.yml=20?= =?UTF-8?q?=ED=8C=8C=EC=9D=BC=20=EC=84=A4=EC=A0=95=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/resources/application-local.yml | 7 +++++++ 1 file changed, 7 insertions(+) diff --git a/src/main/resources/application-local.yml b/src/main/resources/application-local.yml index 6feee07c..a5a7c16b 100644 --- a/src/main/resources/application-local.yml +++ b/src/main/resources/application-local.yml @@ -27,3 +27,10 @@ spring: payment: toss: widget-secret-key: test_gsk_docs_OaPz8L5KdmQXkzRz3y47BMw6 + +cloud: + aws: + region: ap-northeast-2 + s3: + bucket: eatsfine-images + base-url: https://eatsfine-images.s3.ap-northeast-2.amazonaws.com \ No newline at end of file From 20be30d6aba203a2c1de8d70d2754e1b15e86458 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Wed, 28 Jan 2026 16:55:30 +0900 Subject: [PATCH 21/30] =?UTF-8?q?[FEAT]=20:=20BookingMenu=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EA=B0=9C=EB=B0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/booking/entity/Booking.java | 15 +++++++++ .../booking/entity/mapping/BookingMenu.java | 32 +++++++++++++++++++ 2 files changed, 47 insertions(+) create mode 100644 src/main/java/com/eatsfine/eatsfine/domain/booking/entity/mapping/BookingMenu.java diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java index 2cbd3973..00393ab5 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java @@ -1,5 +1,6 @@ package com.eatsfine.eatsfine.domain.booking.entity; +import com.eatsfine.eatsfine.domain.booking.entity.mapping.BookingMenu; import com.eatsfine.eatsfine.domain.booking.entity.mapping.BookingTable; import com.eatsfine.eatsfine.domain.booking.enums.BookingStatus; import com.eatsfine.eatsfine.domain.payment.entity.Payment; @@ -66,6 +67,17 @@ public class Booking extends BaseEntity { @Column(name = "status", length = 20, nullable = false) private BookingStatus status; + @Builder.Default + @OneToMany(mappedBy = "booking", cascade = CascadeType.ALL, orphanRemoval = true) + private List bookingMenus = new ArrayList<>(); + + public void addBookingMenu(BookingMenu bookingMenu) { + this.bookingMenus.add(bookingMenu); + if (bookingMenu.getBooking() != this) { + bookingMenu.confirmBooking(this); + } + } + public void addBookingTable(StoreTable storeTable) { BookingTable bookingTable = BookingTable.builder() .booking(this) @@ -97,4 +109,7 @@ public String getSuccessPaymentKey() { .orElseThrow(() -> new PaymentException(PaymentErrorStatus._PAYMENT_NOT_FOUND)); } + public void setDepositAmount(int totalDeposit) { + this.depositAmount = totalDeposit; + } } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/mapping/BookingMenu.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/mapping/BookingMenu.java new file mode 100644 index 00000000..19cb469f --- /dev/null +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/mapping/BookingMenu.java @@ -0,0 +1,32 @@ +package com.eatsfine.eatsfine.domain.booking.entity.mapping; + +import com.eatsfine.eatsfine.domain.booking.entity.Booking; +import com.eatsfine.eatsfine.domain.menu.entity.Menu; +import jakarta.persistence.*; +import lombok.*; + +@Entity +@NoArgsConstructor(access = AccessLevel.PROTECTED) +@AllArgsConstructor +@Getter +@Builder +public class BookingMenu { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + private Integer quantity; + + private Integer price; + + @ManyToOne(fetch = FetchType.LAZY) + private Booking booking; + + @ManyToOne(fetch = FetchType.LAZY) + private Menu menu; + + public void confirmBooking(Booking booking) { + this.booking = booking; + } + +} From d534aee22a5487e9d2d28767c6fd3e447924791e Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Wed, 28 Jan 2026 16:56:01 +0900 Subject: [PATCH 22/30] =?UTF-8?q?[FEAT]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=9A=94=EC=B2=AD=20DTO=20=EB=B3=80?= =?UTF-8?q?=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/booking/dto/request/BookingRequestDTO.java | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/request/BookingRequestDTO.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/request/BookingRequestDTO.java index de52b9f5..42288939 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/request/BookingRequestDTO.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/request/BookingRequestDTO.java @@ -35,7 +35,13 @@ public record CreateBookingDTO( @NotNull @DateTimeFormat(pattern = "HH:mm") LocalTime time, @NotNull @Min(1) Integer partySize, @NotNull List tableIds, - @NotNull boolean isSplitAccepted + @NotNull boolean isSplitAccepted, + @NotNull List menuItems + ){} + + public record MenuOrderDto( + @NotNull Long menuId, + @NotNull @Min(1) Integer quantity ){} public record PaymentConfirmDTO( From db246b5f5e5fb6e7e5665733a10a93dada51fd4d Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Wed, 28 Jan 2026 16:57:00 +0900 Subject: [PATCH 23/30] =?UTF-8?q?[FEAT]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=83=9D=EC=84=B1=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EB=B3=80=EA=B2=BD(=EB=A9=94=EB=89=B4=20=EA=B3=A0?= =?UTF-8?q?=EB=A0=A4)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/BookingCommandServiceImpl.java | 30 +++++++++++++++++-- 1 file changed, 27 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java index be96563d..8eb531e7 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java @@ -4,11 +4,14 @@ import com.eatsfine.eatsfine.domain.booking.dto.request.BookingRequestDTO; import com.eatsfine.eatsfine.domain.booking.dto.response.BookingResponseDTO; import com.eatsfine.eatsfine.domain.booking.entity.Booking; +import com.eatsfine.eatsfine.domain.booking.entity.mapping.BookingMenu; import com.eatsfine.eatsfine.domain.booking.entity.mapping.BookingTable; import com.eatsfine.eatsfine.domain.booking.enums.BookingStatus; import com.eatsfine.eatsfine.domain.booking.exception.BookingException; import com.eatsfine.eatsfine.domain.booking.repository.BookingRepository; import com.eatsfine.eatsfine.domain.booking.status.BookingErrorStatus; +import com.eatsfine.eatsfine.domain.menu.entity.Menu; +import com.eatsfine.eatsfine.domain.menu.repository.MenuRepository; import com.eatsfine.eatsfine.domain.payment.dto.request.PaymentRequestDTO; import com.eatsfine.eatsfine.domain.payment.dto.response.PaymentResponseDTO; import com.eatsfine.eatsfine.domain.payment.entity.Payment; @@ -31,6 +34,7 @@ import java.time.LocalDate; import java.time.LocalTime; import java.util.List; +import java.util.stream.Collectors; import java.util.stream.Stream; @Service @@ -41,6 +45,7 @@ public class BookingCommandServiceImpl implements BookingCommandService{ private final StoreTableRepository storeTableRepository; private final BookingRepository bookingRepository; private final PaymentService paymentService; + private final MenuRepository menuRepository; @Override @Transactional @@ -59,11 +64,8 @@ public BookingResponseDTO.CreateBookingResultDTO createBooking(User user, Long s } } - int totalDeposit = store.getMinPrice() * store.getDepositRate().getPercent() / 100; // 자세한 예약금 로직은 추후 수정 - Booking booking = Booking.builder() - .depositAmount(totalDeposit) .bookingDate(dto.date()) .bookingTime(dto.time()) .partySize(dto.partySize()) @@ -75,6 +77,28 @@ public BookingResponseDTO.CreateBookingResultDTO createBooking(User user, Long s selectedTables.forEach(booking::addBookingTable); + + // 예약한 메뉴들 저장 및 총 메뉴 가격 계산 + int totalMenuPrice = 0; + for (BookingRequestDTO.MenuOrderDto menuItem : dto.menuItems()) { + Menu menu = menuRepository.findById(menuItem.menuId()) + .orElseThrow(() -> new StoreException(StoreErrorStatus._STORE_NOT_FOUND));//차후 수정 + + BookingMenu bookingMenu = BookingMenu.builder() + .quantity(menuItem.quantity()) + .menu(menu) + .booking(booking) + .price(menu.getPrice()) + .build(); + + booking.addBookingMenu(bookingMenu); + totalMenuPrice += menu.getPrice() * menuItem.quantity(); + } + + // 총 예약금 계산 ( 전체 메뉴 가격 * 가게의 예약금 비율 ) + int totalDeposit = (int)(totalMenuPrice * store.getDepositRate().getPercent() / 100); + booking.setDepositAmount(totalDeposit); + Booking savedBooking = bookingRepository.save(booking); // 결제 대기 데이터 생성 (내부 서비스 호출) From 75033a6da1643d3e8d68518f80fa7d9b9a1d9da3 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Thu, 29 Jan 2026 17:13:36 +0900 Subject: [PATCH 24/30] =?UTF-8?q?[FIX]=20:=20=EC=98=88=EC=95=BD=EA=B8=88,?= =?UTF-8?q?=20=EA=B2=B0=EC=A0=9C=EA=B8=88=EC=95=A1=20=ED=83=80=EC=9E=85=20?= =?UTF-8?q?Integer->Decimal=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/booking/converter/BookingConverter.java | 3 ++- .../booking/dto/response/BookingResponseDTO.java | 9 +++++---- .../eatsfine/domain/booking/entity/Booking.java | 5 +++-- .../domain/booking/entity/mapping/BookingMenu.java | 4 +++- .../booking/service/BookingCommandServiceImpl.java | 14 +++++++++++--- .../payment/dto/request/PaymentConfirmDTO.java | 4 +++- .../payment/dto/response/PaymentResponseDTO.java | 7 ++++--- .../eatsfine/domain/payment/entity/Payment.java | 3 ++- .../domain/payment/service/PaymentService.java | 6 +++--- 9 files changed, 36 insertions(+), 19 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/converter/BookingConverter.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/converter/BookingConverter.java index 6eac1265..a3107088 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/converter/BookingConverter.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/converter/BookingConverter.java @@ -5,12 +5,13 @@ import com.eatsfine.eatsfine.domain.payment.dto.response.PaymentResponseDTO; import com.eatsfine.eatsfine.domain.store.entity.Store; +import java.math.BigDecimal; import java.util.List; public class BookingConverter { public static BookingResponseDTO.CreateBookingResultDTO toCreateBookingResultDTO( - Booking booking, Store store, int totalDeposit, + Booking booking, Store store, BigDecimal totalDeposit, List resultTableDTOS, PaymentResponseDTO.PaymentRequestResultDTO paymentInfo) { diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java index f79b0323..a3333706 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/dto/response/BookingResponseDTO.java @@ -3,6 +3,7 @@ import com.eatsfine.eatsfine.domain.booking.enums.BookingStatus; import lombok.Builder; +import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalDateTime; import java.time.LocalTime; @@ -43,7 +44,7 @@ public record CreateBookingResultDTO( LocalDate date, LocalTime time, Integer partySize, - Integer totalDeposit, + BigDecimal totalDeposit, List tables, LocalDateTime createdAt, // 예약 생성 시간 Long paymentId, // 결제 ID @@ -64,7 +65,7 @@ public record ConfirmPaymentResultDTO( Long bookingId, String status, // CONFIRMED String paymentKey, // PG사 결제 키 - Integer amount // 최종 결제 금액 + BigDecimal amount // 최종 결제 금액 ){} @Builder @@ -73,7 +74,7 @@ public record CancelBookingResultDTO( String status, // CANCELED String cancelReason, // 취소 사유 LocalDateTime canceledAt, // 취소 시간 - Integer refundAmount // 환불 금액 + BigDecimal refundAmount // 환불 금액 ){} @Builder @@ -96,7 +97,7 @@ public record BookingPreviewDTO( LocalTime bookingTime, Integer partySize, String tableNumbers, - Integer amount, + BigDecimal amount, String paymentMethod, String status ){} diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java index 00393ab5..42f94925 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java @@ -14,6 +14,7 @@ import jakarta.persistence.*; import lombok.*; +import java.math.BigDecimal; import java.time.LocalDate; import java.time.LocalTime; import java.util.ArrayList; @@ -86,7 +87,7 @@ public void addBookingTable(StoreTable storeTable) { this.bookingTables.add(bookingTable); } - private Integer depositAmount; + private BigDecimal depositAmount; private String cancelReason; @@ -109,7 +110,7 @@ public String getSuccessPaymentKey() { .orElseThrow(() -> new PaymentException(PaymentErrorStatus._PAYMENT_NOT_FOUND)); } - public void setDepositAmount(int totalDeposit) { + public void setDepositAmount(BigDecimal totalDeposit) { this.depositAmount = totalDeposit; } } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/mapping/BookingMenu.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/mapping/BookingMenu.java index 19cb469f..aefc0374 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/mapping/BookingMenu.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/entity/mapping/BookingMenu.java @@ -5,6 +5,8 @@ import jakarta.persistence.*; import lombok.*; +import java.math.BigDecimal; + @Entity @NoArgsConstructor(access = AccessLevel.PROTECTED) @AllArgsConstructor @@ -17,7 +19,7 @@ public class BookingMenu { private Integer quantity; - private Integer price; + private BigDecimal price; @ManyToOne(fetch = FetchType.LAZY) private Booking booking; diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java index 8eb531e7..665a30f0 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java @@ -31,6 +31,8 @@ import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import java.math.BigDecimal; +import java.math.RoundingMode; import java.time.LocalDate; import java.time.LocalTime; import java.util.List; @@ -79,7 +81,7 @@ public BookingResponseDTO.CreateBookingResultDTO createBooking(User user, Long s // 예약한 메뉴들 저장 및 총 메뉴 가격 계산 - int totalMenuPrice = 0; + BigDecimal itemTotalPrice = BigDecimal.ZERO; for (BookingRequestDTO.MenuOrderDto menuItem : dto.menuItems()) { Menu menu = menuRepository.findById(menuItem.menuId()) .orElseThrow(() -> new StoreException(StoreErrorStatus._STORE_NOT_FOUND));//차후 수정 @@ -92,11 +94,17 @@ public BookingResponseDTO.CreateBookingResultDTO createBooking(User user, Long s .build(); booking.addBookingMenu(bookingMenu); - totalMenuPrice += menu.getPrice() * menuItem.quantity(); + + BigDecimal itemQuantity = BigDecimal.valueOf(menuItem.quantity()); + itemTotalPrice = menu.getPrice().multiply(itemQuantity); } // 총 예약금 계산 ( 전체 메뉴 가격 * 가게의 예약금 비율 ) - int totalDeposit = (int)(totalMenuPrice * store.getDepositRate().getPercent() / 100); + BigDecimal depositRate = BigDecimal.valueOf(store.getDepositRate().getPercent()); + BigDecimal hundred = BigDecimal.valueOf(100); + BigDecimal totalDeposit = itemTotalPrice + .multiply(depositRate) + .divide(hundred, 0, RoundingMode.HALF_UP); booking.setDepositAmount(totalDeposit); Booking savedBooking = bookingRepository.save(booking); diff --git a/src/main/java/com/eatsfine/eatsfine/domain/payment/dto/request/PaymentConfirmDTO.java b/src/main/java/com/eatsfine/eatsfine/domain/payment/dto/request/PaymentConfirmDTO.java index 863c4a69..9c936a68 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/payment/dto/request/PaymentConfirmDTO.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/payment/dto/request/PaymentConfirmDTO.java @@ -3,9 +3,11 @@ import jakarta.validation.constraints.NotNull; import lombok.Builder; +import java.math.BigDecimal; + @Builder public record PaymentConfirmDTO( @NotNull String paymentKey, @NotNull String orderId, - @NotNull Integer amount) { + @NotNull BigDecimal amount) { } diff --git a/src/main/java/com/eatsfine/eatsfine/domain/payment/dto/response/PaymentResponseDTO.java b/src/main/java/com/eatsfine/eatsfine/domain/payment/dto/response/PaymentResponseDTO.java index 9a006481..eaca5109 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/payment/dto/response/PaymentResponseDTO.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/payment/dto/response/PaymentResponseDTO.java @@ -3,6 +3,7 @@ import com.eatsfine.eatsfine.domain.payment.enums.PaymentMethod; import com.eatsfine.eatsfine.domain.payment.enums.PaymentStatus; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.List; @@ -12,7 +13,7 @@ public record PaymentRequestResultDTO( Long paymentId, Long bookingId, String orderId, - Integer amount, + BigDecimal amount, LocalDateTime requestedAt) { } @@ -28,7 +29,7 @@ public record PaymentHistoryResultDTO( Long paymentId, Long bookingId, String restaurantName, - Integer amount, + BigDecimal amount, String paymentType, String paymentMethod, String paymentProvider, @@ -53,7 +54,7 @@ public record PaymentDetailResultDTO( String restaurantName, String paymentMethod, String paymentProvider, - Integer amount, + BigDecimal amount, String paymentType, String status, LocalDateTime requestedAt, diff --git a/src/main/java/com/eatsfine/eatsfine/domain/payment/entity/Payment.java b/src/main/java/com/eatsfine/eatsfine/domain/payment/entity/Payment.java index ebf44154..d0eec138 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/payment/entity/Payment.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/payment/entity/Payment.java @@ -9,6 +9,7 @@ import jakarta.persistence.*; import lombok.*; +import java.math.BigDecimal; import java.time.LocalDateTime; @Entity @@ -32,7 +33,7 @@ public class Payment extends BaseEntity { private String orderId; @Column(name = "amount", nullable = false) - private Integer amount; + private BigDecimal amount; @Column(name = "payment_key") private String paymentKey; diff --git a/src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java b/src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java index ba7a2fa6..0a9f1f77 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java @@ -24,6 +24,7 @@ import org.springframework.transaction.annotation.Transactional; import org.springframework.web.client.RestClient; +import java.math.BigDecimal; import java.time.LocalDateTime; import java.util.UUID; import java.util.List; @@ -47,7 +48,7 @@ public PaymentResponseDTO.PaymentRequestResultDTO requestPayment(PaymentRequestD String orderId = UUID.randomUUID().toString(); // 예약금 검증 - if (booking.getDepositAmount() == null || booking.getDepositAmount() <= 0) { + if (booking.getDepositAmount() == null || booking.getDepositAmount().compareTo(BigDecimal.ZERO) <= 0) { throw new PaymentException(PaymentErrorStatus._PAYMENT_INVALID_DEPOSIT); } @@ -75,11 +76,10 @@ public PaymentResponseDTO.PaymentRequestResultDTO confirmPayment(PaymentConfirmD Payment payment = paymentRepository.findByOrderId(dto.orderId()) .orElseThrow(() -> new PaymentException(PaymentErrorStatus._PAYMENT_NOT_FOUND)); - if (!payment.getAmount().equals(dto.amount())) { + if (payment.getAmount().compareTo(dto.amount()) != 0) { payment.failPayment(); throw new PaymentException(PaymentErrorStatus._PAYMENT_INVALID_AMOUNT); } - // 토스 API 호출 TossPaymentResponse response; try { From c08f6214191e20af45a3c5abe31e92258b06444a Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Thu, 29 Jan 2026 17:13:58 +0900 Subject: [PATCH 25/30] =?UTF-8?q?[FIX]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=A1=B0=ED=9A=8C=20=EA=B8=B0=EB=B3=B8=20=ED=8E=98=EC=9D=B4?= =?UTF-8?q?=EC=A7=80=201=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/domain/booking/controller/BookingController.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java index 861eac96..1b83a840 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java @@ -94,7 +94,8 @@ public ApiResponse getMyBookings( ) { User user = userRepository.findById(1L).orElseThrow(); // 임시로 임의의 유저 사용 + // 서비스 호출 시 page - 1을 넘겨서 0-based index로 맞춰줍니다. return ApiResponse.of(BookingSuccessStatus._BOOKING_FOUND, - bookingQueryService.getBookingList(user, status, page)); + bookingQueryService.getBookingList(user, status, page-1)); } } From 0fa14814961165976eccd4e2dc5b46dfaf088714 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Thu, 29 Jan 2026 17:58:26 +0900 Subject: [PATCH 26/30] =?UTF-8?q?[FIX]=20:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20=EC=98=88=EC=95=BD=20=EC=99=84=EB=A3=8C=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20api=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../booking/controller/BookingController.java | 23 ++++++++++--------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java index 1b83a840..237a1af3 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java @@ -61,17 +61,18 @@ public ApiResponse createBooking( return ApiResponse.onSuccess(bookingCommandService.createBooking(user, storeId, dto)); } - @Operation(summary = "예약 완료 처리", - description = "결제 완료 후 결제 정보를 입력받아 예약 상태를 업데이트합니다. 주의) 외부에서 이 API를 호출하지 않고 " + - "POST /api/v1/payments/confirm API 호출 후 내부적으로 이 API의 로직을 실행합니다.") - @PatchMapping("/bookings/{bookingId}/payments-confirm") - public ApiResponse confirmPayment( - @PathVariable Long bookingId, - @RequestBody @Valid BookingRequestDTO.PaymentConfirmDTO dto - ) { - - return ApiResponse.onSuccess(bookingCommandService.confirmPayment(bookingId,dto)); - } + //불필요한 api 삭제 +// @Operation(summary = "예약 완료 처리", +// description = "결제 완료 후 결제 정보를 입력받아 예약 상태를 업데이트합니다. 주의) 외부에서 이 API를 호출하지 않고 " + +// "POST /api/v1/payments/confirm API 호출 후 내부적으로 이 API의 로직을 실행합니다.") +// @PatchMapping("/bookings/{bookingId}/payments-confirm") +// public ApiResponse confirmPayment( +// @PathVariable Long bookingId, +// @RequestBody @Valid BookingRequestDTO.PaymentConfirmDTO dto +// ) { +// +// return ApiResponse.onSuccess(bookingCommandService.confirmPayment(bookingId,dto)); +// } @Operation(summary = "예약 취소", description = "예약을 취소하고 환불을 진행합니다.") From 48f250b233c0eb5782aa8e034433218f038e3031 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Thu, 29 Jan 2026 17:59:11 +0900 Subject: [PATCH 27/30] =?UTF-8?q?[REFACTOR]=20:=20=EC=A1=B4=EC=9E=AC?= =?UTF-8?q?=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20=ED=85=8C=EC=9D=B4?= =?UTF-8?q?=EB=B8=94=EC=9D=84=20=ED=8F=AC=ED=95=A8=ED=95=B4=20=EC=97=90?= =?UTF-8?q?=EC=95=BD=EC=9D=84=20=EC=83=9D=EC=84=B1=ED=95=98=EB=8A=94=20?= =?UTF-8?q?=EC=98=88=EC=99=B8=20=EC=B2=98=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../booking/service/BookingCommandServiceImpl.java | 9 ++++++++- 1 file changed, 8 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java index 665a30f0..3634af49 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java @@ -24,6 +24,7 @@ import com.eatsfine.eatsfine.domain.store.repository.StoreRepository; import com.eatsfine.eatsfine.domain.store.status.StoreErrorStatus; import com.eatsfine.eatsfine.domain.storetable.entity.StoreTable; +import com.eatsfine.eatsfine.domain.storetable.exception.status.StoreTableErrorStatus; import com.eatsfine.eatsfine.domain.storetable.repository.StoreTableRepository; import com.eatsfine.eatsfine.domain.user.entity.User; import com.eatsfine.eatsfine.domain.user.repository.UserRepository; @@ -58,6 +59,11 @@ public BookingResponseDTO.CreateBookingResultDTO createBooking(User user, Long s List selectedTables = storeTableRepository.findAllByIdWithLock(dto.tableIds()); + // 요청한 ID 개수와 조회된 데이터 개수가 다르면, 존재하지 않는 ID가 포함된 것 + if (selectedTables.size() != dto.tableIds().size()) { + throw new StoreException(StoreTableErrorStatus._TABLE_NOT_FOUND); + } + //이미 예약된 테이블 있는지 최종 점검 List reservedTableIds = bookingRepository.findReservedTableIds(storeId, dto.date(), dto.time()); for (StoreTable storeTable : selectedTables) { @@ -96,7 +102,7 @@ public BookingResponseDTO.CreateBookingResultDTO createBooking(User user, Long s booking.addBookingMenu(bookingMenu); BigDecimal itemQuantity = BigDecimal.valueOf(menuItem.quantity()); - itemTotalPrice = menu.getPrice().multiply(itemQuantity); + itemTotalPrice = itemTotalPrice.add(menu.getPrice().multiply(itemQuantity)); } // 총 예약금 계산 ( 전체 메뉴 가격 * 가게의 예약금 비율 ) @@ -108,6 +114,7 @@ public BookingResponseDTO.CreateBookingResultDTO createBooking(User user, Long s booking.setDepositAmount(totalDeposit); Booking savedBooking = bookingRepository.save(booking); + bookingRepository.flush(); // 결제 대기 데이터 생성 (내부 서비스 호출) PaymentRequestDTO.RequestPaymentDTO paymentRequest = new PaymentRequestDTO.RequestPaymentDTO(savedBooking.getId()); From d75b40fd1d7ffd75b74af0268f43c10b6740dd86 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Thu, 29 Jan 2026 18:00:08 +0900 Subject: [PATCH 28/30] =?UTF-8?q?[CHORE]=20:=20PAYMENT=20=EC=BB=A8?= =?UTF-8?q?=ED=8A=B8=EB=A1=A4=EB=9F=AC=20SWAGGER=20=EC=84=A4=EC=A0=95=20?= =?UTF-8?q?=EB=B3=B5=EC=9B=90?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../eatsfine/domain/payment/controller/PaymentController.java | 4 +--- 1 file changed, 1 insertion(+), 3 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/payment/controller/PaymentController.java b/src/main/java/com/eatsfine/eatsfine/domain/payment/controller/PaymentController.java index 0597b0e1..83c10d6a 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/payment/controller/PaymentController.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/payment/controller/PaymentController.java @@ -25,9 +25,7 @@ public class PaymentController { private final PaymentService paymentService; - @Operation(summary = "결제 요청", description = "예약 ID를 받아 주문 ID를 생성하고 결제 정보를 초기화합니다." + - "주의) 외부에서 이 API를 호출하지 않고 POST /api/v1/stores/{storeId}/bookings API 호출 후 " + - "내부적으로 이 API의 로직을 실행합니다.") + @Operation(summary = "결제 요청", description = "예약 ID를 받아 주문 ID를 생성하고 결제 정보를 초기화합니다.") @PostMapping("/request") public ApiResponse requestPayment( @RequestBody @Valid PaymentRequestDTO.RequestPaymentDTO dto) { From dab39bf1e8ff87196109efdea7412145f265e94f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Fri, 30 Jan 2026 01:22:08 +0900 Subject: [PATCH 29/30] =?UTF-8?q?[FEAT]=20:=20=EC=98=88=EC=95=BD=20?= =?UTF-8?q?=EC=B7=A8=EC=86=8C=20=EB=A1=9C=EC=A7=81=20=EC=98=88=EC=99=B8=20?= =?UTF-8?q?=EC=B2=98=EB=A6=AC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/booking/service/BookingCommandServiceImpl.java | 7 +++++-- 1 file changed, 5 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java index 3634af49..75e0a02f 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java @@ -171,8 +171,11 @@ public BookingResponseDTO.CancelBookingResultDTO cancelBooking(Long bookingId, B .orElseThrow(() -> new BookingException(BookingErrorStatus._BOOKING_NOT_FOUND)); // 예약 중 결제 완료된 결제의 결제키 이용 환불 로직 진행 - PaymentRequestDTO.CancelPaymentDTO cancelDto = new PaymentRequestDTO.CancelPaymentDTO(dto.reason()); - paymentService.cancelPayment(booking.getSuccessPaymentKey(), cancelDto); + if(booking.getStatus() == BookingStatus.CONFIRMED) { + PaymentRequestDTO.CancelPaymentDTO cancelDto = new PaymentRequestDTO.CancelPaymentDTO(dto.reason()); + paymentService.cancelPayment(booking.getSuccessPaymentKey(), cancelDto); + } + //예약 상태 취소로 변경 booking.cancel(dto.reason()); From 01395e2623a2afa1c91ec7e632af9e8c837757e1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A0=95=EC=A4=80=EC=98=81?= Date: Fri, 30 Jan 2026 19:19:22 +0900 Subject: [PATCH 30/30] =?UTF-8?q?[FIX]=20:=20=EA=B2=B0=EC=A0=9C=20?= =?UTF-8?q?=EA=B8=88=EC=95=A1=20=ED=83=80=EC=9E=85=20Integer=20->=20BigDec?= =?UTF-8?q?imal=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/payment/dto/response/PaymentResponseDTO.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/com/eatsfine/eatsfine/domain/payment/dto/response/PaymentResponseDTO.java b/src/main/java/com/eatsfine/eatsfine/domain/payment/dto/response/PaymentResponseDTO.java index 021b3f4e..8c690061 100644 --- a/src/main/java/com/eatsfine/eatsfine/domain/payment/dto/response/PaymentResponseDTO.java +++ b/src/main/java/com/eatsfine/eatsfine/domain/payment/dto/response/PaymentResponseDTO.java @@ -27,7 +27,7 @@ public record PaymentHistoryResultDTO( Long paymentId, Long bookingId, String storeName, - Integer amount, + BigDecimal amount, String paymentType, String paymentMethod, String paymentProvider, @@ -66,7 +66,7 @@ public record PaymentSuccessResultDTO( String status, LocalDateTime approvedAt, String orderId, - Integer amount, + BigDecimal amount, String paymentMethod, String paymentProvider, String receiptUrl) {