diff --git a/README.md b/README.md index 535540ae7..ebdcd16db 100644 --- a/README.md +++ b/README.md @@ -144,7 +144,7 @@ docker compose -p kitchenpos up -d - [x] 주문 아이템이 반드시 있어야 한다. - [x] 주문 아이템의 수량은 0개 이상이어야 한다. - [x] 주문 아이템의 메뉴는 반드시 있어야 한다. - - [x] 배달, 포장 주문시 수량이 0원 이상이어야 한다. + - [x] 배달, 포장 주문시 수량이 0개 이상이어야 한다. - [x] 메뉴가 노출된 상태여야 한다. - [x] 메뉴가격과 주문 아이템 가격이 다르면 안된다. - [x] 배달 주문시 배달 주소가 반드시 있어야 한다. diff --git a/src/main/java/kitchenpos/global/exception/ErrorCode.java b/src/main/java/kitchenpos/global/exception/ErrorCode.java index 560578f2b..8eeea98ab 100644 --- a/src/main/java/kitchenpos/global/exception/ErrorCode.java +++ b/src/main/java/kitchenpos/global/exception/ErrorCode.java @@ -9,6 +9,10 @@ public enum ErrorCode implements ErrorType { NOT_FOUND_MENU("404", "메뉴를 찾을 수 없습니다."), NOT_FOUND_MENU_GROUP("404", "메뉴그룹을 찾을 수 없습니다."), NOT_FOUND_MENU_PRODUCT("404", "메뉴상품을 찾을 수 없습니다."), + NOT_FOUND_ORDER("404", "주문을 찾을 수 없습니다."), + NOT_FOUND_ORDER_MENU("404", "주문메뉴를 찾을 수 없습니다."), + NOT_FOUND_ORDER_TABLE("404", "주문테이블을 찾을 수 없습니다."), + NOT_FOUND_ORDER_ITEM("404", "주문항목을 찾을 수 없습니다."), PRODUCT_PRICE_NOT_ALLOWED("400", "상품가격은 0원 이상이어야 합니다."), PRODUCT_NAME_NOT_ALLOWED("400", "상품명은 필수값 입니다."), @@ -24,7 +28,20 @@ public enum ErrorCode implements ErrorType { MENU_PRICE_OVER_TOTAL_PRODUCTS_NOT_ALLOWED("400", "메뉴가격은 구성 상품의 총 금액보다 클 수 없습니다."), MENU_PRODUCT_NOT_ALLOWED("400", "메뉴 상품은 1개 이상 입력해야 합니다."), - MENU_PRODUCT_QTY_NOT_ALLOWED("400", "메뉴 상품 수량은 0개 이상이어야 합니다."); + MENU_PRODUCT_QTY_NOT_ALLOWED("400", "메뉴 상품 수량은 0개 이상이어야 합니다."), + + DELIVERY_ADDRESS_NOT_ALLOWED("400", "배달 주소는 필수값 입니다."), + ORDER_PRICE_MISMATCH_MENU_PRICE("400", "주문가격이 실제 메뉴 가격과 일치하지 않습니다."), + ORDER_HIDE_MENU_NOT_ALLOWED("400", "숨김 메뉴는 주문할 수 없습니다."), + ORDER_LINE_ITEM_QTY_NOT_ALLOWED("400", "주문 수량은 0개 이상이어야 합니다."), + ORDER_TABLE_NAME_NOT_ALLOWED("400", "주문 테이블명은 필수값 입니다."), + ORDER_TABLE_NUMBER_OF_GUESTS_NOT_ALLOWED("400", "주문 테이블 인원수는 0명 이상이어야 합니다."), + + ORDER_STATUS_IS_NOT_WAITING("400", "주문대기 상태가 아닙니다."), + ORDER_STATUS_IS_NOT_ACCEPTED("400", "주문수락 상태가 아닙니다."), + ORDER_STATUS_IS_NOT_DELIVERED("400", "배달완료 상태가 아닙니다."), + ORDER_STATUS_IS_NOT_SERVED("400", "주문제공 상태가 아닙니다."), + ORDER_TYPE_IS_NOT_ALLOWED("400", "유효한 주문유형이 아닙니다."); private final String code; private final String message; diff --git a/src/main/java/kitchenpos/menu/domain/service/DefaultMenuContextService.java b/src/main/java/kitchenpos/menu/domain/service/DefaultMenuContextService.java new file mode 100644 index 000000000..14d7e92f9 --- /dev/null +++ b/src/main/java/kitchenpos/menu/domain/service/DefaultMenuContextService.java @@ -0,0 +1,37 @@ +package kitchenpos.menu.domain.service; + +import java.math.BigDecimal; +import java.util.List; +import kitchenpos.global.exception.ErrorCode; +import kitchenpos.global.exception.NotFoundException; +import kitchenpos.menu.domain.model.MenuId; +import kitchenpos.menu.domain.model.MenuVo.MenuInfo; +import kitchenpos.menu.domain.repository.MenuRepository; +import kitchenpos.order.common.application.MenuContextProvider; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional(readOnly = true) +@Service +public class DefaultMenuContextService implements MenuContextProvider { + + private final MenuRepository menuRepository; + + public DefaultMenuContextService(MenuRepository menuRepository) { + this.menuRepository = menuRepository; + } + + @Override + public BigDecimal getPrice(MenuId menuId) { + return menuRepository.findByMenuId(menuId) + .map(menu -> menu.getPrice().price()) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_MENU.toString())); + } + + @Override + public List findMenus(List menuIds) { + return menuRepository.findAllByMenuIdIn(menuIds).stream() + .map(MenuInfo::fromEntity) + .toList(); + } +} diff --git a/src/main/java/kitchenpos/order/common/application/MenuContextProvider.java b/src/main/java/kitchenpos/order/common/application/MenuContextProvider.java new file mode 100644 index 000000000..ecf8c43de --- /dev/null +++ b/src/main/java/kitchenpos/order/common/application/MenuContextProvider.java @@ -0,0 +1,13 @@ +package kitchenpos.order.common.application; + +import java.math.BigDecimal; +import java.util.List; +import kitchenpos.menu.domain.model.MenuId; +import kitchenpos.menu.domain.model.MenuVo.MenuInfo; + +public interface MenuContextProvider { + BigDecimal getPrice(MenuId menuId); + + List findMenus(List menuIds); + +} diff --git a/src/main/java/kitchenpos/order/common/application/dto/OrderRequest.java b/src/main/java/kitchenpos/order/common/application/dto/OrderRequest.java new file mode 100644 index 000000000..c2eb7dcac --- /dev/null +++ b/src/main/java/kitchenpos/order/common/application/dto/OrderRequest.java @@ -0,0 +1,39 @@ +package kitchenpos.order.common.application.dto; + +import jakarta.validation.constraints.NotEmpty; +import jakarta.validation.constraints.NotNull; +import java.math.BigDecimal; +import java.util.List; +import java.util.UUID; +import java.util.stream.Collectors; +import kitchenpos.order.common.domain.entity.OrderLineItem; +import kitchenpos.order.common.domain.entity.OrderType; +import kitchenpos.order.common.domain.model.OrderLineItems; +import kitchenpos.order.common.domain.model.OrderVo; +import kitchenpos.order.eatin.domain.model.OrderTableId; + +public record OrderRequest() { + + public record Create( + @NotNull(message = "주문유형은 필수입니다.") + OrderType type, + UUID orderTableId, + @NotEmpty(message = "메뉴 구성 상품을 하나 이상 포함해야 합니다.") + List orderLineItems, + String deliveryAddress + ) { + public OrderVo.Create toVo() { + return new OrderVo.Create( + type, + OrderTableId.of(orderTableId), + OrderLineItems.of(orderLineItems.stream() + .map(item -> OrderLineItem.fromDto(item, null, type)) + .collect(Collectors.toList())), + deliveryAddress + ); + } + } + + public record OrderLineItemCreate(UUID menuId, BigDecimal price, long quantity) {} + +} diff --git a/src/main/java/kitchenpos/order/common/application/dto/OrderResponse.java b/src/main/java/kitchenpos/order/common/application/dto/OrderResponse.java new file mode 100644 index 000000000..5a989b9fb --- /dev/null +++ b/src/main/java/kitchenpos/order/common/application/dto/OrderResponse.java @@ -0,0 +1,43 @@ +package kitchenpos.order.common.application.dto; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.UUID; +import kitchenpos.order.common.domain.entity.OrderLineItem; +import kitchenpos.order.common.domain.entity.OrderStatus; +import kitchenpos.order.common.domain.entity.OrderType; +import kitchenpos.order.common.domain.model.OrderVo; + +public record OrderResponse() { + public record GetOrder( + UUID id, + UUID orderTableId, + List orderItems, + LocalDateTime orderDateTime, + OrderStatus status, + OrderType type + + ) { + public static GetOrder fromVo(OrderVo.OrderInfo vo) { + return new GetOrder( + vo.getOrderId(), + vo.getOrderTableId(), + vo.orderLineItems().getItems().stream() + .map(GetOrderItems::fromVo) + .toList(), + vo.orderDateTime(), + vo.status(), + vo.type() + ); + } + } + + public record GetOrderItems( + UUID menuId, + long quantity + ) { + public static OrderResponse.GetOrderItems fromVo(OrderLineItem vo) { + return new OrderResponse.GetOrderItems(vo.getOrderId().get(), vo.getQuantity().get()); + } + } +} diff --git a/src/main/java/kitchenpos/order/common/application/dto/OrderTableRequest.java b/src/main/java/kitchenpos/order/common/application/dto/OrderTableRequest.java new file mode 100644 index 000000000..cb206040a --- /dev/null +++ b/src/main/java/kitchenpos/order/common/application/dto/OrderTableRequest.java @@ -0,0 +1,37 @@ +package kitchenpos.order.common.application.dto; + +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import java.util.UUID; +import kitchenpos.order.eatin.domain.model.OrderTableGuests; +import kitchenpos.order.eatin.domain.model.OrderTableId; +import kitchenpos.order.eatin.domain.model.OrderTableName; +import kitchenpos.order.eatin.domain.model.OrderTableVo; + +public record OrderTableRequest() { + + public record Create( + @NotNull(message = "테이블 이름은 필수입니다.") + String name + + ) { + public OrderTableVo.Create toVo() { + return new OrderTableVo.Create( + OrderTableName.of(name), + OrderTableGuests.of(0), + false + ); + } + } + + public record UpdateGuests( + UUID orderTableId, + @Positive(message = "테이블 인원 0보다 커야 합니다.") + int numberOfGuests + ) { + + public OrderTableVo.Update toVo() { + return new OrderTableVo.Update(OrderTableId.of(orderTableId), OrderTableGuests.of(numberOfGuests)); + } + } +} diff --git a/src/main/java/kitchenpos/order/common/application/dto/OrderTableResponse.java b/src/main/java/kitchenpos/order/common/application/dto/OrderTableResponse.java new file mode 100644 index 000000000..22cdd5d10 --- /dev/null +++ b/src/main/java/kitchenpos/order/common/application/dto/OrderTableResponse.java @@ -0,0 +1,22 @@ +package kitchenpos.order.common.application.dto; + +import java.util.UUID; +import kitchenpos.order.eatin.domain.model.OrderTableVo; + +public record OrderTableResponse() { + public record GetOrderTable( + UUID id, + String name, + int numberOfGuests, + boolean occupied + ) { + public static GetOrderTable fromVo(OrderTableVo.OrderTableInfo vo) { + return new GetOrderTable( + vo.getOrderTableId(), + vo.getOrderTableName(), + vo.getNumberOfGuests(), + vo.occupied() + ); + } + } +} diff --git a/src/main/java/kitchenpos/order/common/application/facade/OrderFacade.java b/src/main/java/kitchenpos/order/common/application/facade/OrderFacade.java index b24717881..48376b916 100644 --- a/src/main/java/kitchenpos/order/common/application/facade/OrderFacade.java +++ b/src/main/java/kitchenpos/order/common/application/facade/OrderFacade.java @@ -1,20 +1,94 @@ package kitchenpos.order.common.application.facade; -import kitchenpos.order.delivery.application.DeliveryUsecase; -import kitchenpos.order.eatin.application.EatinUsecase; -import kitchenpos.order.takeout.application.TakeoutUsecase; +import java.util.List; +import java.util.UUID; +import kitchenpos.order.common.application.dto.OrderRequest; +import kitchenpos.order.common.application.dto.OrderResponse; +import kitchenpos.order.common.application.dto.OrderResponse.GetOrder; +import kitchenpos.order.common.application.dto.OrderTableRequest.Create; +import kitchenpos.order.common.application.dto.OrderTableRequest.UpdateGuests; +import kitchenpos.order.common.application.dto.OrderTableResponse; +import kitchenpos.order.common.application.dto.OrderTableResponse.GetOrderTable; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.service.OrderCommandService; +import kitchenpos.order.common.domain.service.OrderQueryService; +import kitchenpos.order.delivery.domain.service.DeliveryService; +import kitchenpos.order.eatin.domain.model.OrderTableId; +import kitchenpos.order.eatin.domain.service.EatInService; import org.springframework.stereotype.Component; @Component public class OrderFacade { - private final TakeoutUsecase takeoutUsecase; - private final DeliveryUsecase deliveryUsecase; - private final EatinUsecase eatinUsecase; - - public OrderFacade(TakeoutUsecase takeoutUsecase, DeliveryUsecase deliveryUsecase, - EatinUsecase eatinUsecase) { - this.takeoutUsecase = takeoutUsecase; - this.deliveryUsecase = deliveryUsecase; - this.eatinUsecase = eatinUsecase; + private final DeliveryService deliveryService; + private final EatInService eatinService; + private final OrderQueryService orderQueryService; + + private final OrderCommandService orderCommandService; + + public OrderFacade( + DeliveryService deliveryService, + EatInService eatinService, + OrderQueryService orderQueryService, + OrderCommandService orderCommandService + ) { + this.deliveryService = deliveryService; + this.eatinService = eatinService; + this.orderQueryService = orderQueryService; + this.orderCommandService = orderCommandService; + } + + public GetOrder create(OrderRequest.Create request) { + return GetOrder.fromVo(orderCommandService.create(request.toVo())); + } + + public OrderResponse.GetOrder accept(final UUID orderId) { + return GetOrder.fromVo(orderCommandService.accept(OrderId.of(orderId))); + } + + public OrderResponse.GetOrder serve(final UUID orderId) { + return GetOrder.fromVo(orderCommandService.serve(OrderId.of(orderId))); + } + + public GetOrder startDelivery(final UUID orderId) { + return GetOrder.fromVo(deliveryService.startDelivery(OrderId.of(orderId))); + } + + public GetOrder completeDelivery(final UUID orderId) { + return GetOrder.fromVo(deliveryService.completeDelivery(OrderId.of(orderId))); + } + + + public GetOrder complete(UUID orderId) { + return GetOrder.fromVo(orderCommandService.complete(OrderId.of(orderId))); + } + + public List findAll() { + return orderQueryService.findAll() + .stream() + .map(OrderResponse.GetOrder::fromVo) + .toList(); + } + + public List findOrderTableAll() { + return eatinService.findAll() + .stream() + .map(OrderTableResponse.GetOrderTable::fromVo) + .toList(); + } + + public GetOrderTable createOrderTable(Create request) { + return GetOrderTable.fromVo(eatinService.create(request.toVo())); + } + + public GetOrderTable sit(UUID orderTableId) { + return GetOrderTable.fromVo(eatinService.sit(OrderTableId.of(orderTableId))); + } + + public GetOrderTable clear(UUID orderTableId) { + return GetOrderTable.fromVo(eatinService.clear(OrderTableId.of(orderTableId))); + } + + public GetOrderTable changeNumberOfGuests(UpdateGuests request) { + return GetOrderTable.fromVo(eatinService.changeNumberOfGuests(request.toVo())); } } diff --git a/src/main/java/kitchenpos/order/common/domain/entity/Order.java b/src/main/java/kitchenpos/order/common/domain/entity/Order.java index c2d1f68e2..1860d37e1 100644 --- a/src/main/java/kitchenpos/order/common/domain/entity/Order.java +++ b/src/main/java/kitchenpos/order/common/domain/entity/Order.java @@ -1,33 +1,37 @@ package kitchenpos.order.common.domain.entity; -import jakarta.persistence.CascadeType; +import jakarta.persistence.AttributeOverride; import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorColumn; +import jakarta.persistence.DiscriminatorType; +import jakarta.persistence.Embedded; +import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; import jakarta.persistence.EnumType; import jakarta.persistence.Enumerated; -import jakarta.persistence.FetchType; -import jakarta.persistence.ForeignKey; -import jakarta.persistence.Id; -import jakarta.persistence.JoinColumn; -import jakarta.persistence.ManyToOne; -import jakarta.persistence.OneToMany; +import jakarta.persistence.Inheritance; +import jakarta.persistence.InheritanceType; import jakarta.persistence.Table; -import jakarta.persistence.Transient; import java.time.LocalDateTime; -import java.util.List; -import java.util.UUID; -import kitchenpos.order.eatin.domain.entity.OrderTable; +import kitchenpos.global.exception.ErrorCode; +import kitchenpos.order.common.domain.exception.OrderInvalidException; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItems; +import kitchenpos.order.eatin.domain.model.OrderTableId; +import org.hibernate.annotations.DynamicUpdate; @Table(name = "orders") @Entity +@DynamicUpdate +@Inheritance(strategy = InheritanceType.SINGLE_TABLE) +@DiscriminatorColumn(name = "type", discriminatorType = DiscriminatorType.STRING) public class Order { - @Column(name = "id", columnDefinition = "binary(16)") - @Id - private UUID id; + @EmbeddedId + private OrderId orderId; - @Column(name = "type", nullable = false, columnDefinition = "varchar(255)") @Enumerated(EnumType.STRING) + @Column(name = "type", nullable = false, columnDefinition = "varchar(255)", insertable = false, updatable = false) private OrderType type; @Column(name = "status", nullable = false, columnDefinition = "varchar(255)") @@ -37,93 +41,97 @@ public class Order { @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; + @Embedded + private OrderLineItems orderLineItems; - @Column(name = "delivery_address") - private String deliveryAddress; + @Embedded + @AttributeOverride(name = "id", column = @Column(name = "order_table_id")) + private OrderTableId orderTableId; - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn( - name = "order_table_id", - columnDefinition = "binary(16)", - foreignKey = @ForeignKey(name = "fk_orders_to_order_table") - ) - private OrderTable orderTable; + protected Order() {} - @Transient - private UUID orderTableId; - - public Order() { - } - - public UUID getId() { - return id; + public Order(OrderId orderId, OrderType type, OrderStatus status, LocalDateTime orderDateTime, + OrderLineItems orderLineItems, OrderTableId orderTableId) { + this.orderId = orderId; + this.type = type; + this.status = status; + this.orderDateTime = orderDateTime; + this.orderLineItems = orderLineItems; + this.orderTableId = orderTableId; } - public void setId(final UUID id) { - this.id = id; + public OrderId getOrderId() { + return orderId; } 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 OrderLineItems getOrderLineItems() { + return orderLineItems; } - public List getOrderLineItems() { - return orderLineItems; + public OrderTableId getOrderTableId() { + return orderTableId; } - public void setOrderLineItems(final List orderLineItems) { - this.orderLineItems = orderLineItems; + public void updateOrderStatus(OrderStatus orderStatus) { + this.status = orderStatus; } - public String getDeliveryAddress() { - return deliveryAddress; + public boolean isDelivery() { + return this.getType() == OrderType.DELIVERY; } - public void setDeliveryAddress(final String deliveryAddress) { - this.deliveryAddress = deliveryAddress; + public boolean isEatIn() { + return this.getType() == OrderType.EAT_IN; } - public OrderTable getOrderTable() { - return orderTable; + public boolean isTakeout() { + return this.getType() == OrderType.TAKEOUT; } - public void setOrderTable(final OrderTable orderTable) { - this.orderTable = orderTable; + public boolean isWaiting() { + return this.getStatus() == OrderStatus.WAITING; + } + public boolean isAccepted() { + return this.getStatus() == OrderStatus.ACCEPTED; } - public UUID getOrderTableId() { - return orderTableId; + public boolean isDelivered() { + return this.getStatus() == OrderStatus.DELIVERED; + } + public boolean isServed() { + return this.getStatus() == OrderStatus.SERVED; } - public void setOrderTableId(final UUID orderTableId) { - this.orderTableId = orderTableId; + public void validateIsWaiting() { + if(!isWaiting()) { + throw new OrderInvalidException(ErrorCode.ORDER_STATUS_IS_NOT_WAITING.toString()); + } + } + + public void validateIsAccepted() { + if(!isAccepted()) { + throw new OrderInvalidException(ErrorCode.ORDER_STATUS_IS_NOT_ACCEPTED.toString()); + } + } + + public void validateOrderCompletion() { + if (isDelivery() && !isDelivered()) { + throw new OrderInvalidException(ErrorCode.ORDER_STATUS_IS_NOT_DELIVERED.toString()); + } + if ((isTakeout() || isEatIn()) && !isServed()) { + throw new OrderInvalidException(ErrorCode.ORDER_STATUS_IS_NOT_SERVED.toString()); + } } } diff --git a/src/main/java/kitchenpos/order/common/domain/entity/OrderLineItem.java b/src/main/java/kitchenpos/order/common/domain/entity/OrderLineItem.java index 6c1163d05..6ae97a219 100644 --- a/src/main/java/kitchenpos/order/common/domain/entity/OrderLineItem.java +++ b/src/main/java/kitchenpos/order/common/domain/entity/OrderLineItem.java @@ -1,5 +1,6 @@ package kitchenpos.order.common.domain.entity; +import com.fasterxml.jackson.annotation.JsonBackReference; import jakarta.persistence.AttributeOverride; import jakarta.persistence.Column; import jakarta.persistence.Embedded; @@ -11,6 +12,9 @@ import jakarta.persistence.Transient; import java.math.BigDecimal; import kitchenpos.menu.domain.model.MenuId; +import kitchenpos.order.common.application.dto.OrderRequest.OrderLineItemCreate; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItemQty; @Table(name = "order_line_item") @Entity @@ -25,44 +29,47 @@ public class OrderLineItem { @AttributeOverride(name = "id", column = @Column(name = "menu_id")) private MenuId menuId; - @Column(name = "quantity", nullable = false) - private long quantity; + @Embedded + private OrderLineItemQty quantity; + + @Embedded + @AttributeOverride(name = "id", column = @Column(name = "order_id")) + @JsonBackReference + private OrderId orderId; @Transient private BigDecimal price; - public OrderLineItem() { + public OrderLineItem() {} + + public OrderLineItem(MenuId menuId, OrderId orderId, OrderLineItemQty quantity, BigDecimal price) { + this.menuId = menuId; + this.orderId = orderId; + this.quantity = quantity; + this.price = price; } public Long getSeq() { return seq; } - public void setSeq(final Long seq) { - this.seq = seq; + public MenuId getMenuId() { + return menuId; } - public long getQuantity() { + public OrderLineItemQty getQuantity() { return quantity; } - public void setQuantity(final long quantity) { - this.quantity = quantity; - } - - public MenuId getMenuId() { - return menuId; - } - - public void setMenuId(MenuId menuId) { - this.menuId = menuId; + public OrderId getOrderId() { + return orderId; } public BigDecimal getPrice() { return price; } - public void setPrice(final BigDecimal price) { - this.price = price; + public static OrderLineItem fromDto(OrderLineItemCreate dto, OrderId orderId, OrderType type) { + return new OrderLineItem(MenuId.of(dto.menuId()), orderId, OrderLineItemQty.of(dto.quantity(), type), dto.price()); } } diff --git a/src/main/java/kitchenpos/order/common/domain/exception/OrderHideMenuException.java b/src/main/java/kitchenpos/order/common/domain/exception/OrderHideMenuException.java new file mode 100644 index 000000000..3e4467318 --- /dev/null +++ b/src/main/java/kitchenpos/order/common/domain/exception/OrderHideMenuException.java @@ -0,0 +1,9 @@ +package kitchenpos.order.common.domain.exception; + +import kitchenpos.global.exception.ErrorCode; + +public class OrderHideMenuException extends IllegalStateException { + public OrderHideMenuException() { + super(ErrorCode.ORDER_HIDE_MENU_NOT_ALLOWED.toString()); + } +} diff --git a/src/main/java/kitchenpos/order/common/domain/exception/OrderInvalidException.java b/src/main/java/kitchenpos/order/common/domain/exception/OrderInvalidException.java new file mode 100644 index 000000000..930bf4f5e --- /dev/null +++ b/src/main/java/kitchenpos/order/common/domain/exception/OrderInvalidException.java @@ -0,0 +1,7 @@ +package kitchenpos.order.common.domain.exception; + +public class OrderInvalidException extends IllegalStateException { + public OrderInvalidException(String message) { + super(message); + } +} diff --git a/src/main/java/kitchenpos/order/common/domain/exception/OrderLineItemQtyException.java b/src/main/java/kitchenpos/order/common/domain/exception/OrderLineItemQtyException.java new file mode 100644 index 000000000..8011d6150 --- /dev/null +++ b/src/main/java/kitchenpos/order/common/domain/exception/OrderLineItemQtyException.java @@ -0,0 +1,9 @@ +package kitchenpos.order.common.domain.exception; + +import kitchenpos.global.exception.ErrorCode; + +public class OrderLineItemQtyException extends IllegalArgumentException { + public OrderLineItemQtyException(String message) { + super(message); + } +} diff --git a/src/main/java/kitchenpos/order/common/domain/exception/OrderMenuInvalidException.java b/src/main/java/kitchenpos/order/common/domain/exception/OrderMenuInvalidException.java new file mode 100644 index 000000000..a7e92f7f0 --- /dev/null +++ b/src/main/java/kitchenpos/order/common/domain/exception/OrderMenuInvalidException.java @@ -0,0 +1,9 @@ +package kitchenpos.order.common.domain.exception; + +import kitchenpos.global.exception.ErrorCode; + +public class OrderMenuInvalidException extends IllegalArgumentException { + public OrderMenuInvalidException() { + super(ErrorCode.NOT_FOUND_ORDER_MENU.toString()); + } +} diff --git a/src/main/java/kitchenpos/order/common/domain/exception/OrderPriceInvalidException.java b/src/main/java/kitchenpos/order/common/domain/exception/OrderPriceInvalidException.java new file mode 100644 index 000000000..448429e27 --- /dev/null +++ b/src/main/java/kitchenpos/order/common/domain/exception/OrderPriceInvalidException.java @@ -0,0 +1,9 @@ +package kitchenpos.order.common.domain.exception; + +import kitchenpos.global.exception.ErrorCode; + +public class OrderPriceInvalidException extends IllegalArgumentException { + public OrderPriceInvalidException() { + super(ErrorCode.ORDER_PRICE_MISMATCH_MENU_PRICE.toString()); + } +} diff --git a/src/main/java/kitchenpos/order/common/domain/exception/OrderTypeInvalidException.java b/src/main/java/kitchenpos/order/common/domain/exception/OrderTypeInvalidException.java new file mode 100644 index 000000000..4b63e744b --- /dev/null +++ b/src/main/java/kitchenpos/order/common/domain/exception/OrderTypeInvalidException.java @@ -0,0 +1,9 @@ +package kitchenpos.order.common.domain.exception; + +import kitchenpos.global.exception.ErrorCode; + +public class OrderTypeInvalidException extends IllegalStateException { + public OrderTypeInvalidException() { + super(ErrorCode.ORDER_TYPE_IS_NOT_ALLOWED.toString()); + } +} diff --git a/src/main/java/kitchenpos/order/common/domain/model/OrderId.java b/src/main/java/kitchenpos/order/common/domain/model/OrderId.java new file mode 100644 index 000000000..782149e98 --- /dev/null +++ b/src/main/java/kitchenpos/order/common/domain/model/OrderId.java @@ -0,0 +1,50 @@ +package kitchenpos.order.common.domain.model; + +import jakarta.persistence.Embeddable; +import java.io.Serializable; +import java.util.Objects; +import java.util.UUID; +import kitchenpos.global.exception.ErrorCode; +import kitchenpos.global.exception.NotFoundException; + +@Embeddable +public class OrderId implements Serializable { + + private static final long serialVersionUID = -161403658033403389L; + private UUID id; + + protected OrderId() {} + + public OrderId(UUID id) { + this.id = id; + } + + public static OrderId of(UUID id) { + if (id == null) { + throw new NotFoundException(ErrorCode.NOT_FOUND_ORDER.toString()); + } + return new OrderId(id); + } + + + public UUID get() { + return id; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OrderId menuId = (OrderId) o; + return Objects.equals(id, menuId.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } +} diff --git a/src/main/java/kitchenpos/order/common/domain/model/OrderLineItemQty.java b/src/main/java/kitchenpos/order/common/domain/model/OrderLineItemQty.java new file mode 100644 index 000000000..e97ace528 --- /dev/null +++ b/src/main/java/kitchenpos/order/common/domain/model/OrderLineItemQty.java @@ -0,0 +1,24 @@ +package kitchenpos.order.common.domain.model; + +import jakarta.persistence.Embeddable; +import kitchenpos.global.exception.ErrorCode; +import kitchenpos.order.common.domain.entity.OrderType; +import kitchenpos.order.common.domain.exception.OrderLineItemQtyException; + +@Embeddable +public record OrderLineItemQty(long quantity) { + + public static OrderLineItemQty of(long quantity, OrderType type) { + if (type.equals(OrderType.TAKEOUT) || type.equals(OrderType.DELIVERY)) { + if (quantity < 0) { + throw new OrderLineItemQtyException(ErrorCode.ORDER_LINE_ITEM_QTY_NOT_ALLOWED.toString()); + } + } + return new OrderLineItemQty(quantity); + } + + public long get() { + return quantity; + } +} + diff --git a/src/main/java/kitchenpos/order/common/domain/model/OrderLineItems.java b/src/main/java/kitchenpos/order/common/domain/model/OrderLineItems.java new file mode 100644 index 000000000..8978ebb33 --- /dev/null +++ b/src/main/java/kitchenpos/order/common/domain/model/OrderLineItems.java @@ -0,0 +1,47 @@ +package kitchenpos.order.common.domain.model; + +import com.fasterxml.jackson.annotation.JsonManagedReference; +import jakarta.persistence.CascadeType; +import jakarta.persistence.Embeddable; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.OneToMany; +import java.util.ArrayList; +import java.util.Collections; +import java.util.List; +import kitchenpos.global.exception.ErrorCode; +import kitchenpos.global.exception.NotFoundException; +import kitchenpos.order.common.domain.entity.OrderLineItem; + +@Embeddable +public class OrderLineItems { + + @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) + @JoinColumn(name = "order_id", insertable = false, updatable = false) + @JsonManagedReference + private List items = new ArrayList<>(); + + public static OrderLineItems of(final List items) { + if (items == null || items.isEmpty()) { + throw new NotFoundException(ErrorCode.NOT_FOUND_ORDER_ITEM.toString()); + } + return new OrderLineItems(items); + } + + protected OrderLineItems() {} + + public OrderLineItems(List items) { + this.items = items; + } + + public List getItems() { + return Collections.unmodifiableList(items); + } + + public void addItem(OrderLineItem orderLineItem) { + this.items.add(orderLineItem); + } + + public void removeItem(OrderLineItem orderLineItem) { + this.items.remove(orderLineItem); + } +} diff --git a/src/main/java/kitchenpos/order/common/domain/model/OrderVo.java b/src/main/java/kitchenpos/order/common/domain/model/OrderVo.java new file mode 100644 index 000000000..5de3c9f05 --- /dev/null +++ b/src/main/java/kitchenpos/order/common/domain/model/OrderVo.java @@ -0,0 +1,61 @@ +package kitchenpos.order.common.domain.model; + +import java.time.LocalDateTime; +import java.util.Objects; +import java.util.Optional; +import java.util.UUID; +import kitchenpos.menu.domain.model.MenuId; +import kitchenpos.menu.domain.model.MenuPrice; +import kitchenpos.order.common.domain.entity.Order; +import kitchenpos.order.common.domain.entity.OrderStatus; +import kitchenpos.order.common.domain.entity.OrderType; +import kitchenpos.order.eatin.domain.model.OrderTableId; + +public record OrderVo() { + + public record OrderInfo( + OrderId id, + OrderTableId orderTableId, + OrderLineItems orderLineItems, + LocalDateTime orderDateTime, + OrderStatus status, + OrderType type + ) { + public static OrderInfo fromEntity(Order entity) { + return new OrderInfo( + entity.getOrderId(), + entity.getOrderTableId(), + OrderLineItems.of(entity.getOrderLineItems().getItems()), + entity.getOrderDateTime(), + entity.getStatus(), + entity.getType()); + } + + public UUID getOrderId() { + return id.get(); + } + + public UUID getOrderTableId() { + return Optional.ofNullable(orderTableId) + .map(OrderTableId::get) + .orElse(null); + } + + } + + public record Create( + OrderType type, + OrderTableId orderTableId, + OrderLineItems orderLineItems, + String deliveryAddress + ) { + public Create { + if (Objects.isNull(type)) { + throw new IllegalArgumentException(); + } + } + + } + + public record Update(MenuId menuId, MenuPrice price) {} +} diff --git a/src/main/java/kitchenpos/order/common/domain/repository/OrderRepository.java b/src/main/java/kitchenpos/order/common/domain/repository/OrderRepository.java index 97ea9fd43..4baed7271 100644 --- a/src/main/java/kitchenpos/order/common/domain/repository/OrderRepository.java +++ b/src/main/java/kitchenpos/order/common/domain/repository/OrderRepository.java @@ -2,19 +2,20 @@ import java.util.List; import java.util.Optional; -import java.util.UUID; import kitchenpos.order.common.domain.entity.Order; import kitchenpos.order.common.domain.entity.OrderStatus; -import kitchenpos.order.eatin.domain.entity.OrderTable; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.eatin.domain.model.OrderTableId; public interface OrderRepository { Order save(Order order); - Optional findById(UUID id); + Optional findById(OrderId id); List findAll(); - boolean existsByOrderTableAndStatusNot(OrderTable orderTable, OrderStatus status); + boolean existsByOrderTableIdAndStatusNot(OrderTableId orderTableId, OrderStatus status); + } diff --git a/src/main/java/kitchenpos/order/common/domain/repository/OrderTableRepository.java b/src/main/java/kitchenpos/order/common/domain/repository/OrderTableRepository.java index e1743e57f..0e6f3fc62 100644 --- a/src/main/java/kitchenpos/order/common/domain/repository/OrderTableRepository.java +++ b/src/main/java/kitchenpos/order/common/domain/repository/OrderTableRepository.java @@ -2,14 +2,14 @@ import java.util.List; import java.util.Optional; -import java.util.UUID; import kitchenpos.order.eatin.domain.entity.OrderTable; +import kitchenpos.order.eatin.domain.model.OrderTableId; public interface OrderTableRepository { OrderTable save(OrderTable orderTable); - Optional findById(UUID id); + Optional findById(OrderTableId id); List findAll(); } diff --git a/src/main/java/kitchenpos/order/common/domain/service/DefaultOrderService.java b/src/main/java/kitchenpos/order/common/domain/service/DefaultOrderService.java new file mode 100644 index 000000000..ccabb3809 --- /dev/null +++ b/src/main/java/kitchenpos/order/common/domain/service/DefaultOrderService.java @@ -0,0 +1,154 @@ +package kitchenpos.order.common.domain.service; + +import java.util.List; +import java.util.UUID; +import kitchenpos.global.exception.ErrorCode; +import kitchenpos.global.exception.NotFoundException; +import kitchenpos.menu.domain.model.MenuVo.MenuInfo; +import kitchenpos.order.common.application.MenuContextProvider; +import kitchenpos.order.common.domain.entity.Order; +import kitchenpos.order.common.domain.entity.OrderLineItem; +import kitchenpos.order.common.domain.entity.OrderStatus; +import kitchenpos.order.common.domain.exception.OrderHideMenuException; +import kitchenpos.order.common.domain.exception.OrderMenuInvalidException; +import kitchenpos.order.common.domain.exception.OrderPriceInvalidException; +import kitchenpos.order.common.domain.exception.OrderTypeInvalidException; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItems; +import kitchenpos.order.common.domain.model.OrderVo; +import kitchenpos.order.common.domain.model.OrderVo.Create; +import kitchenpos.order.common.domain.model.OrderVo.OrderInfo; +import kitchenpos.order.common.domain.repository.OrderRepository; +import kitchenpos.order.delivery.domain.service.DeliveryService; +import kitchenpos.order.eatin.domain.service.EatInService; +import kitchenpos.order.takeout.domain.service.TakeoutService; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@Service +public class DefaultOrderService implements OrderQueryService, OrderCommandService { + + private final OrderRepository orderRepository; + private final DeliveryService deliveryService; + private final EatInService eatinService; + private final TakeoutService takeoutService; + + private final MenuContextProvider menuContextProvider; + + public DefaultOrderService( + OrderRepository orderRepository, + DeliveryService deliveryService, + EatInService eatinService, + TakeoutService takeoutService, + MenuContextProvider menuContextProvider + ) { + this.orderRepository = orderRepository; + this.deliveryService = deliveryService; + this.eatinService = eatinService; + this.takeoutService = takeoutService; + this.menuContextProvider = menuContextProvider; + } + + @Override + public OrderVo.OrderInfo create(OrderVo.Create request) { + final OrderId orderId = OrderId.of(UUID.randomUUID()); + OrderLineItems orderLineItems = validateOrderLineItems(orderId, request); + + Order order = switch (request.type()) { + case EAT_IN -> eatinService.createEatInOrder(orderId, request, orderLineItems); + case DELIVERY -> deliveryService.createDeliveryOrder(orderId, request, orderLineItems); + case TAKEOUT -> takeoutService.createTakeoutOrder(orderId, orderLineItems); + default -> throw new OrderTypeInvalidException(); + }; + + return OrderVo.OrderInfo.fromEntity(orderRepository.save(order)); + } + + private OrderLineItems validateOrderLineItems(OrderId orderId, Create request) { + var menuIds = request.orderLineItems().getItems().stream() + .map(OrderLineItem::getMenuId) + .toList(); + + var menuInfos = menuContextProvider.findMenus(menuIds); + + if (menuInfos.size() != menuIds.size()) { + throw new OrderMenuInvalidException(); + } + + var orderLineItems = request.orderLineItems().getItems().stream() + .map(item -> { + MenuInfo menuInfo = menuInfos.stream() + .filter(m -> m.id().equals(item.getMenuId())) + .findFirst() + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_MENU.toString())); + + validateMenu(menuInfo, item); + + return new OrderLineItem(menuInfo.id(), orderId, item.getQuantity(), item.getPrice()); + }) + .toList(); + + return new OrderLineItems(orderLineItems); + } + + private void validateMenu(MenuInfo menuInfo, OrderLineItem item) { + if (!menuInfo.displayed()) { + throw new OrderHideMenuException(); + } + if (!menuInfo.price().isEqual(item.getPrice())) { + throw new OrderPriceInvalidException(); + } + } + + @Override + public OrderVo.OrderInfo accept(OrderId orderId) { + final Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_ORDER.toString())); + + order.validateIsWaiting(); + + if (order.isDelivery()) { + deliveryService.requestDelivery(orderId, order.getOrderLineItems()); + } + + order.updateOrderStatus(OrderStatus.ACCEPTED); + return OrderVo.OrderInfo.fromEntity(order); + } + + @Override + public OrderVo.OrderInfo serve(final OrderId orderId) { + final Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_ORDER.toString())); + + order.validateIsAccepted(); + order.updateOrderStatus(OrderStatus.SERVED); + return OrderVo.OrderInfo.fromEntity(order); + } + + @Override + public OrderInfo complete(OrderId orderId) { + final Order order = orderRepository.findById(orderId) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_ORDER.toString())); + + order.validateOrderCompletion(); + order.updateOrderStatus(OrderStatus.COMPLETED); + + if (order.isEatIn()) { + eatinService.complete(order.getOrderTableId()); + } + + return OrderVo.OrderInfo.fromEntity(order); + } + + + @Override + @Transactional(readOnly = true) + public List findAll() { + return orderRepository.findAll() + .stream() + .map(OrderInfo::fromEntity) + .toList(); + } +} + diff --git a/src/main/java/kitchenpos/order/common/domain/service/OrderCommandService.java b/src/main/java/kitchenpos/order/common/domain/service/OrderCommandService.java new file mode 100644 index 000000000..10dbabc31 --- /dev/null +++ b/src/main/java/kitchenpos/order/common/domain/service/OrderCommandService.java @@ -0,0 +1,13 @@ +package kitchenpos.order.common.domain.service; + +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderVo; +import kitchenpos.order.common.domain.model.OrderVo.Create; + +public interface OrderCommandService { + OrderVo.OrderInfo create(Create request); + OrderVo.OrderInfo accept(final OrderId orderId); + OrderVo.OrderInfo serve(final OrderId orderId); + OrderVo.OrderInfo complete(final OrderId orderId); + +} diff --git a/src/main/java/kitchenpos/order/common/domain/service/OrderQueryService.java b/src/main/java/kitchenpos/order/common/domain/service/OrderQueryService.java new file mode 100644 index 000000000..97baff83a --- /dev/null +++ b/src/main/java/kitchenpos/order/common/domain/service/OrderQueryService.java @@ -0,0 +1,9 @@ +package kitchenpos.order.common.domain.service; + +import java.util.List; +import kitchenpos.order.common.domain.model.OrderVo; + +public interface OrderQueryService { + List findAll(); + +} diff --git a/src/main/java/kitchenpos/order/common/domain/service/OrderService.java b/src/main/java/kitchenpos/order/common/domain/service/OrderService.java deleted file mode 100644 index 9a6262a67..000000000 --- a/src/main/java/kitchenpos/order/common/domain/service/OrderService.java +++ /dev/null @@ -1,15 +0,0 @@ -package kitchenpos.order.common.domain.service; - -import java.util.List; -import java.util.UUID; -import kitchenpos.order.common.domain.entity.Order; - -public interface OrderService { - Order create(final Order request); - Order accept(final UUID orderId); - Order serve(final UUID orderId); - Order startDelivery(final UUID orderId); - Order completeDelivery(final UUID orderId); - Order complete(final UUID orderId); - List findAll(); -} diff --git a/src/main/java/kitchenpos/order/common/domain/service/OrderServiceImpl.java b/src/main/java/kitchenpos/order/common/domain/service/OrderServiceImpl.java deleted file mode 100644 index 05584b7f5..000000000 --- a/src/main/java/kitchenpos/order/common/domain/service/OrderServiceImpl.java +++ /dev/null @@ -1,199 +0,0 @@ -package kitchenpos.order.common.domain.service; - -import java.math.BigDecimal; -import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.UUID; -import kitchenpos.menu.domain.entity.Menu; -import kitchenpos.menu.domain.repository.MenuRepository; -import kitchenpos.order.common.domain.entity.Order; -import kitchenpos.order.common.domain.entity.OrderLineItem; -import kitchenpos.order.common.domain.entity.OrderStatus; -import kitchenpos.order.common.domain.entity.OrderType; -import kitchenpos.order.common.domain.repository.OrderRepository; -import kitchenpos.order.common.domain.repository.OrderTableRepository; -import kitchenpos.order.delivery.domain.service.DeliveryKitchenridersClient; -import kitchenpos.order.eatin.domain.entity.OrderTable; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -// TODO: 바운디드 컨텍스트 따라 분리해야함. -@Service -public class OrderServiceImpl implements OrderService { - - private final OrderRepository orderRepository; - private final MenuRepository menuRepository; - private final OrderTableRepository orderTableRepository; - private final DeliveryKitchenridersClient deliveryKitchenridersClient; - - public OrderServiceImpl( - final OrderRepository orderRepository, - final MenuRepository menuRepository, - final OrderTableRepository orderTableRepository, - final DeliveryKitchenridersClient deliveryKitchenridersClient - ) { - this.orderRepository = orderRepository; - this.menuRepository = menuRepository; - this.orderTableRepository = orderTableRepository; - this.deliveryKitchenridersClient = deliveryKitchenridersClient; - } - - @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.findAllByMenuIdIn( - 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.findByMenuId(orderLineItemRequest.getMenuId()) - .orElseThrow(NoSuchElementException::new); - if (!menu.isDisplayed()) { - throw new IllegalStateException(); - } - - if (!menu.isPriceEqual(orderLineItemRequest.getPrice())) { - throw new IllegalArgumentException(); - } - final OrderLineItem orderLineItem = new OrderLineItem(); - orderLineItem.setMenuId(menu.getMenuId()); - 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 accept(final UUID orderId) { - final Order order = orderRepository.findById(orderId) - .orElseThrow(NoSuchElementException::new); - if (order.getStatus() != OrderStatus.WAITING) { - throw new IllegalStateException(); - } - if (order.getType() == OrderType.DELIVERY) { - BigDecimal sum = BigDecimal.ZERO; - - // TODO: step3 보완 -// for (final OrderLineItem orderLineItem : order.getOrderLineItems()) { -// sum = orderLineItem.getMenu() -// .getPrice() -// .price() -// .multiply(BigDecimal.valueOf(orderLineItem.getQuantity())); -// } -// deliveryKitchenridersClient.requestDelivery(orderId, sum, order.getDeliveryAddress()); - } - order.setStatus(OrderStatus.ACCEPTED); - return order; - } - - @Transactional - public Order serve(final UUID orderId) { - final Order order = 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) - .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) - .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) - .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(); - } -} diff --git a/src/main/java/kitchenpos/order/common/infrastructure/persistence/JpaOrderRepository.java b/src/main/java/kitchenpos/order/common/infrastructure/persistence/JpaOrderRepository.java index c4474636b..0501548f2 100644 --- a/src/main/java/kitchenpos/order/common/infrastructure/persistence/JpaOrderRepository.java +++ b/src/main/java/kitchenpos/order/common/infrastructure/persistence/JpaOrderRepository.java @@ -1,10 +1,10 @@ package kitchenpos.order.common.infrastructure.persistence; -import java.util.UUID; import kitchenpos.order.common.domain.entity.Order; +import kitchenpos.order.common.domain.model.OrderId; import kitchenpos.order.common.domain.repository.OrderRepository; import org.springframework.data.jpa.repository.JpaRepository; -public interface JpaOrderRepository extends OrderRepository, JpaRepository { +public interface JpaOrderRepository extends OrderRepository, JpaRepository { } diff --git a/src/main/java/kitchenpos/order/common/infrastructure/persistence/JpaOrderTableRepository.java b/src/main/java/kitchenpos/order/common/infrastructure/persistence/JpaOrderTableRepository.java index f60d1b068..e448d14f8 100644 --- a/src/main/java/kitchenpos/order/common/infrastructure/persistence/JpaOrderTableRepository.java +++ b/src/main/java/kitchenpos/order/common/infrastructure/persistence/JpaOrderTableRepository.java @@ -1,11 +1,11 @@ package kitchenpos.order.common.infrastructure.persistence; -import java.util.UUID; -import kitchenpos.order.eatin.domain.entity.OrderTable; import kitchenpos.order.common.domain.repository.OrderTableRepository; +import kitchenpos.order.eatin.domain.entity.OrderTable; +import kitchenpos.order.eatin.domain.model.OrderTableId; import org.springframework.data.jpa.repository.JpaRepository; public interface JpaOrderTableRepository extends OrderTableRepository, - JpaRepository { + JpaRepository { } diff --git a/src/main/java/kitchenpos/order/common/presentation/controller/OrderRestController.java b/src/main/java/kitchenpos/order/common/presentation/controller/OrderRestController.java index 2a5822edc..d29e06eb5 100644 --- a/src/main/java/kitchenpos/order/common/presentation/controller/OrderRestController.java +++ b/src/main/java/kitchenpos/order/common/presentation/controller/OrderRestController.java @@ -1,10 +1,12 @@ package kitchenpos.order.common.presentation.controller; +import jakarta.validation.Valid; import java.net.URI; import java.util.List; import java.util.UUID; -import kitchenpos.order.common.domain.service.OrderService; -import kitchenpos.order.common.domain.entity.Order; +import kitchenpos.order.common.application.dto.OrderRequest; +import kitchenpos.order.common.application.dto.OrderResponse; +import kitchenpos.order.common.application.facade.OrderFacade; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -18,46 +20,46 @@ @RestController public class OrderRestController { - private final OrderService orderService; + private final OrderFacade orderFacade; - public OrderRestController(final OrderService orderService) { - this.orderService = orderService; + public OrderRestController(final OrderFacade orderFacade) { + this.orderFacade = orderFacade; } @PostMapping - public ResponseEntity create(@RequestBody final Order request) { - final Order response = orderService.create(request); - return ResponseEntity.created(URI.create("/api/orders/" + response.getId())) + public ResponseEntity create( + @RequestBody @Valid final OrderRequest.Create request + ) { + final OrderResponse.GetOrder response = orderFacade.create(request); + return ResponseEntity.created(URI.create("/api/orders/" + response.id())) .body(response); } - @PutMapping("/{orderId}/accept") - public ResponseEntity accept(@PathVariable final UUID orderId) { - return ResponseEntity.ok(orderService.accept(orderId)); + public ResponseEntity accept(@PathVariable final UUID orderId) { + return ResponseEntity.ok(orderFacade.accept(orderId)); } - @PutMapping("/{orderId}/serve") - public ResponseEntity serve(@PathVariable final UUID orderId) { - return ResponseEntity.ok(orderService.serve(orderId)); + public ResponseEntity serve(@PathVariable final UUID orderId) { + return ResponseEntity.ok(orderFacade.serve(orderId)); } @PutMapping("/{orderId}/start-delivery") - public ResponseEntity startDelivery(@PathVariable final UUID orderId) { - return ResponseEntity.ok(orderService.startDelivery(orderId)); + public ResponseEntity startDelivery(@PathVariable final UUID orderId) { + return ResponseEntity.ok(orderFacade.startDelivery(orderId)); } @PutMapping("/{orderId}/complete-delivery") - public ResponseEntity completeDelivery(@PathVariable final UUID orderId) { - return ResponseEntity.ok(orderService.completeDelivery(orderId)); + public ResponseEntity completeDelivery(@PathVariable final UUID orderId) { + return ResponseEntity.ok(orderFacade.completeDelivery(orderId)); } @PutMapping("/{orderId}/complete") - public ResponseEntity complete(@PathVariable final UUID orderId) { - return ResponseEntity.ok(orderService.complete(orderId)); + public ResponseEntity complete(@PathVariable final UUID orderId) { + return ResponseEntity.ok(orderFacade.complete(orderId)); } @GetMapping - public ResponseEntity> findAll() { - return ResponseEntity.ok(orderService.findAll()); + public ResponseEntity> findAll() { + return ResponseEntity.ok(orderFacade.findAll()); } } diff --git a/src/main/java/kitchenpos/order/common/presentation/controller/OrderTableRestController.java b/src/main/java/kitchenpos/order/common/presentation/controller/OrderTableRestController.java index 0f3a66e0a..dcc7b480a 100644 --- a/src/main/java/kitchenpos/order/common/presentation/controller/OrderTableRestController.java +++ b/src/main/java/kitchenpos/order/common/presentation/controller/OrderTableRestController.java @@ -3,8 +3,10 @@ import java.net.URI; import java.util.List; import java.util.UUID; -import kitchenpos.order.eatin.domain.service.OrderTableService; -import kitchenpos.order.eatin.domain.entity.OrderTable; +import kitchenpos.order.common.application.dto.OrderTableRequest; +import kitchenpos.order.common.application.dto.OrderTableRequest.UpdateGuests; +import kitchenpos.order.common.application.dto.OrderTableResponse; +import kitchenpos.order.common.application.facade.OrderFacade; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; @@ -18,39 +20,41 @@ @RestController public class OrderTableRestController { - private final OrderTableService orderTableService; + private final OrderFacade orderFacade; - public OrderTableRestController(final OrderTableService orderTableService) { - this.orderTableService = orderTableService; + public OrderTableRestController( + final OrderFacade orderFacade + ) { + this.orderFacade = orderFacade; } @PostMapping - public ResponseEntity create(@RequestBody final OrderTable request) { - final OrderTable response = orderTableService.create(request); - return ResponseEntity.created(URI.create("/api/order-tables/" + response.getId())) + public ResponseEntity create(@RequestBody final OrderTableRequest.Create request) { + final OrderTableResponse.GetOrderTable response = orderFacade.createOrderTable(request); + return ResponseEntity.created(URI.create("/api/order-tables/" + response.id())) .body(response); } @PutMapping("/{orderTableId}/sit") - public ResponseEntity sit(@PathVariable final UUID orderTableId) { - return ResponseEntity.ok(orderTableService.sit(orderTableId)); + public ResponseEntity sit(@PathVariable final UUID orderTableId) { + return ResponseEntity.ok(orderFacade.sit(orderTableId)); } @PutMapping("/{orderTableId}/clear") - public ResponseEntity clear(@PathVariable final UUID orderTableId) { - return ResponseEntity.ok(orderTableService.clear(orderTableId)); + public ResponseEntity clear(@PathVariable final UUID orderTableId) { + return ResponseEntity.ok(orderFacade.clear(orderTableId)); } @PutMapping("/{orderTableId}/number-of-guests") - public ResponseEntity changeNumberOfGuests( + public ResponseEntity changeNumberOfGuests( @PathVariable final UUID orderTableId, - @RequestBody final OrderTable request + @RequestBody final OrderTableRequest.UpdateGuests request ) { - return ResponseEntity.ok(orderTableService.changeNumberOfGuests(orderTableId, request)); + return ResponseEntity.ok(orderFacade.changeNumberOfGuests(new UpdateGuests(orderTableId, request.numberOfGuests()))); } @GetMapping - public ResponseEntity> findAll() { - return ResponseEntity.ok(orderTableService.findAll()); + public ResponseEntity> findAll() { + return ResponseEntity.ok(orderFacade.findOrderTableAll()); } } diff --git a/src/main/java/kitchenpos/order/delivery/application/DeliveryUsecase.java b/src/main/java/kitchenpos/order/delivery/application/DeliveryUsecase.java deleted file mode 100644 index 080d5fc27..000000000 --- a/src/main/java/kitchenpos/order/delivery/application/DeliveryUsecase.java +++ /dev/null @@ -1,5 +0,0 @@ -package kitchenpos.order.delivery.application; - -public interface DeliveryUsecase { - -} diff --git a/src/main/java/kitchenpos/order/delivery/domain/dto/DeliveryDto.java b/src/main/java/kitchenpos/order/delivery/domain/dto/DeliveryDto.java deleted file mode 100644 index 1bb428474..000000000 --- a/src/main/java/kitchenpos/order/delivery/domain/dto/DeliveryDto.java +++ /dev/null @@ -1,5 +0,0 @@ -package kitchenpos.order.delivery.domain.dto; - -public record DeliveryDto() { - -} diff --git a/src/main/java/kitchenpos/order/delivery/domain/entity/DeliveryOrder.java b/src/main/java/kitchenpos/order/delivery/domain/entity/DeliveryOrder.java new file mode 100644 index 000000000..9b899382a --- /dev/null +++ b/src/main/java/kitchenpos/order/delivery/domain/entity/DeliveryOrder.java @@ -0,0 +1,70 @@ +package kitchenpos.order.delivery.domain.entity; + +import jakarta.persistence.Column; +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import java.time.LocalDateTime; +import kitchenpos.order.common.domain.entity.Order; +import kitchenpos.order.common.domain.entity.OrderStatus; +import kitchenpos.order.common.domain.entity.OrderType; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItems; +import kitchenpos.order.common.domain.model.OrderVo.Create; +import kitchenpos.order.delivery.domain.model.DeliveryInfo; + +@Entity +@DiscriminatorValue("DELIVERY") +public class DeliveryOrder extends Order { + + @Column(name = "delivery_address") + private String deliveryAddress; + + protected DeliveryOrder() {} + + public DeliveryOrder(OrderId orderId, OrderType orderType, OrderStatus orderStatus, OrderLineItems orderLineItems, DeliveryInfo deliveryInfo) { + super(orderId, orderType, orderStatus, LocalDateTime.now(), orderLineItems, null); + this.deliveryAddress = deliveryInfo.address(); + } + + public DeliveryOrder(OrderId orderId, OrderLineItems orderLineItems, DeliveryInfo deliveryInfo) { + super(orderId, OrderType.DELIVERY, OrderStatus.WAITING, LocalDateTime.now(), orderLineItems, null); + this.deliveryAddress = deliveryInfo.address(); + } + + public static DeliveryOrder createDeliveryOrder(OrderId orderId, Create request, OrderLineItems orderLineItems) { + final var deliveryAddress = DeliveryInfo.of(request.deliveryAddress()); + + return new DeliveryOrder(orderId, orderLineItems, deliveryAddress); + } + + public String getDeliveryAddress() { + return deliveryAddress; + } + + public boolean isDelivery() { + return this.getType() == OrderType.DELIVERY; + } + + public boolean isServed() { + return this.getStatus() == OrderStatus.SERVED; + } + + public boolean isDelivering() { + return this.getStatus() == OrderStatus.DELIVERING; + } + + public void validateStartDeliveryOrder() { + if (!isDelivery()) { + throw new IllegalStateException("배달 주문이 아닙니다."); + } + if (!isServed()) { + throw new IllegalStateException("주문이 준비되지 않았습니다."); + } + } + + public void validateCompleteDeliveryOrder() { + if (!isDelivering()) { + throw new IllegalStateException("배달이 시작되지 않았습니다."); + } + } +} diff --git a/src/main/java/kitchenpos/order/delivery/domain/model/DeliveryInfo.java b/src/main/java/kitchenpos/order/delivery/domain/model/DeliveryInfo.java new file mode 100644 index 000000000..fa4856247 --- /dev/null +++ b/src/main/java/kitchenpos/order/delivery/domain/model/DeliveryInfo.java @@ -0,0 +1,19 @@ +package kitchenpos.order.delivery.domain.model; + +import jakarta.persistence.Embeddable; +import kitchenpos.order.delivery.exception.DeliveryInfoException; + +@Embeddable +public record DeliveryInfo(String address) { + + public static DeliveryInfo of(String address) { + if (address == null || address.isEmpty()) { + throw new DeliveryInfoException(); + } + return new DeliveryInfo(address); + } + + public String get() { + return address; + } +} diff --git a/src/main/java/kitchenpos/order/delivery/domain/repository/DeliveryOrderRepository.java b/src/main/java/kitchenpos/order/delivery/domain/repository/DeliveryOrderRepository.java new file mode 100644 index 000000000..d784c6c65 --- /dev/null +++ b/src/main/java/kitchenpos/order/delivery/domain/repository/DeliveryOrderRepository.java @@ -0,0 +1,10 @@ +package kitchenpos.order.delivery.domain.repository; + +import java.util.Optional; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.delivery.domain.entity.DeliveryOrder; + +public interface DeliveryOrderRepository { + Optional findById(OrderId id); +} + diff --git a/src/main/java/kitchenpos/order/delivery/domain/service/DefaultDeliveryService.java b/src/main/java/kitchenpos/order/delivery/domain/service/DefaultDeliveryService.java new file mode 100644 index 000000000..1a5da1cb4 --- /dev/null +++ b/src/main/java/kitchenpos/order/delivery/domain/service/DefaultDeliveryService.java @@ -0,0 +1,71 @@ +package kitchenpos.order.delivery.domain.service; + +import java.math.BigDecimal; +import kitchenpos.global.exception.ErrorCode; +import kitchenpos.global.exception.NotFoundException; +import kitchenpos.order.common.application.MenuContextProvider; +import kitchenpos.order.common.domain.entity.OrderStatus; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItems; +import kitchenpos.order.common.domain.model.OrderVo; +import kitchenpos.order.common.domain.model.OrderVo.Create; +import kitchenpos.order.delivery.domain.entity.DeliveryOrder; +import kitchenpos.order.delivery.domain.repository.DeliveryOrderRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +public class DefaultDeliveryService implements DeliveryService { + + private final DeliveryOrderRepository deliveryRepository; + private final DeliveryKitchenridersClient deliveryKitchenridersClient; + private final MenuContextProvider menuContextProvider; + + public DefaultDeliveryService( + DeliveryOrderRepository deliveryRepository, + DeliveryKitchenridersClient deliveryKitchenridersClient, + MenuContextProvider menuContextProvider + ) { + this.deliveryRepository = deliveryRepository; + this.deliveryKitchenridersClient = deliveryKitchenridersClient; + this.menuContextProvider = menuContextProvider; + } + + @Transactional + public OrderVo.OrderInfo startDelivery(final OrderId orderId) { + final DeliveryOrder order = deliveryRepository.findById(orderId) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_ORDER.toString())); + + order.validateStartDeliveryOrder(); + order.updateOrderStatus(OrderStatus.DELIVERING); + return OrderVo.OrderInfo.fromEntity(order); + } + + @Transactional + public OrderVo.OrderInfo completeDelivery(final OrderId orderId) { + final DeliveryOrder order = deliveryRepository.findById(orderId) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_ORDER.toString())); + + order.validateCompleteDeliveryOrder(); + order.updateOrderStatus(OrderStatus.DELIVERED); + return OrderVo.OrderInfo.fromEntity(order); + } + + @Override + public void requestDelivery(OrderId orderId, OrderLineItems items) { + final DeliveryOrder order = deliveryRepository.findById(orderId) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_ORDER.toString())); + + BigDecimal sum = order.getOrderLineItems().getItems().stream() + .map(orderLineItem -> menuContextProvider.getPrice(orderLineItem.getMenuId()) + .multiply(BigDecimal.valueOf(orderLineItem.getQuantity().get()))) + .reduce(BigDecimal.ZERO, BigDecimal::add); + + deliveryKitchenridersClient.requestDelivery(orderId.get(), sum, order.getDeliveryAddress()); + } + + @Override + public DeliveryOrder createDeliveryOrder(OrderId orderId, Create request, OrderLineItems orderLineItems) { + return DeliveryOrder.createDeliveryOrder(orderId, request, orderLineItems); + } +} diff --git a/src/main/java/kitchenpos/order/delivery/domain/service/DeliveryService.java b/src/main/java/kitchenpos/order/delivery/domain/service/DeliveryService.java index 21f88fabd..a80271716 100644 --- a/src/main/java/kitchenpos/order/delivery/domain/service/DeliveryService.java +++ b/src/main/java/kitchenpos/order/delivery/domain/service/DeliveryService.java @@ -1,9 +1,13 @@ package kitchenpos.order.delivery.domain.service; -import kitchenpos.order.delivery.application.DeliveryUsecase; -import org.springframework.stereotype.Service; - -@Service -public class DeliveryService implements DeliveryUsecase { +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItems; +import kitchenpos.order.common.domain.model.OrderVo; +import kitchenpos.order.delivery.domain.entity.DeliveryOrder; +public interface DeliveryService { + OrderVo.OrderInfo startDelivery(final OrderId orderId); + OrderVo.OrderInfo completeDelivery(final OrderId orderId); + void requestDelivery(final OrderId orderId, OrderLineItems items); + DeliveryOrder createDeliveryOrder(OrderId orderId, OrderVo.Create request, OrderLineItems orderLineItems); } diff --git a/src/main/java/kitchenpos/order/delivery/exception/DeliveryInfoException.java b/src/main/java/kitchenpos/order/delivery/exception/DeliveryInfoException.java new file mode 100644 index 000000000..ea38e88ad --- /dev/null +++ b/src/main/java/kitchenpos/order/delivery/exception/DeliveryInfoException.java @@ -0,0 +1,9 @@ +package kitchenpos.order.delivery.exception; + +import kitchenpos.global.exception.ErrorCode; + +public class DeliveryInfoException extends IllegalArgumentException { + public DeliveryInfoException() { + super(ErrorCode.DELIVERY_ADDRESS_NOT_ALLOWED.toString()); + } +} diff --git a/src/main/java/kitchenpos/order/delivery/infrastructure/persistence/JpaDeliveryOrderRepository.java b/src/main/java/kitchenpos/order/delivery/infrastructure/persistence/JpaDeliveryOrderRepository.java new file mode 100644 index 000000000..1dd533b4c --- /dev/null +++ b/src/main/java/kitchenpos/order/delivery/infrastructure/persistence/JpaDeliveryOrderRepository.java @@ -0,0 +1,10 @@ +package kitchenpos.order.delivery.infrastructure.persistence; + +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.delivery.domain.entity.DeliveryOrder; +import kitchenpos.order.delivery.domain.repository.DeliveryOrderRepository; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface JpaDeliveryOrderRepository extends DeliveryOrderRepository, JpaRepository { + +} diff --git a/src/main/java/kitchenpos/order/eatin/application/EatinUsecase.java b/src/main/java/kitchenpos/order/eatin/application/EatinUsecase.java deleted file mode 100644 index 3b441c498..000000000 --- a/src/main/java/kitchenpos/order/eatin/application/EatinUsecase.java +++ /dev/null @@ -1,5 +0,0 @@ -package kitchenpos.order.eatin.application; - -public interface EatinUsecase { - -} diff --git a/src/main/java/kitchenpos/order/eatin/domain/dto/EatinDto.java b/src/main/java/kitchenpos/order/eatin/domain/dto/EatinDto.java deleted file mode 100644 index fe6ba4d5a..000000000 --- a/src/main/java/kitchenpos/order/eatin/domain/dto/EatinDto.java +++ /dev/null @@ -1,5 +0,0 @@ -package kitchenpos.order.eatin.domain.dto; - -public record EatinDto() { - -} diff --git a/src/main/java/kitchenpos/order/eatin/domain/entity/OrderTable.java b/src/main/java/kitchenpos/order/eatin/domain/entity/OrderTable.java index 0ae3716ad..e5197b351 100644 --- a/src/main/java/kitchenpos/order/eatin/domain/entity/OrderTable.java +++ b/src/main/java/kitchenpos/order/eatin/domain/entity/OrderTable.java @@ -1,60 +1,73 @@ package kitchenpos.order.eatin.domain.entity; import jakarta.persistence.Column; +import jakarta.persistence.Embedded; +import jakarta.persistence.EmbeddedId; import jakarta.persistence.Entity; -import jakarta.persistence.Id; import jakarta.persistence.Table; -import java.util.UUID; +import kitchenpos.order.eatin.domain.model.OrderTableGuests; +import kitchenpos.order.eatin.domain.model.OrderTableId; +import kitchenpos.order.eatin.domain.model.OrderTableName; +import org.hibernate.annotations.DynamicUpdate; @Table(name = "order_table") @Entity +@DynamicUpdate public class OrderTable { - @Column(name = "id", columnDefinition = "binary(16)") - @Id - private UUID id; + @EmbeddedId + private OrderTableId orderTableId; - @Column(name = "name", nullable = false) - private String name; + @Embedded + private OrderTableName name; - @Column(name = "number_of_guests", nullable = false) - private int numberOfGuests; + @Embedded + private OrderTableGuests numberOfGuests; @Column(name = "occupied", nullable = false) private boolean occupied; - public OrderTable() { + protected OrderTable() {} + + public OrderTableId getOrderTableId() { + return orderTableId; } - public UUID getId() { - return id; + public OrderTableName getName() { + return name; } - public void setId(final UUID id) { - this.id = id; + public OrderTableGuests getNumberOfGuests() { + return numberOfGuests; } - public String getName() { - return name; + public boolean isOccupied() { + return occupied; } - public void setName(final String name) { + public OrderTable(OrderTableId orderTableId, OrderTableName name, OrderTableGuests numberOfGuests, boolean occupied) { + this.orderTableId = orderTableId; this.name = name; + this.numberOfGuests = numberOfGuests; + this.occupied = occupied; } - public int getNumberOfGuests() { - return numberOfGuests; + public void updateOccupied(boolean occupied) { + this.occupied = occupied; } - public void setNumberOfGuests(final int numberOfGuests) { - this.numberOfGuests = numberOfGuests; + public void updateNumberOfGuests(OrderTableGuests guests) { + this.numberOfGuests = guests; } - public boolean isOccupied() { - return occupied; + public void clear() { + updateNumberOfGuests(OrderTableGuests.of(0)); + updateOccupied(false); } - public void setOccupied(final boolean occupied) { - this.occupied = occupied; + public void validateOccupied(){ + if (!this.isOccupied()) { + throw new IllegalStateException(); + }; } } diff --git a/src/main/java/kitchenpos/order/eatin/domain/exception/OrderTableGuestsException.java b/src/main/java/kitchenpos/order/eatin/domain/exception/OrderTableGuestsException.java new file mode 100644 index 000000000..5b12e4448 --- /dev/null +++ b/src/main/java/kitchenpos/order/eatin/domain/exception/OrderTableGuestsException.java @@ -0,0 +1,9 @@ +package kitchenpos.order.eatin.domain.exception; + +import kitchenpos.global.exception.ErrorCode; + +public class OrderTableGuestsException extends IllegalArgumentException { + public OrderTableGuestsException() { + super(ErrorCode.ORDER_TABLE_NUMBER_OF_GUESTS_NOT_ALLOWED.toString()); + } +} diff --git a/src/main/java/kitchenpos/order/eatin/domain/exception/OrderTableNameException.java b/src/main/java/kitchenpos/order/eatin/domain/exception/OrderTableNameException.java new file mode 100644 index 000000000..d6f8bc747 --- /dev/null +++ b/src/main/java/kitchenpos/order/eatin/domain/exception/OrderTableNameException.java @@ -0,0 +1,9 @@ +package kitchenpos.order.eatin.domain.exception; + +import kitchenpos.global.exception.ErrorCode; + +public class OrderTableNameException extends IllegalArgumentException { + public OrderTableNameException() { + super(ErrorCode.ORDER_TABLE_NAME_NOT_ALLOWED.toString()); + } +} diff --git a/src/main/java/kitchenpos/order/eatin/domain/model/EatInOrder.java b/src/main/java/kitchenpos/order/eatin/domain/model/EatInOrder.java new file mode 100644 index 000000000..4af930089 --- /dev/null +++ b/src/main/java/kitchenpos/order/eatin/domain/model/EatInOrder.java @@ -0,0 +1,35 @@ +package kitchenpos.order.eatin.domain.model; + +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import jakarta.persistence.FetchType; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import java.time.LocalDateTime; +import kitchenpos.order.common.domain.entity.Order; +import kitchenpos.order.common.domain.entity.OrderStatus; +import kitchenpos.order.common.domain.entity.OrderType; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItems; +import kitchenpos.order.common.domain.model.OrderVo.Create; +import kitchenpos.order.eatin.domain.entity.OrderTable; + +@Entity +@DiscriminatorValue("EAT_IN") +public class EatInOrder extends Order { + + @ManyToOne(fetch = FetchType.LAZY) + @JoinColumn(name = "order_table_id", insertable = false, updatable = false) + private OrderTable orderTable; + + protected EatInOrder() {} + + public EatInOrder(OrderId orderId, + OrderLineItems orderLineItems, OrderTableId orderTableId) { + super(orderId, OrderType.EAT_IN, OrderStatus.WAITING, LocalDateTime.now(), orderLineItems, orderTableId); + } + + public static EatInOrder createEatInOrder(OrderId orderId, Create request, OrderLineItems orderLineItems) { + return new EatInOrder(orderId, orderLineItems, request.orderTableId()); + } +} diff --git a/src/main/java/kitchenpos/order/eatin/domain/model/OrderTableGuests.java b/src/main/java/kitchenpos/order/eatin/domain/model/OrderTableGuests.java new file mode 100644 index 000000000..5edd31cba --- /dev/null +++ b/src/main/java/kitchenpos/order/eatin/domain/model/OrderTableGuests.java @@ -0,0 +1,24 @@ +package kitchenpos.order.eatin.domain.model; + +import jakarta.persistence.Column; +import jakarta.persistence.Embeddable; +import kitchenpos.order.eatin.domain.exception.OrderTableGuestsException; + +@Embeddable +public record OrderTableGuests( + @Column(name = "number_of_guests", nullable = false) + int guests +) { + + public static OrderTableGuests of(int guests) { + if (guests < 0) { + throw new OrderTableGuestsException(); + } + return new OrderTableGuests(guests); + } + + public int get() { + return guests; + } +} + diff --git a/src/main/java/kitchenpos/order/eatin/domain/model/OrderTableId.java b/src/main/java/kitchenpos/order/eatin/domain/model/OrderTableId.java new file mode 100644 index 000000000..ebdbe2673 --- /dev/null +++ b/src/main/java/kitchenpos/order/eatin/domain/model/OrderTableId.java @@ -0,0 +1,51 @@ +package kitchenpos.order.eatin.domain.model; + +import jakarta.persistence.Embeddable; +import java.io.Serializable; +import java.util.Objects; +import java.util.UUID; +import kitchenpos.global.exception.ErrorCode; +import kitchenpos.global.exception.NotFoundException; + +@Embeddable +public class OrderTableId implements Serializable { + + private static final long serialVersionUID = -161403658033403389L; + + private UUID id; + + protected OrderTableId() {} + + public OrderTableId(UUID id) { + this.id = id; + } + + public static OrderTableId of(UUID id) { + if (id == null) { + throw new NotFoundException(ErrorCode.NOT_FOUND_ORDER_TABLE.toString()); + } + return new OrderTableId(id); + } + + + public UUID get() { + return id; + } + + @Override + public boolean equals(Object o) { + if (this == o) { + return true; + } + if (o == null || getClass() != o.getClass()) { + return false; + } + OrderTableId orderTableId = (OrderTableId) o; + return Objects.equals(id, orderTableId.id); + } + + @Override + public int hashCode() { + return Objects.hashCode(id); + } +} diff --git a/src/main/java/kitchenpos/order/eatin/domain/model/OrderTableName.java b/src/main/java/kitchenpos/order/eatin/domain/model/OrderTableName.java new file mode 100644 index 000000000..6bd9261c3 --- /dev/null +++ b/src/main/java/kitchenpos/order/eatin/domain/model/OrderTableName.java @@ -0,0 +1,19 @@ +package kitchenpos.order.eatin.domain.model; + +import jakarta.persistence.Embeddable; +import kitchenpos.order.eatin.domain.exception.OrderTableNameException; + +@Embeddable +public record OrderTableName(String name) { + + public static OrderTableName of(String name) { + if (name == null || name.isBlank()) { + throw new OrderTableNameException(); + } + return new OrderTableName(name); + } + + public String get() { + return name; + } +} diff --git a/src/main/java/kitchenpos/order/eatin/domain/model/OrderTableVo.java b/src/main/java/kitchenpos/order/eatin/domain/model/OrderTableVo.java new file mode 100644 index 000000000..d23c12132 --- /dev/null +++ b/src/main/java/kitchenpos/order/eatin/domain/model/OrderTableVo.java @@ -0,0 +1,47 @@ +package kitchenpos.order.eatin.domain.model; + +import java.util.UUID; +import kitchenpos.order.eatin.domain.entity.OrderTable; + +public record OrderTableVo() { + + public record OrderTableInfo( + OrderTableId id, + OrderTableName name, + OrderTableGuests numberOfGuests, + boolean occupied + ) { + public static OrderTableInfo fromEntity(OrderTable entity) { + return new OrderTableInfo( + entity.getOrderTableId(), + entity.getName(), + entity.getNumberOfGuests(), + entity.isOccupied() + ); + } + + public UUID getOrderTableId() { + return id.get(); + } + + public String getOrderTableName() { + return name.get(); + } + + public int getNumberOfGuests() { + return numberOfGuests.get(); + } + + } + + public record Create( + OrderTableName name, + OrderTableGuests guests, + boolean occupied + ) { + + + } + + public record Update(OrderTableId orderTableId, OrderTableGuests guests) {} +} diff --git a/src/main/java/kitchenpos/order/eatin/domain/repository/EatinOrderRepository.java b/src/main/java/kitchenpos/order/eatin/domain/repository/EatinOrderRepository.java new file mode 100644 index 000000000..76104c73e --- /dev/null +++ b/src/main/java/kitchenpos/order/eatin/domain/repository/EatinOrderRepository.java @@ -0,0 +1,9 @@ +package kitchenpos.order.eatin.domain.repository; + +import kitchenpos.order.common.domain.entity.OrderStatus; +import kitchenpos.order.eatin.domain.model.OrderTableId; + +public interface EatinOrderRepository { + boolean existsByOrderTableIdAndStatusNot(OrderTableId orderTableId, OrderStatus status); +} + diff --git a/src/main/java/kitchenpos/order/eatin/domain/service/DefaultEatinService.java b/src/main/java/kitchenpos/order/eatin/domain/service/DefaultEatinService.java new file mode 100644 index 000000000..6870f4351 --- /dev/null +++ b/src/main/java/kitchenpos/order/eatin/domain/service/DefaultEatinService.java @@ -0,0 +1,102 @@ +package kitchenpos.order.eatin.domain.service; + +import java.util.List; +import java.util.UUID; +import kitchenpos.global.exception.ErrorCode; +import kitchenpos.global.exception.NotFoundException; +import kitchenpos.order.common.domain.entity.OrderStatus; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItems; +import kitchenpos.order.common.domain.model.OrderVo; +import kitchenpos.order.common.domain.repository.OrderTableRepository; +import kitchenpos.order.eatin.domain.entity.OrderTable; +import kitchenpos.order.eatin.domain.model.EatInOrder; +import kitchenpos.order.eatin.domain.model.OrderTableId; +import kitchenpos.order.eatin.domain.model.OrderTableName; +import kitchenpos.order.eatin.domain.model.OrderTableVo.Create; +import kitchenpos.order.eatin.domain.model.OrderTableVo.OrderTableInfo; +import kitchenpos.order.eatin.domain.model.OrderTableVo.Update; +import kitchenpos.order.eatin.domain.repository.EatinOrderRepository; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Transactional +@Service +public class DefaultEatInService implements EatInService { + + private final EatinOrderRepository orderRepository; + private final OrderTableRepository orderTableRepository; + + public DefaultEatInService(EatinOrderRepository orderRepository, OrderTableRepository orderTableRepository) { + this.orderRepository = orderRepository; + this.orderTableRepository = orderTableRepository; + } + + @Transactional(readOnly = true) + @Override + public List findAll() { + return orderTableRepository.findAll() + .stream() + .map(OrderTableInfo::fromEntity) + .toList(); + } + + @Override + public OrderTableInfo create(Create request) { + final OrderTableName name = request.name(); + final var orderTableId = OrderTableId.of(UUID.randomUUID()); + return OrderTableInfo.fromEntity(orderTableRepository.save(new OrderTable(orderTableId, name, request.guests(), request.occupied()))); + } + + @Override + public OrderTableInfo sit(OrderTableId orderTableId) { + final OrderTable orderTable = orderTableRepository.findById(orderTableId) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_ORDER_TABLE.toString())); + + orderTable.updateOccupied(true); + return OrderTableInfo.fromEntity(orderTable); + } + + @Override + public OrderTableInfo clear(OrderTableId orderTableId) { + final OrderTable orderTable = orderTableRepository.findById(orderTableId) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_ORDER_TABLE.toString())); + + if (orderRepository.existsByOrderTableIdAndStatusNot(orderTableId, OrderStatus.COMPLETED)) { + throw new IllegalStateException(); + } + orderTable.clear(); + return OrderTableInfo.fromEntity(orderTable); + } + + @Override + public OrderTableInfo changeNumberOfGuests(Update request) { + final OrderTable orderTable = orderTableRepository.findById(request.orderTableId()) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_ORDER_TABLE.toString())); + + orderTable.validateOccupied(); + + orderTable.updateNumberOfGuests(request.guests()); + return OrderTableInfo.fromEntity(orderTable); + } + + @Override + public void complete(OrderTableId orderTableId) { + final OrderTable orderTable = orderTableRepository.findById(orderTableId) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_ORDER_TABLE.toString())); + + if (!orderRepository.existsByOrderTableIdAndStatusNot(orderTableId, OrderStatus.COMPLETED)) { + orderTable.clear(); + } + } + + @Override + public EatInOrder createEatInOrder(OrderId orderId, OrderVo.Create request, OrderLineItems orderLineItems) { + final OrderTable orderTable = orderTableRepository.findById(request.orderTableId()) + .orElseThrow(() -> new NotFoundException(ErrorCode.NOT_FOUND_ORDER_TABLE.toString())); + + orderTable.validateOccupied(); + + return EatInOrder.createEatInOrder(orderId, request, orderLineItems); + } +} diff --git a/src/main/java/kitchenpos/order/eatin/domain/service/EatinService.java b/src/main/java/kitchenpos/order/eatin/domain/service/EatinService.java index 97f00b25f..4a235ffda 100644 --- a/src/main/java/kitchenpos/order/eatin/domain/service/EatinService.java +++ b/src/main/java/kitchenpos/order/eatin/domain/service/EatinService.java @@ -1,9 +1,28 @@ package kitchenpos.order.eatin.domain.service; -import kitchenpos.order.eatin.application.EatinUsecase; -import org.springframework.stereotype.Service; +import java.util.List; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItems; +import kitchenpos.order.common.domain.model.OrderVo; +import kitchenpos.order.eatin.domain.model.EatInOrder; +import kitchenpos.order.eatin.domain.model.OrderTableId; +import kitchenpos.order.eatin.domain.model.OrderTableVo.Create; +import kitchenpos.order.eatin.domain.model.OrderTableVo.OrderTableInfo; +import kitchenpos.order.eatin.domain.model.OrderTableVo.Update; -@Service -public class EatinService implements EatinUsecase { +public interface EatInService { + List findAll(); + + OrderTableInfo create(Create request); + + OrderTableInfo sit(OrderTableId orderTableId); + + OrderTableInfo clear(OrderTableId orderTableId); + + OrderTableInfo changeNumberOfGuests(Update request); + + void complete(OrderTableId orderTableId); + + EatInOrder createEatInOrder(OrderId orderId, OrderVo.Create request, OrderLineItems orderLineItems); } diff --git a/src/main/java/kitchenpos/order/eatin/domain/service/OrderTableService.java b/src/main/java/kitchenpos/order/eatin/domain/service/OrderTableService.java deleted file mode 100644 index 59be1e57e..000000000 --- a/src/main/java/kitchenpos/order/eatin/domain/service/OrderTableService.java +++ /dev/null @@ -1,79 +0,0 @@ -package kitchenpos.order.eatin.domain.service; - -import java.util.List; -import java.util.NoSuchElementException; -import java.util.Objects; -import java.util.UUID; -import kitchenpos.order.common.domain.entity.OrderStatus; -import kitchenpos.order.eatin.domain.entity.OrderTable; -import kitchenpos.order.common.domain.repository.OrderRepository; -import kitchenpos.order.common.domain.repository.OrderTableRepository; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -@Service -public class OrderTableService { - - private final OrderTableRepository orderTableRepository; - private final OrderRepository orderRepository; - - public OrderTableService(final OrderTableRepository orderTableRepository, - final OrderRepository orderRepository) { - 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); - } - - @Transactional - public OrderTable sit(final UUID orderTableId) { - final OrderTable orderTable = orderTableRepository.findById(orderTableId) - .orElseThrow(NoSuchElementException::new); - orderTable.setOccupied(true); - return orderTable; - } - - @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; - } - - @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) - .orElseThrow(NoSuchElementException::new); - if (!orderTable.isOccupied()) { - throw new IllegalStateException(); - } - orderTable.setNumberOfGuests(numberOfGuests); - return orderTable; - } - - @Transactional(readOnly = true) - public List findAll() { - return orderTableRepository.findAll(); - } -} diff --git a/src/main/java/kitchenpos/order/eatin/infrastructure/persistence/JpaEatinOrderRepository.java b/src/main/java/kitchenpos/order/eatin/infrastructure/persistence/JpaEatinOrderRepository.java new file mode 100644 index 000000000..4aeb6cfb0 --- /dev/null +++ b/src/main/java/kitchenpos/order/eatin/infrastructure/persistence/JpaEatinOrderRepository.java @@ -0,0 +1,10 @@ +package kitchenpos.order.eatin.infrastructure.persistence; + +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.eatin.domain.model.EatInOrder; +import kitchenpos.order.eatin.domain.repository.EatinOrderRepository; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface JpaEatinOrderRepository extends EatinOrderRepository, JpaRepository { + +} diff --git a/src/main/java/kitchenpos/order/takeout/application/TakeoutUsecase.java b/src/main/java/kitchenpos/order/takeout/application/TakeoutUsecase.java deleted file mode 100644 index 3121e4aae..000000000 --- a/src/main/java/kitchenpos/order/takeout/application/TakeoutUsecase.java +++ /dev/null @@ -1,5 +0,0 @@ -package kitchenpos.order.takeout.application; - -public interface TakeoutUsecase { - -} diff --git a/src/main/java/kitchenpos/order/takeout/domain/dto/TakeoutDto.java b/src/main/java/kitchenpos/order/takeout/domain/dto/TakeoutDto.java deleted file mode 100644 index 58abea932..000000000 --- a/src/main/java/kitchenpos/order/takeout/domain/dto/TakeoutDto.java +++ /dev/null @@ -1,5 +0,0 @@ -package kitchenpos.order.takeout.domain.dto; - -public record TakeoutDto() { - -} diff --git a/src/main/java/kitchenpos/order/takeout/domain/model/TakeOutOrder.java b/src/main/java/kitchenpos/order/takeout/domain/model/TakeOutOrder.java new file mode 100644 index 000000000..d54e3b194 --- /dev/null +++ b/src/main/java/kitchenpos/order/takeout/domain/model/TakeOutOrder.java @@ -0,0 +1,25 @@ +package kitchenpos.order.takeout.domain.model; + +import jakarta.persistence.DiscriminatorValue; +import jakarta.persistence.Entity; +import java.time.LocalDateTime; +import kitchenpos.order.common.domain.entity.Order; +import kitchenpos.order.common.domain.entity.OrderStatus; +import kitchenpos.order.common.domain.entity.OrderType; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItems; + +@Entity +@DiscriminatorValue("TAKEOUT") +public class TakeOutOrder extends Order { + + protected TakeOutOrder() {} + + public TakeOutOrder(OrderId orderId, OrderLineItems orderLineItems) { + super(orderId, OrderType.TAKEOUT, OrderStatus.WAITING, LocalDateTime.now(), orderLineItems, null); + } + + public static TakeOutOrder createTakeoutOrder(OrderId orderId, OrderLineItems orderLineItems) { + return new TakeOutOrder(orderId, orderLineItems); + } +} diff --git a/src/main/java/kitchenpos/order/takeout/domain/service/DefaultTakeoutService.java b/src/main/java/kitchenpos/order/takeout/domain/service/DefaultTakeoutService.java new file mode 100644 index 000000000..2a71e92b0 --- /dev/null +++ b/src/main/java/kitchenpos/order/takeout/domain/service/DefaultTakeoutService.java @@ -0,0 +1,15 @@ +package kitchenpos.order.takeout.domain.service; + +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItems; +import kitchenpos.order.takeout.domain.model.TakeOutOrder; +import org.springframework.stereotype.Service; + +@Service +public class DefaultTakeoutService implements TakeoutService { + + @Override + public TakeOutOrder createTakeoutOrder(OrderId orderId, OrderLineItems orderLineItems) { + return TakeOutOrder.createTakeoutOrder(orderId, orderLineItems); + } +} diff --git a/src/main/java/kitchenpos/order/takeout/domain/service/TakeoutService.java b/src/main/java/kitchenpos/order/takeout/domain/service/TakeoutService.java index a8978f229..402f30f58 100644 --- a/src/main/java/kitchenpos/order/takeout/domain/service/TakeoutService.java +++ b/src/main/java/kitchenpos/order/takeout/domain/service/TakeoutService.java @@ -1,9 +1,9 @@ package kitchenpos.order.takeout.domain.service; -import kitchenpos.order.takeout.application.TakeoutUsecase; -import org.springframework.stereotype.Service; - -@Service -public class TakeoutService implements TakeoutUsecase { +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItems; +import kitchenpos.order.takeout.domain.model.TakeOutOrder; +public interface TakeoutService { + TakeOutOrder createTakeoutOrder(OrderId orderId, OrderLineItems orderLineItems); } diff --git a/src/test/java/kitchenpos/menu/domain/fixture/MenuFixture.java b/src/test/java/kitchenpos/menu/domain/fixture/MenuFixture.java index c3653f068..77fdc170d 100644 --- a/src/test/java/kitchenpos/menu/domain/fixture/MenuFixture.java +++ b/src/test/java/kitchenpos/menu/domain/fixture/MenuFixture.java @@ -14,7 +14,6 @@ import kitchenpos.menu.domain.model.MenuName; import kitchenpos.menu.domain.model.MenuPrice; import kitchenpos.menu.domain.model.MenuProducts; -import kitchenpos.product.domain.fixture.ProductFixture; public record MenuFixture(UUID id, String 메뉴명, BigDecimal 메뉴가격, UUID 메뉴그룹아이디, boolean 노출여부, List 메뉴구성품) { @@ -29,7 +28,7 @@ public static MenuFixture init() { DEFAULT_MENU_PRICE, MenuGroupFixture.init().toEntity().getMenuGroupId().get(), true, - java.util.List.of(MenuProductFixture.init(null).create())); + List.of(MenuProductFixture.init(null).create())); } public static MenuFixture test(String 메뉴명, BigDecimal 메뉴가격, diff --git a/src/test/java/kitchenpos/order/application/OrderFacadeTest.java b/src/test/java/kitchenpos/order/application/OrderFacadeTest.java index b66dc1c8d..12f0dfe63 100644 --- a/src/test/java/kitchenpos/order/application/OrderFacadeTest.java +++ b/src/test/java/kitchenpos/order/application/OrderFacadeTest.java @@ -2,38 +2,54 @@ import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatCode; -import static org.assertj.core.api.Assertions.assertThatExceptionOfType; +import static org.assertj.core.api.AssertionsForClassTypes.assertThatExceptionOfType; import static org.junit.jupiter.api.Assertions.assertAll; import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertFalse; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.mockito.Mockito.doNothing; import static org.mockito.Mockito.when; import java.math.BigDecimal; import java.util.List; -import java.util.NoSuchElementException; import java.util.Optional; +import java.util.UUID; +import kitchenpos.global.exception.ErrorCode; +import kitchenpos.global.exception.NotFoundException; import kitchenpos.menu.domain.entity.Menu; import kitchenpos.menu.domain.fixture.MenuFixture; +import kitchenpos.menu.domain.model.MenuVo.MenuInfo; import kitchenpos.menu.domain.repository.MenuRepository; +import kitchenpos.order.common.application.MenuContextProvider; +import kitchenpos.order.common.application.dto.OrderRequest.OrderLineItemCreate; import kitchenpos.order.common.domain.entity.Order; -import kitchenpos.order.common.domain.entity.OrderLineItem; import kitchenpos.order.common.domain.entity.OrderStatus; import kitchenpos.order.common.domain.entity.OrderType; +import kitchenpos.order.common.domain.exception.OrderHideMenuException; +import kitchenpos.order.common.domain.exception.OrderInvalidException; +import kitchenpos.order.common.domain.exception.OrderLineItemQtyException; +import kitchenpos.order.common.domain.exception.OrderMenuInvalidException; +import kitchenpos.order.common.domain.model.OrderVo; import kitchenpos.order.common.domain.repository.OrderRepository; import kitchenpos.order.common.domain.repository.OrderTableRepository; -import kitchenpos.order.common.domain.service.OrderService; -import kitchenpos.order.delivery.domain.service.DeliveryKitchenridersClient; +import kitchenpos.order.common.domain.service.DefaultOrderService; +import kitchenpos.order.delivery.domain.entity.DeliveryOrder; +import kitchenpos.order.delivery.domain.repository.DeliveryOrderRepository; +import kitchenpos.order.delivery.domain.service.DefaultDeliveryService; +import kitchenpos.order.delivery.domain.service.DeliveryService; +import kitchenpos.order.domain.fixture.DeliveryOrderFixture; import kitchenpos.order.domain.fixture.OrderFixture; import kitchenpos.order.domain.fixture.OrderLineItemFixture; import kitchenpos.order.domain.fixture.OrderTableFixture; import kitchenpos.order.eatin.domain.entity.OrderTable; +import kitchenpos.order.eatin.domain.service.DefaultEatInService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; import org.junit.jupiter.api.Test; import org.junit.jupiter.api.extension.ExtendWith; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.EmptySource; import org.junit.jupiter.params.provider.NullAndEmptySource; import org.junit.jupiter.params.provider.NullSource; import org.junit.jupiter.params.provider.ValueSource; @@ -44,28 +60,38 @@ @ExtendWith(MockitoExtension.class) class OrderFacadeTest { - @InjectMocks - private OrderService orderService; + private DefaultDeliveryService defaultDeliveryService; + @Mock + private DeliveryService deliveryService; + @Mock + private DefaultEatInService eatinService; + @InjectMocks + private DefaultOrderService orderService; @Mock private OrderRepository orderRepository; - + @Mock + private DeliveryOrderRepository deliveryOrderRepository; @Mock private MenuRepository menuRepository; - @Mock private OrderTableRepository orderTableRepository; - @Mock - private DeliveryKitchenridersClient deliveryKitchenridersClient; + private MenuContextProvider menuContextProvider; private Order order; + private DeliveryOrder deliveryOrder; + private OrderTable orderTable; + private OrderVo.Create createVo; private Menu chickenMenu; @BeforeEach void setUp() { chickenMenu = MenuFixture.init().toEntity(); - order = OrderFixture.init().create(); + order = OrderFixture.init().toEntity(); + deliveryOrder = DeliveryOrderFixture.init().toEntity(); + orderTable = OrderTableFixture.init().toEntity(); + createVo = OrderFixture.init().createVo(); } @Nested @@ -75,8 +101,8 @@ class 주문_조회 { @Test @DisplayName("성공 : 특정 조건 없이 상품의 모든 목록을 조회할 수 있다.") void 주문목록_조회() { - when(orderRepository.findAll()).thenReturn(List.of(order)); - List result = orderService.findAll(); + mockFindAllByOrder(order); + var result = orderService.findAll(); assertAll( () -> assertThat(result).isNotEmpty(), @@ -92,18 +118,23 @@ class 주문_등록 { @Test @DisplayName("성공") void 주문등록_성공() { + createVo = OrderFixture.test( + OrderType.DELIVERY, + null, + null, + List.of(OrderLineItemFixture.test(chickenMenu.getMenuId().get(), UUID.randomUUID(), OrderType.DELIVERY, 1, null).create()), + null, + UUID.randomUUID() + ).createVo(); mockCreateOrder(); - var result = orderService.create(order); + var result = orderService.create(createVo); assertAll( () -> assertNotNull(result), - () -> assertEquals(result.getType(), order.getType()), - () -> assertEquals(result.getStatus(), order.getStatus()), - () -> assertEquals(result.getOrderDateTime(), order.getOrderDateTime()), - () -> assertEquals(result.getOrderLineItems(), order.getOrderLineItems()), - () -> assertEquals(result.getDeliveryAddress(), order.getDeliveryAddress()), - () -> assertEquals(result.getOrderTable(), order.getOrderTable()) + () -> assertEquals(result.type(), order.getType()), + () -> assertEquals(result.status(), order.getStatus()), + () -> assertEquals(result.orderDateTime(), order.getOrderDateTime()) ); } @@ -112,78 +143,92 @@ class 주문_등록 { @DisplayName("배달, 먹고가기, 포장(주문 유형)이 반드시 있어야 한다.") @NullSource void 주문유형_있는지_검사(final OrderType orderType) { - order = OrderFixture.test( - orderType, - null, - null, - null, - null, - null - ).create(); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> orderService.create(order)); + .isThrownBy(() -> { + createVo = OrderFixture.test( + orderType, + OrderStatus.DELIVERED, + null, + null, + null, + OrderTableFixture.init().toEntity().getOrderTableId().get() + ).createVo(); + orderService.create(createVo); + }); } @ParameterizedTest @DisplayName("주문 아이템이 반드시 있어야 한다.") - @NullAndEmptySource - void 주문아이템_있는지_검사(final List orderLineItems) { - order = OrderFixture.test( - OrderType.DELIVERY, - null, - null, - orderLineItems, - null, - null - ).create(); - - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> orderService.create(order)); + @EmptySource + void 주문아이템_있는지_검사(final List orderLineItems) { + assertThatExceptionOfType(NotFoundException.class) + .isThrownBy(() -> { + createVo = OrderFixture.test( + OrderType.DELIVERY, + null, + null, + orderLineItems, + null, + UUID.randomUUID() + ).createVo(); + }) + .withMessage(ErrorCode.NOT_FOUND_ORDER_ITEM.toString()); } @ParameterizedTest @DisplayName("주문 아이템의 수량은 0개 이상이어야 한다.") @ValueSource(ints = {-100, 0, 100}) void 주문아이템_수량이_0개이상_인지_검사(final int qty) { - order = OrderFixture.test( - OrderType.DELIVERY, - null, - null, - List.of(OrderLineItemFixture.test( - null, - qty, - null - ).create()), - null, - OrderTableFixture.init().create() - ).create(); - if (qty < 0) { - mockFindAllByMenu(order); - assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> orderService.create(order)); + assertThatExceptionOfType(OrderLineItemQtyException.class) + .isThrownBy(() -> { + createVo = OrderFixture.test( + OrderType.DELIVERY, + null, + null, + List.of(OrderLineItemFixture.test( + chickenMenu.getMenuId().get(), + UUID.randomUUID(), + OrderType.DELIVERY, + qty, + null + ).create()), + null, + OrderTableFixture.init().toEntity().getOrderTableId().get() + ).createVo(); + + mockFindMenus(List.of(MenuInfo.fromEntity(chickenMenu))); + orderService.create(createVo); + }); } } @Test @DisplayName("주문 아이템의 메뉴는 반드시 있어야 한다.") void 주문아이템의_메뉴가_존재하는지_검사() { - mockFindAllByMenu(order); - assertThatExceptionOfType(NoSuchElementException.class) - .isThrownBy(() -> orderService.create(order)); + assertThatExceptionOfType(OrderMenuInvalidException.class) + .isThrownBy(() -> orderService.create(createVo)); } @Test @DisplayName("메뉴가 노출된 상태여야 한다.") void 메뉴가_노출상태인지_검사() { + createVo = OrderFixture.test( + null, + null, + null, + List.of(OrderLineItemFixture.test(chickenMenu.getMenuId().get(), null, null, 1, null).create()), + null, + OrderTableFixture.init().toEntity().getOrderTableId().get() + ).createVo(); + chickenMenu.updateDisplayed(false); - mockFindAllByMenu(order); - mockFindByMenu(chickenMenu); + mockFindMenus(List.of(MenuInfo.fromEntity(chickenMenu))); - assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> orderService.create(order)); + assertThatExceptionOfType(OrderHideMenuException.class) + .isThrownBy(() -> orderService.create(createVo)) + .withMessage(ErrorCode.ORDER_HIDE_MENU_NOT_ALLOWED.toString()); } @Test @@ -192,42 +237,53 @@ class 주문_등록 { chickenMenu = MenuFixture.test(null, BigDecimal.valueOf(500_000), null, true, null) .toEntity(); - mockFindAllByMenu(order); - - mockFindByMenu(chickenMenu); + createVo = OrderFixture.test( + null, + null, + null, + List.of(OrderLineItemFixture.test(chickenMenu.getMenuId().get(), null, null, 1, null).create()), + null, + OrderTableFixture.init().toEntity().getOrderTableId().get() + ).createVo(); assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> orderService.create(order)); + .isThrownBy(() -> orderService.create(createVo)); } @ParameterizedTest @DisplayName("배달 주문시 배달 주소가 반드시 있어야 한다.") @NullAndEmptySource void 배달일경우_배달지주소_여부검사(final String address) { - order.setDeliveryAddress(address); - - mockFindAllByMenu(order); - - mockFindByMenu(chickenMenu); + createVo = OrderFixture.test( + OrderType.DELIVERY, + null, + null, + null, + address, + OrderTableFixture.init().toEntity().getOrderTableId().get() + ).createVo(); assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> orderService.create(order)); + .isThrownBy(() -> orderService.create(createVo)); } @Test @DisplayName("먹고가기(주문유형)의 경우 주문 테이블내역이 있어야 한다.") void 먹고가기일경우_주문테이블내역_여부검사() { - order.setType(OrderType.EAT_IN); - order.getOrderTable().setOccupied(false); - - mockFindAllByMenu(order); - - mockFindByMenu(chickenMenu); - - mockFindByOrderTable(order.getOrderTable()); - - assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> orderService.create(order)); + createVo = OrderFixture.test( + OrderType.EAT_IN, + null, + null, + List.of(OrderLineItemFixture.test(chickenMenu.getMenuId().get(), UUID.randomUUID(), OrderType.EAT_IN, 1, null).create()), + null, + UUID.randomUUID() + ).createVo(); + mockFindMenus(List.of(MenuInfo.fromEntity(chickenMenu))); + mockCreateEatInOrderFail(); + + assertThatExceptionOfType(NotFoundException.class) + .isThrownBy(() -> orderService.create(createVo)) + .withMessage(ErrorCode.NOT_FOUND_ORDER_TABLE.toString()); } } @@ -238,10 +294,9 @@ class 주문_수락 { @Test @DisplayName("성공") void 주문수락_성공() { - mockFindByOrder(); - + mockFindByOrder(order); assertThatCode(() -> { - orderService.accept(order.getId()); + orderService.accept(order.getOrderId()); }).doesNotThrowAnyException(); } @@ -254,23 +309,24 @@ class 주문_수락 { null, null, null, - null - ).create(); - - mockFindByOrder(); + OrderTableFixture.init().toEntity().getOrderTableId().get() + ).toEntity(); + mockFindByOrder(order); - assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> orderService.accept(order.getId())); + assertThatExceptionOfType(OrderInvalidException.class) + .isThrownBy(() -> orderService.accept(order.getOrderId())) + .withMessage(ErrorCode.ORDER_STATUS_IS_NOT_WAITING.toString()); } @Test @DisplayName("배달 주문인 경우, 라이더에게 주문번호, 주문 아이템의 총 금액, 배달 주소를 전달해 배달 요청한다.") void 배달주문_라이더에게_배달정보_전달_후_배달요청() { - mockFindByOrder(); + mockFindByOrder(deliveryOrder); - var result = orderService.accept(order.getId()); - - assertNotNull(result); + mockRequestDelivery(); + assertThatCode(() -> { + orderService.accept(order.getOrderId()); + }).doesNotThrowAnyException(); } } @@ -281,21 +337,22 @@ class 서빙_준비_완료 { @Test @DisplayName("성공") void 주문수락_성공() { - order.setStatus(OrderStatus.ACCEPTED); + order.updateOrderStatus(OrderStatus.ACCEPTED); - mockFindByOrder(); + mockFindByOrder(order); assertThatCode(() -> { - orderService.serve(order.getId()); + orderService.serve(order.getOrderId()); }).doesNotThrowAnyException(); } @Test @DisplayName("현 주문상태가 **수락**이어야 한다.") void 주문상태_수락인지_검사() { - mockFindByOrder(); - assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> orderService.serve(order.getId())); + mockFindByOrder(order); + assertThatExceptionOfType(OrderInvalidException.class) + .isThrownBy(() -> orderService.serve(order.getOrderId())) + .withMessage(ErrorCode.ORDER_STATUS_IS_NOT_ACCEPTED.toString()); } } @@ -307,38 +364,42 @@ class 배달_시작 { @Test @DisplayName("성공") void 배달시작_성공() { - order.setType(OrderType.DELIVERY); - order.setStatus(OrderStatus.SERVED); + deliveryOrder = DeliveryOrderFixture.test( + OrderType.DELIVERY, + OrderStatus.SERVED, + null, + null + ).toEntity(); - mockFindByOrder(); + mockFindByDeliveryOrder(deliveryOrder); assertThatCode(() -> { - orderService.startDelivery(order.getId()); + defaultDeliveryService.startDelivery(order.getOrderId()); }).doesNotThrowAnyException(); } @Test @DisplayName("주문 유형이 **배달**이어야 한다.") void 주문유형_배달인지_검사() { - order = OrderFixture.test( + deliveryOrder = DeliveryOrderFixture.test( OrderType.EAT_IN, - null, - null, - null, + OrderStatus.SERVED, null, null - ).create(); - mockFindByOrder(); + ).toEntity(); + mockFindByDeliveryOrder(deliveryOrder); + assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> orderService.startDelivery(order.getId())); + .isThrownBy(() -> defaultDeliveryService.startDelivery(order.getOrderId())); } @Test @DisplayName("현 주문상태가 **서빙/준비 완료**이어야 한다.") void 주문상태_서빙완료인지_검사() { - mockFindByOrder(); + mockFindByDeliveryOrder(deliveryOrder); + assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> orderService.startDelivery(order.getId())); + .isThrownBy(() -> defaultDeliveryService.startDelivery(order.getOrderId())); } } @@ -349,22 +410,26 @@ class 배달_완료 { @Test @DisplayName("성공") void 배달완료_성공() { - order.setType(OrderType.DELIVERY); - order.setStatus(OrderStatus.DELIVERING); + deliveryOrder = DeliveryOrderFixture.test( + OrderType.DELIVERY, + OrderStatus.DELIVERING, + null, + null + ).toEntity(); - mockFindByOrder(); + mockFindByDeliveryOrder(deliveryOrder); assertThatCode(() -> { - orderService.completeDelivery(order.getId()); + defaultDeliveryService.completeDelivery(order.getOrderId()); }).doesNotThrowAnyException(); } @Test @DisplayName("현 주문상태가 **배달중**이어야 한다.") void 주문상태_배달중인지_검사() { - mockFindByOrder(); + mockFindByDeliveryOrder(deliveryOrder); assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> orderService.completeDelivery(order.getId())); + .isThrownBy(() -> defaultDeliveryService.completeDelivery(order.getOrderId())); } } @@ -375,23 +440,28 @@ class 주문_완료 { @Test @DisplayName("성공") void 주문완료_성공() { - order.setType(OrderType.DELIVERY); - order.setStatus(OrderStatus.DELIVERED); - - mockFindByOrder(); + order = OrderFixture.test( + OrderType.DELIVERY, + OrderStatus.DELIVERED, + null, + null, + null, + OrderTableFixture.init().toEntity().getOrderTableId().get() + ).toEntity(); + mockFindByOrder(order); assertThatCode(() -> { - orderService.complete(order.getId()); + orderService.complete(order.getOrderId()); }).doesNotThrowAnyException(); } @Test @DisplayName("배달(주문유형)인데 배달완료(주문상태)가 아니면 안된다.") void 배달이면_배달완료인지_검사() { - mockFindByOrder(); + mockFindByOrder(order); assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> orderService.complete(order.getId())); + .isThrownBy(() -> orderService.complete(order.getOrderId())); } @Test @@ -403,76 +473,86 @@ class 주문_완료 { null, null, null, - null - ).create(); - - mockFindByOrder(); + OrderTableFixture.init().toEntity().getOrderTableId().get() + ).toEntity(); + mockFindByOrder(order); assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> orderService.complete(order.getId())); + .isThrownBy(() -> orderService.complete(order.getOrderId())); } @Test @DisplayName("먹고가기(주문유형)일 경우, 해당 주문을 완료 처리 하고 해당 테이블에 다른 진행 중인 주문이 없다면 테이블을 비우고 인원 수를 0명으로 설정한다.") void 먹고가기이면_주문완료처리하고_테이블_초기화처리() { + orderTable = OrderTableFixture.init().toEntity(); + order = OrderFixture.test( OrderType.EAT_IN, OrderStatus.SERVED, null, - List.of(OrderLineItemFixture.init().create()), null, - OrderTableFixture.init().create() - ).create(); + null, + orderTable.getOrderTableId().get() + ).toEntity(); - mockFindByOrder(); + mockFindByOrder(order); - mockExistsByOrderTable(order, false); + orderTable.clear(); - var result = orderService.complete(order.getId()); + orderService.complete(order.getOrderId()); assertAll( - () -> assertEquals(result.getOrderTable().getNumberOfGuests(), 0), - () -> assertFalse(result.getOrderTable().isOccupied()) + () -> assertEquals(orderTable.getNumberOfGuests().get(), 0), + () -> assertFalse(orderTable.isOccupied()) ); } } - private void mockFindByOrder() { + private void mockFindAllByOrder(Order request) { + when(orderRepository.findAll()) + .thenReturn(List.of(request)); + } + + private void mockFindByOrder(Order request) { when(orderRepository.findById(Mockito.any())) - .thenReturn(Optional.of(order)); + .thenReturn(Optional.of(request)); } - private void mockExistsByOrderTable(Order order, boolean status) { - when(orderRepository.existsByOrderTableAndStatusNot(order.getOrderTable(), - OrderStatus.COMPLETED)) - .thenReturn(status); + private void mockFindByDeliveryOrder(Order request) { + + when(deliveryOrderRepository.findById(Mockito.any())) + .thenReturn(Optional.of((DeliveryOrder) request)); } - private void mockFindAllByMenu(Order order) { - when(menuRepository.findAllByMenuIdIn(Mockito.any())) - .thenReturn(null); + private void mockCreateEatInOrderFail() { + when(eatinService.createEatInOrder(Mockito.any(), Mockito.any(), Mockito.any())) + .thenThrow(new NotFoundException(ErrorCode.NOT_FOUND_ORDER_TABLE.toString())); + } - private void mockFindByMenu(Menu menu) { - when(menuRepository.findByMenuId(Mockito.any())) - .thenReturn(Optional.of(chickenMenu)); + private void mockCreateDeliveryOrderSuccess() { + when(deliveryService.createDeliveryOrder(Mockito.any(), Mockito.any(), Mockito.any())) + .thenReturn(deliveryOrder); + } - private void mockFindByOrderTable(OrderTable orderTable) { - when(orderTableRepository.findById(Mockito.any())) - .thenReturn(Optional.of(orderTable)); + private void mockCreateOrder() { + mockFindMenus(List.of(MenuInfo.fromEntity(chickenMenu))); + mockCreateDeliveryOrderSuccess(); + mockSaveOrder(order); } - private void mockSaveMenu() { - when(orderRepository.save(Mockito.any(Order.class))).thenReturn(order); + private void mockSaveOrder(Order request) { + when(orderRepository.save(Mockito.any(Order.class))).thenReturn(request); } - private void mockCreateOrder() { - mockFindAllByMenu(order); - mockFindByMenu(chickenMenu); - mockSaveMenu(); + private void mockRequestDelivery() { + doNothing().when(deliveryService).requestDelivery(Mockito.any(), Mockito.any()); } + private void mockFindMenus(List menuInfos) { + when(menuContextProvider.findMenus(Mockito.anyList())).thenReturn(menuInfos); + } } diff --git a/src/test/java/kitchenpos/order/application/OrderTableFacadeTest.java b/src/test/java/kitchenpos/order/application/OrderTableFacadeTest.java index ff6703083..2ca710cc8 100644 --- a/src/test/java/kitchenpos/order/application/OrderTableFacadeTest.java +++ b/src/test/java/kitchenpos/order/application/OrderTableFacadeTest.java @@ -10,11 +10,13 @@ import java.util.List; import java.util.Optional; import kitchenpos.order.common.domain.entity.OrderStatus; -import kitchenpos.order.eatin.domain.entity.OrderTable; -import kitchenpos.order.domain.fixture.OrderTableFixture; -import kitchenpos.order.common.domain.repository.OrderRepository; import kitchenpos.order.common.domain.repository.OrderTableRepository; -import kitchenpos.order.eatin.domain.service.OrderTableService; +import kitchenpos.order.domain.fixture.OrderTableFixture; +import kitchenpos.order.eatin.domain.entity.OrderTable; +import kitchenpos.order.eatin.domain.exception.OrderTableGuestsException; +import kitchenpos.order.eatin.domain.model.OrderTableVo; +import kitchenpos.order.eatin.domain.repository.EatinOrderRepository; +import kitchenpos.order.eatin.domain.service.DefaultEatInService; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Nested; @@ -32,17 +34,20 @@ class OrderTableFacadeTest { @InjectMocks - private OrderTableService orderTableService; + private DefaultEatInService eatinService; @Mock private OrderTableRepository orderTableRepository; @Mock - private OrderRepository orderRepository; - + private EatinOrderRepository orderRepository; private OrderTable orderTable; + private OrderTableVo.Create createOrderTable; + private OrderTableVo.Update updateOrderTable; @BeforeEach void setUp() { - orderTable = OrderTableFixture.init().create(); + eatinService = new DefaultEatInService(orderRepository, orderTableRepository); + orderTable = OrderTableFixture.init().toEntity(); + createOrderTable = OrderTableFixture.init().create(); } @Nested @@ -70,10 +75,12 @@ class 주문_테이블_등록 { @DisplayName("성공") @ValueSource(strings = {"1번 테이블", "2번 테이블"}) void 주문_테이블_등록성공(final String name) { - orderTable = OrderTableFixture.test(name, 0, false).create(); + + mockSaveOrderTable(); assertThatCode(() -> { - orderTableService.create(orderTable); + createOrderTable = OrderTableFixture.test(name, 0, false).create(); + eatinService.create(createOrderTable); }).doesNotThrowAnyException(); } @@ -83,9 +90,11 @@ class 주문_테이블_등록 { @NullAndEmptySource @ValueSource(strings = {" ", " ", "\t", "\n"}) void 테이블명_공란_검사(final String name) { - orderTable = OrderTableFixture.test(name, 0, false).create(); assertThatExceptionOfType(IllegalArgumentException.class) - .isThrownBy(() -> orderTableService.create(orderTable)); + .isThrownBy(() -> { + createOrderTable = OrderTableFixture.test(name, 0, false).create(); + eatinService.create(createOrderTable); + }); } } @@ -100,7 +109,7 @@ class 테이블_착석 { mockFindByOrderTable(); assertThatCode(() -> { - orderTableService.sit(orderTable.getId()); + eatinService.sit(orderTable.getOrderTableId()); }).doesNotThrowAnyException(); } @@ -110,9 +119,9 @@ class 테이블_착석 { void 테이블_사용처리() { mockFindByOrderTable(); - var result = orderTableService.sit(orderTable.getId()); + var result = eatinService.sit(orderTable.getOrderTableId()); - assertThat(result.isOccupied()).isTrue(); + assertThat(result.occupied()).isTrue(); } } @@ -130,7 +139,7 @@ class 테이블_정리 { mockExistsByOrderTable(false); assertThatCode(() -> { - orderTableService.clear(orderTable.getId()); + eatinService.clear(orderTable.getOrderTableId()); }).doesNotThrowAnyException(); } @@ -143,7 +152,7 @@ class 테이블_정리 { mockExistsByOrderTable(true); assertThatExceptionOfType(IllegalStateException.class) - .isThrownBy(() -> orderTableService.clear(orderTable.getId())); + .isThrownBy(() -> eatinService.clear(orderTable.getOrderTableId())); } @Test @@ -152,9 +161,9 @@ class 테이블_정리 { mockFindByOrderTable(); mockExistsByOrderTable(false); - var result = orderTableService.clear(orderTable.getId()); + var result = eatinService.clear(orderTable.getOrderTableId()); - assertThat(result.isOccupied()).isFalse(); + assertThat(result.occupied()).isFalse(); } } @@ -165,12 +174,14 @@ class 테이블_인원_변경 { @Test @DisplayName("성공") void 주문_테이블_인원변경_성공() { - orderTable = OrderTableFixture.test("1번 테이블", 3, true).create(); + + orderTable = OrderTableFixture.test("1법 테이블", 1, true).toEntity(); mockFindByOrderTable(); + updateOrderTable = OrderTableFixture.test("1번 테이블", 3, true).update(); assertThatCode(() -> { - orderTableService.changeNumberOfGuests(orderTable.getId(), orderTable); + eatinService.changeNumberOfGuests(updateOrderTable); }).doesNotThrowAnyException(); } @@ -179,19 +190,22 @@ class 테이블_인원_변경 { @DisplayName("테이블 사용중인 상태여야 한다.") void 테이블_사용여부_검사() { mockFindByOrderTable(); + updateOrderTable = OrderTableFixture.test("1번 테이블", 3, true).update(); assertThatExceptionOfType(IllegalStateException.class) .isThrownBy( - () -> orderTableService.changeNumberOfGuests(orderTable.getId(), orderTable)); + () -> eatinService.changeNumberOfGuests(updateOrderTable)); } @Test @DisplayName("테이블 인원 수는 0명 이상이어야 한다.") void 테이블_인원수_허용범위_검사() { - orderTable = OrderTableFixture.test("test", -1, false).create(); - assertThatExceptionOfType(IllegalArgumentException.class) + assertThatExceptionOfType(OrderTableGuestsException.class) .isThrownBy( - () -> orderTableService.changeNumberOfGuests(orderTable.getId(), orderTable)); + () -> { + updateOrderTable = OrderTableFixture.test("test", -1, false).update(); + eatinService.changeNumberOfGuests(updateOrderTable); + }); } } @@ -202,7 +216,11 @@ private void mockFindByOrderTable() { } private void mockExistsByOrderTable(boolean status) { - when(orderRepository.existsByOrderTableAndStatusNot(orderTable, OrderStatus.COMPLETED)) + when(orderRepository.existsByOrderTableIdAndStatusNot(orderTable.getOrderTableId(), OrderStatus.COMPLETED)) .thenReturn(status); } + + private void mockSaveOrderTable() { + when(orderTableRepository.save(Mockito.any(OrderTable.class))).thenReturn(orderTable); + } } diff --git a/src/test/java/kitchenpos/order/domain/fixture/DeliveryOrderFixture.java b/src/test/java/kitchenpos/order/domain/fixture/DeliveryOrderFixture.java new file mode 100644 index 000000000..1ede6b2bc --- /dev/null +++ b/src/test/java/kitchenpos/order/domain/fixture/DeliveryOrderFixture.java @@ -0,0 +1,61 @@ +package kitchenpos.order.domain.fixture; + +import java.util.List; +import java.util.Objects; +import java.util.UUID; +import java.util.stream.Collectors; +import kitchenpos.order.common.application.dto.OrderRequest.OrderLineItemCreate; +import kitchenpos.order.common.domain.entity.OrderLineItem; +import kitchenpos.order.common.domain.entity.OrderStatus; +import kitchenpos.order.common.domain.entity.OrderType; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItems; +import kitchenpos.order.delivery.domain.entity.DeliveryOrder; +import kitchenpos.order.delivery.domain.model.DeliveryInfo; + +public record DeliveryOrderFixture(UUID id, + OrderType 주문유형, + OrderStatus 주문상태, + List 주문아이템, + String 배달지주소) { + + public static final OrderType DEFAULT_ORDER_TYPE = OrderType.DELIVERY; + public static final OrderStatus DEFAULT_ORDER_STATUS = OrderStatus.WAITING; + public static final String DEFAULT_DELIVERY_ADDRESS = "성남시 분당구 삼평동"; + public static DeliveryOrderFixture init() { + return new DeliveryOrderFixture( + UUID.randomUUID(), + DEFAULT_ORDER_TYPE, + DEFAULT_ORDER_STATUS, + List.of(OrderLineItemFixture.init(null, DEFAULT_ORDER_TYPE).create()), + DEFAULT_DELIVERY_ADDRESS + ); + } + + public static DeliveryOrderFixture test( + OrderType 주문유형, + OrderStatus 주문상태, + List 주문아이템, + String 배달지주소) { + return new DeliveryOrderFixture( + UUID.randomUUID(), + 주문유형, + Objects.requireNonNullElse(주문상태, DEFAULT_ORDER_STATUS), + Objects.requireNonNullElse(주문아이템, List.of(OrderLineItemFixture.init(null, 주문유형).create())), + Objects.requireNonNullElse(배달지주소, DEFAULT_DELIVERY_ADDRESS) + ); + } + + public DeliveryOrder toEntity() { + return new DeliveryOrder( + OrderId.of(id), + 주문유형, + 주문상태, + new OrderLineItems(주문아이템.stream() + .map(주문항목 -> OrderLineItem.fromDto(주문항목, OrderId.of(id), 주문유형)) + .collect(Collectors.toList())), + DeliveryInfo.of(배달지주소) + ); + } +} + diff --git a/src/test/java/kitchenpos/order/domain/fixture/OrderFixture.java b/src/test/java/kitchenpos/order/domain/fixture/OrderFixture.java index 7afc4c45c..79f178218 100644 --- a/src/test/java/kitchenpos/order/domain/fixture/OrderFixture.java +++ b/src/test/java/kitchenpos/order/domain/fixture/OrderFixture.java @@ -4,19 +4,25 @@ import java.util.List; import java.util.Objects; import java.util.UUID; +import java.util.stream.Collectors; +import kitchenpos.order.common.application.dto.OrderRequest; +import kitchenpos.order.common.application.dto.OrderRequest.OrderLineItemCreate; import kitchenpos.order.common.domain.entity.Order; import kitchenpos.order.common.domain.entity.OrderLineItem; import kitchenpos.order.common.domain.entity.OrderStatus; -import kitchenpos.order.eatin.domain.entity.OrderTable; import kitchenpos.order.common.domain.entity.OrderType; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItems; +import kitchenpos.order.common.domain.model.OrderVo; +import kitchenpos.order.eatin.domain.model.OrderTableId; public record OrderFixture(UUID id, OrderType 주문유형, OrderStatus 주문상태, LocalDateTime 주문시간, - List 주문아이템, + List 주문아이템, String 배달지주소, - OrderTable 주문테이블) { + UUID 주문테이블아이디) { public static final OrderType DEFAULT_ORDER_TYPE = OrderType.DELIVERY; public static final OrderStatus DEFAULT_ORDER_STATUS = OrderStatus.WAITING; @@ -29,39 +35,55 @@ public static OrderFixture init() { DEFAULT_ORDER_TYPE, DEFAULT_ORDER_STATUS, DEFAULT_ORDER_TIME, - List.of(OrderLineItemFixture.init().create()), + List.of(OrderLineItemFixture.init(null, DEFAULT_ORDER_TYPE).create()), DEFAULT_DELIVERY_ADDRESS, - OrderTableFixture.init().create() + OrderTableFixture.init().toEntity().getOrderTableId().get() ); } public static OrderFixture test(OrderType 주문유형, OrderStatus 주문상태, LocalDateTime 주문시간, - List 주문아이템, + List 주문아이템, String 배달지주소, - OrderTable 주문테이블) { + UUID 주문테이블아이디) { return new OrderFixture( UUID.randomUUID(), - 주문유형, + Objects.requireNonNullElse(주문유형, DEFAULT_ORDER_TYPE), Objects.requireNonNullElse(주문상태, DEFAULT_ORDER_STATUS), Objects.requireNonNullElse(주문시간, DEFAULT_ORDER_TIME), - 주문아이템, + Objects.requireNonNullElse(주문아이템, List.of(OrderLineItemFixture.init(null, DEFAULT_ORDER_TYPE).create())), 배달지주소, - 주문테이블 + 주문테이블아이디 + ); + } + + public Order toEntity() { + return new Order( + OrderId.of(id), + 주문유형, + 주문상태, + 주문시간, + new OrderLineItems(주문아이템.stream() + .map(주문항목 -> OrderLineItem.fromDto(주문항목, OrderId.of(id), 주문유형)) + .collect(Collectors.toList())), + OrderTableId.of(주문테이블아이디) ); } - public Order create() { - var order = new Order(); - order.setId(id); - order.setType(주문유형); - order.setStatus(주문상태); - order.setOrderDateTime(주문시간); - order.setOrderLineItems(주문아이템); - order.setDeliveryAddress(배달지주소); - order.setOrderTable(주문테이블); - return order; + public OrderRequest.Create create() { + return new OrderRequest.Create(주문유형, 주문테이블아이디, 주문아이템, 배달지주소); + } + public OrderVo.Create createVo() { + return new OrderVo.Create( + 주문유형, + OrderTableId.of(주문테이블아이디), + OrderLineItems.of( + 주문아이템.stream() + .map(주문항목 -> OrderLineItem.fromDto(주문항목, OrderId.of(id), 주문유형)) + .collect(Collectors.toList())), + 배달지주소); } + } diff --git a/src/test/java/kitchenpos/order/domain/fixture/OrderLineItemFixture.java b/src/test/java/kitchenpos/order/domain/fixture/OrderLineItemFixture.java index c7c3d42de..4078d2faf 100644 --- a/src/test/java/kitchenpos/order/domain/fixture/OrderLineItemFixture.java +++ b/src/test/java/kitchenpos/order/domain/fixture/OrderLineItemFixture.java @@ -2,37 +2,47 @@ import java.math.BigDecimal; import java.util.Objects; -import kitchenpos.menu.domain.entity.Menu; +import java.util.UUID; import kitchenpos.menu.domain.fixture.MenuFixture; +import kitchenpos.menu.domain.model.MenuId; +import kitchenpos.order.common.application.dto.OrderRequest; +import kitchenpos.order.common.application.dto.OrderRequest.OrderLineItemCreate; import kitchenpos.order.common.domain.entity.OrderLineItem; +import kitchenpos.order.common.domain.entity.OrderType; +import kitchenpos.order.common.domain.model.OrderId; +import kitchenpos.order.common.domain.model.OrderLineItemQty; -public record OrderLineItemFixture(Menu 메뉴, long 주문수량, BigDecimal 주문가격) { +public record OrderLineItemFixture(UUID 메뉴아이디, UUID 주문아이디, OrderType 주문유형, long 주문수량, BigDecimal 주문가격) { public static final long DEFAULT_ORDER_LINE_ITEM_QTY = 1; public static final BigDecimal DEFAULT_ORDER_LINE_ITEM_PRICE = BigDecimal.valueOf(20_000); - public static OrderLineItemFixture init() { + public static OrderLineItemFixture init(UUID orderId, OrderType 주문유형) { return new OrderLineItemFixture( - MenuFixture.init().toEntity(), + MenuFixture.init().toEntity().getMenuId().get(), + orderId, + 주문유형, DEFAULT_ORDER_LINE_ITEM_QTY, DEFAULT_ORDER_LINE_ITEM_PRICE ); } - public static OrderLineItemFixture test(Menu 메뉴, long 주문수량, BigDecimal 주문가격) { + public static OrderLineItemFixture test(UUID 메뉴아이디, UUID 주문아이디, OrderType 주문유형, long 주문수량, BigDecimal 주문가격) { return new OrderLineItemFixture( - Objects.requireNonNullElse(메뉴, MenuFixture.init().toEntity()), + 메뉴아이디, + 주문아이디, + 주문유형, Objects.requireNonNullElse(주문수량, DEFAULT_ORDER_LINE_ITEM_QTY), Objects.requireNonNullElse(주문가격, DEFAULT_ORDER_LINE_ITEM_PRICE) ); } - public OrderLineItem create() { - var orderLineItem = new OrderLineItem(); -// orderLineItem.setMenu(메뉴); - orderLineItem.setQuantity(주문수량); - orderLineItem.setPrice(주문가격); - return orderLineItem; + public OrderLineItem toEntity() { + return new OrderLineItem(MenuId.of(메뉴아이디), OrderId.of(주문아이디), OrderLineItemQty.of(주문수량, 주문유형), 주문가격); + } + + public OrderRequest.OrderLineItemCreate create() { + return new OrderLineItemCreate(메뉴아이디, 주문가격, 주문수량); } } diff --git a/src/test/java/kitchenpos/order/domain/fixture/OrderTableFixture.java b/src/test/java/kitchenpos/order/domain/fixture/OrderTableFixture.java index e95a93bf9..24c0732d4 100644 --- a/src/test/java/kitchenpos/order/domain/fixture/OrderTableFixture.java +++ b/src/test/java/kitchenpos/order/domain/fixture/OrderTableFixture.java @@ -2,6 +2,12 @@ import java.util.UUID; import kitchenpos.order.eatin.domain.entity.OrderTable; +import kitchenpos.order.eatin.domain.model.OrderTableGuests; +import kitchenpos.order.eatin.domain.model.OrderTableId; +import kitchenpos.order.eatin.domain.model.OrderTableName; +import kitchenpos.order.eatin.domain.model.OrderTableVo; +import kitchenpos.order.eatin.domain.model.OrderTableVo.Create; +import kitchenpos.order.eatin.domain.model.OrderTableVo.Update; public record OrderTableFixture(UUID id, String 테이블명, int 인원수, boolean 사용여부) { @@ -27,13 +33,16 @@ public static OrderTableFixture test(String 테이블명, int 인원수, boolean ); } - public OrderTable create() { - var orderTable = new OrderTable(); - orderTable.setId(id); - orderTable.setName(테이블명); - orderTable.setNumberOfGuests(인원수); - orderTable.setOccupied(사용여부); - return orderTable; + public OrderTable toEntity() { + return new OrderTable(OrderTableId.of(id), OrderTableName.of(테이블명), OrderTableGuests.of(인원수), 사용여부); + } + + public OrderTableVo.Create create() { + return new Create(OrderTableName.of(테이블명), OrderTableGuests.of(인원수), 사용여부); + } + + public OrderTableVo.Update update() { + return new Update(OrderTableId.of(id), OrderTableGuests.of(인원수)); } }