From 348a0ad620a5e5d9b020f478a26ecdeecc8d3210 Mon Sep 17 00:00:00 2001 From: YouSeok518 Date: Wed, 15 Oct 2025 23:58:34 +0900 Subject: [PATCH] =?UTF-8?q?=EC=9E=A5=EB=B0=94=EA=B5=AC=EB=8B=88=20api=20?= =?UTF-8?q?=ED=98=B8=EC=B6=9C=EC=8B=9C=20=EC=97=90=EB=9F=AC=20=EB=B0=9C?= =?UTF-8?q?=EC=83=9D=20=ED=95=B4=EA=B2=B0?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/back/domain/cart/entity/Cart.java | 65 +++++++++++-------- .../back/domain/cart/service/CartService.java | 27 +++++++- .../domain/cart/service/CartServiceTest.java | 8 +-- 3 files changed, 69 insertions(+), 31 deletions(-) diff --git a/src/main/java/com/back/domain/cart/entity/Cart.java b/src/main/java/com/back/domain/cart/entity/Cart.java index b6713981..70c8b9d0 100644 --- a/src/main/java/com/back/domain/cart/entity/Cart.java +++ b/src/main/java/com/back/domain/cart/entity/Cart.java @@ -126,43 +126,49 @@ public boolean isFundingCart() { return this.cartType == CartType.FUNDING; } - // 유효한 장바구니 아이템인지 확인 + // 유효한 장바구니 아이템인지 확인 (null-safe) public boolean isValid() { - // 수량 검증 - if (this.quantity <= 0) { + // 1) 수량 검증 (null 또는 1 미만이면 무효) + if (this.quantity == null || this.quantity <= 0) { return false; } - + + // 2) 펀딩 장바구니 검증 if (isFundingCart()) { - // 펀딩 장바구니 검증 if (this.funding == null) { return false; } - // fundingStock이 설정되어 있으면 그것을 기준으로, 없으면 funding의 stock 사용 - int availableStock = (this.fundingStock != null) ? this.fundingStock : this.funding.getStock(); - return this.quantity <= availableStock; - } else { - // 일반 장바구니 검증 - if (this.product == null) { - return false; - } - - // 상품이 삭제되지 않았고 재고가 있는지 - if (this.product.isDeleted() || this.product.getStock() <= 0) { + Integer fundingStockFromEntity = this.funding.getStock(); + int availableStock = (this.fundingStock != null) + ? this.fundingStock + : (fundingStockFromEntity != null ? fundingStockFromEntity : 0); + + if (availableStock <= 0) { return false; } - - // 일반 재고 확인 - return this.quantity <= this.product.getStock(); + return this.quantity <= availableStock; + } + + // 3) 일반 장바구니 검증 + if (this.product == null) { + return false; } + if (this.product.isDeleted()) { + return false; + } + Integer productStock = this.product.getStock(); + if (productStock == null || productStock <= 0) { + return false; + } + return this.quantity <= productStock; } - // 총 가격 계산 + // 총 가격 계산 (null-safe) public int getTotalPrice() { + int qty = (this.quantity != null && this.quantity > 0) ? this.quantity : 0; int unitPrice; - + if (isFundingCart()) { - // 펀딩 장바구니: fundingPrice 사용 if (this.fundingPrice != null) { unitPrice = this.fundingPrice; } else if (this.funding != null) { @@ -171,14 +177,21 @@ public int getTotalPrice() { return 0; } } else { - // 일반 장바구니: Product 가격 사용 if (this.product == null) { return 0; } - unitPrice = this.product.getDiscountPrice(); + Integer discount = this.product.getDiscountPrice(); + Integer basePrice = this.product.getPrice(); + if (discount != null) { + unitPrice = discount; + } else if (basePrice != null) { + unitPrice = basePrice; + } else { + return 0; + } } - - return unitPrice * this.quantity; + + return unitPrice * qty; } // ===== 도메인 메서드 (선택 상태 조회) ===== diff --git a/src/main/java/com/back/domain/cart/service/CartService.java b/src/main/java/com/back/domain/cart/service/CartService.java index e1a405d0..45c5bd6d 100644 --- a/src/main/java/com/back/domain/cart/service/CartService.java +++ b/src/main/java/com/back/domain/cart/service/CartService.java @@ -298,6 +298,9 @@ public void validateCartItemsForOrder(User user, boolean isFullOrder) { throw new ServiceException("CART_EMPTY", "주문할 장바구니 아이템이 없습니다."); } + // 레거시 데이터 보정 (펀딩 price/stock 누락 대비) + cartItems.forEach(this::normalizeCartFields); + // 각 아이템의 유효성 검증 for (Cart cart : cartItems) { if (!cart.isValid()) { @@ -319,13 +322,35 @@ public void validateCartItemsForOrder(User user, boolean isFullOrder) { } } + private void normalizeCartFields(Cart cart) { + if (cart.isFundingCart() && cart.getFunding() != null) { + if (cart.getFundingPrice() == null) { + cart.updateFundingInfo( + cart.getFundingId(), + (int) cart.getFunding().getPrice(), + cart.getFundingStock() + ); + } + if (cart.getFundingStock() == null) { + cart.updateFundingInfo( + cart.getFundingId(), + cart.getFundingPrice(), + cart.getFunding().getStock() + ); + } + } + } + /** * 장바구니 총 금액 계산 (전체/선택) */ public Integer calculateTotalAmount(User user, boolean isFullOrder) { List cartItems = isFullOrder ? cartRepository.findByUserWithProduct(user) : - cartRepository.findByUserAndIsSelectedTrue(user); + cartRepository.findByUserAndIsSelectedTrueWithProduct(user); + + // 레거시 보정(펀딩 price/stock 누락 대비) + cartItems.forEach(this::normalizeCartFields); return cartItems.stream() .filter(Cart::isValid) diff --git a/src/test/java/com/back/domain/cart/service/CartServiceTest.java b/src/test/java/com/back/domain/cart/service/CartServiceTest.java index fee30e65..ffd7273a 100644 --- a/src/test/java/com/back/domain/cart/service/CartServiceTest.java +++ b/src/test/java/com/back/domain/cart/service/CartServiceTest.java @@ -560,12 +560,12 @@ void validateCartItemsForOrder_SelectedOrder_Success() { // Given testNormalCart.select(); List selectedCarts = Arrays.asList(testNormalCart); - given(cartRepository.findByUserAndIsSelectedTrue(testUser)).willReturn(selectedCarts); + given(cartRepository.findByUserAndIsSelectedTrueWithProduct(testUser)).willReturn(selectedCarts); // When & Then cartService.validateCartItemsForOrder(testUser, false); - verify(cartRepository).findByUserAndIsSelectedTrue(testUser); + verify(cartRepository).findByUserAndIsSelectedTrueWithProduct(testUser); } @Test @@ -601,14 +601,14 @@ void calculateTotalAmount_SelectedOrder_Success() { // Given testNormalCart.select(); List selectedCarts = Arrays.asList(testNormalCart); - given(cartRepository.findByUserAndIsSelectedTrue(testUser)).willReturn(selectedCarts); + given(cartRepository.findByUserAndIsSelectedTrueWithProduct(testUser)).willReturn(selectedCarts); // When Integer result = cartService.calculateTotalAmount(testUser, false); // Then assertThat(result).isNotNull(); - verify(cartRepository).findByUserAndIsSelectedTrue(testUser); + verify(cartRepository).findByUserAndIsSelectedTrueWithProduct(testUser); } @Test