Skip to content

[Feat] 배송지·주문·결제 API 구현#705

Open
CUCU7103 wants to merge 8 commits into
devfrom
feature/order-create
Open

[Feat] 배송지·주문·결제 API 구현#705
CUCU7103 wants to merge 8 commits into
devfrom
feature/order-create

Conversation

@CUCU7103

@CUCU7103 CUCU7103 commented Jun 14, 2026

Copy link
Copy Markdown
Collaborator

History

🚀 Major Changes & Explanations

배송지 CRUD API

  • MemberDeliveryAddress 엔티티 및 MemberDeliveryAddressRepository 추가
  • 배송지 등록·조회·수정·삭제·기본 배송지 변경 5개 엔드포인트 구현 (DeliveryAddressController)
  • 단위 테스트 15건, 통합 테스트(TestContainers) 23건 작성

주문 API

  • 주문서 미리보기 및 주문 생성 API 구현 (CustomerOrderController, CustomerOrderService)
  • OrderStatus.PAYMENT_PENDING 상태 추가, Order.orderGroupId 필드 추가 (다중 스토어 주문 그룹 관리)
  • 단위 테스트 11건 작성

결제 승인 API

  • PaymentClient 인터페이스 및 StubPaymentClient 구현체 추가 (로컬/테스트 환경용 스텁)
  • CustomerPaymentService에 비관적 락 기반 동시성 제어 적용
  • 단위 테스트 10건, 통합 테스트 6건 작성

Flyway 마이그레이션

  • V60: member_delivery_address 테이블 생성
  • V61: payment 테이블에 payment_key 컬럼 추가
  • V62: orders 테이블에 order_group_id 컬럼 및 인덱스 추가

📷 Test Image

💡 ETC

Summary by CodeRabbit

릴리스 노트

  • New Features

    • 배송지 관리 기능 추가: 배송지 목록 조회/추가/수정/삭제, 기본 배송지 설정
    • 주문 기능 추가: 주문 미리보기 및 주문 생성
    • 결제 확인 기능 추가: 주문 결제 승인 및 상태 반영
  • Tests

    • 배송지 동시성/검증 관련 통합 및 단위 테스트 추가
    • 주문 미리보기/생성 단위 테스트 추가
    • 결제 확인 통합 및 단위 테스트 추가

CUCU7103 added 5 commits June 7, 2026 18:36
- POST /api/v1/customer/orders/preview: 스토어별 상품 그룹, Board 단위 배송비 계산, 기본 배송지 포함
- POST /api/v1/customer/orders: 다중 판매자 지원, 스토어별 Order 엔티티 분리 생성
- Flyway V59: member_delivery_address 테이블 추가
- MemberDeliveryAddress 도메인 및 레포지토리 추가
- ProductRepository/ProductImgRepository: N+1 방지 JOIN FETCH 쿼리 추가
- BbangleErrorCode: DELIVERY_ADDRESS_NOT_FOUND, ORDER_PRODUCT_EMPTY 추가
- CustomerApiPath: /api/v1/customer/orders/** 인증 경로 추가
- POST /api/v1/customer/delivery-addresses: 배송지 등록
- GET /api/v1/customer/delivery-addresses: 배송지 목록 조회
- PUT /api/v1/customer/delivery-addresses/{id}: 배송지 수정
- DELETE /api/v1/customer/delivery-addresses/{id}: 배송지 삭제
- PATCH /api/v1/customer/delivery-addresses/{id}/default: 기본 배송지 설정
- MemberDeliveryAddress: update(), setDefault(), unsetDefault() 메서드 추가
- BbangleErrorCode: DELIVERY_ADDRESS_ACCESS_DENIED(-858) 추가
- CustomerApiPath: /api/v1/customer/delivery-addresses/** 인증 경로 추가
- 기본 배송지 동시 설정 시 Member 행 비관적 잠금으로 경쟁 조건 해결
- DeliveryAddressSaveRequest.isDefault boolean → Boolean 변환으로 null 처리
- findByIdAndMemberIdAndIsDeletedFalse 단일 쿼리로 배송지 ID 열거 방지 (항상 404)
- CustomerOrderController RequestMapping 누락 슬래시 추가
- MemberDeliveryAddressRepository orphan 메서드 제거
- CustomerOrderService dead null check 제거
- PaymentStatus.PENDING → COMPLETED 수정 (OrderStatus.PAYMENT_COMPLETED 일관성)
- DeliveryAddressServiceUnitTest 단위 테스트 11개 추가
- DeliveryAddressServiceIntegrationTest 통합 테스트 8개 추가
- CustomerOrderServiceUnitTest 단위 테스트 4개 추가
@coderabbitai

coderabbitai Bot commented Jun 14, 2026

Copy link
Copy Markdown

Review Change Stack

Walkthrough

고객용 배송지 CRUD(기본 배송지 비관적 락 포함), 주문 미리보기/생성(스토어별 그룹핑, 배송비/할인 계산, Order/OrderItem/OrderDelivery/Payment 일괄 생성), 결제 승인(그룹 결제 락, PG 클라이언트 추상화) 기능을 신규 추가하고, 관련 단위/통합 테스트 및 Flyway 마이그레이션 3건을 함께 추가한다.

Changes

배송지 관리 기능

Layer / File(s) Summary
배송지 엔티티, 리포지토리 및 DB 마이그레이션
src/main/resources/flyway/V60__app.sql, src/main/java/com/bbangle/bbangle/member/domain/MemberDeliveryAddress.java, src/main/java/com/bbangle/bbangle/member/repository/MemberDeliveryAddressRepository.java, src/main/java/com/bbangle/bbangle/member/repository/MemberRepository.java
member_delivery_address 테이블 DDL, MemberDeliveryAddress JPA 엔티티(setDefault/unsetDefault/update 메서드 포함), MemberDeliveryAddressRepository 쿼리 메서드, MemberRepository에 비관적 쓰기 락 조회 메서드를 추가한다.
배송지 서비스 로직
src/main/java/com/bbangle/bbangle/member/customer/service/DeliveryAddressService.java
배송지 목록 조회, 추가, 수정, 삭제, 기본 배송지 설정 메서드를 구현한다. 기본 배송지 변경 시 멤버 행에 비관적 락을 획득하고 기존 기본 배송지를 해제한다. findOwnedAddress로 소유권과 삭제 여부를 함께 검증한다.
배송지 DTO 및 컨트롤러
src/main/java/com/bbangle/bbangle/member/customer/controller/dto/request/DeliveryAddressSaveRequest.java, src/main/java/com/bbangle/bbangle/member/customer/controller/dto/response/DeliveryAddressResponse.java, src/main/java/com/bbangle/bbangle/member/customer/controller/DeliveryAddressController.java, src/main/java/com/bbangle/bbangle/config/security/CustomerApiPath.java, src/main/java/com/bbangle/bbangle/exception/BbangleErrorCode.java
요청/응답 DTO record, 5개 REST 엔드포인트 컨트롤러, 신규 에러 코드(DELIVERY_ADDRESS_NOT_FOUND 등 7개), 보안 경로 패턴을 추가한다.
배송지 서비스 단위/통합 테스트
src/test/java/com/bbangle/bbangle/member/customer/service/DeliveryAddressServiceUnitTest.java, src/test/java/com/bbangle/bbangle/member/customer/service/DeliveryAddressServiceIntegrationTest.java
비관적 락 호출 여부, isDefault=null 처리, 소유권 열거 방지(DELIVERY_ADDRESS_NOT_FOUND)를 Mockito 단위 테스트로 검증하고, 동시성 시나리오(CountDownLatch, ExecutorService)와 DB 상태를 통합 테스트로 검증한다.

주문 미리보기/생성 기능

Layer / File(s) Summary
Order 도메인 확장 및 리포지토리
src/main/resources/flyway/V62__app.sql, src/main/java/com/bbangle/bbangle/order/domain/Order.java, src/main/java/com/bbangle/bbangle/order/domain/model/OrderStatus.java, src/main/java/com/bbangle/bbangle/order/domain/OrderItem.java, src/main/java/com/bbangle/bbangle/order/repository/OrderRepository.java
OrderorderGroupId 필드(NOT NULL), OrderStatusPAYMENT_PENDING 상태, OrderItemcompletePayment() 상태 전이 메서드(INVALID_ORDER_STATUS_TRANSITION 검증), OrderRepository에 주문번호/그룹ID 조회 메서드를 추가한다.
주문 요청/응답 DTO 스키마
src/main/java/com/bbangle/bbangle/order/customer/dto/request/OrderPreviewRequest.java, src/main/java/com/bbangle/bbangle/order/customer/dto/request/OrderCreateRequest.java, src/main/java/com/bbangle/bbangle/order/customer/dto/response/OrderPreviewResponse.java, src/main/java/com/bbangle/bbangle/order/customer/dto/response/OrderCreateResponse.java
OrderPreviewRequest(중첩 OrderProductItem), OrderCreateRequest(중첩 OrderProductItem), OrderPreviewResponse(StoreOrderGroup/OrderItemInfo/DeliveryAddressInfo 중첩 record), OrderCreateResponse DTO를 정의한다.
상품 조회 메서드 및 CustomerOrderService
src/main/java/com/bbangle/bbangle/board/repository/ProductRepository.java, src/main/java/com/bbangle/bbangle/board/repository/ProductImgRepository.java, src/main/java/com/bbangle/bbangle/order/customer/service/CustomerOrderService.java
findAllWithBoardAndStoreByIdIn JOIN FETCH, 썸네일 조회 메서드 추가 후, CustomerOrderService에서 미리보기(상품 정규화, 스토어별 그룹핑, 할인율/할인액 계산, 무료배송 조건 검증, 배송비 누적)와 주문 생성(Order/OrderItem/OrderDelivery/Payment 일괄 생성, orderGroupId 생성, 주문번호 포맷) 로직을 구현한다.
CustomerOrderController
src/main/java/com/bbangle/bbangle/order/customer/controller/CustomerOrderController.java
POST /preview/orders 엔드포인트를 추가하고 @Valid 검증과 ResponseService 래핑을 적용한다.
CustomerOrderService 단위 테스트
src/test/java/com/bbangle/bbangle/order/customer/service/CustomerOrderServiceUnitTest.java
ORDER_PRODUCT_EMPTY, NOTFOUND_MEMBER, DELIVERY_ADDRESS_NOT_FOUND 예외 케이스와 RATE 할인/배송비 합산 성공 케이스를 검증한다.

결제 승인 기능

Layer / File(s) Summary
Payment 엔티티 확장 및 클라이언트 인터페이스
src/main/resources/flyway/V61__app.sql, src/main/java/com/bbangle/bbangle/payment/domain/Payment.java, src/main/java/com/bbangle/bbangle/payment/client/PaymentClient.java, src/main/java/com/bbangle/bbangle/payment/client/dto/PaymentConfirmResult.java, src/main/java/com/bbangle/bbangle/payment/client/StubPaymentClient.java
payment_key 컬럼(VARCHAR 200) 마이그레이션, Payment에 confirm(paymentKey, approvedAt)/fail() 메서드(INVALID_ORDER_STATUS_TRANSITION 검증), PaymentClient 인터페이스, PaymentConfirmResult DTO, StubPaymentClient(local/test/dev 프로파일 @Primary)를 추가한다.
PaymentRepository 확장 및 CustomerPaymentService
src/main/java/com/bbangle/bbangle/payment/repository/PaymentRepository.java, src/main/java/com/bbangle/bbangle/payment/customer/service/CustomerPaymentService.java
findByOrderOrderNumber/findByOrderGroupIdWithLock(PESSIMISTIC_WRITE, JOIN FETCH) 메서드 추가 후, CustomerPaymentService.confirm에서 주문 소유권/결제 상태/금액 검증 → PaymentClient.confirm 호출 → 그룹 전체 Payment와 OrderItem을 COMPLETED로 전환하는 @Transactional 흐름을 구현한다.
결제 요청/응답 DTO 및 컨트롤러
src/main/java/com/bbangle/bbangle/payment/customer/dto/request/PaymentConfirmRequest.java, src/main/java/com/bbangle/bbangle/payment/customer/dto/response/PaymentConfirmResponse.java, src/main/java/com/bbangle/bbangle/payment/customer/controller/CustomerPaymentController.java
PaymentConfirmRequest(검증 어노테이션 @NotBlank/@Positive), PaymentConfirmResponse, CustomerPaymentController(POST /payments/confirm, ResponseEntity.ok 반환)를 추가한다.
결제 서비스 단위/통합 테스트
src/test/java/com/bbangle/bbangle/payment/customer/service/CustomerPaymentServiceUnitTest.java, src/test/java/com/bbangle/bbangle/payment/customer/service/CustomerPaymentServiceIntegrationTest.java, src/test/java/com/bbangle/bbangle/fixture/payment/domain/PaymentFixture.java
성공, ORDER_NOT_FOUND, ORDER_ACCESS_DENIED, PAYMENT_NOT_FOUND, PAYMENT_ALREADY_COMPLETED(COMPLETED/CANCELED/REFUNDED), PAYMENT_AMOUNT_MISMATCH 케이스를 단위/통합 테스트로 검증하고 PaymentFixture에 createPaymentWithStatus(order, status) 오버로드를 추가한다.

테스트 픽스처 및 데이터 정책 업데이트

Layer / File(s) Summary
주문 픽스처 및 SQL 데이터
src/test/java/com/bbangle/bbangle/fixture/order/domain/OrderFixture.java, src/test/java/com/bbangle/bbangle/settlement/seller/controller/SellerSettlementItemControllerIntegrationTest.java, src/test/java/com/bbangle/bbangle/settlement/seller/excel/controller/SellerSettlementItemExcelIntegrationTest.java
OrderFixture.defaultOrder()에 orderGroupId 설정 추가, SellerSettlement 통합 테스트 @Sql INSERT 구문의 orders 테이블에 order_group_id 컬럼/값을 추가해 신규 DB 스키마와 동기화한다.

Sequence Diagram(s)

sequenceDiagram
  participant Client as 클라이언트
  participant DeliveryCtrl as DeliveryAddressController
  participant DeliveryService as DeliveryAddressService
  participant OrderCtrl as CustomerOrderController
  participant OrderService as CustomerOrderService
  participant PaymentCtrl as CustomerPaymentController
  participant PaymentService as CustomerPaymentService
  participant PaymentClient as PaymentClient

  rect rgba(100, 149, 237, 0.5)
    Note over Client,DeliveryService: 배송지 관리
    Client->>DeliveryCtrl: POST /delivery-addresses (isDefault=true)
    DeliveryCtrl->>DeliveryService: addDeliveryAddress(memberId, request)
    DeliveryService->>DeliveryService: findByIdWithLock(멤버 비관적 락)
    DeliveryService->>DeliveryService: unsetDefault(기존 기본 배송지)
    DeliveryService-->>DeliveryCtrl: DeliveryAddressResponse
    DeliveryCtrl-->>Client: 201 Created
  end

  rect rgba(70, 130, 180, 0.5)
    Note over Client,OrderService: 주문 미리보기
    Client->>OrderCtrl: POST /orders/preview
    OrderCtrl->>OrderService: getOrderPreview(memberId, request)
    OrderService->>OrderService: 상품 수량 집계, JOIN FETCH, 썸네일 수집
    OrderService->>OrderService: 배송비/할인 계산, 스토어 그룹핑
    OrderService-->>OrderCtrl: OrderPreviewResponse
    OrderCtrl-->>Client: SingleResult
  end

  rect rgba(60, 179, 113, 0.5)
    Note over Client,OrderService: 주문 생성
    Client->>OrderCtrl: POST /orders
    OrderCtrl->>OrderService: createOrders(memberId, request)
    OrderService->>OrderService: 배송지 해석, Order/OrderItem/OrderDelivery/Payment 생성
    OrderService-->>OrderCtrl: OrderCreateResponse(orderIds, orderNumber)
    OrderCtrl-->>Client: SingleResult
  end

  rect rgba(210, 105, 30, 0.5)
    Note over Client,PaymentClient: 결제 승인
    Client->>PaymentCtrl: POST /payments/confirm
    PaymentCtrl->>PaymentService: confirm(memberId, request)
    PaymentService->>PaymentService: 주문 소유권 검증
    PaymentService->>PaymentService: findByOrderGroupIdWithLock(결제 비관적 락)
    PaymentService->>PaymentService: 결제 상태/금액 검증
    PaymentService->>PaymentClient: confirm(paymentKey, orderNumber, amount)
    PaymentClient-->>PaymentService: PaymentConfirmResult
    PaymentService->>PaymentService: Payment/OrderItem COMPLETED 전환
    PaymentService-->>PaymentCtrl: PaymentConfirmResponse(COMPLETED)
    PaymentCtrl-->>Client: 200 OK
  end
Loading

Estimated code review effort

🎯 4 (Complex) | ⏱️ ~90 minutes

Possibly related PRs

  • eco-dessert-platform/backend#612: 동일한 OrderStatus.java에 상태 그룹 상수를 추가하는 변경으로, 이번 PR이 추가한 PAYMENT_PENDING 상태와 열거형 코드 수준에서 중복된다.
  • eco-dessert-platform/backend#587: 동일한 Shipping 클래스에 정적 메서드를 추가하는 변경으로, 이번 PR의 withMemo()와 같은 도메인 확장 패턴을 공유한다.
  • eco-dessert-platform/backend#690: 동일한 BbangleErrorCode.java enum의 EXCEL_CREATE_FAILED 인근에 에러 코드를 추가하는 변경으로 병합 충돌 가능성이 있다.

Suggested labels

feat, test

Suggested reviewers

  • kmindev
  • exjuu

Poem

🐰 토끼가 배송지를 정리하고,
주문서를 꼼꼼히 미리 살펴보고,
비관적 락으로 동시성 방어하고,
결제 버튼 딱! COMPLETED~
테스트 초록 불이 반짝반짝! 🌟

🚥 Pre-merge checks | ✅ 4 | ❌ 1

❌ Failed checks (1 warning)

Check name Status Explanation Resolution
Docstring Coverage ⚠️ Warning Docstring coverage is 11.84% which is insufficient. The required threshold is 80.00%. Write docstrings for the functions missing them to satisfy the coverage threshold.
✅ Passed checks (4 passed)
Check name Status Explanation
Description Check ✅ Passed Check skipped - CodeRabbit’s high-level summary is enabled.
Title check ✅ Passed 제목은 배송지·주문·결제 API 구현이라는 PR의 주요 변경사항을 명확하게 설명하고 있습니다.
Linked Issues check ✅ Passed Check skipped because no linked issues were found for this pull request.
Out of Scope Changes check ✅ Passed Check skipped because no linked issues were found for this pull request.

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

✨ Finishing Touches
📝 Generate docstrings
  • Create stacked PR
  • Commit on current branch
🧪 Generate unit tests (beta)
  • Create PR with unit tests
  • Commit unit tests in branch feature/order-create
⚔️ Resolve merge conflicts
  • Resolve merge conflict in branch feature/order-create

Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out.

❤️ Share

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

@CUCU7103 CUCU7103 self-assigned this Jun 14, 2026
@CUCU7103 CUCU7103 requested review from exjuu, kmindev and mono0801 June 14, 2026 14:12
@CUCU7103 CUCU7103 added the feat New feature or request label Jun 14, 2026

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 10

🧹 Nitpick comments (3)
src/test/java/com/bbangle/bbangle/order/customer/service/CustomerOrderServiceUnitTest.java (1)

188-219: ⚡ Quick win

createOrders 성공 경로 검증 케이스를 추가해 주세요.

현재 성공 검증이 getOrderPreview에만 집중되어 있어 deliveryRequest/결제 생성/주문 그룹 생성 같은 핵심 생성 플로우 회귀를 놓치기 쉽습니다. createOrders 성공 케이스 1개를 추가해 계약을 고정하는 편이 좋습니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/bbangle/bbangle/order/customer/service/CustomerOrderServiceUnitTest.java`
around lines 188 - 219, Add a success test case for the createOrders method in
the OrderPreviewSuccess nested class to verify the complete order creation flow.
The test should mock the necessary dependencies (productRepository,
productImgRepository, memberDeliveryAddressRepository, and other services for
payment/delivery/order creation), create an OrderCreateRequest, call
customerOrderService.createOrders with appropriate parameters, and assert that
the response contains the expected order data and that the core creation
operations (deliveryRequest creation, payment creation, and order group
creation) are properly executed. This will ensure regression coverage for the
full createOrders contract beyond just the getOrderPreview preview calculations.
src/test/java/com/bbangle/bbangle/payment/customer/service/CustomerPaymentServiceIntegrationTest.java (1)

89-187: 🏗️ Heavy lift

비관적 락 핵심 경로(동시 승인) 통합 테스트를 추가하는 것을 권장합니다.

현재는 단일 요청 기준 검증만 있어, 동일 orderNumber 동시 승인 시 “한 건만 성공” 계약을 회귀로부터 보호하기 어렵습니다. 2개 스레드 동시 호출 테스트를 추가해 한 건 성공 + 한 건 PAYMENT_ALREADY_COMPLETED를 검증해 주세요.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/bbangle/bbangle/payment/customer/service/CustomerPaymentServiceIntegrationTest.java`
around lines 89 - 187, Add a new integration test method in the ConfirmFail
nested test class to verify the pessimistic locking behavior during concurrent
payment confirmation. Create a test that spawns two threads making simultaneous
calls to customerPaymentService.confirm() with the same orderNumber and
PaymentConfirmRequest, ensuring proper thread synchronization so both threads
execute the call at nearly the same time. Verify that one thread succeeds (the
first to acquire the lock) and the other thread receives a BbangleException with
BbangleErrorCode.PAYMENT_ALREADY_COMPLETED, confirming that the "only one
success per orderNumber" contract is protected against concurrent confirmation
attempts.
src/main/resources/flyway/V60__app.sql (1)

1-17: ⚡ Quick win

기본 배송지 유일성을 DB 레벨에서도 보장하는 것을 검토해보세요.

현재 스키마는 동일 member_id에 대해 여러 개의 is_default=1 행을 허용합니다. 서비스 레이어에서 비관적 락으로 동시성을 제어하고 있지만, DB 직접 접근이나 애플리케이션 버그 시 데이터 무결성이 깨질 수 있습니다.

MySQL 8.0+에서 partial unique index(filtered index)를 활용하거나, 또는 별도의 CHECK 제약이나 트리거로 보강할 수 있습니다.

💡 부분 유니크 인덱스 예시

MySQL 8.0.13+ 부터 functional index를 사용할 수 있습니다:

CREATE UNIQUE INDEX idx_mda_member_default 
ON member_delivery_address (member_id, (CASE WHEN is_default = 1 THEN 1 END));

또는 애플리케이션 레벨 제어만으로 충분하다고 판단하시면 현재 상태를 유지하셔도 됩니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/main/resources/flyway/V60__app.sql` around lines 1 - 17, The
member_delivery_address table creation lacks database-level enforcement of the
uniqueness constraint for default delivery addresses per member. Currently,
multiple rows with is_default=1 can exist for the same member_id, which could
compromise data integrity if accessed directly or through application bugs. Add
a partial unique index on the member_delivery_address table that enforces
uniqueness of the combination of member_id and is_default when is_default equals
1. This can be accomplished using a functional index with a CASE expression in
MySQL 8.0.13+, placed after the existing index creation statement in the same
migration file.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In
`@src/main/java/com/bbangle/bbangle/member/customer/service/DeliveryAddressService.java`:
- Around line 75-84: The member lock acquisition in the DeliveryAddressService
is conditional on both makeDefault being true AND the address not already being
default (the !address.isDefault() condition on line 77), which creates a race
condition. When an address is already the default delivery address and an
isDefault=true update request comes in, no lock is acquired, allowing concurrent
transactions to change the default address and potentially violate the invariant
of having exactly one default delivery address per member. Remove the
!address.isDefault() condition from the lock path check so that the
memberRepository.findByIdWithLock(memberId) call and subsequent default address
handling is always executed whenever makeDefault is true, regardless of the
current state of the address.

In
`@src/main/java/com/bbangle/bbangle/order/customer/service/CustomerOrderService.java`:
- Around line 270-275: The OrderDelivery.create() method call in
CustomerOrderService is passing null as the third parameter instead of the
deliveryRequest data from the OrderCreateRequest. Replace the null value with
the appropriate deliveryRequest field from the order creation request object to
ensure delivery request information is preserved during order creation.

In `@src/main/java/com/bbangle/bbangle/order/domain/model/OrderStatus.java`:
- Line 16: The new `PAYMENT_PENDING` status is not included in any of the screen
aggregation groups (lines 54-83), which means it will not be properly counted in
the seller dashboard tab aggregation performed by
`SellerOrderService.buildStatusCounts()`. Determine whether `PAYMENT_PENDING`
should be added to the `PAYMENT_COMPLETED_GROUP` constant or if a separate
aggregation group should be created for it based on the intended behavior. Once
decided, add `PAYMENT_PENDING` to the appropriate Set constant, and consider
adding an explicit comment similar to those in the return/exchange status groups
to clarify when statuses should be included in this group to prevent future
oversights.

In `@src/main/java/com/bbangle/bbangle/order/domain/Order.java`:
- Around line 43-44: The orderGroupId field lacks a not-null constraint, which
could allow null values to be persisted and break the payment group inquiry
contract. Add a `@NotNull` (or `@Column` with nullable=false) constraint annotation
to the orderGroupId field declaration in the Order class to enforce that this
payment group key cannot be null. Additionally, ensure any creation paths or
constructors that initialize the Order entity also validate that orderGroupId is
not null before object creation.

In `@src/main/java/com/bbangle/bbangle/order/repository/OrderRepository.java`:
- Around line 10-12: The findByOrderNumber method in OrderRepository is designed
to return a single Optional result, but the database schema does not enforce
uniqueness on the order_number column, risking
IncorrectResultSizeDataAccessException if duplicates occur. Add a UNIQUE
constraint to the order_number column definition in the migration file
src/main/resources/flyway/V21__app.sql, or alternatively change the
findByOrderNumber method signature to return List<Order> instead of
Optional<Order> to match the actual database behavior. The former approach
(adding UNIQUE constraint) is preferred to maintain data integrity and match the
method's single-result contract.

In
`@src/main/java/com/bbangle/bbangle/payment/customer/service/CustomerPaymentService.java`:
- Around line 41-65: The pessimistic lock acquired by
paymentRepository.findByOrderGroupIdWithLock(orderGroupId) is being held
throughout the entire method including the external paymentClient.confirm(...)
network call, which can cause prolonged blocking if the PG experiences delays.
Refactor to separate the lock-based validation phase from the external call
phase: first execute all validation logic (payment status check, amount
verification, etc.) within the locked transaction to ensure data consistency,
then release the lock and make the paymentClient.confirm(...) call in a separate
transaction outside the lock scope. This will reduce lock contention for
concurrent requests on the same orderGroupId.

In `@src/main/java/com/bbangle/bbangle/payment/domain/Payment.java`:
- Around line 60-68: The confirm() and fail() methods in the Payment class lack
state validation guards, allowing invalid state transitions that can corrupt
payment history. Add validation checks at the start of both methods to ensure
the current paymentStatus is PENDING before allowing the transition. If the
status is not PENDING, throw an appropriate exception (such as
IllegalStateException) with a descriptive message indicating the invalid state
transition. This ensures that payment status can only transition from PENDING to
either COMPLETED (via confirm) or FAILED (via fail), preventing accidental state
overwrites.

In `@src/main/java/com/bbangle/bbangle/payment/repository/PaymentRepository.java`:
- Around line 16-18: The findByOrderGroupIdWithLock method currently only JOIN
FETCHes the order but not the orderItems collection, causing N+1 queries when
service code accesses payment.getOrder().getOrderItems() to modify OrderItem
statuses. Modify the `@Query` annotation to add JOIN FETCH for the orderItems
collection alongside the existing order fetch (e.g., JOIN FETCH p.order o JOIN
FETCH o.orderItems), and add DISTINCT to the SELECT clause to eliminate
duplicate rows that collection JOIN FETCH can create.

In
`@src/test/java/com/bbangle/bbangle/member/customer/service/DeliveryAddressServiceIntegrationTest.java`:
- Around line 113-114: The thread pool executor is not being properly shut down
in the concurrency test, which can cause interference between tests. In the
DeliveryAddressServiceIntegrationTest class at lines 113-114 and lines 173-174,
the executor.shutdown() calls are not waiting for the thread pool to actually
terminate before the test completes. After calling executor.shutdown() at both
locations, add executor.awaitTermination() with an appropriate timeout (e.g., a
few seconds), and if it returns false, call executor.shutdownNow() to forcefully
terminate any remaining threads. This ensures proper cleanup of worker threads
and prevents test interference.

In
`@src/test/java/com/bbangle/bbangle/payment/customer/service/CustomerPaymentServiceUnitTest.java`:
- Around line 205-245: Both test methods canceledPaymentRejected() and
refundedPaymentRejected() are missing verification that the
paymentClient.confirm() method is not invoked when payments in CANCELED or
REFUNDED status are rejected. Add a verify statement using Mockito to assert
that paymentClient.confirm() is never called in each of these test methods,
similar to how other failure case tests validate that the payment confirmation
endpoint is not called. This verification should be added in the when & then
section after the assertThatThrownBy assertion.

---

Nitpick comments:
In `@src/main/resources/flyway/V60__app.sql`:
- Around line 1-17: The member_delivery_address table creation lacks
database-level enforcement of the uniqueness constraint for default delivery
addresses per member. Currently, multiple rows with is_default=1 can exist for
the same member_id, which could compromise data integrity if accessed directly
or through application bugs. Add a partial unique index on the
member_delivery_address table that enforces uniqueness of the combination of
member_id and is_default when is_default equals 1. This can be accomplished
using a functional index with a CASE expression in MySQL 8.0.13+, placed after
the existing index creation statement in the same migration file.

In
`@src/test/java/com/bbangle/bbangle/order/customer/service/CustomerOrderServiceUnitTest.java`:
- Around line 188-219: Add a success test case for the createOrders method in
the OrderPreviewSuccess nested class to verify the complete order creation flow.
The test should mock the necessary dependencies (productRepository,
productImgRepository, memberDeliveryAddressRepository, and other services for
payment/delivery/order creation), create an OrderCreateRequest, call
customerOrderService.createOrders with appropriate parameters, and assert that
the response contains the expected order data and that the core creation
operations (deliveryRequest creation, payment creation, and order group
creation) are properly executed. This will ensure regression coverage for the
full createOrders contract beyond just the getOrderPreview preview calculations.

In
`@src/test/java/com/bbangle/bbangle/payment/customer/service/CustomerPaymentServiceIntegrationTest.java`:
- Around line 89-187: Add a new integration test method in the ConfirmFail
nested test class to verify the pessimistic locking behavior during concurrent
payment confirmation. Create a test that spawns two threads making simultaneous
calls to customerPaymentService.confirm() with the same orderNumber and
PaymentConfirmRequest, ensuring proper thread synchronization so both threads
execute the call at nearly the same time. Verify that one thread succeeds (the
first to acquire the lock) and the other thread receives a BbangleException with
BbangleErrorCode.PAYMENT_ALREADY_COMPLETED, confirming that the "only one
success per orderNumber" contract is protected against concurrent confirmation
attempts.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: b52a6a15-9cc9-450a-a17b-717463c1b055

📥 Commits

Reviewing files that changed from the base of the PR and between 49c8b63 and 3bf9d0c.

📒 Files selected for processing (39)
  • src/main/java/com/bbangle/bbangle/board/repository/ProductImgRepository.java
  • src/main/java/com/bbangle/bbangle/board/repository/ProductRepository.java
  • src/main/java/com/bbangle/bbangle/config/security/CustomerApiPath.java
  • src/main/java/com/bbangle/bbangle/exception/BbangleErrorCode.java
  • src/main/java/com/bbangle/bbangle/member/customer/controller/DeliveryAddressController.java
  • src/main/java/com/bbangle/bbangle/member/customer/controller/dto/request/DeliveryAddressSaveRequest.java
  • src/main/java/com/bbangle/bbangle/member/customer/controller/dto/response/DeliveryAddressResponse.java
  • src/main/java/com/bbangle/bbangle/member/customer/service/DeliveryAddressService.java
  • src/main/java/com/bbangle/bbangle/member/domain/MemberDeliveryAddress.java
  • src/main/java/com/bbangle/bbangle/member/repository/MemberDeliveryAddressRepository.java
  • src/main/java/com/bbangle/bbangle/member/repository/MemberRepository.java
  • src/main/java/com/bbangle/bbangle/order/customer/controller/CustomerOrderController.java
  • src/main/java/com/bbangle/bbangle/order/customer/dto/request/OrderCreateRequest.java
  • src/main/java/com/bbangle/bbangle/order/customer/dto/request/OrderPreviewRequest.java
  • src/main/java/com/bbangle/bbangle/order/customer/dto/response/OrderCreateResponse.java
  • src/main/java/com/bbangle/bbangle/order/customer/dto/response/OrderPreviewResponse.java
  • src/main/java/com/bbangle/bbangle/order/customer/service/CustomerOrderService.java
  • src/main/java/com/bbangle/bbangle/order/domain/Order.java
  • src/main/java/com/bbangle/bbangle/order/domain/OrderItem.java
  • src/main/java/com/bbangle/bbangle/order/domain/model/OrderStatus.java
  • src/main/java/com/bbangle/bbangle/order/repository/OrderRepository.java
  • src/main/java/com/bbangle/bbangle/payment/client/PaymentClient.java
  • src/main/java/com/bbangle/bbangle/payment/client/StubPaymentClient.java
  • src/main/java/com/bbangle/bbangle/payment/client/dto/PaymentConfirmResult.java
  • src/main/java/com/bbangle/bbangle/payment/customer/controller/CustomerPaymentController.java
  • src/main/java/com/bbangle/bbangle/payment/customer/dto/request/PaymentConfirmRequest.java
  • src/main/java/com/bbangle/bbangle/payment/customer/dto/response/PaymentConfirmResponse.java
  • src/main/java/com/bbangle/bbangle/payment/customer/service/CustomerPaymentService.java
  • src/main/java/com/bbangle/bbangle/payment/domain/Payment.java
  • src/main/java/com/bbangle/bbangle/payment/repository/PaymentRepository.java
  • src/main/resources/flyway/V60__app.sql
  • src/main/resources/flyway/V61__app.sql
  • src/main/resources/flyway/V62__app.sql
  • src/test/java/com/bbangle/bbangle/fixture/payment/domain/PaymentFixture.java
  • src/test/java/com/bbangle/bbangle/member/customer/service/DeliveryAddressServiceIntegrationTest.java
  • src/test/java/com/bbangle/bbangle/member/customer/service/DeliveryAddressServiceUnitTest.java
  • src/test/java/com/bbangle/bbangle/order/customer/service/CustomerOrderServiceUnitTest.java
  • src/test/java/com/bbangle/bbangle/payment/customer/service/CustomerPaymentServiceIntegrationTest.java
  • src/test/java/com/bbangle/bbangle/payment/customer/service/CustomerPaymentServiceUnitTest.java

Comment thread src/main/java/com/bbangle/bbangle/order/domain/Order.java Outdated
Comment thread src/main/java/com/bbangle/bbangle/payment/domain/Payment.java
Comment on lines +113 to +114
executor.shutdown();

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

⚠️ Potential issue | 🟡 Minor | ⚡ Quick win

동시성 테스트에서 스레드풀이 완전히 종료되지 않아 테스트 간 간섭이 생길 수 있습니다.

Line 113, Line 173에서 shutdown()만 호출하고 종료를 기다리지 않아, 타임아웃/실패 케이스에서 워커가 남을 수 있습니다. awaitTermination과 타임아웃 시 shutdownNow()를 추가해 정리를 보장해 주세요.

🔧 제안 수정안
-            executor.shutdown();
+            executor.shutdown();
+            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
+                executor.shutdownNow();
+            }
@@
-            executor.shutdown();
+            executor.shutdown();
+            if (!executor.awaitTermination(5, TimeUnit.SECONDS)) {
+                executor.shutdownNow();
+            }

Also applies to: 173-174

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In
`@src/test/java/com/bbangle/bbangle/member/customer/service/DeliveryAddressServiceIntegrationTest.java`
around lines 113 - 114, The thread pool executor is not being properly shut down
in the concurrency test, which can cause interference between tests. In the
DeliveryAddressServiceIntegrationTest class at lines 113-114 and lines 173-174,
the executor.shutdown() calls are not waiting for the thread pool to actually
terminate before the test completes. After calling executor.shutdown() at both
locations, add executor.awaitTermination() with an appropriate timeout (e.g., a
few seconds), and if it returns false, call executor.shutdownNow() to forcefully
terminate any remaining threads. This ensures proper cleanup of worker threads
and prevents test interference.

CUCU7103 added 3 commits June 21, 2026 19:15
- Shipping.withMemo() 팩토리 추가 및 주문 생성 시 deliveryRequest null 전달 수정
- Order.orderGroupId nullable = false 제약 추가
- Payment.confirm() / fail() PENDING 상태 전이 가드 추가
- PaymentRepository N+1 쿼리 해결: LEFT JOIN FETCH o.orderItems + DISTINCT 적용
- OrderFixture.defaultOrder()에 orderGroupId 기본값 추가
- SellerSettlementItem 통합 테스트 @SQL INSERT에 order_group_id 컬럼 추가

@coderabbitai coderabbitai Bot left a comment

Copy link
Copy Markdown

Choose a reason for hiding this comment

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

Actionable comments posted: 1

🧹 Nitpick comments (1)
src/test/java/com/bbangle/bbangle/fixture/order/domain/OrderFixture.java (1)

16-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick win

기본 픽스처의 orderGroupId 고정값은 테스트 간 의도치 않은 그룹 결합을 만들 수 있습니다.

defaultOrder()에서 동일한 그룹 ID를 계속 재사용하면, 한 테스트 내 다중 주문 시 그룹 기반 로직 검증이 왜곡될 수 있습니다. 기본값은 생성 시 유니크하게 만들거나, 호출자가 반드시 주입하도록 분리하는 쪽이 더 안전합니다.

🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

In `@src/test/java/com/bbangle/bbangle/fixture/order/domain/OrderFixture.java` at
line 16, The hardcoded orderGroupId value in the defaultOrder() method causes
unintended side effects when multiple orders are created in the same test, as
they will share the same group ID. This can distort validation of group-based
logic. Either generate a unique orderGroupId for each invocation of
defaultOrder() (using UUID or timestamp-based generation), or refactor the
method to accept orderGroupId as a parameter so callers can explicitly provide
or override the value instead of relying on the fixed default.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.

Inline comments:
In `@src/main/resources/flyway/V63__app.sql`:
- Around line 2-5: The migration adds NOT NULL and UNIQUE constraints directly
to the orders table without first ensuring existing data complies with these
constraints, risking Flyway migration failure. Before applying the ALTER TABLE
statements that modify order_group_id to NOT NULL and add the UNIQUE constraint
on order_number, first add preparatory SQL statements to clean the operational
data: include a statement to identify and correct any NULL values in the
order_group_id column (backfill with appropriate defaults or remove problematic
rows), and include a statement to identify and resolve any duplicate
order_number values (detect duplicates and either remove or normalize them).
Only after ensuring data integrity should you apply the ALTER TABLE MODIFY and
ALTER TABLE ADD UNIQUE KEY statements.

---

Nitpick comments:
In `@src/test/java/com/bbangle/bbangle/fixture/order/domain/OrderFixture.java`:
- Line 16: The hardcoded orderGroupId value in the defaultOrder() method causes
unintended side effects when multiple orders are created in the same test, as
they will share the same group ID. This can distort validation of group-based
logic. Either generate a unique orderGroupId for each invocation of
defaultOrder() (using UUID or timestamp-based generation), or refactor the
method to accept orderGroupId as a parameter so callers can explicitly provide
or override the value instead of relying on the fixed default.
🪄 Autofix (Beta)

Fix all unresolved CodeRabbit comments on this PR:

  • Push a commit to this branch (recommended)
  • Create a new PR with the fixes

ℹ️ Review info
⚙️ Run configuration

Configuration used: Organization UI

Review profile: CHILL

Plan: Pro

Run ID: 3cf7545e-893f-4107-8518-f4c9f0409784

📥 Commits

Reviewing files that changed from the base of the PR and between 3bf9d0c and ce26114.

📒 Files selected for processing (9)
  • src/main/java/com/bbangle/bbangle/delivery/domain/Shipping.java
  • src/main/java/com/bbangle/bbangle/order/customer/service/CustomerOrderService.java
  • src/main/java/com/bbangle/bbangle/order/domain/Order.java
  • src/main/java/com/bbangle/bbangle/payment/domain/Payment.java
  • src/main/java/com/bbangle/bbangle/payment/repository/PaymentRepository.java
  • src/main/resources/flyway/V63__app.sql
  • src/test/java/com/bbangle/bbangle/fixture/order/domain/OrderFixture.java
  • src/test/java/com/bbangle/bbangle/settlement/seller/controller/SellerSettlementItemControllerIntegrationTest.java
  • src/test/java/com/bbangle/bbangle/settlement/seller/excel/controller/SellerSettlementItemExcelIntegrationTest.java
🚧 Files skipped from review as they are similar to previous changes (4)
  • src/main/java/com/bbangle/bbangle/order/domain/Order.java
  • src/main/java/com/bbangle/bbangle/payment/repository/PaymentRepository.java
  • src/main/java/com/bbangle/bbangle/payment/domain/Payment.java
  • src/main/java/com/bbangle/bbangle/order/customer/service/CustomerOrderService.java

Comment thread src/main/resources/flyway/V63__app.sql
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

feat New feature or request

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant