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