CartItemService.java
package com.learning.yasminishop.cart;
import com.learning.yasminishop.cart.dto.request.CartItemRequest;
import com.learning.yasminishop.cart.dto.request.CartItemUpdate;
import com.learning.yasminishop.cart.dto.response.CartItemResponse;
import com.learning.yasminishop.cart.mapper.CartItemMapper;
import com.learning.yasminishop.common.entity.CartItem;
import com.learning.yasminishop.common.entity.Product;
import com.learning.yasminishop.common.entity.User;
import com.learning.yasminishop.common.exception.AppException;
import com.learning.yasminishop.common.exception.ErrorCode;
import com.learning.yasminishop.product.ProductRepository;
import com.learning.yasminishop.user.UserRepository;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.math.BigDecimal;
import java.util.List;
import java.util.Optional;
@Service
@RequiredArgsConstructor
@Slf4j
@Transactional(readOnly = true)
public class CartItemService {
private final CartItemRepository cartItemRepository;
private final ProductRepository productRepository;
private final UserRepository userRepository;
private final CartItemMapper cartItemMapper;
@Transactional
public CartItemResponse create(CartItemRequest cartItemRequest) {
Product product = productRepository.findById(cartItemRequest.getProductId())
.orElseThrow(() -> new AppException(ErrorCode.PRODUCT_NOT_FOUND));
String email = SecurityContextHolder.getContext().getAuthentication().getName();
User user = userRepository.findByEmail(email).orElseThrow(() -> new AppException(ErrorCode.USER_NOT_FOUND));
// check if the product is already in the cart
Optional<CartItem> cartItem = cartItemRepository.findByProductAndUser(product, user);
if (cartItem.isPresent()) {
return updateExistingCartItem(cartItem.get(), cartItemRequest.getQuantity(), product);
}
// if the product is not in the cart
validateProductStock(product, cartItemRequest.getQuantity());
CartItem newCartItem = cartItemMapper.toCartItem(cartItemRequest);
newCartItem.setProduct(product);
newCartItem.setUser(user);
newCartItem.setPrice(product.getPrice().multiply(BigDecimal.valueOf(newCartItem.getQuantity())));
return cartItemMapper.toCartResponse(cartItemRepository.save(newCartItem));
}
public List<CartItemResponse> getAll() {
String email = SecurityContextHolder.getContext().getAuthentication().getName();
User user = userRepository.findByEmail(email).orElseThrow(() -> new AppException(ErrorCode.USER_NOT_FOUND));
List<CartItem> cartItems = cartItemRepository.findAllByUserOrderByLastModifiedDateDesc(user);
return cartItems.stream()
.map(cartItemMapper::toCartResponse)
.toList();
}
@Transactional
public CartItemResponse update(String cartId, CartItemUpdate cartItemUpdate) {
CartItem cartItem = cartItemRepository.findById(cartId)
.orElseThrow(() -> new AppException(ErrorCode.CART_ITEM_NOT_FOUND));
// verify the user is the owner of the cart
String email = SecurityContextHolder.getContext().getAuthentication().getName();
if (!cartItem.getUser().getEmail().equals(email)) {
throw new AppException(ErrorCode.CART_ITEM_NOT_FOUND);
}
Product product = cartItem.getProduct();
validateProductStock(product, cartItemUpdate.getQuantity());
cartItem.setQuantity(cartItemUpdate.getQuantity());
cartItem.setPrice(product.getPrice().multiply(BigDecimal.valueOf(cartItemUpdate.getQuantity())));
cartItem = cartItemRepository.save(cartItem);
return cartItemMapper.toCartResponse(cartItem);
}
@Transactional
public void delete(List<String> cartIds) {
List<CartItem> cartItems = cartItemRepository.findAllById(cartIds);
if(cartItems.size() != cartIds.size()) {
throw new AppException(ErrorCode.CART_ITEM_NOT_FOUND);
}
// verify the user is the owner of the cart
String email = SecurityContextHolder.getContext().getAuthentication().getName();
if(cartItems.stream().anyMatch(cartItem -> !cartItem.getUser().getEmail().equals(email))) {
throw new AppException(ErrorCode.CART_ITEM_NOT_FOUND);
}
cartItemRepository.deleteAll(cartItems);
}
@Transactional
public List<CartItemResponse> getCartByIds(List<String> cartIds) {
List<CartItem> cartItems = cartItemRepository.findAllById(cartIds);
if(cartItems.size() != cartIds.size()) {
throw new AppException(ErrorCode.CART_ITEM_NOT_FOUND);
}
// verify the user is the owner of the cart
String email = SecurityContextHolder.getContext().getAuthentication().getName();
if(cartItems.stream().anyMatch(cartItem -> !cartItem.getUser().getEmail().equals(email))) {
throw new AppException(ErrorCode.CART_ITEM_NOT_FOUND);
}
return cartItems.stream()
.map(cartItemMapper::toCartResponse)
.toList();
}
private CartItemResponse updateExistingCartItem(CartItem existingCartItem, int additionalQuantity, Product product) {
int newQuantity = existingCartItem.getQuantity() + additionalQuantity;
validateProductStock(product, newQuantity);
existingCartItem.setQuantity(newQuantity);
// update price
existingCartItem.setPrice(product.getPrice().multiply(BigDecimal.valueOf(existingCartItem.getQuantity())));
return cartItemMapper.toCartResponse(cartItemRepository.save(existingCartItem));
}
private void validateProductStock(Product product, int requestedQuantity) {
if (product.getQuantity() < requestedQuantity) {
throw new AppException(ErrorCode.PRODUCT_STOCK_NOT_ENOUGH);
}
}
}