[실전! 스프링 부트와 JPA 활용1 - 웹 애플리케이션 개발] 06. 주문 도메인 개발

Turtle·2024년 7월 5일
0
post-thumbnail

🙄주문, 주문상품 엔티티 개발

@Entity
@Table(name = "orders")
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class Order {
	@Id
	@GeneratedValue
	@Column(name = "order_id")
	private Long id;

	@ManyToOne(fetch = FetchType.LAZY)
	@JoinColumn(name = "member_id")
	private Member member;

	@OneToMany(mappedBy = "order", cascade = CascadeType.ALL)
	private List<OrderItem> orderItems = new ArrayList<>();

	private LocalDateTime orderDate;

	@Enumerated(value = EnumType.STRING)
	private OrderStatus status;

	@OneToOne(fetch = FetchType.LAZY, cascade = CascadeType.ALL)
	@JoinColumn(name = "delivery_id")
	private Delivery delivery;

	public void setMember(Member member) {
		this.member = member;
		member.getOrders().add(this);
	}

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

	public void setDelivery(Delivery delivery) {
		this.delivery = delivery;
		delivery.setOrder(this);
	}

	// 주문 생성 정적 팩토리 메서드
	public static Order createOrder(Member member, Delivery delivery, OrderItem... orderItems) {
		Order order = new Order();
		order.setMember(member);
		order.setDelivery(delivery);
		for (OrderItem orderItem : orderItems) {
			order.addOrderItem(orderItem);
		}
		order.setStatus(OrderStatus.ORDER);
		order.setOrderDate(LocalDateTime.now());
		return order;
	}

	// 주문 취소
	public void cancel() {
		if (delivery.getStatus() == DeliveryStatus.COMP) {
			throw new IllegalStateException("이미 배송이 완료된 상품입니다.");
		}
		this.setStatus(OrderStatus.CANCEL);
		for (OrderItem orderItem : orderItems) {
			orderItem.cancel();
		}
	}

	// 전체 주문 가격 조회
	public int getTotalPrice() {
		int totalPrice = 0;
		for (OrderItem orderItem : orderItems) {
			totalPrice += orderItem.getOrderPrice() * orderItem.getCount();
		}
		return totalPrice;
	}
}
@Entity
@Getter
@Setter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class OrderItem {
	@Id
	@GeneratedValue
	@Column(name = "orderItem_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 void cancel() {
		getItem().addStock(count);
	}

	// 주문 상품 목록에 상품 추가하는 생성 메서드
	public static OrderItem createOrderItem(Item item, int orderPrice, int count) {
		OrderItem orderItem = new OrderItem();
		orderItem.setItem(item);
		orderItem.setOrderPrice(orderPrice);
		orderItem.setCount(count);

		item.removeStock(count);
		return orderItem;
	}

	// 주문 가격 조회
	public int getTotalPrice() {
		return getOrderPrice() * getCount();
	}
}

🙄주문 리포지토리 개발

@Repository
@RequiredArgsConstructor
public class OrderRepository {
	private final EntityManager em;
	private final OrderRepository orderRepository;

	public void save(Order order) {
		em.persist(order);
	}

	public Order findOne(Long id) {
		return em.find(Order.class, id);
	}
}

🙄주문 서비스 개발

@Service
@Transactional(readOnly = true)
@RequiredArgsConstructor
public class OrderService {
	private final OrderRepository orderRepository;
	private final MemberRepository memberRepository;
	private final ItemRepository itemRepository;

	@Transactional
	public Long order(Long memberId, Long itemId, int count) {
		// 엔티티 조회
		Member member = memberRepository.findOne(memberId);
		Item item = itemRepository.findOne(itemId);

		// 배송정보 생성
		Delivery delivery = new Delivery();
		delivery.setAddress(member.getAddress());

		// 주문상품 생성
		OrderItem orderItem = OrderItem.createOrderItem(item, item.getPrice(), count);

		// 주문 생성
		Order order = Order.createOrder(member, delivery, orderItem);

		// 주문 저장
		orderRepository.save(order);
		return order.getId();
	}

	@Transactional
	public void cancelOrder(Long orderId) {
		Order findOrder = orderRepository.findOne(orderId);
		findOrder.cancel();
	}
}

🙄주문 기능 테스트

@RunWith(SpringRunner.class)
@SpringBootTest
@Transactional
class OrderServiceTest {

	@Autowired private EntityManager em;
	@Autowired private OrderService orderService;
	@Autowired
	private OrderRepository orderRepository;

	@Test
	public void 상품주문() throws Exception {
		// given
		Member member = new Member();
		member.setName("회원");
		member.setAddress(new Address("서울", "강가", "123-123"));
		em.persist(member);

		Book book = new Book();
		book.setName("JPA");
		book.setPrice(10000);
		book.setStockQuantity(10);
		em.persist(book);

		int orderCount = 2;

		// when
		Long orderId = orderService.order(member.getId(), book.getId(), orderCount);


		// then
		Order getOrder = orderRepository.findOne(orderId);
		assertEquals("상품 주문시 상태는 ORDER", OrderStatus.ORDER, getOrder.getStatus());
		assertEquals("주문한 상품 종류 수가 정확해야 한다.", getOrder.getOrderItems().size(), 1);
		assertEquals("주문 가격은 가격 X 수량이다.", getOrder.getTotalPrice(), book.getPrice() * orderCount);
		assertEquals("주문 수량만큼 재고가 줄어야 한다.", book.getStockQuantity(), 8);
	}

	@Test
	public void 주문취소() throws Exception {
		// given
		Member member = new Member();
		member.setName("회원");
		member.setAddress(new Address("서울", "강가", "123-123"));
		em.persist(member);

		Book book = new Book();
		book.setName("JPA");
		book.setPrice(10000);
		book.setStockQuantity(10);
		em.persist(book);

		int orderCount = 2;
		Long orderId = orderService.order(member.getId(), book.getId(), orderCount);

		// when
		orderService.cancelOrder(orderId);
		Order order = orderRepository.findOne(orderId);

		// then
		assertEquals("주문 취소시 상태는 CANCEL", OrderStatus.CANCEL, order.getStatus());
		assertEquals("주문이 취소된 상품은 그만큼 재고가 증가해야 한다.", 10, book.getStockQuantity());
	}

	@Test
	public void 상품주문_재고수량초과() throws Exception {
		// given
		Member member = new Member();
		member.setName("회원");
		member.setAddress(new Address("서울", "강가", "123-123"));
		em.persist(member);

		Book book = new Book();
		book.setName("JPA");
		book.setPrice(10000);
		book.setStockQuantity(10);
		em.persist(book);

		int orderCount = 11;

		// when
		Assertions.assertThrows(NotEnoughStockException.class, () -> orderService.order(member.getId(), book.getId(), orderCount));
	}
}

🙄주문 검색 기능 개발

@Repository
@RequiredArgsConstructor
public class OrderRepository {
	private final EntityManager em;

	public void save(Order order) {
		em.persist(order);
	}

	public Order findOne(Long id) {
		return em.find(Order.class, id);
	}

	public List<Order> findAll(OrderSearch orderSearch) {
		String jpql = "select o from Order as o join o.member as m";
		boolean isFirstCondition = true;

		// 주문 상태 기준 검색
		if (orderSearch.getOrderStatus() != null) {
			if (isFirstCondition) {
				jpql += " where";
				isFirstCondition = false;
			} else {
				jpql += " and";
			}
			jpql += " o.status = :status";
		}

		// 회원 이름 기준 검색
		if (StringUtils.hasText(orderSearch.getMemberName())) {
			if (isFirstCondition) {
				jpql += " where";
				isFirstCondition = false;
			} else {
				jpql += "and";
			}
			jpql += " m.name like :name";
		}


		TypedQuery<Order> query = em.createQuery(jpql, Order.class)
				.setMaxResults(1000);
		if (orderSearch.getOrderStatus() != null) {
			query = query.setParameter("status", orderSearch.getOrderStatus());
		}
		if (orderSearch.getMemberName() != null) {
			query = query.setParameter("name", orderSearch.getMemberName());
		}
		return query.getResultList();
	}
}

0개의 댓글