diff --git a/README.md b/README.md index 3dbac172e..094b67fc1 100644 --- a/README.md +++ b/README.md @@ -56,13 +56,14 @@ docker compose -p kitchenpos up -d - 주문 테이블을 등록할 수 있다. - 주문 테이블의 이름이 올바르지 않으면 등록할 수 없다. - 주문 테이블의 이름은 비워 둘 수 없다. -- 빈 테이블을 해지할 수 있다. +- 빈 테이블을 점유할 수 있다. - 빈 테이블로 설정할 수 있다. - 완료되지 않은 주문이 있는 주문 테이블은 빈 테이블로 설정할 수 없다. -- 방문한 손님 수를 변경할 수 있다. -- 방문한 손님 수가 올바르지 않으면 변경할 수 없다. - - 방문한 손님 수는 0 이상이어야 한다. -- 빈 테이블은 방문한 손님 수를 변경할 수 없다. + +- 고객 인원를 변경할 수 있다. +- 고객 인원이 올바르지 않으면 변경할 수 없다. + - 고객 인원는 0 이상이어야 한다. +- 빈 테이블은 방문한 고객 인원을 변경할 수 없다. - 주문 테이블의 목록을 조회할 수 있다. ### 주문 @@ -147,6 +148,7 @@ docker compose -p kitchenpos up -d | 주문 종류 | Order Category | 주문 가능한 유형 | | 주문 상태 | Order Status | 주문의 생애주기(생성 부터 완료)를 나타내는 상태 | | 배달 장소 | Delivery Address | 배달 주문의 경우, 필수적으로 필요한 손님이 전달을 요청한 장소 | +| 주문 메뉴 | OrderLine Item | 주문을 구성하는 하위 메뉴 항목들 | ### 주문 행위 | 한글명 | 영문명 | 설명 | @@ -249,7 +251,7 @@ docker compose -p kitchenpos up -d - DELIVERING: 배달중 (라이더에게 전달 완료) - DELIVERED: 배달완료 (라이더가 배달 완료) - COMPLETED: 주문완료 (주문완료 설정) - - `주문`은 주문한 메뉴들을 가진다. + - `주문`은 `주문 메뉴`들을 가진다. - `주문`은 `주문 종류`를 가진다. - DELIVERY - 배달 (필수값: `배달 장소`) - TAKEOUT - 포장 @@ -260,7 +262,7 @@ docker compose -p kitchenpos up -d - `주문`을 생성할 때, `대기중(WAITING)` 으로 `주문 상태`를 변경한다. - `메뉴` 내의 `주문상품`들이 삭제된 `상품`이면 안된다. - `메뉴`가 생성된 상태이고 `공개 상태(VISIBLE)` 되어있어야한다. - - `메뉴 가격` 총액과 `주문`에 속한 `상품 가격` 총액이 동일해야한다. + - `주문 가격` 총액과 `주문`에 속한 `메뉴 가격` 총액이 동일해야한다. - `주문`은 `메뉴` 1개 이상으로 구성되어야 한다. 2. `주문 종류`: `매장내주문` - `주문 테이블`이 없으면 `주문`을 생성 할 수 없다. @@ -271,14 +273,15 @@ docker compose -p kitchenpos up -d - 그림의 3번: `주문`을 수락할 때, `주문 상태`는 `접수(ACCEPTED)`(`ACCEPTED` 상태) - 그림의 4번: `주문`을 `손님`에게 전달할 때, `주문 상태`는 `전달(SERVED)`으로 변경한다.(`SERVED` 상태) - 그림의 5번: `주문`을 완료할 때, `주문 상태`는 `주문완료(COMPLETED)`, `주문 테이블`은 `미사용중(UNOCCUPIED)`으로 변경한다.(`COMPLETED` 상태) + 2. `손님`이 `포장주문`을 하여 주문에 대한 메뉴를 전달 받는다. - 행동 1. `주문 종류`: 공통 (해당 조건은 공통으로 지켜져야 한다.) - `주문`을 생성할 때, `대기중(WAITING)` 으로 `주문 상태`를 변경한다. - - `메뉴` 내의 `주문상품`들이 삭제된 `상품`이면 안된다. + - `메뉴` 내의 `주문 메뉴`들이 삭제된 `메뉴`이면 안된다. - `메뉴`가 생성된 상태이고 `공개 상태(VISIBLE)` 되어있어야한다. - - `메뉴 가격` 총액과 `주문`에 속한 `상품 가격` 총액이 동일해야한다. + - `주문 가격` 총액과 `주문`에 속한 `메뉴 가격` 총액이 동일해야한다. - `주문`은 `메뉴` 1개 이상으로 구성되어야 한다. 2. `주문 종류`: `포장주문` - 주문을 생성할 때, 주문상품들의 갯수를 감소시킬 수 없다. @@ -293,12 +296,12 @@ docker compose -p kitchenpos up -d - 행동 1. `주문 종류`: 공통 (해당 조건은 공통으로 지켜져야 한다.) - `주문`을 생성할 때, `대기중(WAITING)` 으로 `주문 상태`를 변경한다. - - `메뉴` 내의 `주문상품`들이 삭제된 `상품`이면 안된다. + - `메뉴` 내의 `주문 메뉴`들이 삭제된 `상품`이면 안된다. - `메뉴`가 생성된 상태이고 `공개 상태(VISIBLE)` 되어있어야한다. - - `메뉴 가격` 총액과 `주문`에 속한 `상품 가격` 총액이 동일해야한다. + - `주문 가격` 총액과 `주문`에 속한 `메뉴 가격` 총액이 동일해야한다. - `주문`은 `메뉴` 1개 이상으로 구성되어야 한다. 2. `주문 종류`: `배달주문` - - `주문`이 생성할 때, `주문 상품`들의 `갯수`를 감소 시킬 수 없다. + - `주문`이 생성할 때, `주문 메뉴`들의 `갯수`를 감소 시킬 수 없다. - `주문`이 생성할 때, `주문 장소`가 필수로 기입되어야 한다. 3. `손님`이 `배달주문`을 하여 `손님`이 주문에 대한 메뉴를 전달 받는다. - 그림의 2번: `주문`을 생성할 때, `주문 상태`는 `대기중(WAITING)`, `주문 종류`는 `배달 주문`으로 변경한다.(`WAITING` 상태) diff --git a/REQUIREMENTS.md b/REQUIREMENTS.md new file mode 100644 index 000000000..42b05fabc --- /dev/null +++ b/REQUIREMENTS.md @@ -0,0 +1,58 @@ +## 과제 요구사항 + +1. 상품 context 리팩터링 +- 도메인 계층 구현하기 +- 도메인 서비스 +- 간접 참조로 aggregate 구현하기 +- REPOSITORY 구현 +- Validator 구현 +- 도메인 부분에서 VO 랑 AGGGREGATE 용 + - 식별을 위한 ID, 추가 + - VALIDATION을 위한 값들 추가 + - 테스트 작성 + + + +3. EAT_IN ORDER 리팩터링 구현사항 +A. 연관관계 설정 및 VO 및 도메인 로직 구현 +- ORDER (바운디드 컨텍스트는 넓게 잡기가 선호되서 하나의 패키지로 구현하였습니다.) + - EAT_IN + - DELIVERY + - DELIVERY 주문의 외부 연동 도메인 로직을 DOMAIN SERVICE로 주입 + - TAKEOUT + - 생성 도메인 로직 + - 상태 변환 도메인 로직 + - FACTORY로 각 ORDER에 대한 생성로직 따로 클래스로 생성 + +- ORDER ITEMS + - 생성 도메인로직 + - 메뉴에 대한 의존성을 MenuClient로 가져오는 객체는 OrderMenu로 명시적 표기 + - N+1 문제를 쿼리 한번으로 변경 + +- ORDERTABLE + - 생성 도메인 로직 + - 치우기 도메인 로직 + - OrderTable에서 Order에 대해 접근할때, domain service로 주입 +- ORDER SERVICE + - 서비스에 있는 도메인 로직 도메인 객체안으로 넣기 + +B. @DOMAIN SERVICE 와 @DOMAIN FACTORY에 대한 에노테이션 생성으로 명시적 표기 + + +1. 책에 따라 DOMAIN SERVICE를 METHOD 호출시 전달하게끔 변경하였습니다. '외부 시스템 연동이 필요한 도메인 로직일때 혹은 계산로직일때' + + +2. 1-N(주문, 주문항목들) 의 경우 N이 많아질 때가 있는데 해당부분은 어떻게 구현하실지 궁금합니다. + + +3. 주문 타입의 경우가 많아지면, 어떤 타입 -> 팩토리에 대한 맵핑을 지원하는 디자인 패턴을 사용하시나요? + + +4. 지금 상태에서 실무에 돌입하게 되면 ORDER 와 EATIN ORDER의 관계에서 상속이 객체지향적이지 않아 보여요! (EJ 합성 > 상속) + - 웅래님은 만약 실무였다면, 어떻게 구현하셨을지 궁금합니다. + - 저라면, 공유커널(COMMON)에 빈 껍데기 ORDER, ORDERITEMS를 두기 + - 공유커널을 상속받아서 DOMAIN ENTITY들을 각각 EATIN_ORDER, DELIVERY_ORDER, TAKEOUT_ORDER 패키지에서 각각 상속받아서 생성했을것같아요 + +5. 마지막으로, 공유커널(COMMON)에 있는 이벤트들은 내부 이벤트(내부 서비스) VS 외부 이벤트(외부 서비스) 들이 있을것 같아요 + - 보통은 어떻게 나누시나요? (인터페이스?, 마커 인터페이스?) + diff --git a/http/order-tables.http b/http/order-tables.http index 64f47c09a..6e080a087 100644 --- a/http/order-tables.http +++ b/http/order-tables.http @@ -1,5 +1,5 @@ ### -POST {{host}}/api/order-tables +POST {{host}}/api/orderRequest-tables Content-Type: application/json { @@ -7,13 +7,13 @@ Content-Type: application/json } ### -PUT {{host}}/api/order-tables/8d710043-29b6-420e-8452-233f5a035520/sit +PUT {{host}}/api/orderRequest-tables/8d710043-29b6-420e-8452-233f5a035520/sit ### -PUT {{host}}/api/order-tables/8d710043-29b6-420e-8452-233f5a035520/clear +PUT {{host}}/api/orderRequest-tables/8d710043-29b6-420e-8452-233f5a035520/clear ### -PUT {{host}}/api/order-tables/8d710043-29b6-420e-8452-233f5a035520/number-of-guests +PUT {{host}}/api/orderRequest-tables/8d710043-29b6-420e-8452-233f5a035520/number-of-guests Content-Type: application/json { @@ -21,4 +21,4 @@ Content-Type: application/json } ### -GET {{host}}/api/order-tables +GET {{host}}/api/orderRequest-tables diff --git a/http/orders.http b/http/orders.http index 9fc586f86..6d48c36d3 100644 --- a/http/orders.http +++ b/http/orders.http @@ -5,7 +5,7 @@ Content-Type: application/json { "type": "EAT_IN", "orderTableId": "8d710043-29b6-420e-8452-233f5a035520", - "orderLineItems": [ + "orderLineItemRequests": [ { "menuId": "f59b1e1c-b145-440a-aa6f-6095a0e2d63b", "price": 16000, diff --git a/src/main/java/kitchenpos/Application.java b/src/main/java/kitchenpos/Application.java index ab2a4740d..431f0908f 100644 --- a/src/main/java/kitchenpos/Application.java +++ b/src/main/java/kitchenpos/Application.java @@ -2,10 +2,8 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; -import org.springframework.scheduling.annotation.EnableAsync; @SpringBootApplication -@EnableAsync public class Application { public static void main(String[] args) { SpringApplication.run(Application.class, args); diff --git a/src/main/java/kitchenpos/common/annotation/DomainService.java b/src/main/java/kitchenpos/common/annotation/DomainService.java new file mode 100644 index 000000000..6b8522175 --- /dev/null +++ b/src/main/java/kitchenpos/common/annotation/DomainService.java @@ -0,0 +1,13 @@ +package kitchenpos.common.annotation; + +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +@Target(ElementType.TYPE) +@Inherited +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Component +public @interface DomainService { +} diff --git a/src/main/java/kitchenpos/common/annotation/FactoryService.java b/src/main/java/kitchenpos/common/annotation/FactoryService.java new file mode 100644 index 000000000..80753ec6b --- /dev/null +++ b/src/main/java/kitchenpos/common/annotation/FactoryService.java @@ -0,0 +1,13 @@ +package kitchenpos.common.annotation; + +import org.springframework.stereotype.Component; + +import java.lang.annotation.*; + +@Target(ElementType.TYPE) +@Inherited +@Documented +@Retention(RetentionPolicy.RUNTIME) +@Component +public @interface FactoryService { +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/OrderStatus.java b/src/main/java/kitchenpos/common/domain/orders/OrderStatus.java similarity index 69% rename from src/main/java/kitchenpos/eatinorders/domain/OrderStatus.java rename to src/main/java/kitchenpos/common/domain/orders/OrderStatus.java index fe0f76c7d..cea05dfa3 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/OrderStatus.java +++ b/src/main/java/kitchenpos/common/domain/orders/OrderStatus.java @@ -1,4 +1,4 @@ -package kitchenpos.eatinorders.domain; +package kitchenpos.common.domain.orders; public enum OrderStatus { WAITING, ACCEPTED, SERVED, DELIVERING, DELIVERED, COMPLETED diff --git a/src/main/java/kitchenpos/common/domain/orders/OrderTableStatus.java b/src/main/java/kitchenpos/common/domain/orders/OrderTableStatus.java new file mode 100644 index 000000000..f54e2159a --- /dev/null +++ b/src/main/java/kitchenpos/common/domain/orders/OrderTableStatus.java @@ -0,0 +1,13 @@ +package kitchenpos.common.domain.orders; + +public enum OrderTableStatus { + UNOCCUPIED, OCCUPIED; + + OrderTableStatus OrderTableStatus(boolean occupied) { + if (occupied) { + return OrderTableStatus.OCCUPIED; + } + + return OrderTableStatus.OCCUPIED; + } +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/OrderType.java b/src/main/java/kitchenpos/common/domain/ordertables/OrderType.java similarity index 55% rename from src/main/java/kitchenpos/eatinorders/domain/OrderType.java rename to src/main/java/kitchenpos/common/domain/ordertables/OrderType.java index 0e3133e69..57505f680 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/OrderType.java +++ b/src/main/java/kitchenpos/common/domain/ordertables/OrderType.java @@ -1,4 +1,4 @@ -package kitchenpos.eatinorders.domain; +package kitchenpos.common.domain.ordertables; public enum OrderType { DELIVERY, TAKEOUT, EAT_IN diff --git a/src/main/java/kitchenpos/common/domain/products/ProductPriceChangeEvent.java b/src/main/java/kitchenpos/common/domain/products/ProductPriceChangeEvent.java new file mode 100644 index 000000000..c6c6fd35c --- /dev/null +++ b/src/main/java/kitchenpos/common/domain/products/ProductPriceChangeEvent.java @@ -0,0 +1,21 @@ +package kitchenpos.common.domain.products; + +import java.util.UUID; + +public class ProductPriceChangeEvent { + private final UUID productId; + private final Long price; + + public ProductPriceChangeEvent(UUID productId, Long price) { + this.productId = productId; + this.price = price; + } + + public UUID getProductId() { + return productId; + } + + public Long getPrice() { + return price; + } +} diff --git a/src/main/java/kitchenpos/eatinorders/application/OrderService.java b/src/main/java/kitchenpos/eatinorders/application/OrderService.java index 9b24a8281..ddb843cdc 100644 --- a/src/main/java/kitchenpos/eatinorders/application/OrderService.java +++ b/src/main/java/kitchenpos/eatinorders/application/OrderService.java @@ -1,21 +1,12 @@ package kitchenpos.eatinorders.application; -import kitchenpos.deliveryorders.infra.KitchenridersClient; -import kitchenpos.eatinorders.domain.Order; -import kitchenpos.eatinorders.domain.OrderLineItem; -import kitchenpos.eatinorders.domain.OrderRepository; -import kitchenpos.eatinorders.domain.OrderStatus; -import kitchenpos.eatinorders.domain.OrderTable; -import kitchenpos.eatinorders.domain.OrderTableRepository; -import kitchenpos.eatinorders.domain.OrderType; -import kitchenpos.menus.domain.tobe.menu.Menu; -import kitchenpos.menus.domain.tobe.menu.MenuRepository; +import kitchenpos.eatinorders.application.dto.OrderLineItemRequest; +import kitchenpos.eatinorders.application.dto.OrderRequest; +import kitchenpos.eatinorders.domain.eatinorder.*; +import kitchenpos.eatinorders.domain.menu.MenuClient; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.ArrayList; import java.util.List; import java.util.NoSuchElementException; import java.util.Objects; @@ -23,171 +14,88 @@ @Service public class OrderService { - private final OrderRepository orderRepository; - private final MenuRepository menuRepository; - private final OrderTableRepository orderTableRepository; - private final KitchenridersClient kitchenridersClient; - - public OrderService( - final OrderRepository orderRepository, - final MenuRepository menuRepository, - final OrderTableRepository orderTableRepository, - final KitchenridersClient kitchenridersClient - ) { - this.orderRepository = orderRepository; - this.menuRepository = menuRepository; - this.orderTableRepository = orderTableRepository; - this.kitchenridersClient = kitchenridersClient; - } + private final OrderRepository orderRepository; + private final PassToRiderService passToRiderService; + private final ClearOrderTableService clearOrderTableService; + private final MenuClient menuClient; + private final OrderFactory orderFactory; + + public OrderService( + final OrderRepository orderRepository, final PassToRiderService passToRiderService, + final ClearOrderTableService clearOrderTableService, final MenuClient menuClient, final OrderFactory orderFactory) { + this.orderRepository = orderRepository; + this.clearOrderTableService = clearOrderTableService; + this.menuClient = menuClient; + this.passToRiderService = passToRiderService; + this.orderFactory = orderFactory; + } - @Transactional - public Order create(final Order request) { - final OrderType type = request.getType(); - if (Objects.isNull(type)) { - throw new IllegalArgumentException(); - } - final List orderLineItemRequests = request.getOrderLineItems(); - if (Objects.isNull(orderLineItemRequests) || orderLineItemRequests.isEmpty()) { - throw new IllegalArgumentException(); - } - final List menus = menuRepository.findAllByIdIn( - orderLineItemRequests.stream() - .map(OrderLineItem::getMenuId) - .toList() - ); - if (menus.size() != orderLineItemRequests.size()) { - throw new IllegalArgumentException(); - } - final List orderLineItems = new ArrayList<>(); - for (final OrderLineItem orderLineItemRequest : orderLineItemRequests) { - final long quantity = orderLineItemRequest.getQuantity(); - if (type != OrderType.EAT_IN) { - if (quantity < 0) { - throw new IllegalArgumentException(); - } - } - final Menu menu = menuRepository.findById(orderLineItemRequest.getMenuId()) - .orElseThrow(NoSuchElementException::new); - if (!menu.isDisplayed()) { - throw new IllegalStateException(); - } - if (menu.getMenuPrice().compareTo(orderLineItemRequest.getPrice()) != 0) { - throw new IllegalArgumentException(); - } - final OrderLineItem orderLineItem = new OrderLineItem(); - orderLineItem.setMenu(menu); - orderLineItem.setQuantity(quantity); - orderLineItems.add(orderLineItem); - } - Order order = new Order(); - order.setId(UUID.randomUUID()); - order.setType(type); - order.setStatus(OrderStatus.WAITING); - order.setOrderDateTime(LocalDateTime.now()); - order.setOrderLineItems(orderLineItems); - if (type == OrderType.DELIVERY) { - final String deliveryAddress = request.getDeliveryAddress(); - if (Objects.isNull(deliveryAddress) || deliveryAddress.isEmpty()) { - throw new IllegalArgumentException(); - } - order.setDeliveryAddress(deliveryAddress); - } - if (type == OrderType.EAT_IN) { - final OrderTable orderTable = orderTableRepository.findById(request.getOrderTableId()) - .orElseThrow(NoSuchElementException::new); - if (!orderTable.isOccupied()) { - throw new IllegalStateException(); - } - order.setOrderTable(orderTable); - } - return orderRepository.save(order); + @Transactional + public Order create(final OrderRequest request) { + if (Objects.isNull(request.getOrderLineItems())) { + throw new IllegalArgumentException("주문 상품들의 상태가 비정상입니다."); } - @Transactional - public Order accept(final UUID orderId) { - final Order order = orderRepository.findById(orderId) + final List orderLineItemList = request.getOrderLineItems() + .stream() + .map(OrderLineItemRequest::to) + .toList(); + + OrderLineItems orderLineItems = OrderLineItems.of(menuClient, orderLineItemList); + + Order order = orderFactory.of(request.getType(), request.getStatus(), orderLineItems, + request.getOrderTable().orElse(null), request.getDeliveryAddress()); + + return orderRepository.save(order); + } + + @Transactional + public Order accept(final UUID orderId) { + final Order orderRequest = orderRepository.findById(orderId) .orElseThrow(NoSuchElementException::new); - if (order.getStatus() != OrderStatus.WAITING) { - throw new IllegalStateException(); - } - if (order.getType() == OrderType.DELIVERY) { - BigDecimal sum = BigDecimal.ZERO; - for (final OrderLineItem orderLineItem : order.getOrderLineItems()) { - sum = orderLineItem.getMenu() - .getMenuPrice() - .multiply(BigDecimal.valueOf(orderLineItem.getQuantity())); - } - kitchenridersClient.requestDelivery(orderId, sum, order.getDeliveryAddress()); - } - order.setStatus(OrderStatus.ACCEPTED); - return order; - } - @Transactional - public Order serve(final UUID orderId) { - final Order order = orderRepository.findById(orderId) + orderRequest.accept(passToRiderService); + return orderRequest; + } + + @Transactional + public Order serve(final UUID orderId) { + final Order orderRequest = orderRepository.findById(orderId) .orElseThrow(NoSuchElementException::new); - if (order.getStatus() != OrderStatus.ACCEPTED) { - throw new IllegalStateException(); - } - order.setStatus(OrderStatus.SERVED); - return order; - } - @Transactional - public Order startDelivery(final UUID orderId) { - final Order order = orderRepository.findById(orderId) + orderRequest.serve(); + return orderRequest; + } + + @Transactional + public Order startDelivery(final UUID orderId) { + final Order orderRequest = orderRepository.findById(orderId) .orElseThrow(NoSuchElementException::new); - if (order.getType() != OrderType.DELIVERY) { - throw new IllegalStateException(); - } - if (order.getStatus() != OrderStatus.SERVED) { - throw new IllegalStateException(); - } - order.setStatus(OrderStatus.DELIVERING); - return order; - } - @Transactional - public Order completeDelivery(final UUID orderId) { - final Order order = orderRepository.findById(orderId) + orderRequest.delivering(); + return orderRequest; + } + + @Transactional + public Order completeDelivery(final UUID orderId) { + final Order orderRequest = orderRepository.findById(orderId) .orElseThrow(NoSuchElementException::new); - if (order.getStatus() != OrderStatus.DELIVERING) { - throw new IllegalStateException(); - } - order.setStatus(OrderStatus.DELIVERED); - return order; - } - @Transactional - public Order complete(final UUID orderId) { - final Order order = orderRepository.findById(orderId) + orderRequest.delivered(); + return orderRequest; + } + + @Transactional + public Order complete(final UUID orderId) { + final Order orderRequest = orderRepository.findById(orderId) .orElseThrow(NoSuchElementException::new); - final OrderType type = order.getType(); - final OrderStatus status = order.getStatus(); - if (type == OrderType.DELIVERY) { - if (status != OrderStatus.DELIVERED) { - throw new IllegalStateException(); - } - } - if (type == OrderType.TAKEOUT || type == OrderType.EAT_IN) { - if (status != OrderStatus.SERVED) { - throw new IllegalStateException(); - } - } - order.setStatus(OrderStatus.COMPLETED); - if (type == OrderType.EAT_IN) { - final OrderTable orderTable = order.getOrderTable(); - if (!orderRepository.existsByOrderTableAndStatusNot(orderTable, OrderStatus.COMPLETED)) { - orderTable.setNumberOfGuests(0); - orderTable.setOccupied(false); - } - } - return order; - } - @Transactional(readOnly = true) - public List findAll() { - return orderRepository.findAll(); - } + orderRequest.complete(clearOrderTableService); + return orderRequest; + } + + @Transactional(readOnly = true) + public List findAll() { + return orderRepository.findAll(); + } } diff --git a/src/main/java/kitchenpos/eatinorders/application/OrderTableService.java b/src/main/java/kitchenpos/eatinorders/application/OrderTableService.java index 1df7e345f..d369dd6e3 100644 --- a/src/main/java/kitchenpos/eatinorders/application/OrderTableService.java +++ b/src/main/java/kitchenpos/eatinorders/application/OrderTableService.java @@ -1,74 +1,48 @@ package kitchenpos.eatinorders.application; -import kitchenpos.eatinorders.domain.OrderRepository; -import kitchenpos.eatinorders.domain.OrderStatus; -import kitchenpos.eatinorders.domain.OrderTable; -import kitchenpos.eatinorders.domain.OrderTableRepository; +import kitchenpos.eatinorders.application.dto.OrderTableRequest; +import kitchenpos.eatinorders.domain.eatinorder.OrderTable; +import kitchenpos.eatinorders.domain.eatinorder.OrderTableRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.List; import java.util.NoSuchElementException; -import java.util.Objects; import java.util.UUID; @Service public class OrderTableService { private final OrderTableRepository orderTableRepository; - private final OrderRepository orderRepository; - public OrderTableService(final OrderTableRepository orderTableRepository, final OrderRepository orderRepository) { + public OrderTableService(final OrderTableRepository orderTableRepository) { this.orderTableRepository = orderTableRepository; - this.orderRepository = orderRepository; } @Transactional - public OrderTable create(final OrderTable request) { - final String name = request.getName(); - if (Objects.isNull(name) || name.isEmpty()) { - throw new IllegalArgumentException(); - } - final OrderTable orderTable = new OrderTable(); - orderTable.setId(UUID.randomUUID()); - orderTable.setName(name); - orderTable.setNumberOfGuests(0); - orderTable.setOccupied(false); - return orderTableRepository.save(orderTable); + public OrderTable create(final OrderTableRequest request) { + + final OrderTable orderTableRequest = OrderTable.of(request.getName(), request.getNumberOfGuests()); + + return orderTableRepository.save(orderTableRequest); } @Transactional public OrderTable sit(final UUID orderTableId) { - final OrderTable orderTable = orderTableRepository.findById(orderTableId) + final OrderTable orderTableRequest = orderTableRepository.findById(orderTableId) .orElseThrow(NoSuchElementException::new); - orderTable.setOccupied(true); - return orderTable; - } + orderTableRequest.occupy(); - @Transactional - public OrderTable clear(final UUID orderTableId) { - final OrderTable orderTable = orderTableRepository.findById(orderTableId) - .orElseThrow(NoSuchElementException::new); - if (orderRepository.existsByOrderTableAndStatusNot(orderTable, OrderStatus.COMPLETED)) { - throw new IllegalStateException(); - } - orderTable.setNumberOfGuests(0); - orderTable.setOccupied(false); - return orderTable; + return orderTableRequest; } @Transactional - public OrderTable changeNumberOfGuests(final UUID orderTableId, final OrderTable request) { - final int numberOfGuests = request.getNumberOfGuests(); - if (numberOfGuests < 0) { - throw new IllegalArgumentException(); - } - final OrderTable orderTable = orderTableRepository.findById(orderTableId) + public OrderTable changeNumberOfGuests(final UUID orderTableId, final OrderTableRequest request) { + + final OrderTable orderTableRequest = orderTableRepository.findById(orderTableId) .orElseThrow(NoSuchElementException::new); - if (!orderTable.isOccupied()) { - throw new IllegalStateException(); - } - orderTable.setNumberOfGuests(numberOfGuests); - return orderTable; + + orderTableRequest.changeCustomerHeadCounts(request.getNumberOfGuests()); + return orderTableRequest; } @Transactional(readOnly = true) diff --git a/src/main/java/kitchenpos/eatinorders/domain/OrderLineItem.java b/src/main/java/kitchenpos/eatinorders/application/dto/OrderLineItemRequest.java similarity index 50% rename from src/main/java/kitchenpos/eatinorders/domain/OrderLineItem.java rename to src/main/java/kitchenpos/eatinorders/application/dto/OrderLineItemRequest.java index a8eec541c..7839919ce 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/OrderLineItem.java +++ b/src/main/java/kitchenpos/eatinorders/application/dto/OrderLineItemRequest.java @@ -1,47 +1,24 @@ -package kitchenpos.eatinorders.domain; - -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.Table; -import jakarta.persistence.Transient; -import kitchenpos.menus.application.dto.MenuRequest; +package kitchenpos.eatinorders.application.dto; + +import kitchenpos.eatinorders.domain.eatinorder.OrderLineItem; import kitchenpos.menus.domain.tobe.menu.Menu; import java.math.BigDecimal; import java.util.UUID; -@Table(name = "order_line_item") -@Entity -public class OrderLineItem { - @Column(name = "seq") - @GeneratedValue(strategy = GenerationType.IDENTITY) - @Id +public class OrderLineItemRequest { + private Long seq; - @ManyToOne(optional = false) - @JoinColumn( - name = "menu_id", - columnDefinition = "binary(16)", - foreignKey = @ForeignKey(name = "fk_order_line_item_to_menu") - ) private Menu menu; - @Column(name = "quantity", nullable = false) private long quantity; - @Transient private UUID menuId; - @Transient private BigDecimal price; - public OrderLineItem() { + public OrderLineItemRequest() { } public Long getSeq() { @@ -83,4 +60,8 @@ public BigDecimal getPrice() { public void setPrice(final BigDecimal price) { this.price = price; } + + public OrderLineItem to(){ + return OrderLineItem.of(menuId, price.longValue(), quantity); + } } diff --git a/src/main/java/kitchenpos/eatinorders/application/dto/OrderRequest.java b/src/main/java/kitchenpos/eatinorders/application/dto/OrderRequest.java new file mode 100644 index 000000000..a58cfac3b --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/application/dto/OrderRequest.java @@ -0,0 +1,92 @@ +package kitchenpos.eatinorders.application.dto; + +import kitchenpos.common.domain.orders.OrderStatus; +import kitchenpos.common.domain.ordertables.OrderType; +import kitchenpos.eatinorders.domain.eatinorder.OrderTable; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; + + +public class OrderRequest { + + private UUID id; + + private OrderType type; + + private OrderStatus status; + + private LocalDateTime orderDateTime; + + private List orderLineItemRequests; + + private String deliveryAddress; + + private OrderTable orderTable; + + public OrderRequest() { + } + + public UUID getId() { + return id; + } + + public void setId(final UUID id) { + this.id = id; + } + + public OrderType getType() { + return type; + } + + public void setType(final OrderType type) { + this.type = type; + } + + public OrderStatus getStatus() { + return status; + } + + public void setStatus(final OrderStatus status) { + this.status = status; + } + + public LocalDateTime getOrderDateTime() { + return orderDateTime; + } + + public void setOrderDateTime(final LocalDateTime orderDateTime) { + this.orderDateTime = orderDateTime; + } + + public List getOrderLineItems() { + return orderLineItemRequests; + } + + public void setOrderLineItems(final List orderLineItemRequests) { + this.orderLineItemRequests = orderLineItemRequests; + } + + public String getDeliveryAddress() { + return deliveryAddress; + } + + public void setDeliveryAddress(final String deliveryAddress) { + this.deliveryAddress = deliveryAddress; + } + + public Optional getOrderTable() { + if(Objects.isNull(orderTable)){ + return Optional.ofNullable(null); + } + + return Optional.of(orderTable); + } + + public void setOrderTable(final OrderTable orderTable) { + this.orderTable = orderTable; + } +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/OrderTable.java b/src/main/java/kitchenpos/eatinorders/application/dto/OrderTableRequest.java similarity index 61% rename from src/main/java/kitchenpos/eatinorders/domain/OrderTable.java rename to src/main/java/kitchenpos/eatinorders/application/dto/OrderTableRequest.java index bdd3dd23c..1be7fefef 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/OrderTable.java +++ b/src/main/java/kitchenpos/eatinorders/application/dto/OrderTableRequest.java @@ -1,29 +1,20 @@ -package kitchenpos.eatinorders.domain; +package kitchenpos.eatinorders.application.dto; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.Id; -import jakarta.persistence.Table; +import kitchenpos.eatinorders.domain.eatinorder.OrderTable; import java.util.UUID; -@Table(name = "order_table") -@Entity -public class OrderTable { - @Column(name = "id", columnDefinition = "binary(16)") - @Id +public class OrderTableRequest { + private UUID id; - @Column(name = "name", nullable = false) private String name; - @Column(name = "number_of_guests", nullable = false) private int numberOfGuests; - @Column(name = "occupied", nullable = false) private boolean occupied; - public OrderTable() { + public OrderTableRequest() { } public UUID getId() { @@ -57,4 +48,8 @@ public boolean isOccupied() { public void setOccupied(final boolean occupied) { this.occupied = occupied; } + + public OrderTable to(){ + return OrderTable.of(name, numberOfGuests); + } } diff --git a/src/main/java/kitchenpos/eatinorders/domain/Order.java b/src/main/java/kitchenpos/eatinorders/domain/Order.java deleted file mode 100644 index 4a5991301..000000000 --- a/src/main/java/kitchenpos/eatinorders/domain/Order.java +++ /dev/null @@ -1,127 +0,0 @@ -package kitchenpos.eatinorders.domain; - -import jakarta.persistence.CascadeType; -import jakarta.persistence.Column; -import jakarta.persistence.Entity; -import jakarta.persistence.EnumType; -import jakarta.persistence.Enumerated; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; -import jakarta.persistence.Table; -import jakarta.persistence.Transient; - -import java.time.LocalDateTime; -import java.util.List; -import java.util.UUID; - -@Table(name = "orders") -@Entity -public class Order { - @Column(name = "id", columnDefinition = "binary(16)") - @Id - private UUID id; - - @Column(name = "type", nullable = false, columnDefinition = "varchar(255)") - @Enumerated(EnumType.STRING) - private OrderType type; - - @Column(name = "status", nullable = false, columnDefinition = "varchar(255)") - @Enumerated(EnumType.STRING) - private OrderStatus status; - - @Column(name = "order_date_time", nullable = false) - private LocalDateTime orderDateTime; - - @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) - @JoinColumn( - name = "order_id", - nullable = false, - columnDefinition = "binary(16)", - foreignKey = @ForeignKey(name = "fk_order_line_item_to_orders") - ) - private List orderLineItems; - - @Column(name = "delivery_address") - private String deliveryAddress; - - @ManyToOne - @JoinColumn( - name = "order_table_id", - columnDefinition = "binary(16)", - foreignKey = @ForeignKey(name = "fk_orders_to_order_table") - ) - private OrderTable orderTable; - - @Transient - private UUID orderTableId; - - public Order() { - } - - public UUID getId() { - return id; - } - - public void setId(final UUID id) { - this.id = id; - } - - public OrderType getType() { - return type; - } - - public void setType(final OrderType type) { - this.type = type; - } - - public OrderStatus getStatus() { - return status; - } - - public void setStatus(final OrderStatus status) { - this.status = status; - } - - public LocalDateTime getOrderDateTime() { - return orderDateTime; - } - - public void setOrderDateTime(final LocalDateTime orderDateTime) { - this.orderDateTime = orderDateTime; - } - - public List getOrderLineItems() { - return orderLineItems; - } - - public void setOrderLineItems(final List orderLineItems) { - this.orderLineItems = orderLineItems; - } - - public String getDeliveryAddress() { - return deliveryAddress; - } - - public void setDeliveryAddress(final String deliveryAddress) { - this.deliveryAddress = deliveryAddress; - } - - public OrderTable getOrderTable() { - return orderTable; - } - - public void setOrderTable(final OrderTable orderTable) { - this.orderTable = orderTable; - } - - public UUID getOrderTableId() { - return orderTableId; - } - - public void setOrderTableId(final UUID orderTableId) { - this.orderTableId = orderTableId; - } -} diff --git a/src/main/java/kitchenpos/eatinorders/domain/eatinorder/ClearOrderTableService.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/ClearOrderTableService.java new file mode 100644 index 000000000..a48f45a9a --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/ClearOrderTableService.java @@ -0,0 +1,32 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import kitchenpos.common.annotation.DomainService; +import kitchenpos.common.domain.orders.OrderStatus; + +@DomainService +public class ClearOrderTableService { + + private final OrderRepository orderRepository; + private final OrderTableRepository orderTableRepository; + + + public ClearOrderTableService(final OrderRepository orderRepository, OrderTableRepository orderTableRepository) { + this.orderRepository = orderRepository; + this.orderTableRepository = orderTableRepository; + } + + public void clear(final Order order) { + if (order.isNotEatIn()) { + return; + } + if (orderTableRepository.findById(order.getOrderTableId()).isEmpty()) { + throw new IllegalArgumentException("주문 테이블이 존재 하지 않습니다."); + } + + if (orderRepository.existsByOrderTableAndStatusNot(order.getOrderTable(), OrderStatus.COMPLETED)) { + throw new IllegalStateException("주문이 완료되지 않은 테이블이 있을때, 주문 테이블을 사용안함으로 변경할 수 없습니다."); + } + + order.clearOrderTable(); + } +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/eatinorder/CustomerHeadcount.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/CustomerHeadcount.java new file mode 100644 index 000000000..fee31b135 --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/CustomerHeadcount.java @@ -0,0 +1,55 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import jakarta.persistence.Embeddable; + +import java.util.Objects; + +@Embeddable +public class CustomerHeadcount { + private static final int ZERO = 0; + private Integer headCounts; + + protected CustomerHeadcount() { + } + + private CustomerHeadcount(Integer headCounts) { + validate(headCounts); + + this.headCounts = headCounts; + } + + public static CustomerHeadcount of(Integer headCounts) { + return new CustomerHeadcount(headCounts); + } + + public static CustomerHeadcount zero() { + return new CustomerHeadcount(ZERO); + } + private void validate(Integer headCounts) { + if (Objects.isNull(headCounts)) { + throw new IllegalArgumentException("방문한 손님 수가 올바르지 않으면 변경할 수 없다."); + } + + } + + public Integer getHeadCounts() { + return headCounts; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + CustomerHeadcount that = (CustomerHeadcount) o; + return Objects.equals(headCounts, that.headCounts); + } + + @Override + public int hashCode() { + return Objects.hash(headCounts); + } +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/eatinorder/DefaultOrderFactory.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/DefaultOrderFactory.java new file mode 100644 index 000000000..194ce499d --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/DefaultOrderFactory.java @@ -0,0 +1,40 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import kitchenpos.common.annotation.FactoryService; +import kitchenpos.common.domain.orders.OrderStatus; +import kitchenpos.common.domain.ordertables.OrderType; + +@FactoryService +public class DefaultOrderFactory implements OrderFactory { + + public Order of(final OrderType orderType, final OrderStatus orderStatus, final OrderLineItems orderLineItems, final OrderTable orderTable, final String orderDelivery) { + if (OrderType.TAKEOUT.equals(orderType)) { + return createTakeoutOrder(orderStatus, orderLineItems); + } else if (OrderType.EAT_IN.equals(orderType)) { + return createEatInOrder(orderStatus, orderLineItems, orderTable); + } else if (OrderType.DELIVERY.equals(orderType)) { + return createDeliveryOrder(orderStatus, orderLineItems, orderDelivery); + } + + throw new IllegalArgumentException("지원하지 않는 주문타입입니다."); + } + + public Order createEatInOrder(final OrderStatus status, final OrderLineItems orderLineItems, final OrderTable orderTable) { + Order order = new EatInOrder(status, orderLineItems, orderTable); + order.mapOrder(); + return order; + } + + public Order createDeliveryOrder(final OrderStatus status, final OrderLineItems orderLineItems, final String deliveryAddress) { + Order order = new DeliveryOrder(status, orderLineItems, deliveryAddress); + order.mapOrder(); + return order; + } + + public Order createTakeoutOrder(final OrderStatus status, final OrderLineItems orderLineItems) { + Order order = new TakeoutOrder(status, orderLineItems); + order.mapOrder(); + return order; + } + +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/eatinorder/DeliveryOrder.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/DeliveryOrder.java new file mode 100644 index 000000000..451326aeb --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/DeliveryOrder.java @@ -0,0 +1,79 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import kitchenpos.common.domain.orders.OrderStatus; +import kitchenpos.common.domain.ordertables.OrderType; + +import java.util.Objects; + +@Entity +@DiscriminatorValue("DELIVERY") +public class DeliveryOrder extends Order { + + @Column(name = "delivery_address") + private String deliveryAddress; + + protected DeliveryOrder() { + + } + + protected DeliveryOrder(OrderStatus orderStatus, OrderLineItems orderLineItems, String deliveryAddress) { + super(OrderType.DELIVERY, orderStatus, orderLineItems); + + if (Objects.isNull(deliveryAddress)) { + throw new IllegalArgumentException("`주문`이 생성할 때, `주문 장소`가 필수로 기입되어야 한다."); + } + + if (deliveryAddress.isEmpty()) { + throw new IllegalArgumentException("배달 주소는 비워 둘 수 없다."); + } + this.deliveryAddress = deliveryAddress; + validate(); + } + + private void validate() { + if (containsNegativeMenuCounts()) { + throw new IllegalArgumentException("주문을 생성할 때, 주문상품들의 갯수를 감소시킬 수 없다."); + } + } + + @Override + public void accept(PassToRiderService passToRiderService) { + if (status != OrderStatus.WAITING) { + throw new IllegalStateException(" `주문 상태`가 `접수(ACCEPTED)`이 아닌 주문은 전달할 수 없습니다."); + } + status = OrderStatus.ACCEPTED; + + passToRiderService.acceptOrder(this); + } + + public void delivering() { + if (status != OrderStatus.SERVED) { + throw new IllegalStateException(" `주문 상태`가 `접수(ACCEPTED)`이 아닌 주문은 배달할 수 없습니다."); + } + status = OrderStatus.DELIVERING; + + } + + public void delivered() { + if (status != OrderStatus.DELIVERING) { + throw new IllegalStateException(" `주문 상태`가 `배달중(DELIVERING)`이 아닌 주문은 배달완료할 수 없습니다."); + } + status = OrderStatus.DELIVERED; + + } + + @Override + public void complete(ClearOrderTableService clearOrderTableService) { + if (status != OrderStatus.DELIVERED) { + throw new IllegalStateException(" `주문 상태`가 `배달완료(DELIVERED)`이 아닌 주문은 완료할 수 없습니다."); + } + status = OrderStatus.COMPLETED; + } + + public String getDeliveryAddress() { + return deliveryAddress; + } +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/eatinorder/EatInOrder.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/EatInOrder.java new file mode 100644 index 000000000..b7f540cf0 --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/EatInOrder.java @@ -0,0 +1,53 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import kitchenpos.common.domain.orders.OrderStatus; +import kitchenpos.common.domain.ordertables.OrderType; + +@Entity +@DiscriminatorValue("EAT_IN") +public class EatInOrder extends Order { + + protected EatInOrder() { + super(); + } + + @Override + public void accept(PassToRiderService passToRiderService) { + if (status != OrderStatus.WAITING) { + throw new IllegalStateException(" `주문 상태`가 `대기중(WAITING)`이 아닌 주문은 수락할 수 없습니다."); + } + status = OrderStatus.ACCEPTED; + } + + @Override + public void delivering() { + throw new IllegalStateException("해당 주문 타입은 라이더에게 전달되지 않아도 됩니다."); + + } + + @Override + public void delivered() { + throw new IllegalStateException("해당 주문 타입은 라이더에게 전달되지 않아도 됩니다."); + } + + protected EatInOrder(OrderStatus orderStatus, OrderLineItems orderLineItems, OrderTable orderTable) { + super(OrderType.EAT_IN, orderStatus, orderLineItems, orderTable); + validate(); + } + + @Override + public void complete(ClearOrderTableService clearOrderTableService) { + if (status != OrderStatus.SERVED) { + throw new IllegalStateException(" `주문 상태`가 `전달(SERVED)`이 아닌 주문은 전달할 수 없습니다."); + } + status = OrderStatus.COMPLETED; + + clearOrderTableService.clear(this); + } + + private void validate(){ + isNotOccupied(); + } +} diff --git a/src/main/java/kitchenpos/deliveryorders/infra/KitchenridersClient.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/KitchenridersClient.java similarity index 78% rename from src/main/java/kitchenpos/deliveryorders/infra/KitchenridersClient.java rename to src/main/java/kitchenpos/eatinorders/domain/eatinorder/KitchenridersClient.java index 0c8278791..55e9a9cfc 100644 --- a/src/main/java/kitchenpos/deliveryorders/infra/KitchenridersClient.java +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/KitchenridersClient.java @@ -1,4 +1,4 @@ -package kitchenpos.deliveryorders.infra; +package kitchenpos.eatinorders.domain.eatinorder; import java.math.BigDecimal; import java.util.UUID; diff --git a/src/main/java/kitchenpos/eatinorders/domain/eatinorder/Order.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/Order.java new file mode 100644 index 000000000..218be1bcf --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/Order.java @@ -0,0 +1,158 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import jakarta.persistence.*; +import kitchenpos.common.domain.orders.OrderStatus; +import kitchenpos.common.domain.ordertables.OrderType; + +import java.math.BigDecimal; +import java.time.LocalDateTime; +import java.util.Objects; +import java.util.UUID; + +@Table(name = "orders") +@Entity +@Inheritance +@DiscriminatorColumn(name = "order_type") +public abstract class Order { + + @Column(name = "id", columnDefinition = "binary(16)") + @Id + private UUID id; + + @Column(name = "type", nullable = false, columnDefinition = "varchar(255)") + @Enumerated(EnumType.STRING) + protected OrderType type; + + @Column(name = "status", nullable = false, columnDefinition = "varchar(255)") + @Enumerated(EnumType.STRING) + protected OrderStatus status; + + @Column(name = "order_date_time", nullable = false) + private LocalDateTime orderDateTime; + + @Embedded + private OrderLineItems orderLineItems; + + @ManyToOne + @JoinColumn( + name = "order_table_id", + columnDefinition = "binary(16)", + foreignKey = @ForeignKey(name = "fk_orders_to_order_table") + ) + private OrderTable orderTable; + + protected Order() { + } + + protected Order(final OrderType type, final OrderStatus status, final OrderLineItems orderLineItems, final OrderTable orderTable) { + validate(type, status); + this.id = UUID.randomUUID(); + this.type = type; + this.status = status; + this.orderLineItems = orderLineItems; + this.orderDateTime = LocalDateTime.now(); + this.orderTable = orderTable; + } + + protected Order(final OrderType type, final OrderStatus status, final OrderLineItems orderLineItems) { + this(type, status, orderLineItems, null); + } + + protected Order(final OrderType type, final OrderLineItems orderLineItems) { + this(type, OrderStatus.WAITING, orderLineItems, null); + } + + protected Order(final OrderType type, final OrderLineItems orderLineItems, final OrderTable orderTable) { + this(type, OrderStatus.WAITING, orderLineItems, orderTable); + } + + private void validate(final OrderType type, final OrderStatus status) { + if (Objects.isNull(type)) { + throw new IllegalArgumentException("주문 타입이 올바르지 않습니다."); + } + + if (Objects.isNull(status)) { + throw new IllegalArgumentException("주문 상태가 올바르지 않습니다."); + } + } + + + public abstract void accept(PassToRiderService acceptOrderService); + + public abstract void delivering(); + + public abstract void delivered(); + + public void serve() { + if (status != OrderStatus.ACCEPTED) { + throw new IllegalStateException(" `주문 상태`가 `접수(ACCEPTED)`이 아닌 주문은 전달할 수 없습니다."); + } + status = OrderStatus.SERVED; + } + + public abstract void complete(ClearOrderTableService clearOrderTableService); + + protected void mapOrder() { + orderLineItems.setOrder(this); + } + + public BigDecimal getOrderLineItemsSum() { + return orderLineItems.getSum(); + } + protected void clearOrderTable(){ + orderTable.clear(); + } + + public boolean isDelivery() { + return getType().equals(OrderType.DELIVERY); + } + + public boolean isNotEatIn() { + return !getType().equals(OrderType.EAT_IN); + } + public UUID getId() { + return id; + } + + protected OrderType getType() { + return type; + } + + public boolean hasStatus(OrderStatus status) { + return this.status.equals(status); + } + + public OrderTable getOrderTable() { + return orderTable; + } + public void isNotOccupied() { + orderTable.validateOccupied(); + } + public UUID getOrderTableId() { + return orderTable.getId(); + } + + public boolean containsNegativeMenuCounts() { + return orderLineItems.containsNegativeMenuQuantity(); + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + Order order = (Order) o; + return Objects.equals(id, order.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } + + + +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderFactory.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderFactory.java new file mode 100644 index 000000000..c9718b179 --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderFactory.java @@ -0,0 +1,14 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import kitchenpos.common.domain.orders.OrderStatus; +import kitchenpos.common.domain.ordertables.OrderType; + +public interface OrderFactory { + Order of(final OrderType orderType, final OrderStatus orderStatus, final OrderLineItems orderLineItems, final OrderTable orderTable, final String orderDelivery); + + Order createEatInOrder(final OrderStatus status, final OrderLineItems orderLineItems, final OrderTable orderTable); + + Order createDeliveryOrder(final OrderStatus status, final OrderLineItems orderLineItems, final String deliveryAddress); + + Order createTakeoutOrder(final OrderStatus status, final OrderLineItems orderLineItems); +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderLineItem.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderLineItem.java new file mode 100644 index 000000000..392a6a404 --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderLineItem.java @@ -0,0 +1,84 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import jakarta.persistence.*; +import kitchenpos.common.domain.Price; + +import java.math.BigDecimal; +import java.util.Objects; +import java.util.UUID; + +@Table(name = "order_line_item") +@Entity +public class OrderLineItem { + + @Column(name = "seq") + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Id + private Long seq; + + @Column(name = "quantity", nullable = false) + private long quantity; + + @Column(name = "menu_id", nullable = false) + private UUID menuId; + + @Embedded + private Price price; + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn( + name = "order_id", + columnDefinition = "binary(16)", + foreignKey = @ForeignKey(name = "fk_order_line_items_to_orders") + ) + private Order order; + + protected OrderLineItem() { + } + + private OrderLineItem(final UUID menuId, final Price price, final Long quantity) { + validate(menuId, quantity); + this.menuId = menuId; + this.quantity = quantity; + this.price = price; + } + + public static OrderLineItem of(final UUID menuId, final Long price, final Long quantity) { + return new OrderLineItem(menuId, Price.from(price), quantity); + } + + protected void setOrder(final Order order){ + this.order = order; + + } + private void validate(final UUID uuid, final Long quantity) { + if (Objects.isNull(uuid) || Objects.isNull(quantity)) { + throw new RuntimeException("메뉴 혹은 상품 수량이 비정상이라면 등록할 수 없다."); + } + } + + protected UUID getMenuId() { + return menuId; + } + + protected BigDecimal getPrice() { + return price.getPrice(); + } + + protected Long getQuantity() { + return quantity; + } + + @Override + public boolean equals(Object o) { + if (this == o) return true; + if (o == null || getClass() != o.getClass()) return false; + OrderLineItem that = (OrderLineItem) o; + return Objects.equals(seq, that.seq); + } + + @Override + public int hashCode() { + return Objects.hash(seq); + } +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderLineItems.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderLineItems.java new file mode 100644 index 000000000..6674dc660 --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderLineItems.java @@ -0,0 +1,109 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import jakarta.persistence.CascadeType; +import jakarta.persistence.Embeddable; +import jakarta.persistence.FetchType; +import jakarta.persistence.OneToMany; +import kitchenpos.eatinorders.domain.menu.MenuClient; +import kitchenpos.eatinorders.domain.menu.OrderMenu; +import kitchenpos.menus.domain.tobe.menu.MenuProduct; +import kitchenpos.menus.domain.tobe.menu.MenuProducts; +import kitchenpos.menus.domain.tobe.menu.ProductClient; + +import java.math.BigDecimal; +import java.util.*; +import java.util.stream.Collectors; + +@Embeddable +public class OrderLineItems { + + @OneToMany(cascade = {CascadeType.PERSIST, + CascadeType.MERGE}, fetch = FetchType.LAZY, mappedBy = "order") + private List orderLineItems = new ArrayList<>(); + + protected OrderLineItems (){ + + } + + private OrderLineItems(MenuClient menuClient, List orderLineItems){ + validate(menuClient, orderLineItems); + this.orderLineItems.addAll(orderLineItems); + } + public static OrderLineItems of(MenuClient menuClient, List orderLineItems) { + return new OrderLineItems(menuClient, orderLineItems); + } + + protected void setOrder(final Order order){ + orderLineItems + .forEach(item -> item.setOrder(order)); + } + + protected int getSize() { + return orderLineItems.size(); + } + + protected BigDecimal getSum(){ + return orderLineItems.stream().map(item -> item.getPrice().multiply(BigDecimal.valueOf(item.getQuantity()))) + .reduce(BigDecimal.ZERO, (a, b) -> a.add(b)); + } + private void validate(MenuClient menuClient, List orderLineItems){ + if (orderLineItems.isEmpty()){ + throw new IllegalArgumentException("주문 메뉴들이 비어있습니다."); + } + + final List menuIds = orderLineItems.stream() + .map(OrderLineItem::getMenuId) + .toList(); + + Map orderMenus = menuClient.findMenuInfoByMenuIds(menuIds); + + if (orderMenus.size() != orderLineItems.size()){ + throw new IllegalArgumentException("`메뉴` 내의 `주문상품`들이 삭제된 `상품`이면 안된다."); + } + + validateHiddenMenu(orderLineItems, orderMenus); + validateMenuPrice(orderLineItems, orderMenus); + + } + + private void validateMenuPrice(List orderLineItems, Map orderMenus) { + orderLineItems.stream() + .filter( + orderLineItem -> matchPrice(orderMenus.getOrDefault(orderLineItem.getMenuId(), null), orderLineItem.getPrice()) + ) + .findAny() + .ifPresent(menu -> { + throw new IllegalArgumentException("주문한 메뉴의 가격은 실제 메뉴 가격과 일치해야 한다."); + }); + } + + private void validateHiddenMenu(List orderLineItems, Map orderMenus) { + orderLineItems.stream() + .filter( + orderLineItem -> matchIsDisplayed(orderMenus.getOrDefault(orderLineItem.getMenuId(), null)) + ).findAny() + .ifPresent(menu -> { + throw new IllegalStateException("숨겨진 메뉴는 주문할 수 없다."); + }); + } + + private boolean matchIsDisplayed(OrderMenu menu){ + Optional orderMenu = Optional.ofNullable(menu); + + OrderMenu matched = orderMenu.orElseThrow(() -> new IllegalArgumentException("메뉴가 존재하지 않습니다.")); + + return !matched.isDisplayed(); + } + + private boolean matchPrice(OrderMenu menu, BigDecimal orderLineItemPrice){ + Optional orderMenu = Optional.ofNullable(menu); + + OrderMenu matched = orderMenu.orElseThrow(() -> new IllegalArgumentException("메뉴가 존재하지 않습니다.")); + + return !orderLineItemPrice.equals(matched.price()); + } + + protected boolean containsNegativeMenuQuantity(){ + return orderLineItems.stream().anyMatch(item -> item.getQuantity() < 0); + } +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/OrderRepository.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderRepository.java similarity index 58% rename from src/main/java/kitchenpos/eatinorders/domain/OrderRepository.java rename to src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderRepository.java index f98d45c15..28f7e042c 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/OrderRepository.java +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderRepository.java @@ -1,4 +1,7 @@ -package kitchenpos.eatinorders.domain; +package kitchenpos.eatinorders.domain.eatinorder; + +import kitchenpos.common.domain.orders.OrderStatus; +import kitchenpos.eatinorders.application.dto.OrderTableRequest; import java.util.List; import java.util.Optional; @@ -11,6 +14,6 @@ public interface OrderRepository { List findAll(); - boolean existsByOrderTableAndStatusNot(OrderTable orderTable, OrderStatus status); + boolean existsByOrderTableAndStatusNot(OrderTable orderTable, OrderStatus orderStatus); } diff --git a/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderTable.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderTable.java new file mode 100644 index 000000000..b94241b21 --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderTable.java @@ -0,0 +1,111 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import jakarta.persistence.*; +import kitchenpos.common.domain.orders.OrderTableStatus; + +import java.util.Objects; +import java.util.UUID; + +@Table(name = "order_table") +@Entity +public class OrderTable { + private static final int ZERO = 0; + @Column(name = "id", columnDefinition = "binary(16)") + @Id + private UUID id; + + @Column(name = "name", nullable = false) + @Embedded + private OrderTableName name; + + @Column(name = "customer_headcounts", nullable = false) + private CustomerHeadcount customerHeadcount; + + @Column(name = "occupied", nullable = false) + @Enumerated(EnumType.STRING) + private OrderTableStatus occupied; + + protected OrderTable() { + } + + private OrderTable(OrderTableName orderTableName, CustomerHeadcount customerHeadCount) { + this.id = UUID.randomUUID(); + this.name = orderTableName; + this.customerHeadcount = customerHeadCount; + this.occupied = OrderTableStatus.UNOCCUPIED; + } + + private OrderTable(boolean occupied, OrderTableName orderTableName, CustomerHeadcount customerHeadCount) { + this(orderTableName, customerHeadCount); + if (occupied) { + this.occupied = OrderTableStatus.OCCUPIED; + } else { + this.occupied = OrderTableStatus.UNOCCUPIED; + } + } + + public static OrderTable of(String orderTableName, Integer customerHeadCount) { + return new OrderTable(OrderTableName.of(orderTableName), CustomerHeadcount.of(customerHeadCount)); + } + + public static OrderTable of(boolean occupied, String orderTableName, Integer customerHeadCount) { + return new OrderTable(occupied, OrderTableName.of(orderTableName), CustomerHeadcount.of(customerHeadCount)); + } + + public void clear() { + unoccupy(); + } + + protected void unoccupy() { + this.occupied = OrderTableStatus.UNOCCUPIED; + this.customerHeadcount = CustomerHeadcount.zero(); + } + + public void occupy() { + this.occupied = OrderTableStatus.OCCUPIED; + } + + public void changeCustomerHeadCounts(Integer headCounts) { + validateOccupied(); + if (headCounts.compareTo(ZERO) <= ZERO) { + throw new IllegalArgumentException("방문한 손님 수는 0 이상이어야 한다."); + } + this.customerHeadcount = CustomerHeadcount.of(headCounts); + } + + protected void validateOccupied() { + if (this.occupied.equals(OrderTableStatus.UNOCCUPIED)) { + throw new IllegalStateException("사용중이 아닌 주문 테이블의 고객 수를 바꿀 수 없습니다."); + } + } + + public UUID getId() { + return id; + } + + public Integer getCustomerHeadcount() { + return customerHeadcount.getHeadCounts(); + } + + public OrderTableStatus getOccupied() { + return occupied; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OrderTable that = (OrderTable) o; + return Objects.equals(id, that.id); + } + + @Override + public int hashCode() { + return Objects.hash(id); + } +} + diff --git a/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderTableName.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderTableName.java new file mode 100644 index 000000000..6eb12751e --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderTableName.java @@ -0,0 +1,38 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import jakarta.persistence.Embeddable; +import java.util.Objects; + +@Embeddable +public class OrderTableName { + private String name; + + protected OrderTableName() { + } + + private OrderTableName(String name) { + validate(name); + this.name = name; + } + + public static OrderTableName of(String name){ + return new OrderTableName(name); + } + + public String getName() { + return name; + } + + private void validate(String name){ + if(Objects.isNull(name)){ + throw new IllegalArgumentException("주문 테이블의 이름이 올바르지 않으면 등록할 수 없다."); + } + + if (name.isEmpty()){ + throw new IllegalArgumentException("주문 테이블의 이름은 비워 둘 수 없다."); + } + } + + +} + diff --git a/src/main/java/kitchenpos/eatinorders/domain/OrderTableRepository.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderTableRepository.java similarity index 69% rename from src/main/java/kitchenpos/eatinorders/domain/OrderTableRepository.java rename to src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderTableRepository.java index 1e9047d43..8ce992305 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/OrderTableRepository.java +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/OrderTableRepository.java @@ -1,4 +1,6 @@ -package kitchenpos.eatinorders.domain; +package kitchenpos.eatinorders.domain.eatinorder; + +import kitchenpos.common.domain.orders.OrderStatus; import java.util.List; import java.util.Optional; diff --git a/src/main/java/kitchenpos/eatinorders/domain/eatinorder/PassToRiderService.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/PassToRiderService.java new file mode 100644 index 000000000..3be73aa76 --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/PassToRiderService.java @@ -0,0 +1,19 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import kitchenpos.common.annotation.DomainService; + +@DomainService +public class PassToRiderService { + private final KitchenridersClient kitchenridersClient; + + public PassToRiderService(KitchenridersClient kitchenridersClient) { + this.kitchenridersClient = kitchenridersClient; + } + + public void acceptOrder(Order order) { + if (order.isDelivery()) { + DeliveryOrder deliveryOrder = (DeliveryOrder) order; + kitchenridersClient.requestDelivery(deliveryOrder.getId(), deliveryOrder.getOrderLineItemsSum(), deliveryOrder.getDeliveryAddress()); + } + } +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/eatinorder/TakeoutOrder.java b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/TakeoutOrder.java new file mode 100644 index 000000000..b848f3c2f --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/eatinorder/TakeoutOrder.java @@ -0,0 +1,52 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import kitchenpos.common.domain.orders.OrderStatus; +import kitchenpos.common.domain.ordertables.OrderType; + +@Entity +@DiscriminatorValue("TAKEOUT") +public class TakeoutOrder extends Order { + + protected TakeoutOrder() { + super(); + } + + protected TakeoutOrder(OrderStatus orderStatus, OrderLineItems orderLineItems) { + super(OrderType.TAKEOUT, orderStatus, orderLineItems); + validate(); + } + + private void validate() { + if (containsNegativeMenuCounts()) { + throw new IllegalArgumentException("주문을 생성할 때, 주문상품들의 갯수를 감소시킬 수 없다."); + } + } + + @Override + public void accept(PassToRiderService passToRiderService) { + if (status != OrderStatus.WAITING) { + throw new IllegalStateException(" `주문 상태`가 `대기중(WAITING)`이 아닌 주문은 수락할 수 없습니다."); + } + status = OrderStatus.ACCEPTED; + } + + @Override + public void delivering() { + throw new IllegalStateException("해당 주문 타입은 라이더에게 전달되지 않아도 됩니다."); + } + + @Override + public void delivered() { + throw new IllegalStateException("해당 주문 타입은 라이더에게 전달되지 않아도 됩니다."); + } + + @Override + public void complete(ClearOrderTableService clearOrderTableService) { + if (status != OrderStatus.SERVED) { + throw new IllegalStateException(" `주문 상태`가 `전달(SERVED)`이 아닌 주문은 주문완료할 수 없습니다."); + } + status = OrderStatus.COMPLETED; + } +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/menu/MenuClient.java b/src/main/java/kitchenpos/eatinorders/domain/menu/MenuClient.java new file mode 100644 index 000000000..034d3223c --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/menu/MenuClient.java @@ -0,0 +1,9 @@ +package kitchenpos.eatinorders.domain.menu; + +import java.util.List; +import java.util.Map; +import java.util.UUID; + +public interface MenuClient { + Map findMenuInfoByMenuIds(List menuIds); +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/menu/OrderMenu.java b/src/main/java/kitchenpos/eatinorders/domain/menu/OrderMenu.java new file mode 100644 index 000000000..588ed9f46 --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/domain/menu/OrderMenu.java @@ -0,0 +1,9 @@ +package kitchenpos.eatinorders.domain.menu; + +import java.math.BigDecimal; +import java.util.UUID; + +public record OrderMenu(UUID id, BigDecimal price, boolean isDisplayed) { + + +} diff --git a/src/main/java/kitchenpos/deliveryorders/infra/DefaultKitchenridersClient.java b/src/main/java/kitchenpos/eatinorders/infra/DefaultKitchenridersClient.java similarity index 74% rename from src/main/java/kitchenpos/deliveryorders/infra/DefaultKitchenridersClient.java rename to src/main/java/kitchenpos/eatinorders/infra/DefaultKitchenridersClient.java index 31d6d8cee..1818f3b0c 100644 --- a/src/main/java/kitchenpos/deliveryorders/infra/DefaultKitchenridersClient.java +++ b/src/main/java/kitchenpos/eatinorders/infra/DefaultKitchenridersClient.java @@ -1,5 +1,6 @@ -package kitchenpos.deliveryorders.infra; +package kitchenpos.eatinorders.infra; +import kitchenpos.eatinorders.domain.eatinorder.KitchenridersClient; import org.springframework.stereotype.Component; import java.math.BigDecimal; diff --git a/src/main/java/kitchenpos/eatinorders/infra/DefaultMenuClient.java b/src/main/java/kitchenpos/eatinorders/infra/DefaultMenuClient.java new file mode 100644 index 000000000..f8e47d05f --- /dev/null +++ b/src/main/java/kitchenpos/eatinorders/infra/DefaultMenuClient.java @@ -0,0 +1,31 @@ +package kitchenpos.eatinorders.infra; + +import kitchenpos.eatinorders.domain.menu.MenuClient; +import kitchenpos.eatinorders.domain.menu.OrderMenu; +import kitchenpos.menus.domain.tobe.menu.MenuRepository; +import org.springframework.stereotype.Component; + +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +@Component +public class DefaultMenuClient implements MenuClient { + private final MenuRepository menuRepository; + + public DefaultMenuClient(MenuRepository menuRepository) { + this.menuRepository = menuRepository; + } + + + @Override + public Map findMenuInfoByMenuIds(List menuIds) { + + return menuRepository.findAllByIdIn(menuIds).stream() + .map(m -> new OrderMenu(m.getId(), m.getMenuPrice(), m.isDisplayed())) + .collect(Collectors.toMap( + OrderMenu::id, m-> m + )); + } +} diff --git a/src/main/java/kitchenpos/eatinorders/domain/JpaOrderRepository.java b/src/main/java/kitchenpos/eatinorders/infra/JpaOrderRepository.java similarity index 53% rename from src/main/java/kitchenpos/eatinorders/domain/JpaOrderRepository.java rename to src/main/java/kitchenpos/eatinorders/infra/JpaOrderRepository.java index 01c825c45..79c423e55 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/JpaOrderRepository.java +++ b/src/main/java/kitchenpos/eatinorders/infra/JpaOrderRepository.java @@ -1,8 +1,9 @@ -package kitchenpos.eatinorders.domain; - -import org.springframework.data.jpa.repository.JpaRepository; +package kitchenpos.eatinorders.infra; import java.util.UUID; +import kitchenpos.eatinorders.domain.eatinorder.Order; +import kitchenpos.eatinorders.domain.eatinorder.OrderRepository; +import org.springframework.data.jpa.repository.JpaRepository; public interface JpaOrderRepository extends OrderRepository, JpaRepository { } diff --git a/src/main/java/kitchenpos/eatinorders/domain/JpaOrderTableRepository.java b/src/main/java/kitchenpos/eatinorders/infra/JpaOrderTableRepository.java similarity index 53% rename from src/main/java/kitchenpos/eatinorders/domain/JpaOrderTableRepository.java rename to src/main/java/kitchenpos/eatinorders/infra/JpaOrderTableRepository.java index 84c0d3c6f..b4b10db99 100644 --- a/src/main/java/kitchenpos/eatinorders/domain/JpaOrderTableRepository.java +++ b/src/main/java/kitchenpos/eatinorders/infra/JpaOrderTableRepository.java @@ -1,8 +1,9 @@ -package kitchenpos.eatinorders.domain; - -import org.springframework.data.jpa.repository.JpaRepository; +package kitchenpos.eatinorders.infra; import java.util.UUID; +import kitchenpos.eatinorders.domain.eatinorder.OrderTable; +import kitchenpos.eatinorders.domain.eatinorder.OrderTableRepository; +import org.springframework.data.jpa.repository.JpaRepository; public interface JpaOrderTableRepository extends OrderTableRepository, JpaRepository { } diff --git a/src/main/java/kitchenpos/eatinorders/ui/OrderRestController.java b/src/main/java/kitchenpos/eatinorders/ui/OrderRestController.java index dbb5568ad..feab4e87a 100644 --- a/src/main/java/kitchenpos/eatinorders/ui/OrderRestController.java +++ b/src/main/java/kitchenpos/eatinorders/ui/OrderRestController.java @@ -1,7 +1,8 @@ package kitchenpos.eatinorders.ui; import kitchenpos.eatinorders.application.OrderService; -import kitchenpos.eatinorders.domain.Order; +import kitchenpos.eatinorders.application.dto.OrderRequest; +import kitchenpos.eatinorders.domain.eatinorder.Order; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -25,7 +26,7 @@ public OrderRestController(final OrderService orderService) { } @PostMapping - public ResponseEntity create(@RequestBody final Order request) { + public ResponseEntity create(@RequestBody final OrderRequest request) { final Order response = orderService.create(request); return ResponseEntity.created(URI.create("/api/orders/" + response.getId())) .body(response); diff --git a/src/main/java/kitchenpos/eatinorders/ui/OrderTableRestController.java b/src/main/java/kitchenpos/eatinorders/ui/OrderTableRestController.java index 1ceabe86f..499fe2c52 100644 --- a/src/main/java/kitchenpos/eatinorders/ui/OrderTableRestController.java +++ b/src/main/java/kitchenpos/eatinorders/ui/OrderTableRestController.java @@ -1,7 +1,8 @@ package kitchenpos.eatinorders.ui; import kitchenpos.eatinorders.application.OrderTableService; -import kitchenpos.eatinorders.domain.OrderTable; +import kitchenpos.eatinorders.application.dto.OrderTableRequest; +import kitchenpos.eatinorders.domain.eatinorder.OrderTable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -25,7 +26,7 @@ public OrderTableRestController(final OrderTableService orderTableService) { } @PostMapping - public ResponseEntity create(@RequestBody final OrderTable request) { + public ResponseEntity create(@RequestBody final OrderTableRequest request) { final OrderTable response = orderTableService.create(request); return ResponseEntity.created(URI.create("/api/order-tables/" + response.getId())) .body(response); @@ -36,15 +37,10 @@ public ResponseEntity sit(@PathVariable final UUID orderTableId) { return ResponseEntity.ok(orderTableService.sit(orderTableId)); } - @PutMapping("/{orderTableId}/clear") - public ResponseEntity clear(@PathVariable final UUID orderTableId) { - return ResponseEntity.ok(orderTableService.clear(orderTableId)); - } - @PutMapping("/{orderTableId}/number-of-guests") public ResponseEntity changeNumberOfGuests( @PathVariable final UUID orderTableId, - @RequestBody final OrderTable request + @RequestBody final OrderTableRequest request ) { return ResponseEntity.ok(orderTableService.changeNumberOfGuests(orderTableId, request)); } diff --git a/src/main/java/kitchenpos/menus/application/MenuService.java b/src/main/java/kitchenpos/menus/application/MenuService.java index dcad7b791..d15c2e019 100644 --- a/src/main/java/kitchenpos/menus/application/MenuService.java +++ b/src/main/java/kitchenpos/menus/application/MenuService.java @@ -56,7 +56,6 @@ public Menu changePrice(final UUID menuId, final MenuRequest request) { @TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT) @Transactional(propagation = Propagation.REQUIRES_NEW) - @Async public void changeMenuProdutPrice(final ProductPriceChangeEvent productPriceChangeEvent) { final List menus = menuRepository.findAllByProductId(productPriceChangeEvent.getProductId()); diff --git a/src/main/java/kitchenpos/menus/domain/tobe/menu/Menu.java b/src/main/java/kitchenpos/menus/domain/tobe/menu/Menu.java index ee59cdb31..a57cad73a 100644 --- a/src/main/java/kitchenpos/menus/domain/tobe/menu/Menu.java +++ b/src/main/java/kitchenpos/menus/domain/tobe/menu/Menu.java @@ -45,15 +45,23 @@ private Menu(MenuName menuName, Price menuPrice, UUID menuGroupId, boolean displ this.menuGroupId = menuGroupId; this.displayed = displayed; this.menuProducts = menuProducts; + + } public static Menu of(final String name, final Long price, final UUID menuGroupId, final boolean displayed, final MenuProducts menuProducts, final ProfanityValidator profanityValidator) { + Menu menu = new Menu(MenuName.of(name, profanityValidator), Price.from(price), menuGroupId, displayed, menuProducts); - return new Menu(MenuName.of(name, profanityValidator), Price.from(price), menuGroupId, displayed, menuProducts); + menu.mapMenu(); + + return menu; } public static Menu of(final String name, final BigDecimal price, final UUID menuGroupId, final boolean displayed, final MenuProducts menuProducts, final ProfanityValidator profanityValidator) { + Menu menu = new Menu(MenuName.of(name, profanityValidator), Price.from(price), menuGroupId, displayed, menuProducts); + + menu.mapMenu(); - return new Menu(MenuName.of(name, profanityValidator), Price.from(price), menuGroupId, displayed, menuProducts); + return menu; } private void validate(final Price menuPrice, final BigDecimal price, final MenuProducts menuProducts, final UUID menuGroupId) { if (Objects.isNull(menuGroupId)){ @@ -78,6 +86,9 @@ public void display() { validate(menuPrice, menuProducts.sum(), menuProducts, menuGroupId); this.displayed = true; } + protected void mapMenu() { + menuProducts.mapping(this); + } public void changePrice(BigDecimal price) { Price menuPriceRequest = Price.from(price); @@ -93,10 +104,6 @@ public void changeMenuProductPrice(UUID productId, Long price) { } } - public void addMenuProduct(MenuProduct menuProduct){ - menuProducts.addMenuProduct(menuProduct); - } - public BigDecimal sumMenuProducts(){ return menuProducts.sum(); } diff --git a/src/main/java/kitchenpos/menus/domain/tobe/menu/MenuProduct.java b/src/main/java/kitchenpos/menus/domain/tobe/menu/MenuProduct.java index f9c4469c4..72dadb071 100644 --- a/src/main/java/kitchenpos/menus/domain/tobe/menu/MenuProduct.java +++ b/src/main/java/kitchenpos/menus/domain/tobe/menu/MenuProduct.java @@ -13,7 +13,7 @@ public class MenuProduct { private static final int ZERO = 0; @Column(name = "seq") - @GeneratedValue(strategy = GenerationType.IDENTITY) + @GeneratedValue(strategy = GenerationType.SEQUENCE) @Id private Long seq; @Column(name = "productId", nullable = false) @@ -53,6 +53,9 @@ public BigDecimal amount(){ return price.multiply(BigDecimal.valueOf(quantity)); } + protected void mapping(final Menu menu){ + this.menu = menu; + } public UUID getId(){ return productId; } diff --git a/src/main/java/kitchenpos/menus/domain/tobe/menu/MenuProducts.java b/src/main/java/kitchenpos/menus/domain/tobe/menu/MenuProducts.java index 8d6065713..671585699 100644 --- a/src/main/java/kitchenpos/menus/domain/tobe/menu/MenuProducts.java +++ b/src/main/java/kitchenpos/menus/domain/tobe/menu/MenuProducts.java @@ -18,14 +18,17 @@ public class MenuProducts { protected MenuProducts() { } + @Deprecated private MenuProducts(List menuProducts) { products.addAll(menuProducts); } + private MenuProducts(List menuProducts, ProductClient productClient) { validate(menuProducts, productClient); products.addAll(menuProducts); } + private void validate(List menuProducts, ProductClient productClient) { if (Objects.isNull(menuProducts)) { throw new IllegalArgumentException("메뉴는 1개이상의 메뉴상품으로 구성되어야 합니다."); @@ -39,6 +42,7 @@ private void validate(List menuProducts, ProductClient productClien public static MenuProducts of(ProductClient productClient, MenuProduct... menuProducts) { return new MenuProducts(List.of(menuProducts), productClient); } + @Deprecated public static MenuProducts of(MenuProduct... menuProducts) { return new MenuProducts(List.of(menuProducts)); @@ -78,7 +82,10 @@ protected List getMenuProductIds(List menuProducts) { .map(MenuProduct::getId) .toList(); } - + protected void mapping(final Menu menu){ + products + .forEach(product -> product.mapping(menu)); + } @Override public boolean equals(Object o) { if (this == o) return true; diff --git a/src/main/java/kitchenpos/products/application/ProductService.java b/src/main/java/kitchenpos/products/application/ProductService.java index d05dca6b3..fe67e4a8b 100644 --- a/src/main/java/kitchenpos/products/application/ProductService.java +++ b/src/main/java/kitchenpos/products/application/ProductService.java @@ -1,12 +1,10 @@ package kitchenpos.products.application; -import kitchenpos.common.domain.Price; -import kitchenpos.common.domain.ProductPriceChangeEvent; -import kitchenpos.menus.domain.tobe.menu.MenuRepository; +import kitchenpos.common.domain.ProfanityValidator; +import kitchenpos.common.domain.products.ProductPriceChangeEvent; import kitchenpos.products.application.dto.ProductRequest; import kitchenpos.products.domain.tobe.Product; import kitchenpos.products.domain.tobe.ProductRepository; -import kitchenpos.common.domain.ProfanityValidator; import org.springframework.context.ApplicationEventPublisher; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -18,17 +16,14 @@ @Service public class ProductService { private final ProductRepository productRepository; - private final MenuRepository menuRepository; private final ProfanityValidator profanityValidator; private final ApplicationEventPublisher publisher; public ProductService( final ProductRepository productRepository, - final MenuRepository menuRepository, final ProfanityValidator profanityValidator, - ApplicationEventPublisher publisher) { + final ApplicationEventPublisher publisher) { this.productRepository = productRepository; - this.menuRepository = menuRepository; this.profanityValidator = profanityValidator; this.publisher = publisher; } diff --git a/src/main/java/kitchenpos/products/domain/tobe/Product.java b/src/main/java/kitchenpos/products/domain/tobe/Product.java index 75de67ee6..45ad9f029 100644 --- a/src/main/java/kitchenpos/products/domain/tobe/Product.java +++ b/src/main/java/kitchenpos/products/domain/tobe/Product.java @@ -58,8 +58,6 @@ public BigDecimal getProductPrice() { } @Override - - public boolean equals(Object o) { if (this == o) { return true; diff --git a/src/test/java/kitchenpos/Fixtures.java b/src/test/java/kitchenpos/Fixtures.java index 6fe0d31cd..695fff149 100644 --- a/src/test/java/kitchenpos/Fixtures.java +++ b/src/test/java/kitchenpos/Fixtures.java @@ -1,24 +1,24 @@ package kitchenpos; -import kitchenpos.eatinorders.domain.*; +import kitchenpos.common.domain.ProfanityValidator; +import kitchenpos.common.domain.orders.OrderStatus; +import kitchenpos.common.domain.ordertables.OrderType; +import kitchenpos.eatinorders.application.dto.OrderLineItemRequest; +import kitchenpos.eatinorders.application.dto.OrderRequest; +import kitchenpos.eatinorders.domain.eatinorder.*; +import kitchenpos.eatinorders.infra.FakeMenuClient; import kitchenpos.menus.domain.tobe.menu.Menu; import kitchenpos.menus.domain.tobe.menu.MenuProduct; import kitchenpos.menus.domain.tobe.menu.MenuProducts; -import kitchenpos.menus.domain.tobe.menu.ProductClient; import kitchenpos.menus.domain.tobe.menugroup.MenuGroup; -import kitchenpos.menus.infra.DefaultProductClient; -import kitchenpos.menus.infra.InMemoryMenuRepository; -import kitchenpos.products.application.ProductService; import kitchenpos.products.domain.tobe.Product; -import kitchenpos.common.domain.ProfanityValidator; -import kitchenpos.products.domain.tobe.ProductRepository; -import kitchenpos.products.infra.FakeProductClient; import kitchenpos.products.infra.FakeProfanityValidator; -import kitchenpos.products.infra.InMemoryProductRepository; -import org.junit.jupiter.api.BeforeEach; +import java.math.BigDecimal; +import java.math.BigInteger; import java.time.LocalDateTime; import java.util.Arrays; +import java.util.List; import java.util.Random; import java.util.UUID; @@ -26,7 +26,8 @@ public class Fixtures { public static final UUID INVALID_ID = new UUID(0L, 0L); public static ProfanityValidator profanityValidator = new FakeProfanityValidator(); public static Product product = createProduct(); - + public static DefaultOrderFactory defaultOrderFactory = new DefaultOrderFactory(); + public static FakeMenuClient fakeMenuClient = new FakeMenuClient(); public static Menu menu() { return menu(19_000L, true, menuProduct()); } @@ -72,55 +73,50 @@ public static MenuProduct menuProduct(final Product product, final long quantity } public static Order order(final OrderStatus status, final String deliveryAddress) { - final Order order = new Order(); - order.setId(UUID.randomUUID()); - order.setType(OrderType.DELIVERY); - order.setStatus(status); - order.setOrderDateTime(LocalDateTime.of(2020, 1, 1, 12, 0)); - order.setOrderLineItems(Arrays.asList(orderLineItem())); - order.setDeliveryAddress(deliveryAddress); - return order; + return defaultOrderFactory.of(OrderType.DELIVERY, status, OrderLineItems.of(fakeMenuClient, List.of(orderLineItem())), + null,deliveryAddress); } public static Order order(final OrderStatus status) { - final Order order = new Order(); - order.setId(UUID.randomUUID()); - order.setType(OrderType.TAKEOUT); - order.setStatus(status); - order.setOrderDateTime(LocalDateTime.of(2020, 1, 1, 12, 0)); - order.setOrderLineItems(Arrays.asList(orderLineItem())); - return order; - } - - public static Order order(final OrderStatus status, final OrderTable orderTable) { - final Order order = new Order(); - order.setId(UUID.randomUUID()); - order.setType(OrderType.EAT_IN); - order.setStatus(status); - order.setOrderDateTime(LocalDateTime.of(2020, 1, 1, 12, 0)); - order.setOrderLineItems(Arrays.asList(orderLineItem())); - order.setOrderTable(orderTable); - return order; + + return defaultOrderFactory.of(OrderType.TAKEOUT, status, OrderLineItems.of(fakeMenuClient, List.of(orderLineItem())), + null,""); + } + + public static Order order(final OrderStatus status, final OrderTable orderTableRequest) { + + return defaultOrderFactory.of(OrderType.EAT_IN, status, OrderLineItems.of(fakeMenuClient, List.of(orderLineItem())), + orderTableRequest, ""); } public static OrderLineItem orderLineItem() { - final OrderLineItem orderLineItem = new OrderLineItem(); - orderLineItem.setSeq(new Random().nextLong()); - orderLineItem.setMenu(menu()); - return orderLineItem; + Menu menu = menu(); + fakeMenuClient.add(menu.getId(), menu.getMenuPrice(), menu.isDisplayed()); + return OrderLineItem.of(menu.getId(), menu.getMenuPrice().longValue(), 1L); + } + public static OrderLineItem orderLineItem(FakeMenuClient fakeMenuClient) { + Menu menu = menu(); + fakeMenuClient.add(menu.getId(), menu.getMenuPrice(), menu.isDisplayed()); + return OrderLineItem.of(menu.getId(), menu.getMenuPrice().longValue(), 1L); } + public static OrderLineItem orderLineItem(FakeMenuClient fakeMenuClient, boolean isDisplayed) { + Menu menu = menu(); + fakeMenuClient.add(menu.getId(), menu.getMenuPrice(), isDisplayed); + return OrderLineItem.of(menu.getId(), menu.getMenuPrice().longValue(), 1L); + } + + public static OrderLineItem orderLineItem(FakeMenuClient fakeMenuClient, BigDecimal price) { + Menu menu = menu(); + fakeMenuClient.add(menu.getId(), price, menu.isDisplayed()); + return OrderLineItem.of(menu.getId(), menu.getMenuPrice().longValue(), 1L); + } public static OrderTable orderTable() { return orderTable(false, 0); } public static OrderTable orderTable(final boolean occupied, final int numberOfGuests) { - final OrderTable orderTable = new OrderTable(); - orderTable.setId(UUID.randomUUID()); - orderTable.setName("1번"); - orderTable.setNumberOfGuests(numberOfGuests); - orderTable.setOccupied(occupied); - return orderTable; + return OrderTable.of(occupied,"1번", numberOfGuests); } public static Product product() { diff --git a/src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java b/src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java index 7f863a556..c43ebd79f 100644 --- a/src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java +++ b/src/test/java/kitchenpos/eatinorders/application/OrderServiceTest.java @@ -1,12 +1,15 @@ package kitchenpos.eatinorders.application; -import kitchenpos.eatinorders.domain.Order; -import kitchenpos.eatinorders.domain.OrderLineItem; -import kitchenpos.eatinorders.domain.OrderRepository; -import kitchenpos.eatinorders.domain.OrderStatus; -import kitchenpos.eatinorders.domain.OrderTable; -import kitchenpos.eatinorders.domain.OrderTableRepository; -import kitchenpos.eatinorders.domain.OrderType; +import kitchenpos.eatinorders.application.dto.OrderLineItemRequest; +import kitchenpos.eatinorders.application.dto.OrderRequest; +import kitchenpos.eatinorders.domain.eatinorder.*; +import kitchenpos.common.domain.orders.OrderStatus; +import kitchenpos.common.domain.ordertables.OrderType; +import kitchenpos.eatinorders.domain.menu.MenuClient; +import kitchenpos.eatinorders.infra.DefaultMenuClient; +import kitchenpos.eatinorders.infra.FakeKitchenridersClient; +import kitchenpos.eatinorders.infra.InMemoryOrderRepository; +import kitchenpos.eatinorders.infra.InMemoryOrderTableRepository; import kitchenpos.menus.infra.InMemoryMenuRepository; import kitchenpos.menus.domain.tobe.menu.MenuRepository; import org.junit.jupiter.api.BeforeEach; @@ -39,36 +42,46 @@ class OrderServiceTest { private OrderRepository orderRepository; - private MenuRepository menuRepository; private OrderTableRepository orderTableRepository; private FakeKitchenridersClient kitchenridersClient; private OrderService orderService; - + private MenuRepository menuRepository; + private MenuClient menuClient; + private ClearOrderTableService clearOrderTableService; + private PassToRiderService passToRiderService; + private OrderFactory orderFactory; @BeforeEach void setUp() { orderRepository = new InMemoryOrderRepository(); menuRepository = new InMemoryMenuRepository(); orderTableRepository = new InMemoryOrderTableRepository(); kitchenridersClient = new FakeKitchenridersClient(); - orderService = new OrderService(orderRepository, menuRepository, orderTableRepository, kitchenridersClient); + + menuClient = new DefaultMenuClient(menuRepository); + clearOrderTableService = new ClearOrderTableService(orderRepository, orderTableRepository); + passToRiderService = new PassToRiderService(kitchenridersClient); + orderFactory = new DefaultOrderFactory(); + + orderService = new OrderService(orderRepository, passToRiderService, clearOrderTableService, + menuClient, orderFactory); } @DisplayName("1개 이상의 등록된 메뉴로 배달 주문을 등록할 수 있다.") @Test void createDeliveryOrder() { final UUID menuId = menuRepository.save(menu(19_000L, true, menuProduct())).getId(); - final Order expected = createOrderRequest( - OrderType.DELIVERY, "서울시 송파구 위례성대로 2", createOrderLineItemRequest(menuId, 19_000L, 3L) + final OrderRequest expected = createOrderRequest( + OrderType.DELIVERY, OrderStatus.WAITING ,"서울시 송파구 위례성대로 2", createOrderLineItemRequest(menuId, 19_000L, 3L) ); final Order actual = orderService.create(expected); + final DeliveryOrder deliveryOrder = (DeliveryOrder) actual; assertThat(actual).isNotNull(); assertAll( - () -> assertThat(actual.getId()).isNotNull(), - () -> assertThat(actual.getType()).isEqualTo(expected.getType()), - () -> assertThat(actual.getStatus()).isEqualTo(OrderStatus.WAITING), - () -> assertThat(actual.getOrderDateTime()).isNotNull(), - () -> assertThat(actual.getOrderLineItems()).hasSize(1), - () -> assertThat(actual.getDeliveryAddress()).isEqualTo(expected.getDeliveryAddress()) + () -> assertThat(deliveryOrder.getId()).isNotNull(), + () -> assertThat(deliveryOrder.isDelivery()).isTrue(), + () -> assertThat(deliveryOrder.hasStatus(OrderStatus.WAITING)).isTrue(), + () -> assertThat(deliveryOrder.getOrderLineItemsSum()).isEqualTo(BigDecimal.valueOf(57_000L)), + () -> assertThat(deliveryOrder.getDeliveryAddress()).isEqualTo(expected.getDeliveryAddress()) ); } @@ -76,15 +89,16 @@ void createDeliveryOrder() { @Test void createTakeoutOrder() { final UUID menuId = menuRepository.save(menu(19_000L, true, menuProduct())).getId(); - final Order expected = createOrderRequest(OrderType.TAKEOUT, createOrderLineItemRequest(menuId, 19_000L, 3L)); + final OrderRequest expected = createOrderRequest(OrderType.TAKEOUT, createOrderLineItemRequest(menuId, 19_000L, 3L)); final Order actual = orderService.create(expected); + final TakeoutOrder eatInOrder = (TakeoutOrder) actual; + assertThat(actual).isNotNull(); assertAll( - () -> assertThat(actual.getId()).isNotNull(), - () -> assertThat(actual.getType()).isEqualTo(expected.getType()), - () -> assertThat(actual.getStatus()).isEqualTo(OrderStatus.WAITING), - () -> assertThat(actual.getOrderDateTime()).isNotNull(), - () -> assertThat(actual.getOrderLineItems()).hasSize(1) + () -> assertThat(eatInOrder.getId()).isNotNull(), + () -> assertThat(eatInOrder.isNotEatIn()).isTrue(), + () -> assertThat(eatInOrder.hasStatus(OrderStatus.WAITING)).isTrue(), + () -> assertThat(eatInOrder.getOrderLineItemsSum()).isEqualTo(BigDecimal.valueOf(57_000L)) ); } @@ -92,17 +106,18 @@ void createTakeoutOrder() { @Test void createEatInOrder() { final UUID menuId = menuRepository.save(menu(19_000L, true, menuProduct())).getId(); - final UUID orderTableId = orderTableRepository.save(orderTable(true, 4)).getId(); - final Order expected = createOrderRequest(OrderType.EAT_IN, orderTableId, createOrderLineItemRequest(menuId, 19_000L, 3L)); + final OrderTable orderTable = orderTableRepository.save(orderTable(true, 4)); + final OrderRequest expected = createOrderRequest(OrderType.EAT_IN, orderTable, createOrderLineItemRequest(menuId, 19_000L, 3L)); final Order actual = orderService.create(expected); + final EatInOrder eatInOrder = (EatInOrder) actual; + assertThat(actual).isNotNull(); assertAll( - () -> assertThat(actual.getId()).isNotNull(), - () -> assertThat(actual.getType()).isEqualTo(expected.getType()), - () -> assertThat(actual.getStatus()).isEqualTo(OrderStatus.WAITING), - () -> assertThat(actual.getOrderDateTime()).isNotNull(), - () -> assertThat(actual.getOrderLineItems()).hasSize(1), - () -> assertThat(actual.getOrderTable().getId()).isEqualTo(expected.getOrderTableId()) + () -> assertThat(eatInOrder.getId()).isNotNull(), + () -> assertThat(eatInOrder.isNotEatIn()).isFalse(), + () -> assertThat(eatInOrder.hasStatus(OrderStatus.WAITING)).isTrue(), + () -> assertThat(eatInOrder.getOrderLineItemsSum()).isEqualTo(BigDecimal.valueOf(57_000L)), + () -> assertThat(eatInOrder.getOrderTable()).isEqualTo(orderTable) ); } @@ -111,7 +126,7 @@ void createEatInOrder() { @ParameterizedTest void create(final OrderType type) { final UUID menuId = menuRepository.save(menu(19_000L, true, menuProduct())).getId(); - final Order expected = createOrderRequest(type, createOrderLineItemRequest(menuId, 19_000L, 3L)); + final OrderRequest expected = createOrderRequest(type, createOrderLineItemRequest(menuId, 19_000L, 3L)); assertThatThrownBy(() -> orderService.create(expected)) .isInstanceOf(IllegalArgumentException.class); } @@ -119,8 +134,8 @@ void create(final OrderType type) { @DisplayName("메뉴가 없으면 등록할 수 없다.") @MethodSource("orderLineItems") @ParameterizedTest - void create(final List orderLineItems) { - final Order expected = createOrderRequest(OrderType.TAKEOUT, orderLineItems); + void create(final List orderLineItemRequests) { + final OrderRequest expected = createOrderRequest(OrderType.TAKEOUT, orderLineItemRequests); assertThatThrownBy(() -> orderService.create(expected)) .isInstanceOf(IllegalArgumentException.class); } @@ -138,9 +153,9 @@ private static List orderLineItems() { @ParameterizedTest void createEatInOrder(final long quantity) { final UUID menuId = menuRepository.save(menu(19_000L, true, menuProduct())).getId(); - final UUID orderTableId = orderTableRepository.save(orderTable(true, 4)).getId(); - final Order expected = createOrderRequest( - OrderType.EAT_IN, orderTableId, createOrderLineItemRequest(menuId, 19_000L, quantity) + final OrderTable orderTable = orderTableRepository.save(orderTable(true, 4)); + final OrderRequest expected = createOrderRequest( + OrderType.EAT_IN, orderTable, createOrderLineItemRequest(menuId, 19_000L, quantity) ); assertDoesNotThrow(() -> orderService.create(expected)); } @@ -150,7 +165,7 @@ OrderType.EAT_IN, orderTableId, createOrderLineItemRequest(menuId, 19_000L, quan @ParameterizedTest void createWithoutEatInOrder(final long quantity) { final UUID menuId = menuRepository.save(menu(19_000L, true, menuProduct())).getId(); - final Order expected = createOrderRequest( + final OrderRequest expected = createOrderRequest( OrderType.TAKEOUT, createOrderLineItemRequest(menuId, 19_000L, quantity) ); assertThatThrownBy(() -> orderService.create(expected)) @@ -162,8 +177,8 @@ OrderType.TAKEOUT, createOrderLineItemRequest(menuId, 19_000L, quantity) @ParameterizedTest void create(final String deliveryAddress) { final UUID menuId = menuRepository.save(menu(19_000L, true, menuProduct())).getId(); - final Order expected = createOrderRequest( - OrderType.DELIVERY, deliveryAddress, createOrderLineItemRequest(menuId, 19_000L, 3L) + final OrderRequest expected = createOrderRequest( + OrderType.DELIVERY, OrderStatus.WAITING, deliveryAddress, createOrderLineItemRequest(menuId, 19_000L, 3L) ); assertThatThrownBy(() -> orderService.create(expected)) .isInstanceOf(IllegalArgumentException.class); @@ -173,9 +188,9 @@ OrderType.DELIVERY, deliveryAddress, createOrderLineItemRequest(menuId, 19_000L, @Test void createEmptyTableEatInOrder() { final UUID menuId = menuRepository.save(menu(19_000L, true, menuProduct())).getId(); - final UUID orderTableId = orderTableRepository.save(orderTable(false, 0)).getId(); - final Order expected = createOrderRequest( - OrderType.EAT_IN, orderTableId, createOrderLineItemRequest(menuId, 19_000L, 3L) + final OrderTable orderTable = orderTableRepository.save(orderTable(false, 0)); + final OrderRequest expected = createOrderRequest( + OrderType.EAT_IN, orderTable, createOrderLineItemRequest(menuId, 19_000L, 3L) ); assertThatThrownBy(() -> orderService.create(expected)) .isInstanceOf(IllegalStateException.class); @@ -185,7 +200,7 @@ OrderType.EAT_IN, orderTableId, createOrderLineItemRequest(menuId, 19_000L, 3L) @Test void createNotDisplayedMenuOrder() { final UUID menuId = menuRepository.save(menu(19_000L, false, menuProduct())).getId(); - final Order expected = createOrderRequest(OrderType.TAKEOUT, createOrderLineItemRequest(menuId, 19_000L, 3L)); + final OrderRequest expected = createOrderRequest(OrderType.TAKEOUT, createOrderLineItemRequest(menuId, 19_000L, 3L)); assertThatThrownBy(() -> orderService.create(expected)) .isInstanceOf(IllegalStateException.class); } @@ -194,7 +209,7 @@ void createNotDisplayedMenuOrder() { @Test void createNotMatchedMenuPriceOrder() { final UUID menuId = menuRepository.save(menu(19_000L, true, menuProduct())).getId(); - final Order expected = createOrderRequest(OrderType.TAKEOUT, createOrderLineItemRequest(menuId, 16_000L, 3L)); + final OrderRequest expected = createOrderRequest(OrderType.TAKEOUT, createOrderLineItemRequest(menuId, 16_000L, 3L)); assertThatThrownBy(() -> orderService.create(expected)) .isInstanceOf(IllegalArgumentException.class); } @@ -204,7 +219,7 @@ void createNotMatchedMenuPriceOrder() { void accept() { final UUID orderId = orderRepository.save(order(OrderStatus.WAITING, orderTable(true, 4))).getId(); final Order actual = orderService.accept(orderId); - assertThat(actual.getStatus()).isEqualTo(OrderStatus.ACCEPTED); + assertThat(actual.hasStatus(OrderStatus.ACCEPTED)).isTrue(); } @DisplayName("접수 대기 중인 주문만 접수할 수 있다.") @@ -222,7 +237,7 @@ void acceptDeliveryOrder() { final UUID orderId = orderRepository.save(order(OrderStatus.WAITING, "서울시 송파구 위례성대로 2")).getId(); final Order actual = orderService.accept(orderId); assertAll( - () -> assertThat(actual.getStatus()).isEqualTo(OrderStatus.ACCEPTED), + () -> assertThat(actual.hasStatus(OrderStatus.ACCEPTED)).isTrue(), () -> assertThat(kitchenridersClient.getOrderId()).isEqualTo(orderId), () -> assertThat(kitchenridersClient.getDeliveryAddress()).isEqualTo("서울시 송파구 위례성대로 2") ); @@ -233,7 +248,7 @@ void acceptDeliveryOrder() { void serve() { final UUID orderId = orderRepository.save(order(OrderStatus.ACCEPTED)).getId(); final Order actual = orderService.serve(orderId); - assertThat(actual.getStatus()).isEqualTo(OrderStatus.SERVED); + assertThat(actual.hasStatus(OrderStatus.SERVED)).isTrue(); } @DisplayName("접수된 주문만 서빙할 수 있다.") @@ -250,7 +265,7 @@ void serve(final OrderStatus status) { void startDelivery() { final UUID orderId = orderRepository.save(order(OrderStatus.SERVED, "서울시 송파구 위례성대로 2")).getId(); final Order actual = orderService.startDelivery(orderId); - assertThat(actual.getStatus()).isEqualTo(OrderStatus.DELIVERING); + assertThat(actual.hasStatus(OrderStatus.DELIVERING)).isTrue(); } @DisplayName("배달 주문만 배달할 수 있다.") @@ -275,7 +290,7 @@ void startDelivery(final OrderStatus status) { void completeDelivery() { final UUID orderId = orderRepository.save(order(OrderStatus.DELIVERING, "서울시 송파구 위례성대로 2")).getId(); final Order actual = orderService.completeDelivery(orderId); - assertThat(actual.getStatus()).isEqualTo(OrderStatus.DELIVERED); + assertThat(actual.hasStatus(OrderStatus.DELIVERED)).isTrue(); } @DisplayName("배달 중인 주문만 배달 완료할 수 있다.") @@ -292,7 +307,7 @@ void completeDelivery(final OrderStatus status) { void complete() { final Order expected = orderRepository.save(order(OrderStatus.DELIVERED, "서울시 송파구 위례성대로 2")); final Order actual = orderService.complete(expected.getId()); - assertThat(actual.getStatus()).isEqualTo(OrderStatus.COMPLETED); + assertThat(actual.hasStatus(OrderStatus.COMPLETED)).isTrue(); } @DisplayName("배달 주문의 경우 배달 완료된 주문만 완료할 수 있다.") @@ -316,81 +331,86 @@ void completeTakeoutAndEatInOrder(final OrderStatus status) { @DisplayName("주문 테이블의 모든 매장 주문이 완료되면 빈 테이블로 설정한다.") @Test void completeEatInOrder() { - final OrderTable orderTable = orderTableRepository.save(orderTable(true, 4)); - final Order expected = orderRepository.save(order(OrderStatus.SERVED, orderTable)); + final OrderTable orderTableRequest = orderTableRepository.save(orderTable(true, 4)); + final Order expected = orderRepository.save(order(OrderStatus.SERVED, + orderTableRequest)); final Order actual = orderService.complete(expected.getId()); assertAll( - () -> assertThat(actual.getStatus()).isEqualTo(OrderStatus.COMPLETED), - () -> assertThat(orderTableRepository.findById(orderTable.getId()).get().isOccupied()).isFalse(), - () -> assertThat(orderTableRepository.findById(orderTable.getId()).get().getNumberOfGuests()).isEqualTo(0) + () -> assertThat(actual.hasStatus(OrderStatus.COMPLETED)).isTrue(), + () -> assertThatThrownBy(() -> actual.getOrderTable().changeCustomerHeadCounts(3)) + .isInstanceOf(IllegalStateException.class) ); } @DisplayName("완료되지 않은 매장 주문이 있는 주문 테이블은 빈 테이블로 설정하지 않는다.") @Test void completeNotTable() { - final OrderTable orderTable = orderTableRepository.save(orderTable(true, 4)); - orderRepository.save(order(OrderStatus.ACCEPTED, orderTable)); - final Order expected = orderRepository.save(order(OrderStatus.SERVED, orderTable)); - final Order actual = orderService.complete(expected.getId()); + final OrderTable orderTableRequest = orderTableRepository.save(orderTable(true, 4)); + orderRepository.save(order(OrderStatus.ACCEPTED, orderTableRequest)); + final Order expected = orderRepository.save(order(OrderStatus.SERVED, + orderTableRequest)); + assertAll( - () -> assertThat(actual.getStatus()).isEqualTo(OrderStatus.COMPLETED), - () -> assertThat(orderTableRepository.findById(orderTable.getId()).get().isOccupied()).isTrue(), - () -> assertThat(orderTableRepository.findById(orderTable.getId()).get().getNumberOfGuests()).isEqualTo(4) + () -> assertThatThrownBy(() -> orderService.complete(expected.getId())) + .isInstanceOf(IllegalStateException.class) ); } @DisplayName("주문의 목록을 조회할 수 있다.") @Test void findAll() { - final OrderTable orderTable = orderTableRepository.save(orderTable(true, 4)); - orderRepository.save(order(OrderStatus.SERVED, orderTable)); + final OrderTable orderTableRequest = orderTableRepository.save(orderTable(true, 4)); + orderRepository.save(order(OrderStatus.SERVED, orderTableRequest)); orderRepository.save(order(OrderStatus.DELIVERED, "서울시 송파구 위례성대로 2")); final List actual = orderService.findAll(); assertThat(actual).hasSize(2); } - private Order createOrderRequest( + private OrderRequest createOrderRequest( final OrderType type, + final OrderStatus orderStatus, final String deliveryAddress, - final OrderLineItem... orderLineItems + final OrderLineItemRequest... orderLineItemRequests ) { - final Order order = new Order(); - order.setType(type); - order.setDeliveryAddress(deliveryAddress); - order.setOrderLineItems(Arrays.asList(orderLineItems)); - return order; + final OrderRequest orderRequest = new OrderRequest(); + orderRequest.setType(type); + orderRequest.setStatus(orderStatus); + orderRequest.setDeliveryAddress(deliveryAddress); + orderRequest.setOrderLineItems(Arrays.asList(orderLineItemRequests)); + return orderRequest; } - private Order createOrderRequest(final OrderType orderType, final OrderLineItem... orderLineItems) { - return createOrderRequest(orderType, Arrays.asList(orderLineItems)); + private OrderRequest createOrderRequest(final OrderType orderType, final OrderLineItemRequest... orderLineItemRequests) { + return createOrderRequest(orderType, Arrays.asList(orderLineItemRequests)); } - private Order createOrderRequest(final OrderType orderType, final List orderLineItems) { - final Order order = new Order(); - order.setType(orderType); - order.setOrderLineItems(orderLineItems); - return order; + private OrderRequest createOrderRequest(final OrderType orderType, final List orderLineItemRequests) { + final OrderRequest orderRequest = new OrderRequest(); + orderRequest.setType(orderType); + orderRequest.setStatus(OrderStatus.WAITING); + orderRequest.setOrderLineItems(orderLineItemRequests); + return orderRequest; } - private Order createOrderRequest( + private OrderRequest createOrderRequest( final OrderType type, - final UUID orderTableId, - final OrderLineItem... orderLineItems + final OrderTable orderTable, + final OrderLineItemRequest... orderLineItemRequests ) { - final Order order = new Order(); - order.setType(type); - order.setOrderTableId(orderTableId); - order.setOrderLineItems(Arrays.asList(orderLineItems)); - return order; - } - - private static OrderLineItem createOrderLineItemRequest(final UUID menuId, final long price, final long quantity) { - final OrderLineItem orderLineItem = new OrderLineItem(); - orderLineItem.setSeq(new Random().nextLong()); - orderLineItem.setMenuId(menuId); - orderLineItem.setPrice(BigDecimal.valueOf(price)); - orderLineItem.setQuantity(quantity); - return orderLineItem; + final OrderRequest orderRequest = new OrderRequest(); + orderRequest.setType(type); + orderRequest.setStatus(OrderStatus.WAITING); + orderRequest.setOrderTable(orderTable); + orderRequest.setOrderLineItems(Arrays.asList(orderLineItemRequests)); + return orderRequest; + } + + private static OrderLineItemRequest createOrderLineItemRequest(final UUID menuId, final long price, final long quantity) { + final OrderLineItemRequest orderLineItemRequest = new OrderLineItemRequest(); + orderLineItemRequest.setSeq(new Random().nextLong()); + orderLineItemRequest.setMenuId(menuId); + orderLineItemRequest.setPrice(BigDecimal.valueOf(price)); + orderLineItemRequest.setQuantity(quantity); + return orderLineItemRequest; } } diff --git a/src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java b/src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java index 01551a8d4..122f59612 100644 --- a/src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java +++ b/src/test/java/kitchenpos/eatinorders/application/OrderTableServiceTest.java @@ -1,9 +1,13 @@ package kitchenpos.eatinorders.application; -import kitchenpos.eatinorders.domain.OrderRepository; -import kitchenpos.eatinorders.domain.OrderStatus; -import kitchenpos.eatinorders.domain.OrderTable; -import kitchenpos.eatinorders.domain.OrderTableRepository; +import kitchenpos.common.domain.orders.OrderTableStatus; +import kitchenpos.eatinorders.domain.eatinorder.ClearOrderTableService; +import kitchenpos.eatinorders.domain.eatinorder.OrderRepository; +import kitchenpos.eatinorders.application.dto.OrderTableRequest; +import kitchenpos.eatinorders.domain.eatinorder.OrderTable; +import kitchenpos.eatinorders.domain.eatinorder.OrderTableRepository; +import kitchenpos.eatinorders.infra.InMemoryOrderRepository; +import kitchenpos.eatinorders.infra.InMemoryOrderTableRepository; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -23,26 +27,28 @@ class OrderTableServiceTest { private OrderTableRepository orderTableRepository; private OrderRepository orderRepository; + private ClearOrderTableService clearOrderTableService; + private OrderTableService orderTableService; @BeforeEach void setUp() { orderTableRepository = new InMemoryOrderTableRepository(); orderRepository = new InMemoryOrderRepository(); - orderTableService = new OrderTableService(orderTableRepository, orderRepository); + clearOrderTableService = new ClearOrderTableService(orderRepository, orderTableRepository); + orderTableService = new OrderTableService(orderTableRepository); } @DisplayName("주문 테이블을 등록할 수 있다.") @Test void create() { - final OrderTable expected = createOrderTableRequest("1번"); + final OrderTableRequest expected = createOrderTableRequest("1번"); final OrderTable actual = orderTableService.create(expected); assertThat(actual).isNotNull(); assertAll( () -> assertThat(actual.getId()).isNotNull(), - () -> assertThat(actual.getName()).isEqualTo(expected.getName()), - () -> assertThat(actual.getNumberOfGuests()).isZero(), - () -> assertThat(actual.isOccupied()).isFalse() + () -> assertThat(actual.getOccupied()).isEqualTo(OrderTableStatus.UNOCCUPIED), + () -> assertThat(actual.getCustomerHeadcount()).isZero() ); } @@ -50,47 +56,26 @@ void create() { @NullAndEmptySource @ParameterizedTest void create(final String name) { - final OrderTable expected = createOrderTableRequest(name); + final OrderTableRequest expected = createOrderTableRequest(name); assertThatThrownBy(() -> orderTableService.create(expected)) .isInstanceOf(IllegalArgumentException.class); } - @DisplayName("빈 테이블을 해지할 수 있다.") + @DisplayName("빈 테이블을 점유할 수 있다.") @Test void sit() { final UUID orderTableId = orderTableRepository.save(orderTable(false, 0)).getId(); final OrderTable actual = orderTableService.sit(orderTableId); - assertThat(actual.isOccupied()).isTrue(); - } - - @DisplayName("빈 테이블로 설정할 수 있다.") - @Test - void clear() { - final UUID orderTableId = orderTableRepository.save(orderTable(true, 4)).getId(); - final OrderTable actual = orderTableService.clear(orderTableId); - assertAll( - () -> assertThat(actual.getNumberOfGuests()).isZero(), - () -> assertThat(actual.isOccupied()).isFalse() - ); - } - - @DisplayName("완료되지 않은 주문이 있는 주문 테이블은 빈 테이블로 설정할 수 없다.") - @Test - void clearWithUncompletedOrders() { - final OrderTable orderTable = orderTableRepository.save(orderTable(true, 4)); - final UUID orderTableId = orderTable.getId(); - orderRepository.save(order(OrderStatus.ACCEPTED, orderTable)); - assertThatThrownBy(() -> orderTableService.clear(orderTableId)) - .isInstanceOf(IllegalStateException.class); + assertThat(actual.getOccupied()).isEqualTo(OrderTableStatus.OCCUPIED); } @DisplayName("방문한 손님 수를 변경할 수 있다.") @Test void changeNumberOfGuests() { final UUID orderTableId = orderTableRepository.save(orderTable(true, 0)).getId(); - final OrderTable expected = changeNumberOfGuestsRequest(4); + final OrderTableRequest expected = changeNumberOfGuestsRequest(4); final OrderTable actual = orderTableService.changeNumberOfGuests(orderTableId, expected); - assertThat(actual.getNumberOfGuests()).isEqualTo(4); + assertThat(actual.getCustomerHeadcount()).isEqualTo(4); } @DisplayName("방문한 손님 수가 올바르지 않으면 변경할 수 없다.") @@ -98,7 +83,7 @@ void changeNumberOfGuests() { @ParameterizedTest void changeNumberOfGuests(final int numberOfGuests) { final UUID orderTableId = orderTableRepository.save(orderTable(true, 0)).getId(); - final OrderTable expected = changeNumberOfGuestsRequest(numberOfGuests); + final OrderTableRequest expected = changeNumberOfGuestsRequest(numberOfGuests); assertThatThrownBy(() -> orderTableService.changeNumberOfGuests(orderTableId, expected)) .isInstanceOf(IllegalArgumentException.class); } @@ -107,7 +92,7 @@ void changeNumberOfGuests(final int numberOfGuests) { @Test void changeNumberOfGuestsInEmptyTable() { final UUID orderTableId = orderTableRepository.save(orderTable(false, 0)).getId(); - final OrderTable expected = changeNumberOfGuestsRequest(4); + final OrderTableRequest expected = changeNumberOfGuestsRequest(4); assertThatThrownBy(() -> orderTableService.changeNumberOfGuests(orderTableId, expected)) .isInstanceOf(IllegalStateException.class); } @@ -120,15 +105,15 @@ void findAll() { assertThat(actual).hasSize(1); } - private OrderTable createOrderTableRequest(final String name) { - final OrderTable orderTable = new OrderTable(); - orderTable.setName(name); - return orderTable; + private OrderTableRequest createOrderTableRequest(final String name) { + final OrderTableRequest orderTableRequest = new OrderTableRequest(); + orderTableRequest.setName(name); + return orderTableRequest; } - private OrderTable changeNumberOfGuestsRequest(final int numberOfGuests) { - final OrderTable orderTable = new OrderTable(); - orderTable.setNumberOfGuests(numberOfGuests); - return orderTable; + private OrderTableRequest changeNumberOfGuestsRequest(final int numberOfGuests) { + final OrderTableRequest orderTableRequest = new OrderTableRequest(); + orderTableRequest.setNumberOfGuests(numberOfGuests); + return orderTableRequest; } } diff --git a/src/test/java/kitchenpos/eatinorders/domain/eatinorder/CustomerHeadCountTest.java b/src/test/java/kitchenpos/eatinorders/domain/eatinorder/CustomerHeadCountTest.java new file mode 100644 index 000000000..270597821 --- /dev/null +++ b/src/test/java/kitchenpos/eatinorders/domain/eatinorder/CustomerHeadCountTest.java @@ -0,0 +1,33 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import kitchenpos.eatinorders.domain.eatinorder.CustomerHeadcount; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.jupiter.api.Assertions.assertAll; + +public class CustomerHeadCountTest { + @DisplayName("고객 인원을 생성할 수 있다.") + @Test + void createCustomerHeadCount() { + CustomerHeadcount actual = CustomerHeadcount.of(21); + + assertAll( + () -> assertThat(actual.getHeadCounts()).isEqualTo(21) + ); + } + + @DisplayName("고객 인원이 올바르지 않으면 변경할 수 없다.") + @ParameterizedTest + @NullSource + void changeCustomerHeadCountWithNullPrice(Integer headCounts) { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> CustomerHeadcount.of(headCounts)) + .withMessageContaining("방문한 손님 수가 올바르지 않으면 변경할 수 없다."); + } +} diff --git a/src/test/java/kitchenpos/eatinorders/domain/eatinorder/DefaultOrderFactoryTest.java b/src/test/java/kitchenpos/eatinorders/domain/eatinorder/DefaultOrderFactoryTest.java new file mode 100644 index 000000000..efd4a5ed4 --- /dev/null +++ b/src/test/java/kitchenpos/eatinorders/domain/eatinorder/DefaultOrderFactoryTest.java @@ -0,0 +1,4 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +public class DefaultOrderFactoryTest { +} diff --git a/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderLineItemTest.java b/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderLineItemTest.java new file mode 100644 index 000000000..e5de11b52 --- /dev/null +++ b/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderLineItemTest.java @@ -0,0 +1,24 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.junit.jupiter.api.Assertions.assertAll; + +public class OrderLineItemTest { + @DisplayName("주문 메뉴를 생성할 수 있다.") + @Test + void creatOrderLineItem() { + OrderLineItem actual = OrderLineItem.of(UUID.randomUUID(), 10_000L, 2L); + + assertAll( + () -> assertThat(actual.getMenuId()).isNotNull(), + () -> assertThat(actual.getPrice()).isEqualTo(BigDecimal.valueOf(10_000L)), + () -> assertThat(actual.getQuantity()).isEqualTo(2L) + ); + } +} diff --git a/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderLineItemsTest.java b/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderLineItemsTest.java new file mode 100644 index 000000000..5772ce97f --- /dev/null +++ b/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderLineItemsTest.java @@ -0,0 +1,88 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import kitchenpos.Fixtures; +import kitchenpos.common.domain.ProfanityValidator; +import kitchenpos.eatinorders.domain.menu.MenuClient; +import kitchenpos.eatinorders.infra.DefaultMenuClient; +import kitchenpos.eatinorders.infra.FakeMenuClient; +import kitchenpos.menus.domain.tobe.menu.Menu; +import kitchenpos.menus.domain.tobe.menu.MenuProducts; +import kitchenpos.menus.domain.tobe.menu.MenuRepository; +import kitchenpos.menus.domain.tobe.menu.ProductClient; +import kitchenpos.menus.infra.DefaultProductClient; +import kitchenpos.menus.infra.InMemoryMenuRepository; +import kitchenpos.products.domain.tobe.Product; +import kitchenpos.products.domain.tobe.ProductRepository; +import kitchenpos.products.infra.FakeProfanityValidator; +import kitchenpos.products.infra.InMemoryProductRepository; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; + +import java.math.BigDecimal; +import java.util.List; + +import static kitchenpos.Fixtures.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.jupiter.api.Assertions.assertAll; + +public class OrderLineItemsTest { + private ProfanityValidator profanityValidator; + private MenuProducts menuProducts; + + public static ProductRepository productRepository; + public static ProductClient productClient; + public static FakeMenuClient menuClient; + public static MenuRepository menuRepository; + public static Product product; + public static Menu menu; + @BeforeEach + void setUp() { + menuRepository = new InMemoryMenuRepository(); + menuClient = new FakeMenuClient(); + profanityValidator = new FakeProfanityValidator(); + productRepository = new InMemoryProductRepository(); + productClient = new DefaultProductClient(productRepository); + product = Fixtures.product(); + productRepository.save(product); + menuProducts = MenuProducts.of(productClient, Fixtures.menuProduct(product, 2)); + + menu = Menu.of("후라이드+후라이드", 20_000L, menuGroup().getId(), true, menuProducts, profanityValidator); + } + + + @DisplayName("주문 메뉴 리스트를 생성할 수 있다.") + @Test + void creatOrderLineItems() { + OrderLineItem item1 = OrderLineItem.of(menu.getId(), 20_000L, 2L); + + OrderLineItems actual = OrderLineItems.of(menuClient, List.of(orderLineItem(menuClient))); + + assertAll( + () -> assertThat(actual).isNotNull(), + () -> assertThat(actual.getSize()).isEqualTo(1) + ); + } + + + @DisplayName("주문한 메뉴의 가격은 실제 메뉴 가격과 일치해야 한다.") + @Test + void validateMenuPrice() { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> OrderLineItems.of(menuClient, List.of(orderLineItem(menuClient, BigDecimal.valueOf(10_000_000L))))) + .withMessageContaining("주문한 메뉴의 가격은 실제 메뉴 가격과 일치해야 한다."); + } + + @DisplayName("숨겨진 메뉴는 주문할 수 없다.") + @Test + void validateHiddenMenu() { + + menu.hide(); + OrderLineItem item1 = OrderLineItem.of(menu.getId(), 10_000L, 2L); + + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> OrderLineItems.of(menuClient, List.of(orderLineItem(menuClient, false)))) + .withMessageContaining("숨겨진 메뉴는 주문할 수 없다."); + } +} diff --git a/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderTableNameTest.java b/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderTableNameTest.java new file mode 100644 index 000000000..423bd45ee --- /dev/null +++ b/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderTableNameTest.java @@ -0,0 +1,31 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import kitchenpos.eatinorders.domain.eatinorder.OrderTableName; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.jupiter.api.Assertions.assertAll; + +public class OrderTableNameTest { + @DisplayName("주문 테이블의 이름을 등록할 수 있다.") + @Test + void createOrderTableName() { + OrderTableName actual = OrderTableName.of("udon"); + + assertAll( + () -> assertThat(actual.getName()).isEqualTo("udon") + ); + } + + @DisplayName("주문 테이블의 이름이 올바르지 않으면 등록할 수 없다.") + @ParameterizedTest + @NullAndEmptySource + void changeOrderTableNameWithNull(String name) { + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> OrderTableName.of(name)); + } +} diff --git a/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderTableTest.java b/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderTableTest.java new file mode 100644 index 000000000..181f385cc --- /dev/null +++ b/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderTableTest.java @@ -0,0 +1,88 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +import kitchenpos.common.domain.orders.OrderTableStatus; +import kitchenpos.eatinorders.domain.eatinorder.OrderTable; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.junit.jupiter.api.Assertions.assertAll; + +public class OrderTableTest { + @DisplayName("주문 테이블을 등록할 수 있다.") + @Test + void createOrderTable() { + OrderTable actual = OrderTable.of("udon", 3); + + assertAll( + () -> assertThat(actual.getCustomerHeadcount()).isEqualTo(3), + () -> assertThat(actual.getOccupied()).isEqualTo(OrderTableStatus.UNOCCUPIED) + ); + } + + @DisplayName("빈 테이블을 점유할 수 있다.") + @Test + void occupyOrderTable() { + OrderTable actual = OrderTable.of("udon", 3); + + actual.occupy(); + + assertAll( + () -> assertThat(actual.getOccupied()).isEqualTo(OrderTableStatus.OCCUPIED) + ); + } + + @DisplayName("빈 테이블로 설정할 수 있다.") + @Test + void clearOrderTable() { + OrderTable actual = OrderTable.of("udon", 3); + + actual.occupy(); + + assertThat(actual.getOccupied()).isEqualTo(OrderTableStatus.OCCUPIED); + + actual.unoccupy(); + + assertThat(actual.getOccupied()).isEqualTo(OrderTableStatus.UNOCCUPIED); + } + + @DisplayName("고객 인원를 변경할 수 있다.") + @ParameterizedTest + @ValueSource(ints = {1,2,3,4}) + void changeHeadCounts(int headCounts) { + OrderTable actual = OrderTable.of("udon", 3); + + actual.occupy(); + actual.changeCustomerHeadCounts(headCounts); + + assertThat(actual.getCustomerHeadcount()).isEqualTo(headCounts); + } + + @DisplayName("빈 테이블은 방문한 고객 인원을 변경할 수 없다.") + @Test + void failToChangeHeadCountsWithUnoccupied() { + OrderTable actual = OrderTable.of("udon", 3); + + assertThat(actual.getOccupied()).isEqualTo(OrderTableStatus.UNOCCUPIED); + + assertThatExceptionOfType(IllegalStateException.class) + .isThrownBy(() -> actual.changeCustomerHeadCounts(3)); + } + + @DisplayName("고객 인원은 0명 이상이어야 한다.") + @ParameterizedTest + @ValueSource(ints = {-10_000, -1}) + void changeCustomerHeadCountWithNegativePrice(int headCounts) { + + OrderTable actual = OrderTable.of("udon", 3); + + actual.occupy(); + + assertThatExceptionOfType(IllegalArgumentException.class) + .isThrownBy(() -> actual.changeCustomerHeadCounts(headCounts)) + .withMessageContaining("방문한 손님 수는 0 이상이어야 한다."); + } +} diff --git a/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderTest.java b/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderTest.java new file mode 100644 index 000000000..a864746cd --- /dev/null +++ b/src/test/java/kitchenpos/eatinorders/domain/eatinorder/OrderTest.java @@ -0,0 +1,4 @@ +package kitchenpos.eatinorders.domain.eatinorder; + +public class OrderTest { +} diff --git a/src/test/java/kitchenpos/eatinorders/application/FakeKitchenridersClient.java b/src/test/java/kitchenpos/eatinorders/infra/FakeKitchenridersClient.java similarity index 86% rename from src/test/java/kitchenpos/eatinorders/application/FakeKitchenridersClient.java rename to src/test/java/kitchenpos/eatinorders/infra/FakeKitchenridersClient.java index 301e1377b..d163c117c 100644 --- a/src/test/java/kitchenpos/eatinorders/application/FakeKitchenridersClient.java +++ b/src/test/java/kitchenpos/eatinorders/infra/FakeKitchenridersClient.java @@ -1,6 +1,6 @@ -package kitchenpos.eatinorders.application; +package kitchenpos.eatinorders.infra; -import kitchenpos.deliveryorders.infra.KitchenridersClient; +import kitchenpos.eatinorders.domain.eatinorder.KitchenridersClient; import java.math.BigDecimal; import java.util.UUID; diff --git a/src/test/java/kitchenpos/eatinorders/infra/FakeMenuClient.java b/src/test/java/kitchenpos/eatinorders/infra/FakeMenuClient.java new file mode 100644 index 000000000..91319445b --- /dev/null +++ b/src/test/java/kitchenpos/eatinorders/infra/FakeMenuClient.java @@ -0,0 +1,29 @@ +package kitchenpos.eatinorders.infra; + +import kitchenpos.eatinorders.domain.menu.MenuClient; +import kitchenpos.eatinorders.domain.menu.OrderMenu; + +import java.math.BigDecimal; +import java.util.HashMap; +import java.util.List; +import java.util.Map; +import java.util.UUID; +import java.util.stream.Collectors; + +public class FakeMenuClient implements MenuClient { + private final Map orderMenus = new HashMap<>(); + @Override + public Map findMenuInfoByMenuIds(List menuIds) { + return menuIds.stream() + .filter(orderMenus::containsKey) + .map(orderMenus::get) + .collect(Collectors.toMap( + OrderMenu::id, m-> m + )); + } + + public void add(UUID menuid, BigDecimal price, boolean isDisplayed){ + orderMenus.put(menuid, new OrderMenu(menuid, price, isDisplayed)); + } + +} diff --git a/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderRepository.java b/src/test/java/kitchenpos/eatinorders/infra/InMemoryOrderRepository.java similarity index 51% rename from src/test/java/kitchenpos/eatinorders/application/InMemoryOrderRepository.java rename to src/test/java/kitchenpos/eatinorders/infra/InMemoryOrderRepository.java index 85f27e38d..7cffee19a 100644 --- a/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderRepository.java +++ b/src/test/java/kitchenpos/eatinorders/infra/InMemoryOrderRepository.java @@ -1,9 +1,10 @@ -package kitchenpos.eatinorders.application; +package kitchenpos.eatinorders.infra; -import kitchenpos.eatinorders.domain.Order; -import kitchenpos.eatinorders.domain.OrderRepository; -import kitchenpos.eatinorders.domain.OrderStatus; -import kitchenpos.eatinorders.domain.OrderTable; +import kitchenpos.common.domain.orders.OrderStatus; +import kitchenpos.eatinorders.application.dto.OrderTableRequest; +import kitchenpos.eatinorders.domain.eatinorder.Order; +import kitchenpos.eatinorders.domain.eatinorder.OrderRepository; +import kitchenpos.eatinorders.domain.eatinorder.OrderTable; import java.util.ArrayList; import java.util.HashMap; @@ -32,9 +33,9 @@ public List findAll() { } @Override - public boolean existsByOrderTableAndStatusNot(final OrderTable orderTable, final OrderStatus status) { - return orders.values() - .stream() - .anyMatch(order -> order.getOrderTable().equals(orderTable) && order.getStatus() != status); + public boolean existsByOrderTableAndStatusNot(OrderTable orderTableRequest, OrderStatus orderStatus) { + return orders.values().stream().filter(order -> order.getOrderTableId().equals(orderTableRequest.getId())) + .anyMatch(order -> !order.hasStatus(orderStatus)); } + } diff --git a/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderTableRepository.java b/src/test/java/kitchenpos/eatinorders/infra/InMemoryOrderTableRepository.java similarity index 59% rename from src/test/java/kitchenpos/eatinorders/application/InMemoryOrderTableRepository.java rename to src/test/java/kitchenpos/eatinorders/infra/InMemoryOrderTableRepository.java index 663de6289..4932aeea1 100644 --- a/src/test/java/kitchenpos/eatinorders/application/InMemoryOrderTableRepository.java +++ b/src/test/java/kitchenpos/eatinorders/infra/InMemoryOrderTableRepository.java @@ -1,7 +1,4 @@ -package kitchenpos.eatinorders.application; - -import kitchenpos.eatinorders.domain.OrderTable; -import kitchenpos.eatinorders.domain.OrderTableRepository; +package kitchenpos.eatinorders.infra; import java.util.ArrayList; import java.util.HashMap; @@ -10,13 +7,17 @@ import java.util.Optional; import java.util.UUID; +import kitchenpos.common.domain.orders.OrderStatus; +import kitchenpos.eatinorders.domain.eatinorder.OrderTable; +import kitchenpos.eatinorders.domain.eatinorder.OrderTableRepository; + public class InMemoryOrderTableRepository implements OrderTableRepository { private final Map orderTables = new HashMap<>(); @Override - public OrderTable save(final OrderTable orderTable) { - orderTables.put(orderTable.getId(), orderTable); - return orderTable; + public OrderTable save(final OrderTable orderTableRequest) { + orderTables.put(orderTableRequest.getId(), orderTableRequest); + return orderTableRequest; } @Override @@ -28,4 +29,5 @@ public Optional findById(final UUID id) { public List findAll() { return new ArrayList<>(orderTables.values()); } + } diff --git a/src/test/java/kitchenpos/menus/domain/menuproduct/MenuGroupNameTest.java b/src/test/java/kitchenpos/menus/domain/menugroup/MenuGroupNameTest.java similarity index 95% rename from src/test/java/kitchenpos/menus/domain/menuproduct/MenuGroupNameTest.java rename to src/test/java/kitchenpos/menus/domain/menugroup/MenuGroupNameTest.java index 7bd46c56f..c660f5d63 100644 --- a/src/test/java/kitchenpos/menus/domain/menuproduct/MenuGroupNameTest.java +++ b/src/test/java/kitchenpos/menus/domain/menugroup/MenuGroupNameTest.java @@ -1,4 +1,4 @@ -package kitchenpos.menus.domain.menuproduct; +package kitchenpos.menus.domain.menugroup; import kitchenpos.menus.domain.tobe.menugroup.MenuGroupName; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/kitchenpos/menus/domain/menuproduct/MenuGroupTest.java b/src/test/java/kitchenpos/menus/domain/menugroup/MenuGroupTest.java similarity index 93% rename from src/test/java/kitchenpos/menus/domain/menuproduct/MenuGroupTest.java rename to src/test/java/kitchenpos/menus/domain/menugroup/MenuGroupTest.java index 6debe60f0..f26eeba21 100644 --- a/src/test/java/kitchenpos/menus/domain/menuproduct/MenuGroupTest.java +++ b/src/test/java/kitchenpos/menus/domain/menugroup/MenuGroupTest.java @@ -1,4 +1,4 @@ -package kitchenpos.menus.domain.menuproduct; +package kitchenpos.menus.domain.menugroup; import kitchenpos.menus.domain.tobe.menugroup.MenuGroup; import org.junit.jupiter.api.DisplayName; diff --git a/src/test/java/kitchenpos/products/application/ProductServiceEventTest.java b/src/test/java/kitchenpos/products/application/ProductServiceEventTest.java new file mode 100644 index 000000000..ff084d367 --- /dev/null +++ b/src/test/java/kitchenpos/products/application/ProductServiceEventTest.java @@ -0,0 +1,57 @@ +package kitchenpos.products.application; + +import kitchenpos.Fixtures; +import kitchenpos.common.domain.ProductPriceChangeEvent; +import kitchenpos.menus.domain.tobe.menu.Menu; +import kitchenpos.menus.domain.tobe.menu.MenuRepository; +import kitchenpos.products.application.dto.ProductRequest; +import kitchenpos.products.domain.tobe.Product; +import kitchenpos.products.domain.tobe.ProductRepository; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.test.context.event.ApplicationEvents; +import org.springframework.test.context.event.RecordApplicationEvents; +import org.springframework.transaction.annotation.Transactional; + +import java.math.BigDecimal; +import java.util.List; + +import static kitchenpos.Fixtures.menu; +import static kitchenpos.Fixtures.product; +import static org.assertj.core.api.Assertions.assertThat; +@SpringBootTest +@RecordApplicationEvents +public class ProductServiceEventTest { + @Autowired + private ProductRepository productRepository; + @Autowired + private MenuRepository menuRepository; + @Autowired + private ProductService productService; + @Autowired + private ApplicationEventPublisher publisher; + @Autowired + private ApplicationEvents events; + + @DisplayName("상품의 가격이 변경될 때 메뉴의 가격이 메뉴에 속한 상품 금액의 합보다 크면 메뉴가 숨겨진다.") + @Test + void changePriceInMenu() { + final Product product = productRepository.save(product("후라이드", 16_000L)); + final Menu menu = menuRepository.save(menu(19_000L, true, Fixtures.menuProduct(product, 2L))); + productService.changePrice(product.getId(), changePriceRequest(8_000L)); + + List changeEvents = events.stream(ProductPriceChangeEvent.class).toList(); + assertThat(changeEvents.size()).isEqualTo(1); + assertThat(changeEvents.getFirst().getProductId()).isEqualTo(product.getId()); + assertThat(BigDecimal.valueOf(changeEvents.getFirst().getPrice())).isEqualTo(product.getProductPrice()); + + } + private ProductRequest changePriceRequest(final Long price) { + final ProductRequest product = new ProductRequest(); + product.setPrice(price); + return product; + } +} diff --git a/src/test/java/kitchenpos/products/application/ProductServiceTest.java b/src/test/java/kitchenpos/products/application/ProductServiceTest.java index a87d00d79..e3ada0a12 100644 --- a/src/test/java/kitchenpos/products/application/ProductServiceTest.java +++ b/src/test/java/kitchenpos/products/application/ProductServiceTest.java @@ -22,6 +22,7 @@ import org.springframework.context.ApplicationEventPublisher; import org.springframework.test.context.event.ApplicationEvents; import org.springframework.test.context.event.RecordApplicationEvents; +import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; import java.util.List; @@ -50,7 +51,7 @@ void setUp() { productRepository = new InMemoryProductRepository(); menuRepository = new InMemoryMenuRepository(); profanityValidator = new FakeProfanityValidator(); - productService = new ProductService(productRepository, menuRepository, profanityValidator, publisher); + productService = new ProductService(productRepository, profanityValidator, publisher); } @DisplayName("상품을 등록할 수 있다.") @@ -107,19 +108,7 @@ void changePrice(final Long price) { } - @DisplayName("상품의 가격이 변경될 때 메뉴의 가격이 메뉴에 속한 상품 금액의 합보다 크면 메뉴가 숨겨진다.") - @Test - void changePriceInMenu() { - final Product product = productRepository.save(product("후라이드", 16_000L)); - final Menu menu = menuRepository.save(menu(19_000L, true, Fixtures.menuProduct(product, 2L))); - productService.changePrice(product.getId(), changePriceRequest(8_000L)); - - List changeEvents = events.stream(ProductPriceChangeEvent.class).toList(); - assertThat(changeEvents.size()).isEqualTo(1); - assertThat(changeEvents.getFirst().getProductId()).isEqualTo(product.getId()); - assertThat(BigDecimal.valueOf(changeEvents.getFirst().getPrice())).isEqualTo(product.getProductPrice()); - } @DisplayName("상품의 목록을 조회할 수 있다.") @Test