#10 [스프링 스터디] 쇼핑몰 만들기 프로젝트 - 상품 주문 (장바구니)

myeonji·2022년 1월 31일
0

주문 관련 Entity 생성 에러

** 미리 방지 **
Order와 OrderItem Entity를 구현하고 실행하는데 에러가 났습니다.

[SQL Error] you have an error in your SQL Syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near ' order

간단한 에러였지만 실수할 수 있으니 주의해야겠습니다!!


오늘은 상품 주문 기능에 대해 다뤄보겠습니다. 상품 주문은 장바구니에서 할 수 있고 상품 상세 페이지에서 바로 구매할 수도 있습니다!

먼저 장바구니 상품 주문을 보겠습니다.

1. Order Entity와 OrderItem Entity를 생성합니다.

  • Order와 OrderItem Entity 사이의 연관관계를 설정합니다.
  • (주의!!!!) OrderItem Entity에서는 item과 연관관계를 맺어야 할 것 같지만, int와 String으로 변수를 선언해 Item 객체가 아닌 item 정보만 저장할 수 있도록 합니다! 이유는 이곳으로..

< Order >

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Entity
@Table(name = "orders")
public class Order {

  @Id
  @GeneratedValue(strategy = GenerationType.IDENTITY)
  private int id;

  @ManyToOne(fetch = FetchType.EAGER)
  @JoinColumn(name = "user_id")
  private User user; // 구매자

  @OneToMany(mappedBy = "order")
  private List<OrderItem> orderItems = new ArrayList<>();

  @DateTimeFormat(pattern = "yyyy-mm-dd")
  private LocalDate createDate; // 구매 날짜

  @PrePersist
  public void createDate() {
      this.createDate = LocalDate.now();
  }

  public void addOrderItem(OrderItem orderItem) {
      orderItems.add(orderItem);
      orderItem.setOrder(this);
  }

  public static Order createOrder(User user, List<OrderItem> orderItemList) {
      Order order = new Order();
      order.setUser(user);
      for (OrderItem orderItem : orderItemList) {
          order.addOrderItem(orderItem);
      }
      order.setCreateDate(order.createDate);
      return order;
  }

  public static Order createOrder(User user) {
      Order order = new Order();
      order.setUser(user);
      order.setCreateDate(order.createDate);
      return order;
  }

}

< OrderItem >

@Builder
@AllArgsConstructor
@NoArgsConstructor
@Getter
@Setter
@Entity
public class OrderItem {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name="order_id")
    private Order order;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "user_id")
    private User user; // 구매자

    private int itemId; // 주문 상품 번호
    private String itemName; // 주문 상품 이름
    private int itemPrice; // 주문 상품 가격
    private int itemCount; // 주문 상품 수량
    private int itemTotalPrice; // 가격*수량

    @OneToOne(fetch = FetchType.EAGER)
    @JoinColumn(name="saleItem_id")
    private SaleItem saleItem; // 주문상품에 매핑되는 판매상품

    private int isCancel; // 주문 취소 여부 (0:주문완료 / 1:주문취소)

    // 장바구니 전체 주문
    public static OrderItem createOrderItem(int itemId, User user, CartItem cartItem, SaleItem saleItem) {
        OrderItem orderItem = new OrderItem();
        orderItem.setItemId(itemId);
        orderItem.setUser(user);
        orderItem.setItemName(cartItem.getItem().getName());
        orderItem.setItemPrice(cartItem.getItem().getPrice());
        orderItem.setItemCount(cartItem.getCount());
        orderItem.setItemTotalPrice(cartItem.getItem().getPrice()*cartItem.getCount());
        orderItem.setSaleItem(saleItem);
        return orderItem;
    }

    // 상품 개별 주문
    public static OrderItem createOrderItem(int itemId, User user, Item item, int count, Order order, SaleItem saleItem) {
        OrderItem orderItem = new OrderItem();
        orderItem.setItemId(itemId);
        orderItem.setUser(user);
        orderItem.setOrder(order);
        orderItem.setItemName(item.getName());
        orderItem.setItemPrice(item.getPrice());
        orderItem.setItemCount(count);
        orderItem.setItemTotalPrice(item.getPrice()*count);
        orderItem.setSaleItem(saleItem);
        return orderItem;
    }

}

2. Sale Entity와 SaleItem Entity를 생성합니다.

  • Sale와 SaleItem Entity 사이의 연관관계를 설정합니다.
  • SaleItem 또한 OrderItem과 마찬가지 입니다. item 정보만 받을 수 있도록 int와 String으로 변수들을 선언합니다.

< Sale >

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Entity
public class Sale {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name="seller_id")
    private User seller; // 판매자

    @OneToMany(mappedBy = "sale")
    private List<SaleItem> saleItems = new ArrayList<>();

    private int totalCount; // 총 판매 개수

    public static Sale createSale(User seller) {
        Sale sale = new Sale();
        sale.setSeller(seller);
        sale.setTotalCount(0);
        return sale;
    }

}

< SaleItem >

@Builder
@NoArgsConstructor
@AllArgsConstructor
@Getter
@Setter
@Entity
public class SaleItem {

    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private int id;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name="sale_id")
    private Sale sale;

    @ManyToOne(fetch = FetchType.EAGER)
    @JoinColumn(name = "seller_id")
    private User seller; // 판매자

    private int itemId; // 주문 상품 번호
    private String itemName; // 주문 상품 이름
    private int itemPrice; // 주문 상품 가격
    private int itemCount; // 주문 상품 수량
    private int itemTotalPrice; // 가격*수량

    @OneToOne(mappedBy = "saleItem")
    private OrderItem orderItem; // 판매 상품에 매핑되는 주문 상품

    private int isCancel; // 판매 취소 여부 (0:판매완료 / 1:판매취소)

    @DateTimeFormat(pattern = "yyyy-mm-dd")
    private LocalDate createDate; // 날짜

    @PrePersist
    public void createDate(){
        this.createDate = LocalDate.now();
    }

    // 장바구니 전체 주문
    public static SaleItem createSaleItem(int itemId, Sale sale, User seller, CartItem cartItem) {
        SaleItem saleItem = new SaleItem();
        saleItem.setItemId(itemId);
        saleItem.setSale(sale);
        saleItem.setSeller(seller);
        saleItem.setItemName(cartItem.getItem().getName());
        saleItem.setItemPrice(cartItem.getItem().getPrice());
        saleItem.setItemCount(cartItem.getCount());
        saleItem.setItemTotalPrice(cartItem.getItem().getPrice()*cartItem.getCount());
        return saleItem;
    }

    // 상품 개별 주문
    public static SaleItem createSaleItem(int itemId, Sale sale, User seller, Item item, int count) {
        SaleItem saleItem = new SaleItem();
        saleItem.setItemId(itemId);
        saleItem.setSale(sale);
        saleItem.setSeller(seller);
        saleItem.setItemName(item.getName());
        saleItem.setItemPrice(item.getPrice());
        saleItem.setItemCount(count);
        saleItem.setItemTotalPrice(item.getPrice()*count);
        return saleItem;
    }
}

3. userCart.html 에서 구매 버튼을 생성합니다.

  • form을 통해 th:action을 사용하여 Controller로 보냅니다.

4. Controller에서 POST 처리를 합니다.

  • 장바구니에 담은 상품의 재고가 주문량보다 적을 경우 예외처리를 합니다.
  • 주문 총 가격보다 구매자의 잔액이 부족할 경우 예외처리를 합니다.
  • JS로 에러 메세지를 띄웁니다. -> 이 부분은 따로 다루겠습니다!
  • 판매자 수익 증가, 상품 재고 감소, 상품 판매량 증가 후 상품을 orderItem과 saleItem에 담습니다.
  • 구매를 완료했으니, 장바구니 내역은 모두 삭제시킵니다.
    // 장바구니 상품 전체 주문
    @Transactional
    @PostMapping("/user/cart/checkout/{id}")
    public String cartCheckout(@PathVariable("id") Integer id, @AuthenticationPrincipal PrincipalDetails principalDetails, Model model) {
        // 로그인이 되어있는 유저의 id와 주문하는 id가 같아야 함
        if(principalDetails.getUser().getId() == id) {
            User user = userPageService.findUser(id);

            // 유저 카트 찾기
            Cart userCart = cartService.findUserCart(user.getId());

            // 유저 카트 안에 있는 상품들
            List<CartItem> userCartItems = cartService.allUserCartView(userCart);

            // 최종 결제 금액
            int totalPrice = 0;
            for (CartItem cartItem : userCartItems) {
                // 장바구니 안에 있는 상품의 재고가 없거나 재고보다 많이 주문할 경우
                if (cartItem.getItem().getStock() == 0 || cartItem.getItem().getStock() < cartItem.getCount()) {
                    return "redirect:/main";
                }
                totalPrice += cartItem.getCount() * cartItem.getItem().getPrice();
            }

            int userCoin = user.getCoin();
            // 유저의 현재 잔액이 부족하다면
            if (userCoin < totalPrice) {
                return "redirect:/main";
            } else {
                // 유저 돈에서 최종 결제금액 빼야함
                user.setCoin(user.getCoin() - totalPrice);

                List<OrderItem> orderItemList = new ArrayList<>();

                for (CartItem cartItem : userCartItems) {
                    // 각 상품에 대한 판매자
                    User seller = cartItem.getItem().getSeller();

                    // 판매자 수익 증가
                    seller.setCoin(seller.getCoin() + (cartItem.getCount() * cartItem.getItem().getPrice()));

                    // 재고 감소
                    cartItem.getItem().setStock(cartItem.getItem().getStock() - cartItem.getCount());

                    // 상품 개별로 판매 개수 증가
                    cartItem.getItem().setCount(cartItem.getItem().getCount() + cartItem.getCount());

                    // sale, saleItem 에 담기
                    SaleItem saleItem = saleService.addSale(cartItem.getItem().getId(), seller.getId(), cartItem);

                    // order, orderItem 에 담기
                    OrderItem orderItem = orderService.addCartOrder(cartItem.getItem().getId(), user.getId(), cartItem, saleItem);

                    orderItemList.add(orderItem);
                }

                orderService.addOrder(user, orderItemList);

                // 장바구니 상품 모두 삭제
                cartService.allCartItemDelete(id);
            }

            model.addAttribute("totalPrice", totalPrice);
            model.addAttribute("cartItems", userCartItems);
            model.addAttribute("user", userPageService.findUser(id));

            return "redirect:/user/cart/{id}";
        } else {
            return "redirect:/main";
        }
    }

5. cartService, orderService, saleService에서 로직을 각각 구현합니다.

  • cartService에서는 장바구니 구매를 완료했으니 장바구니 상품을 모두 삭제해야 합니다.
  • orderService에서는 구매한 상품들을 orderItem에 저장해야 합니다.
  • saleService에서는 구매한 상품들을 saleItem에 저장해야 합니다. (판매자에게는 판매한 상품이 되는 것입니다.)

< cartService >

    // 장바구니 아이템 전체 삭제 -> 매개변수는 유저 id
    public void allCartItemDelete(int id) {

        // 전체 cartItem 찾기
        List<CartItem> cartItems = cartItemRepository.findAll();

        // 반복문을 이용하여 해당하는 User 의 CartItem 만 찾아서 삭제
        for(CartItem cartItem : cartItems){

            if(cartItem.getCart().getUser().getId() == id) {

                cartItem.getCart().setCount(0);

                cartItemRepository.deleteById(cartItem.getId());
            }
        }
    }

< orderService >

    // 장바구니상품주문
    @Transactional
    public OrderItem addCartOrder(int itemId, int userId, CartItem cartItem, SaleItem saleItem) {

        User user = userPageService.findUser(userId);

        OrderItem orderItem = OrderItem.createOrderItem(itemId, user, cartItem, saleItem);

        orderItemRepository.save(orderItem);

        return orderItem;
    }

< saleService >

    // 판매내역에 저장 (장바구니 전체 주문)
    @Transactional
    public SaleItem addSale (int itemId, int sellerId, CartItem cartItem) {

        User seller = userPageService.findUser(sellerId);
        Sale sale = saleRepository.findBySellerId(sellerId);
        sale.setTotalCount(sale.getTotalCount()+cartItem.getCount());
        saleRepository.save(sale);
        SaleItem saleItem = SaleItem.createSaleItem(itemId, sale, seller, cartItem);
        saleItemRepository.save(saleItem);

        return saleItem;
    }

이렇게 하면 장바구니의 상품 주문이 성공적으로 끝납니다!!
구매자는 이제 다양한 상품을 장바구니에 담아놓고, 한 번에 주문 할 수 있습니다!

0개의 댓글