고객이 상품을 주문하면 현재 상품의 재고에서 주문 수량만큼 재고를 감소 시켜야합니다.
상품의 주문수량보다 재고의 수가 적을 때 발생시킬 exception을 정의하겠습니다.
com.shop 패키지 아래에 exception 패키지를 생성한 후 runtimeExecprion을 상속받는 OutOfStockException클래스를 생성합니다.
package com.shop.shop.exception;
public class OutOfStockException extends RuntimeException{
public OutOfStockException(String message){
super(message);
}
}
public void removeStock(int stockNumber){
//상품의 재고수량에서 주문 후 남은 재고 수량을 구합니다.
int restStock = this.stockNumber -stockNumber;
if (restStock<0){
//상품의 재고가 주문 수량보다 작은 경우 재고 부족 예외를 발생시킵니다.
throw new OptimisticLockException("상품의 재고가 부족합니다.(현재 재고 수량:" + this.stockNumber +")");
}
//주문 후 남은 재고 수량을 상품의 현재 재고 값으로 할당합니다.
this.stockNumber = restStock;
}
package com.shop.shop.entity;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.time.LocalDateTime;
@Entity
@Getter
@Setter
public class OrderItem {
@Id
@GeneratedValue
@Column(name = "order_item_id")
private Long id;
@ManyToOne(fetch =FetchType.LAZY)
@JoinColumn(name = "item_id")
private Item item;
@ManyToOne(fetch =FetchType.LAZY)
@JoinColumn(name = "order_id")
private Order order;
private int orderPrice;
private int count;
public static OrderItem createOrderItem(Item item,int count){
OrderItem orderItem = new OrderItem();
orderItem.setItem(item);//주문상품
orderItem.setCount(count);//주문수량
//현재시간 기준으로 상품 가격을 주문 가격으로 세팅합니다. 상품 가격은 시간에 따라서 달라질수 있습니다. 또한 쿠폰이나 할인을 적용하는 케이스들도 있지만 여기서는 고려하지 않겠습니다.
orderItem.setOrderPrice(item.getPrice());
//주문수량만큼의 상품의 재고 수량을 감소시킵니다.
item.removeStock(count);
return orderItem;
}
//주문가격과 주문 수량을 곱해서 해당 상품을 주문한 총 가격을 계산하는 메소드입니다.
public int getTotalPrice(){
return orderPrice * count;
}
// private LocalDateTime regTime;
// private LocalDateTime updateTime;
}
package com.shop.shop.entity;
import com.shop.shop.constant.OrderStatus;
import lombok.Getter;
import lombok.Setter;
import javax.persistence.*;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
@Entity
@Table(name ="orders")
@Getter
@Setter
public class Order {
@Id
@GeneratedValue
@Column(name = "order_id")
private Long id;
@ManyToOne(fetch = FetchType.LAZY)//주문만 알수있음 멤버도 내용을 알려면 양방향이 되어야한다.
@JoinColumn(name = "member_id")
private Member member;
private LocalDateTime orderDate; //주문일
@Enumerated(EnumType.STRING)
private OrderStatus orderStatus; //주문상태
@OneToMany(mappedBy = "order", cascade = CascadeType.ALL, orphanRemoval = true,fetch = FetchType.LAZY) //한개 삭제,한개 저장
//주인이 아닌쪽은 연관관계매핑시 mappyBy속성의 값으로 연관관계의 주인설정(변수명)
private List<OrderItem> orderItems = new ArrayList<>();
//객체선언하면 연관관계 매핑해주어야한다.
private LocalDateTime regTime;
private LocalDateTime updateTime;
public void addOrderItem(OrderItem orderItem){
orderItems.add(orderItem);
orderItem.setOrder(this);
}
public static Order createOrder(Member member, List<OrderItem> orderItemList){
Order order = new Order();
order.setMember(member);
for (OrderItem orderItem : orderItemList){
order.addOrderItem(orderItem);
}
order.setOrderStatus(OrderStatus.ORDER);
order.setOrderDate(LocalDateTime.now());
return order;
}
public int getTotalPrice(){
int totalPrice = 0;
for(OrderItem orderItem : orderItems){
totalPrice += orderItem.getTotalPrice();
}
return totalPrice;
}
}
import lombok.Getter;
import lombok.Setter;
import javax.validation.constraints.Max;
import javax.validation.constraints.Min;
import javax.validation.constraints.NotNull;
@Getter
@Setter
public class OrderDto {
@NotNull(message = "상품 아이디는 필수 입력 값입니다.")
private Long itemId;
@Min(value = 1, message = "최소 주문 수량은 1개입니다.")
@Max(value = 999,message = "최대 주문 수량은 999개 입니다.")
private int count;
}
package com.shop.shop.service;
import com.shop.shop.dto.OrderDto;
import com.shop.shop.entity.Item;
import com.shop.shop.entity.Member;
import com.shop.shop.entity.Order;
import com.shop.shop.entity.OrderItem;
import com.shop.shop.repository.ItemImgRepository;
import com.shop.shop.repository.ItemRepository;
import com.shop.shop.repository.MemberRepository;
import com.shop.shop.repository.OrderRepository;
import lombok.RequiredArgsConstructor;
import lombok.Setter;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import javax.persistence.EntityNotFoundException;
import java.util.ArrayList;
import java.util.List;
@Service
@Transactional
@RequiredArgsConstructor
public class OrderService {
private final ItemRepository itemRepository;
private final MemberRepository memberRepository;
private final OrderRepository orderRepository;
private final ItemImgRepository itemImgRepository;
public Long order(OrderDto orderDto, String email){
Item item = itemRepository.findById(orderDto.getItemId())
.orElseThrow(EntityNotFoundException::new);
Member member = memberRepository.findByEmail(email);
List<OrderItem> orderItemList = new ArrayList<>();
OrderItem orderItem =
OrderItem.createOrderItem(item, orderDto.getCount());
orderItemList.add(orderItem);
Order order = Order.createOrder(member, orderItemList);
orderRepository.save(order);
return order.getId();
}
}
package com.shop.shop.controller;
import com.shop.shop.dto.OrderDto;
import com.shop.shop.service.OrderService;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.stereotype.Controller;
import org.springframework.validation.BindingResult;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.ResponseBody;
import javax.validation.Valid;
import java.security.Principal;
import java.util.List;
@Controller
@RequiredArgsConstructor
public class OrderController {
private final OrderService orderService;
@PostMapping(value = "/order")
public @ResponseBody ResponseEntity order (@RequestBody @Valid OrderDto orderDto,
BindingResult bindingResult, Principal principal){
if (bindingResult.hasErrors()){
StringBuilder sb = new StringBuilder();
List<FieldError> fieldErrors = bindingResult.getFieldErrors();
for (FieldError fieldError : fieldErrors){
sb.append(fieldError.getDefaultMessage());
}
return new ResponseEntity<String>(sb.toString(), HttpStatus.BAD_REQUEST);
}
String email = principal.getName();
Long orderId;
try {
orderId = orderService.order(orderDto,email);
}catch (Exception e){
return new ResponseEntity<String>(e.getMessage(),HttpStatus.BAD_REQUEST);
}
return new ResponseEntity<Long>(orderId,HttpStatus.OK);
}
}
public void addStock(int stockNumber){
this.stockNumber += stockNumber;
}
public void cancel(){
this.getItem().addStock(count);
}
public void cancelOrder(){
this.orderStatus = OrderStatus.CANCEL;
for (OrderItem orderItem : orderItems){
orderItem.cancel();
}
}