Skip to content

[FEAT] : 예약 취소 API 개발 / 마이페이지 예약 조회 API 개발 #85

Merged
CokaNuri merged 36 commits intodevelopfrom
feature/booking
Jan 30, 2026
Merged

[FEAT] : 예약 취소 API 개발 / 마이페이지 예약 조회 API 개발 #85
CokaNuri merged 36 commits intodevelopfrom
feature/booking

Conversation

@CokaNuri
Copy link
Contributor

@CokaNuri CokaNuri commented Jan 29, 2026

💡 작업 개요

  1. 기능 개발
  • 메뉴 정보를 포함한 예약 생성 로직 구현 (BookingMenu 매핑 엔티티 도입)

  • 예약 취소 API 구현 및 결제 취소(환불) 로직 연동

  • 마이페이지 내 예약 내역 조회 API 구현 (기본 페이지 번호 1 설정 및 페이징 처리)

  1. 리팩토링
  • 결제 및 금액 관련 데이터 타입을 Integer에서 BigDecimal로 변경하여 연산 정확도 향상

  • 불필요한 결제 승인 API( PATCH /bookings/{bookingId}/payments-confirm) 제거 및 기존 승인 로직 통합

✅ 작업 내용

  • 기능 개발
  • 버그 수정
  • 리팩토링
  • 주석/포맷 정리
  • 기타 설정

🧪 테스트 내용

  1. 예약 생성
    예약 생성 후 예약 상태 : PENDING
스크린샷 2026-01-29 165411

결제 완료 후 예약 상태 : PENDING -> CONFIMED 로 변경

스크린샷 2026-01-29 170629

결제 상태 : COMPLETED

스크린샷 2026-01-29 170724
  1. 예약 취소
스크린샷 2026-01-29 170742

예약 취소 후 결제 상태 : REFUNDED

스크린샷 2026-01-29 170754

예약 취소 후 예약 상태 : CANCELED

스크린샷 2026-01-29 170809
  1. 마이페이지 예약 내역 조회
스크린샷 2026-01-29 171110

📝 기타 참고 사항

  • Menu, BookingMenu, Payment 테이블의 금액 컬럼이 DECIMAL로 변경되었습니다

Summary by CodeRabbit

  • New Features

    • Cancel bookings with a reason and receive refund details.
    • View your bookings with status filtering and pagination.
    • Add menu items when creating a reservation; payment info included in responses.
  • Improvements

    • Deposit now calculated from selected menu items and store rates.
    • Monetary values use higher-precision (BigDecimal) amounts for payments and refunds.
    • Booking previews include payment, table and pagination details.

✏️ Tip: You can customize this high-level summary in your review settings.

# Conflicts:
#	src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java
@coderabbitai
Copy link

coderabbitai bot commented Jan 29, 2026

📝 Walkthrough

Walkthrough

Adds menu-item support and BigDecimal monetary types, computes dynamic deposit, implements booking cancellation with payment refund flow, and exposes a paginated user bookings endpoint with optional status filtering and new success/error statuses.

Changes

Cohort / File(s) Summary
Controller
src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java
Removed confirmPayment; added cancelBooking (PATCH /bookings/{bookingId}/cancel) and getMyBookings (GET /users/bookings) endpoints; uses BookingSuccessStatus.
Domain Entities & Mapping
src/main/java/.../booking/entity/Booking.java, src/main/java/.../booking/entity/mapping/BookingMenu.java
Added bookingMenus, depositAmount: BigDecimal, cancelReason; methods to add menus, cancel, get success payment key, set deposit; new BookingMenu entity linking Booking↔Menu with quantity and price.
DTOs
src/main/java/.../booking/dto/request/BookingRequestDTO.java, src/main/java/.../booking/dto/response/BookingResponseDTO.java, src/main/java/.../payment/dto/...
Added MenuOrderDto, CancelBookingDTO; switched monetary fields to BigDecimal; added CancelBookingResultDTO, BookingPreviewDTO/BookingPreviewListDTO; CreateBookingResultDTO now includes paymentId and orderId.
Service Layer
src/main/java/.../booking/service/BookingCommandService*.java, src/main/java/.../booking/service/BookingQueryService*.java
Create flow persists BookingMenu items, calculates dynamic deposit, invokes PaymentService and returns payment info; added cancelBooking (triggers payment cancellation when applicable) and getBookingList returning paginated previews filtered by status.
Repository, Enums, Statuses
src/main/java/.../booking/repository/BookingRepository.java, src/main/java/.../booking/enums/BookingStatus.java, src/main/java/.../booking/status/*.java
Added paginated findAllByUser and findAllByUserAndStatus; renamed enum constant CANCELLEDCANCELED; added _ALREADY_CANCELED error and new booking success statuses (_BOOKING_CREATED/_BOOKING_CONFIRMED/_BOOKING_CANCELED).
Payment Domain & Validation
src/main/java/.../payment/entity/Payment.java, src/main/java/.../payment/service/PaymentService.java
Migrated payment amount and related DTO fields to BigDecimal; updated validation and equality checks to use compareTo.
Converter & Config
src/main/java/.../booking/converter/BookingConverter.java, src/main/resources/application-local.yml
Added toCreateBookingResultDTO to include BigDecimal deposit and payment identifiers; added local AWS S3 config (region, bucket, base-url).

Sequence Diagrams

sequenceDiagram
    actor Client
    participant Controller as "BookingController"
    participant CmdService as "BookingCommandService"
    participant MenuRepo as "MenuRepository"
    participant BookingRepo as "BookingRepository"
    participant Payment as "PaymentService"
    participant DB as "Database"

    Client->>Controller: POST /bookings (tables, menuItems)
    Controller->>CmdService: createBooking(request)
    CmdService->>MenuRepo: fetch menus by ids
    MenuRepo-->>CmdService: menus
    CmdService->>CmdService: calculate itemTotal & deposit (BigDecimal)
    CmdService->>BookingRepo: save Booking (+ BookingMenu entries)
    BookingRepo-->>CmdService: saved Booking
    CmdService->>Payment: requestPayment(depositAmount)
    Payment-->>CmdService: PaymentRequestResultDTO(paymentId, orderId)
    CmdService-->>Controller: CreateBookingResultDTO (bookingId, paymentId, orderId)
    Controller-->>Client: HTTP 201
Loading
sequenceDiagram
    actor Client
    participant Controller as "BookingController"
    participant CmdService as "BookingCommandService"
    participant BookingRepo as "BookingRepository"
    participant Payment as "PaymentService"

    Client->>Controller: PATCH /bookings/{id}/cancel (reason)
    Controller->>CmdService: cancelBooking(id, dto)
    CmdService->>BookingRepo: fetch booking (with payments)
    BookingRepo-->>CmdService: booking
    CmdService->>CmdService: getSuccessPaymentKey()
    CmdService->>Payment: cancelPayment(paymentKey)
    Payment-->>CmdService: refundAmount
    CmdService->>BookingRepo: persist booking.status = CANCELED, cancelReason
    CmdService-->>Controller: CancelBookingResultDTO(refundAmount, canceledAt)
    Controller-->>Client: HTTP 200
Loading
sequenceDiagram
    actor Client
    participant Controller as "BookingController"
    participant QueryService as "BookingQueryService"
    participant BookingRepo as "BookingRepository"

    Client->>Controller: GET /users/bookings?status=&page=
    Controller->>QueryService: getBookingList(user, status, page)
    QueryService->>BookingRepo: findAllByUser[AndStatus](user, pageable)
    BookingRepo-->>QueryService: Page<Booking>
    QueryService->>QueryService: map to BookingPreviewDTOs (payments, tables)
    QueryService-->>Controller: BookingPreviewListDTO (list, pagination)
    Controller-->>Client: HTTP 200
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~45 minutes

Possibly related PRs

Suggested reviewers

  • sonjunkyu
  • SungMinju
  • zerochani
  • twodo0

Poem

🐰 I nibbled menus, counted each bite,
Deposits now precise, rounded just right,
A gentle cancel, a refunded cheer,
Paginated hops for each booking here,
Eatsfine blossoms — wiggle, twirl, and light.

🚥 Pre-merge checks | ✅ 2 | ❌ 1
❌ Failed checks (1 warning)
Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 10.71% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (2 passed)
Check name Status Explanation
Title check ✅ Passed The PR title directly and specifically describes the two main features: booking cancellation API development and My Page booking inquiry API development, matching the core changes.
Description check ✅ Passed The description covers all major changes including feature development, refactoring details, comprehensive test results with screenshots, and relevant notes about data type changes.

✏️ Tip: You can configure your own custom pre-merge checks in the settings.

✨ Finishing touches
  • 📝 Generate docstrings
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Post copyable unit tests in a comment
  • Commit unit tests in branch feature/booking

Comment @coderabbitai help to get the list of available commands and usage tips.

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 7

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/com/eatsfine/eatsfine/domain/booking/dto/request/BookingRequestDTO.java (1)

32-45: Change primitive boolean to Boolean and add @Valid for nested validation.

@NotNull has no effect on primitive boolean fields (primitives default to false, never null). Nested MenuOrderDto constraints won't be validated without @Valid on the collection.

Suggested fix
-import jakarta.validation.constraints.Min;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.Min;
     public record CreateBookingDTO(
             `@NotNull` `@DateTimeFormat`(pattern = "yyyy-MM-dd") LocalDate date,
             `@Schema`(type = "string", example = "18:00", description = "HH:mm 형식으로 입력하세요.")
             `@NotNull` `@DateTimeFormat`(pattern = "HH:mm") LocalTime time,
             `@NotNull` `@Min`(1) Integer partySize,
             `@NotNull` List<Long> tableIds,
-            `@NotNull` boolean isSplitAccepted,
-            `@NotNull` List<MenuOrderDto> menuItems
+            `@NotNull` Boolean isSplitAccepted,
+            `@NotNull` `@Valid` List<MenuOrderDto> menuItems
     ){}
🤖 Fix all issues with AI agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java`:
- Around line 92-100: Replace the hardcoded user lookup in
BookingController.getMyBookings (userRepository.findById(1L)) with retrieval of
the authenticated user from your security context (e.g., obtain Authentication
from SecurityContextHolder and resolve principal to User or load by principal
id) and pass that User to bookingQueryService.getBookingList; additionally
validate the page request parameter (ensure page >= 1) before computing page-1
and either return a 400 Bad Request (throw an appropriate exception) or coerce
to 1, then call bookingQueryService.getBookingList(user, status, page - 1) and
return via ApiResponse.of with BookingResponseDTO.BookingPreviewListDTO.

In `@src/main/java/com/eatsfine/eatsfine/domain/booking/enums/BookingStatus.java`:
- Line 5: Existing persisted/serialized values using "CANCELLED" will break
after renaming to CANCELED; add a compatibility layer and migration: implement a
tolerant deserializer/static factory on the BookingStatus enum (e.g., a static
fromString or a `@JsonCreator` method in BookingStatus) that maps the legacy
string "CANCELLED" to BookingStatus.CANCELED, and add a DB backfill migration
(SQL update to replace "CANCELLED" with "CANCELED" in the bookings table) so
stored rows are fixed; ensure any serialization annotations (Jackson/Gson)
accept the legacy spelling as well.

In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java`:
- Around line 180-184: The CancelBookingResultDTO builder is missing
cancelReason and canceledAt, so populate them from the booking entity: call
booking.getCancelReason() for cancelReason and booking.getCanceledAt() (or the
actual timestamp getter on Booking) for canceledAt when building
BookingResponseDTO.CancelBookingResultDTO; ensure the timestamp is converted to
the DTO type expected (e.g., Instant or ISO string) before passing to the
builder.
- Around line 169-176: In cancelBooking, guard against double cancellation by
checking the booking's current status and payment state before invoking
paymentService.cancelPayment: retrieve the Booking (existing code uses
bookingRepository.findById and throws BookingException with
BookingErrorStatus._BOOKING_NOT_FOUND), then verify the booking is in a
cancellable state (e.g., not already CANCELED or REFUNDED and has a non-null
successPaymentKey) and throw a domain exception (create/ reuse an appropriate
BookingErrorStatus like _INVALID_BOOKING_STATE) if it isn’t; only call
paymentService.cancelPayment(booking.getSuccessPaymentKey(), cancelDto) and
transition the booking status to CANCELED/REFUNDED (and persist via
bookingRepository.save or bookingRepository.saveAndFlush) after the payment
cancel completes successfully.
- Around line 92-105: Replace the current menu lookup and error with a
menu-specific existence and ownership check: use
menuRepository.findById(menuItem.menuId()) and if absent throw a MenuException
(e.g., MenuException with MenuErrorStatus._MENU_NOT_FOUND) instead of
StoreException/StoreErrorStatus._STORE_NOT_FOUND, then verify
menu.getStore().getId() (or menu.getStoreId()) matches the booking’s store id
(booking.getStore().getId() or the method parameter storeId) and if it does not
match throw a MenuException (or StoreException with a clear code like
MenuErrorStatus._MENU_NOT_OWNED_BY_STORE); keep the rest of the BookingMenu
builder/price calculation (BookingMenu.builder(), booking.addBookingMenu(...),
itemTotalPrice update) unchanged.

In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingQueryServiceImpl.java`:
- Around line 141-151: getBookingList should validate the incoming page and
status before calling PageRequest.of and BookingStatus.valueOf: ensure page is
null-handled and converted to a zero-based non-negative int (e.g., page == null
? 0 : Math.max(0, page - 1)) and validate status string against BookingStatus
(use a safe lookup or try/catch around BookingStatus.valueOf) and throw a domain
exception with an appropriate BookingErrorStatus (either add _INVALID_PAGE and
_INVALID_STATUS to BookingErrorStatus or reuse an existing code such as
_INVALID_PARTY_SIZE) instead of letting PageRequest.of or valueOf raise an
unchecked exception; update getBookingList to use the validated page index and
status (still calling bookingRepository.findAllByUser(...) or
findAllByUserAndStatus(...)) and propagate a clear domain error when validation
fails.

In
`@src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java`:
- Around line 124-126: Update the Booking.confirm() implementation to validate
current status before changing it: inside the confirm() method (the one invoked
via payment.getBooking().confirm()), check that this.status ==
BookingStatus.PENDING and only then set it to BookingStatus.CONFIRMED; otherwise
return (or throw per business rules) to make the operation idempotent and avoid
transitioning already-confirmed or canceled bookings.
🧹 Nitpick comments (6)
src/main/java/com/eatsfine/eatsfine/domain/payment/entity/Payment.java (1)

35-36: Consider specifying precision and scale for the monetary column.

For BigDecimal monetary fields, it's recommended to explicitly define precision and scale in the JPA mapping to ensure consistent behavior between the entity and database schema.

♻️ Suggested improvement
-    `@Column`(name = "amount", nullable = false)
+    `@Column`(name = "amount", nullable = false, precision = 19, scale = 2)
     private BigDecimal amount;
src/main/java/com/eatsfine/eatsfine/domain/booking/entity/mapping/BookingMenu.java (2)

20-28: Add explicit column definitions and constraints.

The quantity and price fields lack null constraints, and the foreign key columns lack explicit naming. This could lead to unexpected null values and inconsistent column names across different JPA implementations.

♻️ Suggested improvements
+    `@Column`(nullable = false)
     private Integer quantity;
 
+    `@Column`(nullable = false, precision = 19, scale = 2)
     private BigDecimal price;
 
     `@ManyToOne`(fetch = FetchType.LAZY)
+    `@JoinColumn`(name = "booking_id", nullable = false)
     private Booking booking;
 
     `@ManyToOne`(fetch = FetchType.LAZY)
+    `@JoinColumn`(name = "menu_id", nullable = false)
     private Menu menu;

30-32: Method name confirmBooking is misleading.

This method associates the BookingMenu with a Booking, but the name suggests it confirms the booking status. Consider renaming to setBooking or associateBooking for clarity.

♻️ Suggested rename
-    public void confirmBooking(Booking booking) {
+    public void setBooking(Booking booking) {
         this.booking = booking;
     }

Note: This would also require updating the caller in Booking.addBookingMenu().

src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingQueryServiceImpl.java (1)

157-160: Prefer deterministic selection of the “latest” successful payment.

If multiple completed/refunded payments exist, findFirst() depends on collection ordering and may return an older payment. Consider selecting the most recent by approved time or id.

src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java (1)

64-75: Remove the commented-out endpoint block instead of keeping dead code.

Keeps the controller cleaner and avoids confusion about supported endpoints.

src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java (1)

90-90: Consider explicit precision/scale for depositAmount.

Relying on vendor defaults for BigDecimal columns can lead to unexpected precision/scale. It’s safer to declare them to match your DECIMAL schema.

💡 Example column definition (tune precision/scale to your schema)
-    private BigDecimal depositAmount;
+    `@Column`(name = "deposit_amount", precision = 12, scale = 2)
+    private BigDecimal depositAmount;

Comment on lines +92 to +100
public ApiResponse<BookingResponseDTO.BookingPreviewListDTO> getMyBookings(
@RequestParam(name = "status", required = false) String status,
@RequestParam(name = "page", defaultValue = "1") Integer page
) {
User user = userRepository.findById(1L).orElseThrow(); // 임시로 임의의 유저 사용

// 서비스 호출 시 page - 1을 넘겨서 0-based index로 맞춰줍니다.
return ApiResponse.of(BookingSuccessStatus._BOOKING_FOUND,
bookingQueryService.getBookingList(user, status, page-1));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

cat -n src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java | sed -n '85,110p'

Repository: Eatsfine/BE

Length of output: 905


🏁 Script executed:

cat -n src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java | head -50

Repository: Eatsfine/BE

Length of output: 2641


🏁 Script executed:

rg "findById.*orElseThrow" src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java -A 2 -B 2

Repository: Eatsfine/BE

Length of output: 474


🏁 Script executed:

rg "PageRequest" src/main/java/com/eatsfine/eatsfine/domain/booking/ -A 2 -B 2

Repository: Eatsfine/BE

Length of output: 1421


Resolve hardcoded user ID and add page bounds validation.

Hardcoding findById(1L) bypasses authentication, exposing all users' booking data regardless of who is requesting. Replace with authenticated user from security context.

Additionally, page - 1 without bounds checking will cause PageRequest.of() to throw if invalid values are passed. Although the parameter has defaultValue = "1", explicit negative or zero values can bypass this. Validate that page >= 1 before calculation.

🛠️ Example fix (auth retrieval left to your security layer)
-        User user = userRepository.findById(1L).orElseThrow(); // 임시로 임의의 유저 사용
-
-        // 서비스 호출 시 page - 1을 넘겨서 0-based index로 맞춰줍니다.
+        User user = /* resolve authenticated user from security context */;
+
+        // 서비스 호출 시 page - 1을 넘겨서 0-based index로 맞춰줍니다.
+        int pageIndex = (page == null || page < 1) ? 0 : page - 1;
         return ApiResponse.of(BookingSuccessStatus._BOOKING_FOUND,
-                bookingQueryService.getBookingList(user, status, page-1));
+                bookingQueryService.getBookingList(user, status, pageIndex));
🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java`
around lines 92 - 100, Replace the hardcoded user lookup in
BookingController.getMyBookings (userRepository.findById(1L)) with retrieval of
the authenticated user from your security context (e.g., obtain Authentication
from SecurityContextHolder and resolve principal to User or load by principal
id) and pass that User to bookingQueryService.getBookingList; additionally
validate the page request parameter (ensure page >= 1) before computing page-1
and either return a 400 Bad Request (throw an appropriate exception) or coerce
to 1, then call bookingQueryService.getBookingList(user, status, page - 1) and
return via ApiResponse.of with BookingResponseDTO.BookingPreviewListDTO.

public enum BookingStatus {

PENDING, CONFIRMED, COMPLETED, CANCELLED, NOSHOW
PENDING, CONFIRMED, COMPLETED, CANCELED, NOSHOW
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Handle backward compatibility for persisted/serialized CANCELLED.
Renaming an enum constant can break existing DB rows (e.g., CANCELLED) and client payloads. Please add a migration/backfill or a compatibility layer before release.

🤖 Prompt for AI Agents
In `@src/main/java/com/eatsfine/eatsfine/domain/booking/enums/BookingStatus.java`
at line 5, Existing persisted/serialized values using "CANCELLED" will break
after renaming to CANCELED; add a compatibility layer and migration: implement a
tolerant deserializer/static factory on the BookingStatus enum (e.g., a static
fromString or a `@JsonCreator` method in BookingStatus) that maps the legacy
string "CANCELLED" to BookingStatus.CANCELED, and add a DB backfill migration
(SQL update to replace "CANCELLED" with "CANCELED" in the bookings table) so
stored rows are fixed; ensure any serialization annotations (Jackson/Gson)
accept the legacy spelling as well.

Comment on lines +92 to +105
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);

BigDecimal itemQuantity = BigDecimal.valueOf(menuItem.quantity());
itemTotalPrice = itemTotalPrice.add(menu.getPrice().multiply(itemQuantity));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Validate menu ownership and use a menu-specific error.

Right now a missing menuId triggers _STORE_NOT_FOUND, and there’s no check that the menu actually belongs to the storeId being booked. That can allow cross-store menu orders and misleading errors.

🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java`
around lines 92 - 105, Replace the current menu lookup and error with a
menu-specific existence and ownership check: use
menuRepository.findById(menuItem.menuId()) and if absent throw a MenuException
(e.g., MenuException with MenuErrorStatus._MENU_NOT_FOUND) instead of
StoreException/StoreErrorStatus._STORE_NOT_FOUND, then verify
menu.getStore().getId() (or menu.getStoreId()) matches the booking’s store id
(booking.getStore().getId() or the method parameter storeId) and if it does not
match throw a MenuException (or StoreException with a clear code like
MenuErrorStatus._MENU_NOT_OWNED_BY_STORE); keep the rest of the BookingMenu
builder/price calculation (BookingMenu.builder(), booking.addBookingMenu(...),
itemTotalPrice update) unchanged.

Comment on lines 169 to 176
public BookingResponseDTO.CancelBookingResultDTO cancelBooking(Long bookingId, BookingRequestDTO.CancelBookingDTO dto) {
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);

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Guard against double-cancel / invalid status before calling refund.

Calling cancelPayment(...) without checking current booking/payment status can lead to duplicate refunds or non-idempotent failures. Please block cancellation if the booking is already canceled or not in a cancellable state.

🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java`
around lines 169 - 176, In cancelBooking, guard against double cancellation by
checking the booking's current status and payment state before invoking
paymentService.cancelPayment: retrieve the Booking (existing code uses
bookingRepository.findById and throws BookingException with
BookingErrorStatus._BOOKING_NOT_FOUND), then verify the booking is in a
cancellable state (e.g., not already CANCELED or REFUNDED and has a non-null
successPaymentKey) and throw a domain exception (create/ reuse an appropriate
BookingErrorStatus like _INVALID_BOOKING_STATE) if it isn’t; only call
paymentService.cancelPayment(booking.getSuccessPaymentKey(), cancelDto) and
transition the booking status to CANCELED/REFUNDED (and persist via
bookingRepository.save or bookingRepository.saveAndFlush) after the payment
cancel completes successfully.

Comment on lines +180 to +184
return BookingResponseDTO.CancelBookingResultDTO.builder()
.bookingId(booking.getId())
.status(booking.getStatus().name())
.refundAmount(booking.getDepositAmount())
.build();
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

Populate cancelReason and canceledAt in the response.

CancelBookingResultDTO includes cancelReason and canceledAt, but the response builder sets neither, so clients will receive nulls. Please set these fields (e.g., from the booking state or a cancellation timestamp).

🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java`
around lines 180 - 184, The CancelBookingResultDTO builder is missing
cancelReason and canceledAt, so populate them from the booking entity: call
booking.getCancelReason() for cancelReason and booking.getCanceledAt() (or the
actual timestamp getter on Booking) for canceledAt when building
BookingResponseDTO.CancelBookingResultDTO; ensure the timestamp is converted to
the DTO type expected (e.g., Instant or ISO string) before passing to the
builder.

Comment on lines +141 to +151
public BookingResponseDTO.BookingPreviewListDTO getBookingList(User user, String status, Integer page) {
PageRequest pageRequest = PageRequest.of(page, 10, Sort.by("bookingDate").descending());

Page<Booking> bookingPage;

if(status == null || status.equals("ALL")) {
bookingPage = bookingRepository.findAllByUser(user, pageRequest);
} else {
BookingStatus bookingStatus = BookingStatus.valueOf(status);
bookingPage = bookingRepository.findAllByUserAndStatus(user, bookingStatus, pageRequest);
}
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

find . -type f -name "*.java" | grep -i booking | head -20

Repository: Eatsfine/BE

Length of output: 1424


🏁 Script executed:

cat -n src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingQueryServiceImpl.java | head -160

Repository: Eatsfine/BE

Length of output: 8849


🏁 Script executed:

find . -type f -name "BookingException.java" -o -name "BookingErrorStatus.java" -o -name "BookingStatus.java"

Repository: Eatsfine/BE

Length of output: 301


🏁 Script executed:

rg "BookingException|BookingErrorStatus" --type java -A 3 | head -100

Repository: Eatsfine/BE

Length of output: 7836


🏁 Script executed:

rg "PageRequest.of\(" --type java -B 2 -A 2 | head -80

Repository: Eatsfine/BE

Length of output: 1961


🏁 Script executed:

cat -n src/main/java/com/eatsfine/eatsfine/domain/booking/status/BookingErrorStatus.java

Repository: Eatsfine/BE

Length of output: 2131


🏁 Script executed:

cat -n src/main/java/com/eatsfine/eatsfine/domain/booking/enums/BookingStatus.java

Repository: Eatsfine/BE

Length of output: 232


🏁 Script executed:

cat -n src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java | grep -A 10 "getBookingList"

Repository: Eatsfine/BE

Length of output: 158


🏁 Script executed:

rg "page.*null|page.*<|page.*PageRequest" --type java -B 2 -A 2

Repository: Eatsfine/BE

Length of output: 2938


Add input validation for page and status parameters; note that proposed error codes don't exist.

Validation is needed—PageRequest.of(page, ...) throws IllegalArgumentException on negative page values, and BookingStatus.valueOf(status) throws on invalid status strings. Both will surface as 500 errors without proper guards.

However, BookingErrorStatus._INVALID_PAGE and BookingErrorStatus._INVALID_STATUS do not exist in the BookingErrorStatus enum. Before applying validation, add these error codes to BookingErrorStatus or reuse an existing error code like _INVALID_PARTY_SIZE (adjust as needed for your domain semantics).

Reference PaymentService for a similar validation pattern: if (page != null && page > 0) { page - 1 } else { 0 }.

🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingQueryServiceImpl.java`
around lines 141 - 151, getBookingList should validate the incoming page and
status before calling PageRequest.of and BookingStatus.valueOf: ensure page is
null-handled and converted to a zero-based non-negative int (e.g., page == null
? 0 : Math.max(0, page - 1)) and validate status string against BookingStatus
(use a safe lookup or try/catch around BookingStatus.valueOf) and throw a domain
exception with an appropriate BookingErrorStatus (either add _INVALID_PAGE and
_INVALID_STATUS to BookingErrorStatus or reuse an existing code such as
_INVALID_PARTY_SIZE) instead of letting PageRequest.of or valueOf raise an
unchecked exception; update getBookingList to use the validated page index and
status (still calling bookingRepository.findAllByUser(...) or
findAllByUserAndStatus(...)) and propagate a clear domain error when validation
fails.

Comment on lines 124 to 126
// 예약 확정 처리
payment.getBooking().confirm();

Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟠 Major

🧩 Analysis chain

🏁 Script executed:

#!/bin/bash
# Find the Booking class and its confirm() method
find . -type f -name "Booking.java" | head -20

Repository: Eatsfine/BE

Length of output: 127


🏁 Script executed:

#!/bin/bash
# Read the Booking.java file to examine the confirm() method
cat -n ./src/main/java/com/eatsfine/eatsfine/domain/booking/entity/Booking.java

Repository: Eatsfine/BE

Length of output: 4669


🏁 Script executed:

#!/bin/bash
# Find and examine the BookingStatus enum
find . -type f -name "BookingStatus.java"

Repository: Eatsfine/BE

Length of output: 132


🏁 Script executed:

#!/bin/bash
# Read the BookingStatus enum
cat -n ./src/main/java/com/eatsfine/eatsfine/domain/booking/enums/BookingStatus.java

Repository: Eatsfine/BE

Length of output: 232


🏁 Script executed:

#!/bin/bash
# Check the PaymentService where confirm() is called (around lines 124-126)
sed -n '110,140p' ./src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java

Repository: Eatsfine/BE

Length of output: 1568


🏁 Script executed:

#!/bin/bash
# Check if there's any validation before confirm() in the payment flow
sed -n '100,130p' ./src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java

Repository: Eatsfine/BE

Length of output: 1536


Add state validation to prevent invalid booking status transitions.

The confirm() method lacks guards for state validation. In a payment webhook context where retries and duplicate calls are expected, calling confirm() on an already-confirmed booking (or worse, a canceled one) should be explicitly handled. Add a check to prevent invalid state transitions:

public void confirm() {
    if (this.status != BookingStatus.PENDING) {
        return; // or throw exception based on business logic
    }
    this.status = BookingStatus.CONFIRMED;
}

This ensures idempotent behavior during webhook retries and prevents silent overrides of canceled bookings.

🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/payment/service/PaymentService.java`
around lines 124 - 126, Update the Booking.confirm() implementation to validate
current status before changing it: inside the confirm() method (the one invoked
via payment.getBooking().confirm()), check that this.status ==
BookingStatus.PENDING and only then set it to BookingStatus.CONFIRMED; otherwise
return (or throw per business rules) to make the operation idempotent and avoid
transitioning already-confirmed or canceled bookings.

Booking booking = bookingRepository.findById(bookingId)
.orElseThrow(() -> new BookingException(BookingErrorStatus._BOOKING_NOT_FOUND));

// 예약 중 결제 완료된 결제의 결제키 이용 환불 로직 진행
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

cancelBooking 로직은 결제가 완료된 예약을 전제로 작성되어 있어, 결제 전 pending 상태에서 사용자가 취소를 시도할 경우 에러가 발생합니다. 따라서 if (booking.getStatus() == BookingStatus.CONFIRMED) 조건안에 PaymentRequestDTO.CancelPaymentDTO cancelDto = new PaymentRequestDTO.CancelPaymentDTO(dto.reason());
paymentService.cancelPayment(booking.getSuccessPaymentKey(), cancelDto); 넣어줘야할 것 같습니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰 감사합니다!

생각해보니까 예약만 하고 결제는 하지 않은 케이스 처리에 대해 더 고려를 해봐야 할 거 같습니다.
미결제 예약에 대한 후처리로 일정 시간(예: 10분)마다 배치를 돌려 PENDING상태이고 결제 기한이 지난 데이터를
스케줄러를 이용해 주기적으로 삭제하는 방식을 도입하는 건 어떨까요?

더 좋은 다른 방식 있으면 의견 주시면 감사하겠습니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 좋은 방식인 것 같습니다! 하지만, 현재 elasticache도 연결되어있는 상황에서 Redis에 예약 정보를 저장하고 TTL(Time To Live)을 10분으로 설정하고 시간이 지나면 데이터가 자동으로 사라지는 방식으로 사용해도 좋을 것 같습니다. 어떤가요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Redis를 활용하는 방식이 성능 측면에서 더 좋을 것 같습니다!
제가 말한 스케줄러 방식은 구현이 매우 간단해서 일단 그렇게 적용해 놓고 제가 나머지 예약 파트 개발까지 끝나면
Redis를 활용한 방식으로 해보겠습니다.

Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

넵 좋습니다!

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

🤖 Fix all issues with AI agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java`:
- Line 186: In BookingCommandServiceImpl the builder call setting
refundAmount(booking.getDepositAmount()) is misleading for non-CONFIRMED
bookings (e.g., PENDING) because no payment was taken; change the logic so
refundAmount is BigDecimal.ZERO unless booking.getStatus() equals CONFIRMED (or
another status that indicates an actual payment), i.e., compute refund =
booking.getStatus() == BookingStatus.CONFIRMED ? booking.getDepositAmount() :
BigDecimal.ZERO and pass that value to refundAmount instead of
booking.getDepositAmount().

return BookingResponseDTO.CancelBookingResultDTO.builder()
.bookingId(booking.getId())
.status(booking.getStatus().name())
.refundAmount(booking.getDepositAmount())
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🟡 Minor

refundAmount is misleading for non-CONFIRMED bookings.

When a PENDING booking is canceled (no payment made), refundAmount still returns depositAmount. This implies a refund occurred when none did. Return BigDecimal.ZERO when no actual refund was processed.

🛠️ Suggested fix
+        BigDecimal refundAmount = BigDecimal.ZERO;
+
         // 예약 중 결제 완료된 결제의 결제키 이용 환불 로직 진행
         if(booking.getStatus() == BookingStatus.CONFIRMED) {
             PaymentRequestDTO.CancelPaymentDTO cancelDto = new PaymentRequestDTO.CancelPaymentDTO(dto.reason());
             paymentService.cancelPayment(booking.getSuccessPaymentKey(), cancelDto);
+            refundAmount = booking.getDepositAmount();
         }


         //예약 상태 취소로 변경
         booking.cancel(dto.reason());

         return BookingResponseDTO.CancelBookingResultDTO.builder()
                 .bookingId(booking.getId())
                 .status(booking.getStatus().name())
-                .refundAmount(booking.getDepositAmount())
+                .refundAmount(refundAmount)
                 .build();
🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java`
at line 186, In BookingCommandServiceImpl the builder call setting
refundAmount(booking.getDepositAmount()) is misleading for non-CONFIRMED
bookings (e.g., PENDING) because no payment was taken; change the logic so
refundAmount is BigDecimal.ZERO unless booking.getStatus() equals CONFIRMED (or
another status that indicates an actual payment), i.e., compute refund =
booking.getStatus() == BookingStatus.CONFIRMED ? booking.getDepositAmount() :
BigDecimal.ZERO and pass that value to refundAmount instead of
booking.getDepositAmount().

Copy link

@coderabbitai coderabbitai bot left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Actionable comments posted: 1

Caution

Some comments are outside the diff and can’t be posted inline due to platform limitations.

⚠️ Outside diff range comments (1)
src/main/java/com/eatsfine/eatsfine/domain/payment/dto/response/PaymentResponseDTO.java (1)

26-35: ⚠️ Potential issue | 🟠 Major

Align remaining amount fields with BigDecimal to avoid precision loss.

PaymentHistoryResultDTO.amount and PaymentSuccessResultDTO.amount still use Integer. With the monetary migration to DECIMAL/BigDecimal, this will truncate cents and create inconsistent response schemas across endpoints. Consider switching these to BigDecimal too.

🔧 Suggested update
 public record PaymentHistoryResultDTO(
         Long paymentId,
         Long bookingId,
         String storeName,
-        Integer amount,
+        BigDecimal amount,
         String paymentType,
         String paymentMethod,
         String paymentProvider,
         String status,
         LocalDateTime approvedAt) {
 }
@@
 public record PaymentSuccessResultDTO(
         Long paymentId,
         String status,
         LocalDateTime approvedAt,
         String orderId,
-        Integer amount,
+        BigDecimal amount,
         String paymentMethod,
         String paymentProvider,
         String receiptUrl) {
 }

Also applies to: 64-72

🤖 Fix all issues with AI agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java`:
- Around line 77-85: The cancelBooking endpoint currently lacks ownership
validation: update the controller method (BookingController.cancelBooking) to
obtain the authenticated user (e.g., add an `@AuthenticationPrincipal` /
SecurityContext extraction or mirror createBooking's signature) and pass the
user identity into BookingCommandService.cancelBooking; then update
BookingCommandService.cancelBooking and BookingCommandServiceImpl.cancelBooking
to accept the user (userId or UserPrincipal) and, inside the impl, fetch the
Booking (e.g., via bookingRepository.findById), verify booking.getOwnerId()
equals the authenticated user id, and throw an appropriate
Forbidden/AccessDenied exception if not matching before proceeding with
cancellation and refund.
🧹 Nitpick comments (1)
src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java (1)

64-75: Remove commented-out endpoint code instead of leaving dead blocks.
It’s cleaner to delete the old API entirely.

🧹 Suggested cleanup
-    //불필요한 api 삭제
-//    `@Operation`(summary = "예약 완료 처리",
-//            description = "결제 완료 후 결제 정보를 입력받아 예약 상태를 업데이트합니다. 주의) 외부에서 이 API를 호출하지 않고  " +
-//                    "POST /api/v1/payments/confirm API 호출 후 내부적으로 이 API의 로직을 실행합니다.")
-//    `@PatchMapping`("/bookings/{bookingId}/payments-confirm")
-//    public ApiResponse<BookingResponseDTO.ConfirmPaymentResultDTO> confirmPayment(
-//            `@PathVariable` Long bookingId,
-//            `@RequestBody` `@Valid` BookingRequestDTO.PaymentConfirmDTO dto
-//    ) {
-//
-//        return ApiResponse.onSuccess(bookingCommandService.confirmPayment(bookingId,dto));
-//    }

Comment on lines +77 to +85
@Operation(summary = "예약 취소",
description = "예약을 취소하고 환불을 진행합니다.")
@PatchMapping("/bookings/{bookingId}/cancel")
public ApiResponse<BookingResponseDTO.CancelBookingResultDTO> cancelBooking(
@PathVariable Long bookingId,
@RequestBody @Valid BookingRequestDTO.CancelBookingDTO dto
) {
return ApiResponse.of(BookingSuccessStatus._BOOKING_CANCELED,
bookingCommandService.cancelBooking(bookingId, dto));
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

⚠️ Potential issue | 🔴 Critical

🧩 Analysis chain

🏁 Script executed:

cd /tmp/repo && find . -name "*.java" -type f | head -20

Repository: Eatsfine/BE

Length of output: 114


🏁 Script executed:

# Search for cancelBooking implementation in the codebase
rg -n "def cancelBooking|void cancelBooking|cancelBooking.*{" --type java -A 10

Repository: Eatsfine/BE

Length of output: 224


🏁 Script executed:

# Look for the BookingCommandService implementation
rg -n "class BookingCommandService|interface BookingCommandService" --type java -A 30

Repository: Eatsfine/BE

Length of output: 5492


🏁 Script executed:

# Search for security context usage and `@AuthenticationPrincipal` annotations
rg -n "SecurityContextHolder|@AuthenticationPrincipal|@PreAuthorize|@Secured" --type java -B 2 -A 3

Repository: Eatsfine/BE

Length of output: 37


🏁 Script executed:

# Search for ownership validation patterns
rg -n "getUser\(\)|getCurrentUser|getPrincipal|getAuthentication|getMember|getCustomer" --type java -B 2 -A 5

Repository: Eatsfine/BE

Length of output: 37


🏁 Script executed:

rg -n "cancelBooking" --type java -B 3 -A 25

Repository: Eatsfine/BE

Length of output: 7449


🏁 Script executed:

# Read the BookingCommandServiceImpl file to see the complete cancelBooking implementation
cat src/main/java/com/eatsfine/eatsfine/domain/booking/service/BookingCommandServiceImpl.java | head -250

Repository: Eatsfine/BE

Length of output: 8522


🏁 Script executed:

# Search for ownership validation patterns in booking service
rg -n "user|owner|member|authorization|permission|BookingException" src/main/java/com/eatsfine/eatsfine/domain/booking/service/ --type java -B 2 -A 2

Repository: Eatsfine/BE

Length of output: 15577


Add ownership validation to cancelBooking.

The cancelBooking method in BookingCommandServiceImpl has no ownership check. Any authenticated user can cancel another user's booking by knowing the booking ID. Unlike createBooking, which explicitly receives the authenticated user as a parameter, cancelBooking only takes bookingId and dto.

Add user context extraction and verify that the authenticated user owns the booking before processing the cancellation, or refactor the method signature to accept the authenticated user like createBooking does.

🤖 Prompt for AI Agents
In
`@src/main/java/com/eatsfine/eatsfine/domain/booking/controller/BookingController.java`
around lines 77 - 85, The cancelBooking endpoint currently lacks ownership
validation: update the controller method (BookingController.cancelBooking) to
obtain the authenticated user (e.g., add an `@AuthenticationPrincipal` /
SecurityContext extraction or mirror createBooking's signature) and pass the
user identity into BookingCommandService.cancelBooking; then update
BookingCommandService.cancelBooking and BookingCommandServiceImpl.cancelBooking
to accept the user (userId or UserPrincipal) and, inside the impl, fetch the
Booking (e.g., via bookingRepository.findById), verify booking.getOwnerId()
equals the authenticated user id, and throw an appropriate
Forbidden/AccessDenied exception if not matching before proceeding with
cancellation and refund.

@CokaNuri CokaNuri merged commit 6e01492 into develop Jan 30, 2026
2 checks passed
CokaNuri added a commit that referenced this pull request Jan 31, 2026
* Initial commit

* [CHORE]: CI 스크립트 작성

* [CHORE]: CI 배포 프로필 구성

* [FEAT]: HealthCheck API 구현 및 테스트 추가

* [FEAT]: Docker 빌드 환경 구성

* [FEAT]: CD 배포 파이프라인 구성

* [FIX]: 도커 이미지 풀링 및 컨테이너 실행 부분 수정

* [FIX]: 헬스체크 실패 방지를 위한 앱 기동 대기 추가

* [FIX]: invalid duration 문자열 추가

* [FEAT]: 의존성 추가

* [FIX]: 대문자를 소문자로 변경

* [FIX]: 경로 대소문자 에러 수정

* [FIX]: CI 파이프라인 수정

* [FIX]: CI 파이프라인 원래대로 다시 수정

* [FIX]: 스크립트 수정

* [FIX]: 도커파일 수정

* [FIX]: 도커파일 수정

* [chore] 빌드 및 패키지 설정 변경

* [chore] 빌드 및 패키지 설정 변경

* [chore] 빌드 및 패키지 설정 변경

* [chore] 빌드 및 패키지 설정 변경

* [FEAT]: 로컬 개발 환경 구성을 위한 Docker Compose 및 설정 추가

* [FEAT]: BaseEntity 추가

* [FEAT]: 예약 도메인 엔티티(Booking) 기본 설정

* [FIX]: Entity에 식별자 추가

* [FIX]: 불필요한 import문 제거

* [FEAT]: BusinessHours 요일 enum 정의

* [FEAT]: BusinessHours 엔티티 초기 세팅

* [FEAT]: Region 엔티티 설계

* [FEAT]: TableImage 엔티티 초기 세팅

* [REFACTOR]: BusinessHours isClosed 필드를 isHoliday로 변경

* [REFACTOR]: BusinessHours Store 연관관계 적용 및 BaseEntity 상속

* [FEAT]: 가게 승인 상태 enum 추가

* [FEAT]: 가게 카테고리 enum 추가

* [FEAT]: Store 엔티티 설계

* [REFACTOR]: TableImage Store 연관관계 적용 및 BaseEntity 상속

* [FEAT]: StoreRepository 세팅

* [FEAT]: TableImageRepository 세팅

* [FEAT]: BusinessHoursRepository 세팅

* [FEAT]: RegionRepository 세팅

* [CHORE]: yml 파일 수정

* [REFACTOR]: Store와 BusinessHours 양방향 연관관계 및 편의 메서드 추가

* [FIX]: StoreTable 연관관계 주석처리

* [REFACTOR]: Store와 TableImage 양방향 연관관계 및 편의 메서드 추가

* [REFACTOR]: Store-TableImage 양방향 연관관계 삭제 편의 메서드 추가 및 생명주기 관리 설정

* [REFACTOR]: ApiResponse 실패 응답을 BaseErrorCode 기반으로 통합

* [FEAT]: HTTPS 적용 및 NGINX Blue-Green 무중단 배포 전환 로직 수정

* [FEAT]: HTTPS 적용 및 NGINX Blue-Green 무중단 배포 전환 로직 수정

* [FEAT] 예약 가능 시간대 및 테이블 조회 DTO 개발

* [FEAT] 예약 가능 시간대,테이블 조회 컨트롤러 개발 및 스웨거 설정

* [FEAT] BookingTable 엔티티 개발

* [FIX] Booking 엔티티 수정 (BookingTable 연관관계 고려)

* [FEAT] BookingRepository- findReservedTableIds 메서드 구현

* [FIX] Store 엔티티 businessHours,tableImages 필드에 @Builder.Default 애노테이션 추가

* [FEAT] StoreTable 엔티티 기본 설정

* [FEAT] 예약 가능 시간 조회 시 '나눠 앉기' 동의 여부 로직 추가

* [FEAT] 테이블 유형 enum 추가

* [FEAT] TableLayout 엔티티 기본 설정 및 Store, StoreTable 연관관계 수정

* [FEAT] BusinessHours의 DayofWeek을 커스텀 enum이 아닌java.time 으로 변경

* [FIX] BusinessHours의 DayofWeek @Enumerated추가

* [FIX] rows 필드 db에서 오류 -> lows로 수정

* [FEAT] GET /api/v1/stores/{storeId}/bookings/available-times 구현

* [FEAT] GeneralException 구현

* [FIX] 로컬 개발 환경에서 https->http 사용

* [FEAT] User 엔티티 기본 설정

* [FEAT]: 식당 상세 조회 DTO 및 BusinessHours 변환 로직 추가

* [FEAT]: StoreSuccessStatus, StoreErrorStatus 추가

* [FEAT]: GeneralException(프로젝트 exception) 세팅

* [FEAT]: StoreException 세팅

* [FEAT]: 식당 상세 조회 API (Controller/Service) 구현

* [FEAT]: 전역 예외 핸들러(GeneralExceptionAdvice) 구현

* [REFACTOR]: BusinessHours 응답 DTO 및 컨버터 휴무 처리 단순화

* [FEAT] 예약 시간대 설정 후 테이블 조회 DTO 개발

* [FEAT] StoreTable에 Table의 위치 필드 추가

* [FEAT] 시간대 설정 후 예약 가능한 테이블 조회 서비스 구현

* [FEAT] 테이블 분리 허용 여부에 따라 테이블 조회 로직 수정

* [FEAT] GET /api/v1/stores/{storeId}/bookings/available-tables 구현

* [CHORE] Swagger 버전 변경 2.3.0 -> 2.8.1

* [FEAT] BookingException 및 ErrorStatus 추가

* [FEAT] 에러 핸들러 구현

* [FEAT] BookingController에 BookingException 포함

* 예전 테스트 코드 제거

* [REFACTOR] : BookingErrorStatus를 공통 ErrorStatus에서 Booking 도메인으로 분리

* [REFACTOR]: 전역 예외 핸들러 제거

* [REFACTOR]: BusinessHoursResDto.summary 응답 시간 포맷(HH:mm) 적용

* [REFACTOR]: BusinessHours 응답 DTO isClosed로 필드명 변경

* [FIX]: NGINX Blue-Green 자동 전환 배포 로직 수정

* [FIX]: NGINX Blue-Green 자동 전환 배포 로직 수정 (다시 복구)

* Update database password in application-local.yml

* [FEAT] : 예약 생성 DTO 개발

* [FIX]: NGINX Blue-Green 존재 여부 확인 후 stop 하도록 로직 수정

* [REFACTOR] : 예약 조회 DTO, 컨트롤러 구조 변경

* [FEAT] : BookingErrorStatus 추가

* [FEAT] : CreateBookingDTO에 isSplitAccepted 필드 추가

* [FEAT] : createdAt JPA 자동 설정 가능하게 애노테이션 추가

* [FEAT] : 비관적 락 적용해 테이블 조회하는 메서드 개발

* [FIX] : Post 방식 API ModelAttribute->RequestBody 변경

* [FIX] : boolingTables 필드에 @Builder.Default 애노테이션 추가

* [FEAT] : 예약 생성 비즈니스 로직 개발

* [FIX]: 배포 대상 판단 로직을 nginx upstream 기준으로 수정

* [FIX]: deploy.yml 로직 약간수정

* [FIX]: 기존으로 복구 및 로그 추가

* [FIX]: 기존으로 복구

* [FIX]: 배포 스크립트 Nginx 리로드 경로

* [FIX]: HTTPS 환경에서 깨지던 blue-green 배포 판단 로직 수정

* [FIX]: 로직 순서 수정

* [FIX]: 로직 순서 수정

* [FIX]: 코드 약간 수정

* [FIX]: 코드 약간 수정

* [FIX]: 코드 약간 수정

* [FIX]: 코드 약간 수정

* [FIX]: 코드 약간 수정

* [BUILD]: QueryDSL 설정 추가 (.gitignore, build.gradle)

* [CHORE] : DB_PASSWORD 환경변수로 관리

* [FEAT] : UserRepository 임시 사용

* [FEAT] : BookingErrorStatus 추가

* [FEAT] : 결제 요청 관련 DTO 개발

* [FEAT] : StoreErrorStatus 추가

* [REFACTOR] : Swagger 테스트용 @ParameterObject 애노테이션 추가

* [REFACTOR] : 영업시간 정보 없을 시 예외 처리 로직 추가

* [FEAT] : Booking 엔티티에 depositAmount 필드 추가 및 confirm 메서드 추가

* [FIX] : 예약 상태가 PENDING 혹은 CONFIRMED인 테이블을 조회하도록 수정

* [FEAT] : 결제 완료 처리 API 개발

* [FIX] : JpaAuditing 설정 변경

* [FEAT] Swagger 운영 도메인 등록

* [FEAT]: 결제 요청 API 구현 및 Booking 연동

* [FEAT]: 가게 등록 및 Region 예외 추가

* [REFACTOR]: Store 엔티티 초기 구조 보정

* [FEAT]: StoreConverter 추가

* [FEAT]: Store/Region 성공 상태 코드 추가

* [FEAT]: 가게 등록 및 영업시간 요청 처리

* [FEAT]: 영업시간 검증 및 예외처리

* [REFACTOR]: 영업시간 및 이미지 필드 수정

* [FEAT]: 가게 검색 기본 구조 추가

* [FEAT]: QueryDSL 기반 가게 검색 구현

* [FEAT]: 실시간 영업 여부 체크 로직 구현

* [FEAT]: 가게 검색에 키워드 검색, 지역 필터 기능 추가

* [FEAT]: 가게별 예약금 로직 추가

* [FEAT]: 가게 테이블(StoreTable) 도메인 엔티티 설계 (#51)

- StoreTable: Soft Delete 적용, 좌석 인원(min/max) 필드 세분화
- TableLayout: Soft Delete 적용, 배치도 활성/비활성 로직, Cascade 옵션 적용
- TableBlock: 예약 차단 시간 관리를 위한 엔티티 설계
- SlotStatus: 예약 시간대 상태 관리를 위한 Enum 및 DTO 구조 설계
- Common: DB 스키마 정합성을 위한 @column 제약조건 명시

* [FIX]: 간편결제만 놔두기

* [FEAT]: 토스페이먼츠 결제위젯 연동 및 승인 프로세스 구현

* [FEAT]: 독립적인 테스트 환경 구성을 위한 H2 DB 및 설정 추가해서 빌드 실패 해결

* [FIX]: username 삭제

* Feature/payment (#54)

* [FIX]: 간편결제만 놔두기

* [FEAT]: 토스페이먼츠 결제위젯 연동 및 승인 프로세스 구현

* [FEAT]: 독립적인 테스트 환경 구성을 위한 H2 DB 및 설정 추가해서 빌드 실패 해결

* [FIX]: username 삭제

* [FEAT]: 식당 기본 정보, 영업시간 수정, 브레이크타임 설정 API 개발  (#55)

* [FEAT]: 가게 기본 정보 수정 DTO 추가

* [FEAT]: StoreSuccessStatus에 가게 기본 정보 수정 성공 응답 추가

* [FEAT]: Store 엔티티에 가게 기본 정보 수정 메서드 추가

* [FEAT]: 가게 기본 정보 수정 로직 개발

* [FEAT]: StoreController에 가게 기본 정보 수정 API 추가

* [FEAT]: 영업시간 검증 메서드(생성용/수정용) 분리

* [FEAT]: 영업시간 수정 DTO 추가

* [FEAT]: 응답 컨버터 및 상태코드 추가

* [FEAT]: 영업 시간 수정 로직 개발

* [REFACTOR]: 영업시간 변경 응답 7일 모두 내려주도록 수정 및 필드명 변경

* [FEAT]: 브레이크타임 설정 DTO 및 응답코드 추가

* [FEAT]: 요청된 브레이크타임 유효성 검증 로직 개발

* [FEAT]: 브레이크타임 설정 로직 구현

* [FEAT]: 상세조회 응답에 브레이크타임 추가

* [FEAT]: 결제 승인 결과(성공/실패) 처리

* [REFACTOR]: Payment 예외 처리 리팩토링

* [FEAT]: 결제 취소(환불) API 구현

* [FEAT]: 결제 내역 조회 및 상세 조회 API 구현

* [FEAT]: 테이블 배치도 생성, 조회 API 구현 (#56)

* [REFACTOR]: TableLayout SQLDelete 쿼리문 수정 및 Builder.Default 추가

* [FEAT]: TableLayoutException 세팅

* [FEAT]: TableLayout 성공/실패 응답코드 추가

* [FEAT]: Controller에 배치도 생성 API 추가

* [FEAT]: 배치도 DTO, Converter 추가

* [FEAT]: 테이블 배치도 생성 로직 개발

* [REFACTOR]: StoreTable SQLDelete 쿼리문 수정

* [REFACTOR]: TableLayout - StoreTable 연관관계 Cascade.REMOVE 추가

* [FEAT]: 테이블 배치도 조회 API 추가

* [FEAT]: 테이블 배치도 조회 로직 개발

* [REFACTOR]: 테이블 배치도 조회 응답 DTO, Converter에 reviewCount 필드 추가

* [FEAT]: 가게 이미지, 테이블 이미지(S3) 업로드/조회/삭제 기능 구현 (#61)

* [REFACTOR]: url -> key로 변수명 변경 및 imageOrder 필드 추가

* [BUILD]: AWS S3 관련 의존성 추가

* [FEAT]: S3Config, S3Service 추가

* [FEAT]: 이미지 업로드 관련 예외 및 에러 상태코드 추가

* [FEAT]: 가게 메인 이미지 등록 응답 DTO 및 성공 상태코드 추가

* [FEAT]: 가게 메인 이미지 등록 로직 구현

* [FEAT]: 가게 매인 이미지 조회 응답 DTO 및 성공 상태코드 추가

* [FEAT]: 가게 메인 이미지 조회 로직 구현

* [FEAT]: 테이블 이미지 등록 응답 DTO 및 성공 상태코드 추가

* [FEAT]: 가게 테이블 이미지(TableImage) 등록 로직 구현

* [FEAT]: 가게 테이블 이미지(TableImage) 조회 로직 구현

* [REFACTOR]: 테이블 이미지 등록 및 조회 성공 코드 수정

* [FEAT]: 테이블 이미지 삭제 응답 DTO 및 성공 상태코드 추가

* [FEAT]: 가게 테이블 이미지(TableImage) 삭제 로직 구현

* [CHORE]: application.yml에 AWS S3 설정 추가

* [FIX]: StoreSuccessStatus 충돌 해결

* [FIX]: 에러 상태코드 중복 수정

* [CHORE]: application-test.yml에 aws s3 설정 추가

* [FIX]: S3 설정 위치 수정

* [REFACTOR]: AWS S3 설정값 환경변수 처리

* [FEAT]: 가게 테이블 생성 API 구현 (#62)

* [FEAT]: StoreTableException 세팅

* [FEAT]: 테이블 생성 성공/실패 응답코드 추가

* [FEAT]: StoreTableController 가게 테이블 생성 API 및 명세 추가

* [FEAT]: 테이블 생성 DTO, Converter 추가

* [FEAT]: 테이블 생성 검증 로직 추가

* [FEAT]: 테이블 생성 검증 서비스 로직 추가

* [FIX]: 무중단 배포 구조 스크립트에서 단일 컨테이너 배포 구조 스크립트로 변경

* [FIX]: EC2 SSH 배포 방식 수정 및 GitHub Actions 안정화

* [FIX]: GitHub Actions SSH 포트 임시 오픈/회수 로직 정리

* [FIX]: 배포 워크플로우 main으로 변경

* [CHORE]: 불필요 도커 이미지 및 컨테이너 정리

* [FEAT]: 테이블 예약 시간대 목록 조회, 관리 API 구현 (#69)

* [REFACTOR]: 테이블 슬롯 조회 생성/실패 응답 코드 추가 및 에러코드 통일

* [FEAT]: StoreTableController에 가게 슬롯 조회 API 및 명세 추가

* [FEAT]: TableBlockRepository 생성

* [FEAT]: BookingRepository에 JPQL 메소드 추가

* [FEAT]: 테이블 슬롯 조회 응답 DTO, Converter 추가

* [FEAT]: 테이블 슬롯 계산, 검증 로직 추가

* [FEAT]: 테이블 슬롯 조회 서비스 로직 추가

* [FEAT]: 테이블 슬롯 상태 변경 성공/실패 에러 핸들러 추가

* [FEAT]: TableBlockController에 테이블 슬롯 상태 변경 API 및 명세 추가

* [FEAT]: TableBlockRepository 특정 테이블 차단 내역 조회 메소드 추가

* [FEAT]: BookingRepository 테이블 예약 여부 조회 메소드 추가

* [FEAT]: 테이블 상태 변경 요청/응답 DTO, Converter 추가

* [FEAT]: 테이블 상태 변경시 검증 로직 추가

* [FEAT]: 테이블 상태 변경 서비스 로직 추가

* [FEAT]: 가게 등록 시 사업자번호 검증 추가 및 Region 엔티티 구조 변경 (#75)

* [REFACTOR]: 주소(Region) 저장 체계 변경

* [FEAT]: 사업자번호 검증 API 관련 의존성 및 설정파일 추가

* [FEAT]: 사업자번호 검증 로직 추가

* [FEAT]: 가게 등록 요청 시 사업자번호 검증부 추가

* [REFACTOR]: WebClient -> RestClient로 변경

* [FEAT]: Real, Mock으로 사업자번호 검증 분리

* [CHORE]: 사업자번호 검증 성공 로그 추가

* [FEAT]: application.yml에 사업자번호 검증 api key 설정부 추가

* [REFACTOR]: 사업자번호 검증 코드 패키지명 변경

* [FIX]: test의 application.yml에 더미 키값 추가

* [CHORE]: 멈춰있는 컨테이너 전부 삭제 명령어 제거

* [FEAT]: DTO 분리 및 필드명 변경

* [REFACTOR]: 결제 승인 로직 개선 및 DTO 적용

* [REFACTOR]: / 붙여줌

* [FEAT]: 메뉴 엔티티 설계 (#82)

* [FEAT]: 가게 테이블 상세 조회/수정/삭제 API 구현 (#77)

* [FEAT]: 테이블 상세 정보 조회 성공 응답 코드 추가

* [FEAT]: 테이블 상세 정보 조회 api 및 명세 추가

* [FEAT]: 테이블 상세 정보 조회 응답 DTO, Converter 추가

* [FEAT]: 테이블 상세 정보 조회 서비스 로직 추가

* [FEAT]: 테이블 정보 수정 성공/실패 응답 코드 추가

* [FEAT]: 테이블 정보 수정 api 및 명세 추가

* [FEAT]: StoreTable 엔티티 편의 메소드 추가

* [FEAT]: 테이블 정보 수정 요청/응답 DTO, Converter 추가

* [FEAT]: 테이블 정보 수정 서비스 로직 구현
- 테이블 번호 수정
- 테이블 좌석 수 변경
- 테이블 유형 변경

* [FEAT]: 테이블 삭제 성공/실패 응답 코드 추가

* [FEAT]: 테이블 삭제 api 및 명세 추가

* [FEAT]: 테이블 삭제 응답 DTO, Converter 추가

* [FEAT]: 테이블 삭제 서비스 로직 구현
- 삭제 시 현재 시간을 기준으로 예약 존재 여부 확인
- 삭제 시 soft delete

* 가게 개별 테이블 이미지 등록/삭제 API 구현 (#83)

* [FEAT]: 테이블 이미지 업로드 API 및 명세 추가

* [FEAT]: StoreTable 이미지 업로드 메소드 추가

* [FEAT]: 테이블 이미지 업로드 응답 DTO, Converter 추가

* [FEAT]: 테이블 이미지 업로드 서비스 로직 추가

* [REFACTOR]: 테이블 상세 조회 시 테이블 이미지 URL을 포함하도록 수정

* [FEAT]: 테이블 이미지 삭제 API 및 명세 추가

* [FEAT]: StoreTable 테이블 이미지 삭제 메소드 추가

* [FEAT]: 테이블 이미지 삭제 응답 DTO, Converter 추가

* [FEAT]: 테이블 이미지 삭제 서비스 로직 추가

* [FEAT] : 예약 취소 API 개발 / 마이페이지 예약 조회 API 개발  (#85)

* [FEAT] : 예약 취소 DTO 개발

* [FEAT] : 예약 취소 DTO 개발

* [FEAT] : 예약 취소 성공,실패 상태 추가

* [FIX] : 예약 상태 이름 변경

* [FEAT] : Booking 엔티티에 cancel 비지니스 메서드 추가

* [FEAT] : 예약 취소 API 구현

* [FEAT] : Booking 엔티티 취소 이유 필드와 취소 메서드 구현

* [FEAT] : BookingConverter 구현

* [FEAT] : 예약생성 응답 DTO에 paymentID, orderID 추가

* [CHORE] : 결제 요청 API 스웨거 설명 수정

* [REFACTOR] : 결제 승인 API에서 예약 상태도 변경하도록 코드 구조 변경

* [CHORE] : 결제 완료 처리 api 스웨거 설정 변경

* [REFACTOR] : 예약 생성 api에서 내부적으로 결제 요청 로직까지 진행하도록 코드 변경

* [FEAT] : 예약 엔티티에서 결제 완료된 결제키를 찾는 편의 메서드 개발

* [FEAT] : 예약 취소시 환불까지 연동되는 비즈니스로직 개발

* [FEAT] : 예약 조회 응답 DTO 개발

* [FEAT] : 예약 조회 Repository 개발

* [FEAT] : 예약 조회 Service 개발

* [FEAT] : 예약 조회 Controller 개발

* [CHORE] : application-local.yml 파일 설정 추가

* [FEAT] : BookingMenu 엔티티 개발

* [FEAT] : 예약 생성 요청 DTO 변경

* [FEAT] : 예약 생성 서비스 로직 변경(메뉴 고려)

* [FIX] : 예약금, 결제금액 타입 Integer->Decimal로 변경

* [FIX] : 예약 조회 기본 페이지 1로 변경

* [FIX] : 불필요한 예약 완료 처리 api 삭제

* [REFACTOR] : 존재하지 않는 테이블을 포함해 에약을 생성하는 예외 처리

* [CHORE] : PAYMENT 컨트롤러 SWAGGER 설정 복원

* [FEAT] : 예약 취소 로직 예외 처리 추가

* [FIX] : 결제 금액 타입 Integer -> BigDecimal로 변경

* [FEAT]: 메뉴 관리 CRUD 구현 및 S3 수명주기 추가 (#87)

* [FEAT]: 메뉴 도메인 DTO 추가

* [FEAT]: 메뉴 API 상태코드 추가

* [FEAT]: 메뉴, 메뉴 이미지 등록, 삭제 로직 추가

* [FEAT]: 메뉴 수정 DTO 추가

* [FEAT]: 메뉴 수정 성공 응답코드 추가

* [FEAT]: 메뉴 수정 로직 추가

* [REFACTOR]: S3 예외처리 세분화

* [FEAT]: 메뉴 조회 DTO 및 성공 응답코드 추가

* [FEAT]: 메뉴 조회 로직 추가

* [FEAT]: 메뉴 조회 시 N+1 방지를 위해 fetch join 쿼리 추가

* [FEAT]: 품절여부 변경 DTO 및 성공 응답코드 추가

* [FEAT]: 메뉴 품절여부 변경 로직 추가

* [FEAT]: 메뉴 삭제 시 soft delete 설정

* [FEAT]: @Valid, @RequestParam, @PathVaraible 유효성 검사 실패 시 에러 메시지 보이도록 메서드 추가

* [FEAT]: 이미지 선 업로드에 따른 S3 수명 주기 연동 로직 구현

* [REFACTOR]: 이미지 Url 리턴하도록 수정

* [FEAT]: 품절 여부 수정 요청이 기존과 동일하다면 바로 리턴하도록 조건 추가

* [REFACTOR]: 이미지 삭제 API를 이미 등록된 이미지 삭제하는 것으로 역할 수정

* [REFACTOR]: soft delete된 Menu는 안 가져오도록 쿼리 수정

* [REFACTOR]: @where -> @SQLRestriction 으로 수정

* [FIX]: isSuccess(false)로 수정

* [REFACTOR]: 예약금 정책 변경에 따라 minPrice 필드 삭제 및 로직 삭제

* [REFACTOR]: 남아있던 minPrice 사용하는 로직들 수정

* [FEAT]: 사용하지 않는 removeMenu 메서드 삭제

* [REFACTOR]: 메뉴가 없는 가게도 조회될 수 있도록 쿼리 조건 수정

* [REFACTOR]: 트랜잭션 커밋 이후에 S3에 접근할 수 있도록 로직 수정

* [FEAT]: moveObject()에서 이동 경로가 동일한 경우 예외처리 추가

* [FIX] : QClass 관련 빌드 에러 수정

---------

Co-authored-by: CHAN <150508884+zerochani@users.noreply.github.com>
Co-authored-by: SungMinju <minju2928@naver.com>
Co-authored-by: J_lisa&& <155425927+SungMinju@users.noreply.github.com>
Co-authored-by: twodo0 <dopal0426@gmail.com>
Co-authored-by: 손준규 <144649271+sonjunkyu@users.noreply.github.com>
@coderabbitai coderabbitai bot mentioned this pull request Feb 4, 2026
5 tasks
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

3 participants