OrderServiceImpl.java
package hello.commerce.order;
import hello.commerce.common.exception.BusinessException;
import hello.commerce.common.exception.ErrorCode;
import hello.commerce.order.dto.OrderRequestV1;
import hello.commerce.order.model.Order;
import hello.commerce.order.model.OrderStatus;
import hello.commerce.product.ProductReader;
import hello.commerce.product.model.Product;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Service;
@Service
@RequiredArgsConstructor
public class OrderServiceImpl implements OrderService {
private final OrderRepository orderRepository;
private final ProductReader productReader;
@Transactional
@Override
public Order createOrder(OrderRequestV1 orderRequest) {
// 1. 유효성 검사 - product는 영속상태
Product product = validateOrderCondition(orderRequest);
// 2. 주문 생성 및 저장
int totalAmount = product.getAmount() * orderRequest.getQuantity();
Order order = createOrderEntity(orderRequest, product, totalAmount);
orderRepository.save(order);
// 3. 재고 차감 - 영속상태인 product에 변경감지가 되어 커밋 시점에 JPA가 자동으로 update 쿼리 실행
product.decreaseStock(orderRequest.getQuantity());
// 4. 응답 반환
return orderRepository.findById(order.getId())
.orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND_ORDER));
}
@Override
public Page<Order> getOrders(Pageable pageable, OrderStatus orderStatus) {
return orderRepository.findAllByOrderStatus(pageable, orderStatus);
}
@Override
public Page<Order> getOrders(Pageable pageable) {
return orderRepository.findAll(pageable);
}
@Override
public Order getOrderById(Long orderId) {
return orderRepository.findById(orderId)
.orElseThrow(() -> new BusinessException(ErrorCode.NOT_FOUND_ORDER));
}
private static Order createOrderEntity(OrderRequestV1 orderRequest, Product product, int totalAmount) {
return Order.builder()
.product(product)
.userId(orderRequest.getUserId())
.quantity(orderRequest.getQuantity())
.totalAmount(totalAmount)
.orderStatus(OrderStatus.INITIAL)
.build();
}
private Product validateOrderCondition(OrderRequestV1 orderRequest) {
// 수량 유효성
if (orderRequest.getQuantity() <= 0) {
throw new BusinessException(ErrorCode.INVALID_ORDER_QUANTITY);
}
// product는 영속상태가 됨
Product product = productReader.findByIdForUpdate(orderRequest.getProductId());
// 재고 확인
if (product.getStock() < orderRequest.getQuantity()) {
throw new BusinessException(ErrorCode.INSUFFICIENT_STOCK);
}
return product;
}
}