diff --git a/README.md b/README.md index 4a91e3f71..d96c55836 100644 --- a/README.md +++ b/README.md @@ -13,12 +13,12 @@ docker compose -p kitchenpos up -d - 상품을 등록할 수 있다. - 상품의 가격이 올바르지 않으면 등록할 수 없다. - - 상품의 가격은 0원 이상이어야 한다. + - 상품의 가격은 0원 이상이어야 한다. - 상품의 이름이 올바르지 않으면 등록할 수 없다. - - 상품의 이름에는 비속어가 포함될 수 없다. + - 상품의 이름에는 비속어가 포함될 수 없다. - 상품의 가격을 변경할 수 있다. - 상품의 가격이 올바르지 않으면 변경할 수 없다. - - 상품의 가격은 0원 이상이어야 한다. + - 상품의 가격은 0원 이상이어야 한다. - 상품의 가격이 변경될 때 메뉴의 가격이 메뉴에 속한 상품 금액의 합보다 크면 메뉴가 숨겨진다. - 상품의 목록을 조회할 수 있다. @@ -26,7 +26,7 @@ docker compose -p kitchenpos up -d - 메뉴 그룹을 등록할 수 있다. - 메뉴 그룹의 이름이 올바르지 않으면 등록할 수 없다. - - 메뉴 그룹의 이름은 비워 둘 수 없다. + - 메뉴 그룹의 이름은 비워 둘 수 없다. - 메뉴 그룹의 목록을 조회할 수 있다. ### 메뉴 @@ -35,14 +35,14 @@ docker compose -p kitchenpos up -d - 상품이 없으면 등록할 수 없다. - 메뉴에 속한 상품의 수량은 0 이상이어야 한다. - 메뉴의 가격이 올바르지 않으면 등록할 수 없다. - - 메뉴의 가격은 0원 이상이어야 한다. + - 메뉴의 가격은 0원 이상이어야 한다. - 메뉴에 속한 상품 금액의 합은 메뉴의 가격보다 크거나 같아야 한다. - 메뉴는 특정 메뉴 그룹에 속해야 한다. - 메뉴의 이름이 올바르지 않으면 등록할 수 없다. - - 메뉴의 이름에는 비속어가 포함될 수 없다. + - 메뉴의 이름에는 비속어가 포함될 수 없다. - 메뉴의 가격을 변경할 수 있다. - 메뉴의 가격이 올바르지 않으면 변경할 수 없다. - - 메뉴의 가격은 0원 이상이어야 한다. + - 메뉴의 가격은 0원 이상이어야 한다. - 메뉴에 속한 상품 금액의 합은 메뉴의 가격보다 크거나 같아야 한다. - 메뉴를 노출할 수 있다. - 메뉴의 가격이 메뉴에 속한 상품 금액의 합보다 높을 경우 메뉴를 노출할 수 없다. @@ -53,13 +53,13 @@ docker compose -p kitchenpos up -d - 주문 테이블을 등록할 수 있다. - 주문 테이블의 이름이 올바르지 않으면 등록할 수 없다. - - 주문 테이블의 이름은 비워 둘 수 없다. + - 주문 테이블의 이름은 비워 둘 수 없다. - 빈 테이블을 해지할 수 있다. - 빈 테이블로 설정할 수 있다. - 완료되지 않은 주문이 있는 주문 테이블은 빈 테이블로 설정할 수 없다. - 방문한 손님 수를 변경할 수 있다. - 방문한 손님 수가 올바르지 않으면 변경할 수 없다. - - 방문한 손님 수는 0 이상이어야 한다. + - 방문한 손님 수는 0 이상이어야 한다. - 빈 테이블은 방문한 손님 수를 변경할 수 없다. - 주문 테이블의 목록을 조회할 수 있다. @@ -73,7 +73,7 @@ docker compose -p kitchenpos up -d - 매장 주문은 주문 항목의 수량이 0 미만일 수 있다. - 매장 주문을 제외한 주문의 경우 주문 항목의 수량은 0 이상이어야 한다. - 배달 주소가 올바르지 않으면 배달 주문을 등록할 수 없다. - - 배달 주소는 비워 둘 수 없다. + - 배달 주소는 비워 둘 수 없다. - 빈 테이블에는 매장 주문을 등록할 수 없다. - 숨겨진 메뉴는 주문할 수 없다. - 주문한 메뉴의 가격은 실제 메뉴 가격과 일치해야 한다. diff --git a/src/main/java/kitchenpos/menus/application/MenuGroupService.java b/src/main/java/kitchenpos/menus/application/MenuGroupService.java index c468893a2..67efcb74e 100644 --- a/src/main/java/kitchenpos/menus/application/MenuGroupService.java +++ b/src/main/java/kitchenpos/menus/application/MenuGroupService.java @@ -2,6 +2,7 @@ import kitchenpos.menus.domain.MenuGroup; import kitchenpos.menus.domain.MenuGroupRepository; +import kitchenpos.menus.domain.dto.MenuGroupRequest; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -18,14 +19,9 @@ public MenuGroupService(final MenuGroupRepository menuGroupRepository) { } @Transactional - public MenuGroup create(final MenuGroup request) { + public MenuGroup create(final MenuGroupRequest request) { final String name = request.getName(); - if (Objects.isNull(name) || name.isEmpty()) { - throw new IllegalArgumentException(); - } - final MenuGroup menuGroup = new MenuGroup(); - menuGroup.setId(UUID.randomUUID()); - menuGroup.setName(name); + final MenuGroup menuGroup = new MenuGroup(UUID.randomUUID(), name); return menuGroupRepository.save(menuGroup); } diff --git a/src/main/java/kitchenpos/menus/application/MenuService.java b/src/main/java/kitchenpos/menus/application/MenuService.java index abf913c11..830d9f298 100644 --- a/src/main/java/kitchenpos/menus/application/MenuService.java +++ b/src/main/java/kitchenpos/menus/application/MenuService.java @@ -1,6 +1,9 @@ package kitchenpos.menus.application; import kitchenpos.menus.domain.*; +import kitchenpos.menus.domain.dto.MenuPriceRequest; +import kitchenpos.menus.domain.dto.MenuRequest; +import kitchenpos.menus.domain.dto.MenuRequest.MenuProductRequest; import kitchenpos.products.domain.Product; import kitchenpos.products.domain.ProductRepository; import kitchenpos.products.infra.PurgomalumClient; @@ -8,7 +11,10 @@ import org.springframework.transaction.annotation.Transactional; import java.math.BigDecimal; -import java.util.*; +import java.util.ArrayList; +import java.util.List; +import java.util.NoSuchElementException; +import java.util.UUID; import java.util.stream.Collectors; @Service @@ -19,10 +25,10 @@ public class MenuService { private final PurgomalumClient purgomalumClient; public MenuService( - final MenuRepository menuRepository, - final MenuGroupRepository menuGroupRepository, - final ProductRepository productRepository, - final PurgomalumClient purgomalumClient + final MenuRepository menuRepository, + final MenuGroupRepository menuGroupRepository, + final ProductRepository productRepository, + final PurgomalumClient purgomalumClient ) { this.menuRepository = menuRepository; this.menuGroupRepository = menuGroupRepository; @@ -31,107 +37,74 @@ public MenuService( } @Transactional - public Menu create(final Menu request) { - final BigDecimal price = request.getPrice(); - if (Objects.isNull(price) || price.compareTo(BigDecimal.ZERO) < 0) { - throw new IllegalArgumentException(); - } + public Menu create(final MenuRequest request) { final MenuGroup menuGroup = menuGroupRepository.findById(request.getMenuGroupId()) - .orElseThrow(NoSuchElementException::new); - final List menuProductRequests = request.getMenuProducts(); - if (Objects.isNull(menuProductRequests) || menuProductRequests.isEmpty()) { - throw new IllegalArgumentException(); - } + .orElseThrow(NoSuchElementException::new); + final List menuProducts = new ArrayList<>(); + final Menu menu = new Menu(UUID.randomUUID(), request.getName(), request.getPrice(), menuGroup, request.isDisplayed(), menuProducts); + + final List menuProductRequests = request.getMenuProducts(); final List products = productRepository.findAllByIdIn( - menuProductRequests.stream() - .map(MenuProduct::getProductId) - .collect(Collectors.toList()) + menuProductRequests.stream() + .map(MenuProductRequest::getProductId) + .collect(Collectors.toList()) ); + if (products.size() != menuProductRequests.size()) { throw new IllegalArgumentException(); } - final List menuProducts = new ArrayList<>(); - BigDecimal sum = BigDecimal.ZERO; - for (final MenuProduct menuProductRequest : menuProductRequests) { - final long quantity = menuProductRequest.getQuantity(); - if (quantity < 0) { - throw new IllegalArgumentException(); - } + for (final MenuProductRequest menuProductRequest : menuProductRequests) { final Product product = productRepository.findById(menuProductRequest.getProductId()) - .orElseThrow(NoSuchElementException::new); - sum = sum.add( - product.getPrice() - .multiply(BigDecimal.valueOf(quantity)) - ); - final MenuProduct menuProduct = new MenuProduct(); - menuProduct.setProduct(product); - menuProduct.setQuantity(quantity); + .orElseThrow(NoSuchElementException::new); + + final MenuProduct menuProduct = new MenuProduct(product.getId(), product.getPrice(), menuProductRequest.getQuantity()); menuProducts.add(menuProduct); } - if (price.compareTo(sum) > 0) { + + BigDecimal sum = menu.calculateMenuProductsPrice(); + + if (menu.getPrice().compareTo(sum) > 0) { throw new IllegalArgumentException(); } - final String name = request.getName(); - if (Objects.isNull(name) || purgomalumClient.containsProfanity(name)) { + + if (purgomalumClient.containsProfanity(menu.getName())) { throw new IllegalArgumentException(); } - final Menu menu = new Menu(); - menu.setId(UUID.randomUUID()); - menu.setName(name); - menu.setPrice(price); - menu.setMenuGroup(menuGroup); - menu.setDisplayed(request.isDisplayed()); - menu.setMenuProducts(menuProducts); return menuRepository.save(menu); } @Transactional - public Menu changePrice(final UUID menuId, final Menu request) { + public Menu changePrice(final UUID menuId, final MenuPriceRequest request) { final BigDecimal price = request.getPrice(); - if (Objects.isNull(price) || price.compareTo(BigDecimal.ZERO) < 0) { - throw new IllegalArgumentException(); - } final Menu menu = menuRepository.findById(menuId) - .orElseThrow(NoSuchElementException::new); - BigDecimal sum = BigDecimal.ZERO; - for (final MenuProduct menuProduct : menu.getMenuProducts()) { - sum = sum.add( - menuProduct.getProduct() - .getPrice() - .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) - ); - } + .orElseThrow(NoSuchElementException::new); + menu.changePrice(price); + + BigDecimal sum = menu.calculateMenuProductsPrice(); + if (price.compareTo(sum) > 0) { throw new IllegalArgumentException(); } - menu.setPrice(price); return menu; } @Transactional public Menu display(final UUID menuId) { final Menu menu = menuRepository.findById(menuId) - .orElseThrow(NoSuchElementException::new); - BigDecimal sum = BigDecimal.ZERO; - for (final MenuProduct menuProduct : menu.getMenuProducts()) { - sum = sum.add( - menuProduct.getProduct() - .getPrice() - .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) - ); - } + .orElseThrow(NoSuchElementException::new); + BigDecimal sum = menu.calculateMenuProductsPrice(); if (menu.getPrice().compareTo(sum) > 0) { throw new IllegalStateException(); } - menu.setDisplayed(true); + menu.changeDisplay(true); return menu; } @Transactional public Menu hide(final UUID menuId) { final Menu menu = menuRepository.findById(menuId) - .orElseThrow(NoSuchElementException::new); - menu.setDisplayed(false); + .orElseThrow(NoSuchElementException::new); + menu.changeDisplay(false); return menu; } diff --git a/src/main/java/kitchenpos/menus/application/ProductPriceChangeEventListener.java b/src/main/java/kitchenpos/menus/application/ProductPriceChangeEventListener.java new file mode 100644 index 000000000..8c6ff2498 --- /dev/null +++ b/src/main/java/kitchenpos/menus/application/ProductPriceChangeEventListener.java @@ -0,0 +1,40 @@ +package kitchenpos.menus.application; + +import kitchenpos.menus.domain.Menu; +import kitchenpos.menus.domain.MenuProduct; +import kitchenpos.menus.domain.MenuRepository; +import kitchenpos.products.application.ProductPriceChangeEvent; +import org.springframework.context.ApplicationListener; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.List; + +@Component +public class ProductPriceChangeEventListener implements ApplicationListener { + + private final MenuRepository menuRepository; + + public ProductPriceChangeEventListener(MenuRepository menuRepository) { + this.menuRepository = menuRepository; + } + + @Override + public void onApplicationEvent(ProductPriceChangeEvent event) { + final List menus = menuRepository.findAllByProductId(event.getProductId()); + for (final Menu menu : menus) { + BigDecimal sum = BigDecimal.ZERO; + for (final MenuProduct menuProduct : menu.getMenuProducts()) { + menuProduct.changePrice(event.getPrice()); + sum = sum.add( + menuProduct + .getPrice() + .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) + ); + } + if (menu.getPrice().compareTo(sum) > 0) { + menu.changeDisplay(false); + } + } + } +} diff --git a/src/main/java/kitchenpos/menus/domain/JpaMenuRepository.java b/src/main/java/kitchenpos/menus/domain/JpaMenuRepository.java index 796499c30..c8959599e 100644 --- a/src/main/java/kitchenpos/menus/domain/JpaMenuRepository.java +++ b/src/main/java/kitchenpos/menus/domain/JpaMenuRepository.java @@ -8,7 +8,7 @@ import java.util.UUID; public interface JpaMenuRepository extends MenuRepository, JpaRepository { - @Query("select m from Menu m join m.menuProducts mp where mp.product.id = :productId") + @Query("select m from Menu m join m.menuProducts mp where mp.productId = :productId") @Override List findAllByProductId(@Param("productId") UUID productId); } diff --git a/src/main/java/kitchenpos/menus/domain/Menu.java b/src/main/java/kitchenpos/menus/domain/Menu.java index e923a6e5d..ac3e2b47e 100644 --- a/src/main/java/kitchenpos/menus/domain/Menu.java +++ b/src/main/java/kitchenpos/menus/domain/Menu.java @@ -1,5 +1,8 @@ package kitchenpos.menus.domain; +import kitchenpos.menus.domain.vo.MenuName; +import kitchenpos.menus.domain.vo.MenuPrice; + import javax.persistence.*; import java.math.BigDecimal; import java.util.List; @@ -12,17 +15,17 @@ public class Menu { @Id private UUID id; - @Column(name = "name", nullable = false) - private String name; + @Embedded + private MenuName name; - @Column(name = "price", nullable = false) - private BigDecimal price; + @Embedded + private MenuPrice price; @ManyToOne(optional = false) @JoinColumn( - name = "menu_group_id", - columnDefinition = "binary(16)", - foreignKey = @ForeignKey(name = "fk_menu_to_menu_group") + name = "menu_group_id", + columnDefinition = "binary(16)", + foreignKey = @ForeignKey(name = "fk_menu_to_menu_group") ) private MenuGroup menuGroup; @@ -31,56 +34,50 @@ public class Menu { @OneToMany(cascade = {CascadeType.PERSIST, CascadeType.MERGE}) @JoinColumn( - name = "menu_id", - nullable = false, - columnDefinition = "binary(16)", - foreignKey = @ForeignKey(name = "fk_menu_product_to_menu") + name = "menu_id", + nullable = false, + columnDefinition = "binary(16)", + foreignKey = @ForeignKey(name = "fk_menu_product_to_menu") ) private List menuProducts; - @Transient - private UUID menuGroupId; + protected Menu() { + } - public Menu() { + public Menu(UUID id, String name, BigDecimal price, MenuGroup menuGroup, boolean displayed, List menuProducts) { + this.id = id; + this.name = new MenuName(name); + this.price = new MenuPrice(price); + this.menuGroup = menuGroup; + this.displayed = displayed; + this.menuProducts = menuProducts; } public UUID getId() { return id; } - public void setId(final UUID id) { - this.id = id; - } - public String getName() { - return name; - } - - public void setName(final String name) { - this.name = name; + return name.getName(); } public BigDecimal getPrice() { - return price; + return price.getPrice(); } - public void setPrice(final BigDecimal price) { - this.price = price; + public void changePrice(final BigDecimal price) { + this.price.changePrice(price); } public MenuGroup getMenuGroup() { return menuGroup; } - public void setMenuGroup(final MenuGroup menuGroup) { - this.menuGroup = menuGroup; - } - public boolean isDisplayed() { return displayed; } - public void setDisplayed(final boolean displayed) { + public void changeDisplay(final boolean displayed) { this.displayed = displayed; } @@ -88,15 +85,10 @@ public List getMenuProducts() { return menuProducts; } - public void setMenuProducts(final List menuProducts) { - this.menuProducts = menuProducts; - } - - public UUID getMenuGroupId() { - return menuGroupId; + public BigDecimal calculateMenuProductsPrice() { + return menuProducts.stream() + .map(MenuProduct::calculatePrice) + .reduce(BigDecimal.ZERO, BigDecimal::add); } - public void setMenuGroupId(final UUID menuGroupId) { - this.menuGroupId = menuGroupId; - } } diff --git a/src/main/java/kitchenpos/menus/domain/MenuGroup.java b/src/main/java/kitchenpos/menus/domain/MenuGroup.java index e65017271..026499ded 100644 --- a/src/main/java/kitchenpos/menus/domain/MenuGroup.java +++ b/src/main/java/kitchenpos/menus/domain/MenuGroup.java @@ -1,5 +1,7 @@ package kitchenpos.menus.domain; +import kitchenpos.menus.domain.vo.MenuGroupName; + import javax.persistence.Column; import javax.persistence.Entity; import javax.persistence.Id; @@ -14,24 +16,22 @@ public class MenuGroup { private UUID id; @Column(name = "name", nullable = false) - private String name; + private MenuGroupName name; - public MenuGroup() { + protected MenuGroup() { } - public UUID getId() { - return id; + public MenuGroup(UUID id, String name) { + this.id = id; + this.name = new MenuGroupName(name); } - public void setId(final UUID id) { - this.id = id; + public UUID getId() { + return id; } public String getName() { - return name; + return name.getName(); } - public void setName(final String name) { - this.name = name; - } } diff --git a/src/main/java/kitchenpos/menus/domain/MenuProduct.java b/src/main/java/kitchenpos/menus/domain/MenuProduct.java index 2836096e6..5bccb7b0c 100644 --- a/src/main/java/kitchenpos/menus/domain/MenuProduct.java +++ b/src/main/java/kitchenpos/menus/domain/MenuProduct.java @@ -1,8 +1,10 @@ package kitchenpos.menus.domain; -import kitchenpos.products.domain.Product; +import kitchenpos.menus.domain.vo.Quantity; +import kitchenpos.products.domain.ProductPrice; import javax.persistence.*; +import java.math.BigDecimal; import java.util.UUID; @Table(name = "menu_product") @@ -13,52 +15,51 @@ public class MenuProduct { @Id private Long seq; - @ManyToOne(optional = false) - @JoinColumn( - name = "product_id", - columnDefinition = "binary(16)", - foreignKey = @ForeignKey(name = "fk_menu_product_to_product") - ) - private Product product; + @Column(name = "product_id", columnDefinition = "binary(16)", nullable = false) + private UUID productId; - @Column(name = "quantity", nullable = false) - private long quantity; + @Embedded + private ProductPrice price; - @Transient - private UUID productId; + @Embedded + private Quantity quantity; - public MenuProduct() { + protected MenuProduct() { } - public Long getSeq() { - return seq; + public MenuProduct(UUID productId, BigDecimal price, long quantity) { + this(null, productId, price, quantity); } - public void setSeq(final Long seq) { + private MenuProduct(Long seq, UUID productId, BigDecimal price, long quantity) { this.seq = seq; + this.productId = productId; + this.price = new ProductPrice(price); + this.quantity = new Quantity(quantity); } - public Product getProduct() { - return product; - } - - public void setProduct(final Product product) { - this.product = product; + public void setSeq(final Long seq) { + this.seq = seq; } - public long getQuantity() { - return quantity; + public BigDecimal getPrice() { + return price.getValue(); } - public void setQuantity(final long quantity) { - this.quantity = quantity; + public void changePrice(final BigDecimal price) { + this.price.changePrice(price); } public UUID getProductId() { return productId; } - public void setProductId(final UUID productId) { - this.productId = productId; + public long getQuantity() { + return quantity.getValue(); } + + public BigDecimal calculatePrice() { + return price.getValue().multiply(BigDecimal.valueOf(quantity.getValue())); + } + } diff --git a/src/main/java/kitchenpos/menus/domain/dto/MenuGroupRequest.java b/src/main/java/kitchenpos/menus/domain/dto/MenuGroupRequest.java new file mode 100644 index 000000000..481368cf4 --- /dev/null +++ b/src/main/java/kitchenpos/menus/domain/dto/MenuGroupRequest.java @@ -0,0 +1,18 @@ +package kitchenpos.menus.domain.dto; + + +public class MenuGroupRequest { + + private String name; + + protected MenuGroupRequest() { + } + + public MenuGroupRequest(String name) { + this.name = name; + } + + public String getName() { + return name; + } +} diff --git a/src/main/java/kitchenpos/menus/domain/dto/MenuPriceRequest.java b/src/main/java/kitchenpos/menus/domain/dto/MenuPriceRequest.java new file mode 100644 index 000000000..0e9ff02f6 --- /dev/null +++ b/src/main/java/kitchenpos/menus/domain/dto/MenuPriceRequest.java @@ -0,0 +1,18 @@ +package kitchenpos.menus.domain.dto; + +import java.math.BigDecimal; + +public class MenuPriceRequest { + private BigDecimal price; + + protected MenuPriceRequest() { + } + + public MenuPriceRequest(BigDecimal price) { + this.price = price; + } + + public BigDecimal getPrice() { + return price; + } +} diff --git a/src/main/java/kitchenpos/menus/domain/dto/MenuRequest.java b/src/main/java/kitchenpos/menus/domain/dto/MenuRequest.java new file mode 100644 index 000000000..fe3af2940 --- /dev/null +++ b/src/main/java/kitchenpos/menus/domain/dto/MenuRequest.java @@ -0,0 +1,71 @@ +package kitchenpos.menus.domain.dto; + +import java.math.BigDecimal; +import java.util.List; +import java.util.Objects; +import java.util.UUID; + +public class MenuRequest { + private String name; + private BigDecimal price; + private UUID menuGroupId; + + private boolean displayed; + private List menuProducts; + + protected MenuRequest() { + } + + public MenuRequest(String name, BigDecimal price, UUID menuGroupId, boolean displayed, List menuProducts) { + this.name = name; + this.price = price; + this.menuGroupId = menuGroupId; + this.displayed = displayed; + this.menuProducts = menuProducts; + } + + public String getName() { + return name; + } + + public BigDecimal getPrice() { + return price; + } + + public UUID getMenuGroupId() { + return menuGroupId; + } + + public List getMenuProducts() { + if (Objects.isNull(menuProducts) || menuProducts.isEmpty()) { + throw new IllegalArgumentException(); + } + return menuProducts; + } + + public boolean isDisplayed() { + return displayed; + } + + public static class MenuProductRequest { + private UUID productId; + private long quantity; + + protected MenuProductRequest() { + } + + public MenuProductRequest(UUID productId, long quantity) { + this.productId = productId; + this.quantity = quantity; + } + + public UUID getProductId() { + return productId; + } + + public long getQuantity() { + return quantity; + } + } + +} diff --git a/src/main/java/kitchenpos/menus/domain/vo/MenuGroupName.java b/src/main/java/kitchenpos/menus/domain/vo/MenuGroupName.java new file mode 100644 index 000000000..2438f230d --- /dev/null +++ b/src/main/java/kitchenpos/menus/domain/vo/MenuGroupName.java @@ -0,0 +1,31 @@ +package kitchenpos.menus.domain.vo; + + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import java.util.Objects; + +@Embeddable +public class MenuGroupName { + + @Column(name = "name", nullable = false) + private String name; + + protected MenuGroupName() { + } + + public MenuGroupName(String name) { + validateMenuGroupName(name); + this.name = name; + } + + public String getName() { + return name; + } + + private void validateMenuGroupName(String name) { + if (Objects.isNull(name) || name.isEmpty()) { + throw new IllegalArgumentException(); + } + } +} diff --git a/src/main/java/kitchenpos/menus/domain/vo/MenuName.java b/src/main/java/kitchenpos/menus/domain/vo/MenuName.java new file mode 100644 index 000000000..9979d07e4 --- /dev/null +++ b/src/main/java/kitchenpos/menus/domain/vo/MenuName.java @@ -0,0 +1,30 @@ +package kitchenpos.menus.domain.vo; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import java.util.Objects; + +@Embeddable +public class MenuName { + + @Column(name = "name", nullable = false) + private String name; + + protected MenuName() { + } + + public MenuName(String name) { + validateMenuName(name); + this.name = name; + } + + public String getName() { + return this.name; + } + + private void validateMenuName(String name) { + if (Objects.isNull(name)) { + throw new IllegalArgumentException(); + } + } +} diff --git a/src/main/java/kitchenpos/menus/domain/vo/MenuPrice.java b/src/main/java/kitchenpos/menus/domain/vo/MenuPrice.java new file mode 100644 index 000000000..3de8016d8 --- /dev/null +++ b/src/main/java/kitchenpos/menus/domain/vo/MenuPrice.java @@ -0,0 +1,37 @@ +package kitchenpos.menus.domain.vo; + +import javax.persistence.Column; +import javax.persistence.Embeddable; +import java.math.BigDecimal; +import java.util.Objects; + +@Embeddable +public class MenuPrice { + + @Column(name = "price", nullable = false) + private BigDecimal price; + + protected MenuPrice() { + } + + public MenuPrice(BigDecimal price) { + validateMenuPrice(price); + this.price = price; + } + + private void validateMenuPrice(BigDecimal price) { + if (Objects.isNull(price) || price.compareTo(BigDecimal.ZERO) < 0) { + throw new IllegalArgumentException(); + } + } + + public BigDecimal getPrice() { + return this.price; + } + + public void changePrice(BigDecimal price) { + validateMenuPrice(price); + this.price = price; + } + +} diff --git a/src/main/java/kitchenpos/menus/domain/vo/Quantity.java b/src/main/java/kitchenpos/menus/domain/vo/Quantity.java new file mode 100644 index 000000000..9f0584c41 --- /dev/null +++ b/src/main/java/kitchenpos/menus/domain/vo/Quantity.java @@ -0,0 +1,29 @@ +package kitchenpos.menus.domain.vo; + +import javax.persistence.Column; +import javax.persistence.Embeddable; + +@Embeddable +public class Quantity { + + @Column(name = "quantity", nullable = false) + private long quantity; + + protected Quantity() { + } + + public Quantity(long quantity) { + validateQuantity(quantity); + this.quantity = quantity; + } + + private void validateQuantity(long quantity) { + if (quantity < 0) { + throw new IllegalArgumentException(); + } + } + + public long getValue() { + return quantity; + } +} diff --git a/src/main/java/kitchenpos/menus/ui/MenuGroupRestController.java b/src/main/java/kitchenpos/menus/ui/MenuGroupRestController.java index 1a5d0fd06..cd2ae364c 100644 --- a/src/main/java/kitchenpos/menus/ui/MenuGroupRestController.java +++ b/src/main/java/kitchenpos/menus/ui/MenuGroupRestController.java @@ -2,6 +2,7 @@ import kitchenpos.menus.application.MenuGroupService; import kitchenpos.menus.domain.MenuGroup; +import kitchenpos.menus.domain.dto.MenuGroupRequest; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -18,10 +19,10 @@ public MenuGroupRestController(final MenuGroupService menuGroupService) { } @PostMapping - public ResponseEntity create(@RequestBody final MenuGroup request) { + public ResponseEntity create(@RequestBody final MenuGroupRequest request) { final MenuGroup response = menuGroupService.create(request); return ResponseEntity.created(URI.create("/api/menu-groups/" + response.getId())) - .body(response); + .body(response); } @GetMapping diff --git a/src/main/java/kitchenpos/menus/ui/MenuRestController.java b/src/main/java/kitchenpos/menus/ui/MenuRestController.java index 3e3a0e23a..d6a0b0094 100644 --- a/src/main/java/kitchenpos/menus/ui/MenuRestController.java +++ b/src/main/java/kitchenpos/menus/ui/MenuRestController.java @@ -2,6 +2,8 @@ import kitchenpos.menus.application.MenuService; import kitchenpos.menus.domain.Menu; +import kitchenpos.menus.domain.dto.MenuPriceRequest; +import kitchenpos.menus.domain.dto.MenuRequest; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; @@ -19,14 +21,14 @@ public MenuRestController(final MenuService menuService) { } @PostMapping - public ResponseEntity create(@RequestBody final Menu request) { + public ResponseEntity create(@RequestBody final MenuRequest request) { final Menu response = menuService.create(request); return ResponseEntity.created(URI.create("/api/menus/" + response.getId())) - .body(response); + .body(response); } @PutMapping("/{menuId}/price") - public ResponseEntity changePrice(@PathVariable final UUID menuId, @RequestBody final Menu request) { + public ResponseEntity changePrice(@PathVariable final UUID menuId, @RequestBody final MenuPriceRequest request) { return ResponseEntity.ok(menuService.changePrice(menuId, request)); } diff --git a/src/main/java/kitchenpos/products/application/ProductPriceChangeEvent.java b/src/main/java/kitchenpos/products/application/ProductPriceChangeEvent.java new file mode 100644 index 000000000..395f76684 --- /dev/null +++ b/src/main/java/kitchenpos/products/application/ProductPriceChangeEvent.java @@ -0,0 +1,26 @@ +package kitchenpos.products.application; + +import org.springframework.context.ApplicationEvent; + +import java.math.BigDecimal; +import java.util.UUID; + +public class ProductPriceChangeEvent extends ApplicationEvent { + + private final BigDecimal price; + private final UUID productId; + + public ProductPriceChangeEvent(Object source, BigDecimal price, UUID productId) { + super(source); + this.price = price; + this.productId = productId; + } + + public BigDecimal getPrice() { + return price; + } + + public UUID getProductId() { + return productId; + } +} diff --git a/src/main/java/kitchenpos/products/application/ProductPriceChangeEventPublisher.java b/src/main/java/kitchenpos/products/application/ProductPriceChangeEventPublisher.java new file mode 100644 index 000000000..4e9da1da9 --- /dev/null +++ b/src/main/java/kitchenpos/products/application/ProductPriceChangeEventPublisher.java @@ -0,0 +1,22 @@ +package kitchenpos.products.application; + +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Component; + +import java.math.BigDecimal; +import java.util.UUID; + +@Component +public class ProductPriceChangeEventPublisher { + + private final ApplicationEventPublisher applicationEventPublisher; + + public ProductPriceChangeEventPublisher(ApplicationEventPublisher applicationEventPublisher) { + this.applicationEventPublisher = applicationEventPublisher; + } + + public void publishEvent(BigDecimal price, UUID productId) { + applicationEventPublisher.publishEvent(new ProductPriceChangeEvent(this, price, productId)); + } + +} diff --git a/src/main/java/kitchenpos/products/application/ProductService.java b/src/main/java/kitchenpos/products/application/ProductService.java index 62dcc3433..54066cf7e 100644 --- a/src/main/java/kitchenpos/products/application/ProductService.java +++ b/src/main/java/kitchenpos/products/application/ProductService.java @@ -1,8 +1,5 @@ package kitchenpos.products.application; -import kitchenpos.menus.domain.Menu; -import kitchenpos.menus.domain.MenuProduct; -import kitchenpos.menus.domain.MenuRepository; import kitchenpos.products.domain.Product; import kitchenpos.products.domain.ProductRepository; import kitchenpos.products.domain.dto.ProductPriceRequest; @@ -19,16 +16,16 @@ @Service public class ProductService { private final ProductRepository productRepository; - private final MenuRepository menuRepository; + private final ProductPriceChangeEventPublisher productPriceChangeEventPublisher; private final PurgomalumClient purgomalumClient; public ProductService( final ProductRepository productRepository, - final MenuRepository menuRepository, + final ProductPriceChangeEventPublisher productPriceChangeEventPublisher, final PurgomalumClient purgomalumClient ) { this.productRepository = productRepository; - this.menuRepository = menuRepository; + this.productPriceChangeEventPublisher = productPriceChangeEventPublisher; this.purgomalumClient = purgomalumClient; } @@ -50,21 +47,7 @@ public Product changePrice(final UUID productId, final ProductPriceRequest reque final Product product = productRepository.findById(productId) .orElseThrow(NoSuchElementException::new); product.changePrice(price); - - final List menus = menuRepository.findAllByProductId(productId); - for (final Menu menu : menus) { - BigDecimal sum = BigDecimal.ZERO; - for (final MenuProduct menuProduct : menu.getMenuProducts()) { - sum = sum.add( - menuProduct.getProduct() - .getPrice() - .multiply(BigDecimal.valueOf(menuProduct.getQuantity())) - ); - } - if (menu.getPrice().compareTo(sum) > 0) { - menu.setDisplayed(false); - } - } + productPriceChangeEventPublisher.publishEvent(price, productId); return product; } diff --git a/src/main/java/kitchenpos/products/domain/Product.java b/src/main/java/kitchenpos/products/domain/Product.java index cf06b4359..9c8e5efee 100644 --- a/src/main/java/kitchenpos/products/domain/Product.java +++ b/src/main/java/kitchenpos/products/domain/Product.java @@ -35,7 +35,7 @@ public String getName() { } public BigDecimal getPrice() { - return price.getPrice(); + return price.getValue(); } public void changePrice(final BigDecimal price) { diff --git a/src/main/java/kitchenpos/products/domain/ProductPrice.java b/src/main/java/kitchenpos/products/domain/ProductPrice.java index f985010eb..d4fdf8d4a 100644 --- a/src/main/java/kitchenpos/products/domain/ProductPrice.java +++ b/src/main/java/kitchenpos/products/domain/ProductPrice.java @@ -2,9 +2,11 @@ import javax.persistence.Column; +import javax.persistence.Embeddable; import java.math.BigDecimal; import java.util.Objects; +@Embeddable public class ProductPrice { @Column(name = "price", nullable = false) @@ -24,7 +26,7 @@ private void validatePrice(BigDecimal price) { } } - public BigDecimal getPrice() { + public BigDecimal getValue() { return price; } diff --git a/src/main/resources/db/migration/V3__Add_price_column.sql b/src/main/resources/db/migration/V3__Add_price_column.sql new file mode 100644 index 000000000..d4ac7cc3a --- /dev/null +++ b/src/main/resources/db/migration/V3__Add_price_column.sql @@ -0,0 +1,2 @@ +alter table menu_product + add price decimal(19, 2) not null; \ No newline at end of file diff --git a/src/test/java/kitchenpos/Fixtures.java b/src/test/java/kitchenpos/Fixtures.java index 005792b68..3209f3669 100644 --- a/src/test/java/kitchenpos/Fixtures.java +++ b/src/test/java/kitchenpos/Fixtures.java @@ -24,14 +24,7 @@ public static Menu menu(final long price, final MenuProduct... menuProducts) { } public static Menu menu(final long price, final boolean displayed, final MenuProduct... menuProducts) { - final Menu menu = new Menu(); - menu.setId(UUID.randomUUID()); - menu.setName("후라이드+후라이드"); - menu.setPrice(BigDecimal.valueOf(price)); - menu.setMenuGroup(menuGroup()); - menu.setDisplayed(displayed); - menu.setMenuProducts(Arrays.asList(menuProducts)); - return menu; + return new Menu(UUID.randomUUID(), "후라이드+후라이드", BigDecimal.valueOf(price), menuGroup(), displayed, Arrays.asList(menuProducts)); } public static MenuGroup menuGroup() { @@ -39,25 +32,19 @@ public static MenuGroup menuGroup() { } public static MenuGroup menuGroup(final String name) { - final MenuGroup menuGroup = new MenuGroup(); - menuGroup.setId(UUID.randomUUID()); - menuGroup.setName(name); - return menuGroup; + return new MenuGroup(UUID.randomUUID(), name); } public static MenuProduct menuProduct() { - final MenuProduct menuProduct = new MenuProduct(); + Product product = product(); + MenuProduct menuProduct = new MenuProduct(product.getId(), product.getPrice(), 2L); menuProduct.setSeq(new Random().nextLong()); - menuProduct.setProduct(product()); - menuProduct.setQuantity(2L); return menuProduct; } public static MenuProduct menuProduct(final Product product, final long quantity) { - final MenuProduct menuProduct = new MenuProduct(); + MenuProduct menuProduct = new MenuProduct(product.getId(), product.getPrice(), quantity); menuProduct.setSeq(new Random().nextLong()); - menuProduct.setProduct(product); - menuProduct.setQuantity(quantity); return menuProduct; } diff --git a/src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java b/src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java index 971ded7e3..508678fd4 100644 --- a/src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java +++ b/src/test/java/kitchenpos/menus/application/InMemoryMenuRepository.java @@ -28,16 +28,16 @@ public List findAll() { @Override public List findAllByIdIn(final List ids) { return menus.values() - .stream() - .filter(menu -> ids.contains(menu.getId())) - .collect(Collectors.toList()); + .stream() + .filter(menu -> ids.contains(menu.getId())) + .collect(Collectors.toList()); } @Override public List findAllByProductId(final UUID productId) { return menus.values() - .stream() - .filter(menu -> menu.getMenuProducts().stream().anyMatch(menuProduct -> menuProduct.getProduct().getId().equals(productId))) - .collect(Collectors.toList()); + .stream() + .filter(menu -> menu.getMenuProducts().stream().anyMatch(menuProduct -> menuProduct.getProductId().equals(productId))) + .collect(Collectors.toList()); } } diff --git a/src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java b/src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java index a5fbc71d1..42ebc182f 100644 --- a/src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java +++ b/src/test/java/kitchenpos/menus/application/MenuGroupServiceTest.java @@ -2,6 +2,7 @@ import kitchenpos.menus.domain.MenuGroup; import kitchenpos.menus.domain.MenuGroupRepository; +import kitchenpos.menus.domain.dto.MenuGroupRequest; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -28,12 +29,12 @@ void setUp() { @DisplayName("메뉴 그룹을 등록할 수 있다.") @Test void create() { - final MenuGroup expected = createMenuGroupRequest("두마리메뉴"); + final MenuGroupRequest expected = createMenuGroupRequest("두마리메뉴"); final MenuGroup actual = menuGroupService.create(expected); assertThat(actual).isNotNull(); assertAll( - () -> assertThat(actual.getId()).isNotNull(), - () -> assertThat(actual.getName()).isEqualTo(expected.getName()) + () -> assertThat(actual.getId()).isNotNull(), + () -> assertThat(actual.getName()).isEqualTo(expected.getName()) ); } @@ -41,9 +42,9 @@ void create() { @NullAndEmptySource @ParameterizedTest void create(final String name) { - final MenuGroup expected = createMenuGroupRequest(name); + final MenuGroupRequest expected = createMenuGroupRequest(name); assertThatThrownBy(() -> menuGroupService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("메뉴 그룹의 목록을 조회할 수 있다.") @@ -54,9 +55,7 @@ void findAll() { assertThat(actual).hasSize(1); } - private MenuGroup createMenuGroupRequest(final String name) { - final MenuGroup menuGroup = new MenuGroup(); - menuGroup.setName(name); - return menuGroup; + private MenuGroupRequest createMenuGroupRequest(final String name) { + return new MenuGroupRequest(name); } } diff --git a/src/test/java/kitchenpos/menus/application/MenuServiceIntegrationTest.java b/src/test/java/kitchenpos/menus/application/MenuServiceIntegrationTest.java new file mode 100644 index 000000000..85fc96c7d --- /dev/null +++ b/src/test/java/kitchenpos/menus/application/MenuServiceIntegrationTest.java @@ -0,0 +1,80 @@ +package kitchenpos.menus.application; + +import kitchenpos.menus.domain.*; +import kitchenpos.products.application.ProductService; +import kitchenpos.products.domain.Product; +import kitchenpos.products.domain.ProductRepository; +import kitchenpos.products.domain.dto.ProductPriceRequest; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Nested; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; + +import java.math.BigDecimal; +import java.util.List; +import java.util.UUID; + +import static kitchenpos.Fixtures.menuGroup; +import static kitchenpos.Fixtures.product; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +@SpringBootTest +public class MenuServiceIntegrationTest { + + @Autowired + private ProductRepository productRepository; + @Autowired + private ProductService productService; + @Autowired + private MenuGroupRepository menuGroupRepository; + @Autowired + private MenuRepository menuRepository; + + @Nested + @DisplayName("상품의 가격을 변경할 때") + class PriceChangeTest { + + @DisplayName("상품의 가격을 변경할 수 있다.") + @Test + void changePrice() { + final UUID productId = productRepository.save(product("후라이드", 16_000L)).getId(); + final ProductPriceRequest expected = changePriceRequest(15_000L); + final Product actual = productService.changePrice(productId, expected); + assertThat(actual.getPrice()).isEqualTo(expected.getPrice()); + } + + @DisplayName("상품의 가격이 올바르지 않으면 변경할 수 없다.") + @ValueSource(strings = "-1000") + @NullSource + @ParameterizedTest + void changePrice(final BigDecimal price) { + final UUID productId = productRepository.save(product("후라이드", 16_000L)).getId(); + final ProductPriceRequest expected = changePriceRequest(price); + assertThatThrownBy(() -> productService.changePrice(productId, expected)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("상품의 가격이 변경될 때 메뉴의 가격이 메뉴에 속한 상품 금액의 합보다 크면 메뉴가 숨겨진다.") + @Test + void changePriceInMenu() { + final Product product = productRepository.save(product("후라이드", 16_000L)); + MenuGroup menuGroup = menuGroupRepository.save(menuGroup()); + Menu menu = menuRepository.save(new Menu(UUID.randomUUID(), "후라이드 세트", BigDecimal.valueOf(19000), menuGroup, true, List.of(new MenuProduct(product.getId(), product.getPrice(), 2L)))); + productService.changePrice(product.getId(), changePriceRequest(8_000L)); + assertThat(menuRepository.findById(menu.getId()).get().isDisplayed()).isFalse(); + } + + private ProductPriceRequest changePriceRequest(final long price) { + return changePriceRequest(BigDecimal.valueOf(price)); + } + + private ProductPriceRequest changePriceRequest(final BigDecimal price) { + return new ProductPriceRequest(price); + } + } +} diff --git a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java index 23f260848..852419b4c 100644 --- a/src/test/java/kitchenpos/menus/application/MenuServiceTest.java +++ b/src/test/java/kitchenpos/menus/application/MenuServiceTest.java @@ -2,8 +2,10 @@ import kitchenpos.menus.domain.Menu; import kitchenpos.menus.domain.MenuGroupRepository; -import kitchenpos.menus.domain.MenuProduct; import kitchenpos.menus.domain.MenuRepository; +import kitchenpos.menus.domain.dto.MenuPriceRequest; +import kitchenpos.menus.domain.dto.MenuRequest; +import kitchenpos.menus.domain.dto.MenuRequest.MenuProductRequest; import kitchenpos.products.application.FakePurgomalumClient; import kitchenpos.products.application.InMemoryProductRepository; import kitchenpos.products.domain.Product; @@ -49,46 +51,46 @@ void setUp() { @DisplayName("1개 이상의 등록된 상품으로 메뉴를 등록할 수 있다.") @Test void create() { - final Menu expected = createMenuRequest( - "후라이드+후라이드", 19_000L, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) + final MenuRequest expected = createMenuRequest( + "후라이드+후라이드", 19_000L, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) ); final Menu actual = menuService.create(expected); assertThat(actual).isNotNull(); assertAll( - () -> assertThat(actual.getId()).isNotNull(), - () -> assertThat(actual.getName()).isEqualTo(expected.getName()), - () -> assertThat(actual.getPrice()).isEqualTo(expected.getPrice()), - () -> assertThat(actual.getMenuGroup().getId()).isEqualTo(expected.getMenuGroupId()), - () -> assertThat(actual.isDisplayed()).isEqualTo(expected.isDisplayed()), - () -> assertThat(actual.getMenuProducts()).hasSize(1) + () -> assertThat(actual.getId()).isNotNull(), + () -> assertThat(actual.getName()).isEqualTo(expected.getName()), + () -> assertThat(actual.getPrice()).isEqualTo(expected.getPrice()), + () -> assertThat(actual.getMenuGroup().getId()).isEqualTo(expected.getMenuGroupId()), + () -> assertThat(actual.isDisplayed()).isEqualTo(expected.isDisplayed()), + () -> assertThat(actual.getMenuProducts()).hasSize(1) ); } @DisplayName("상품이 없으면 등록할 수 없다.") @MethodSource("menuProducts") @ParameterizedTest - void create(final List menuProducts) { - final Menu expected = createMenuRequest("후라이드+후라이드", 19_000L, menuGroupId, true, menuProducts); + void create(final List menuProducts) { + final MenuRequest expected = createMenuRequest("후라이드+후라이드", 19_000L, menuGroupId, true, menuProducts); assertThatThrownBy(() -> menuService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } private static List menuProducts() { return Arrays.asList( - null, - Arguments.of(Collections.emptyList()), - Arguments.of(Arrays.asList(createMenuProductRequest(INVALID_ID, 2L))) + null, + Arguments.of(Collections.emptyList()), + Arguments.of(Arrays.asList(createMenuProductRequest(INVALID_ID, 2L))) ); } @DisplayName("메뉴에 속한 상품의 수량은 0개 이상이어야 한다.") @Test void createNegativeQuantity() { - final Menu expected = createMenuRequest( - "후라이드+후라이드", 19_000L, menuGroupId, true, createMenuProductRequest(product.getId(), -1L) + final MenuRequest expected = createMenuRequest( + "후라이드+후라이드", 19_000L, menuGroupId, true, createMenuProductRequest(product.getId(), -1L) ); assertThatThrownBy(() -> menuService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("메뉴의 가격이 올바르지 않으면 등록할 수 없다.") @@ -96,32 +98,32 @@ void createNegativeQuantity() { @NullSource @ParameterizedTest void create(final BigDecimal price) { - final Menu expected = createMenuRequest( - "후라이드+후라이드", price, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) + final MenuRequest expected = createMenuRequest( + "후라이드+후라이드", price, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) ); assertThatThrownBy(() -> menuService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("메뉴에 속한 상품 금액의 합은 메뉴의 가격보다 크거나 같아야 한다.") @Test void createExpensiveMenu() { - final Menu expected = createMenuRequest( - "후라이드+후라이드", 33_000L, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) + final MenuRequest expected = createMenuRequest( + "후라이드+후라이드", 33_000L, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) ); assertThatThrownBy(() -> menuService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("메뉴는 특정 메뉴 그룹에 속해야 한다.") @NullSource @ParameterizedTest void create(final UUID menuGroupId) { - final Menu expected = createMenuRequest( - "후라이드+후라이드", 19_000L, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) + final MenuRequest expected = createMenuRequest( + "후라이드+후라이드", 19_000L, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) ); assertThatThrownBy(() -> menuService.create(expected)) - .isInstanceOf(NoSuchElementException.class); + .isInstanceOf(NoSuchElementException.class); } @DisplayName("메뉴의 이름이 올바르지 않으면 등록할 수 없다.") @@ -129,18 +131,18 @@ void create(final UUID menuGroupId) { @NullSource @ParameterizedTest void create(final String name) { - final Menu expected = createMenuRequest( - name, 19_000L, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) + final MenuRequest expected = createMenuRequest( + name, 19_000L, menuGroupId, true, createMenuProductRequest(product.getId(), 2L) ); assertThatThrownBy(() -> menuService.create(expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("메뉴의 가격을 변경할 수 있다.") @Test void changePrice() { final UUID menuId = menuRepository.save(menu(19_000L, menuProduct(product, 2L))).getId(); - final Menu expected = changePriceRequest(16_000L); + final MenuPriceRequest expected = changePriceRequest(16_000L); final Menu actual = menuService.changePrice(menuId, expected); assertThat(actual.getPrice()).isEqualTo(expected.getPrice()); } @@ -151,18 +153,18 @@ void changePrice() { @ParameterizedTest void changePrice(final BigDecimal price) { final UUID menuId = menuRepository.save(menu(19_000L, menuProduct(product, 2L))).getId(); - final Menu expected = changePriceRequest(price); + final MenuPriceRequest expected = changePriceRequest(price); assertThatThrownBy(() -> menuService.changePrice(menuId, expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("메뉴에 속한 상품 금액의 합은 메뉴의 가격보다 크거나 같아야 한다.") @Test void changePriceToExpensive() { final UUID menuId = menuRepository.save(menu(19_000L, menuProduct(product, 2L))).getId(); - final Menu expected = changePriceRequest(33_000L); + final MenuPriceRequest expected = changePriceRequest(33_000L); assertThatThrownBy(() -> menuService.changePrice(menuId, expected)) - .isInstanceOf(IllegalArgumentException.class); + .isInstanceOf(IllegalArgumentException.class); } @DisplayName("메뉴를 노출할 수 있다.") @@ -178,7 +180,7 @@ void display() { void displayExpensiveMenu() { final UUID menuId = menuRepository.save(menu(33_000L, false, menuProduct(product, 2L))).getId(); assertThatThrownBy(() -> menuService.display(menuId)) - .isInstanceOf(IllegalStateException.class); + .isInstanceOf(IllegalStateException.class); } @DisplayName("메뉴를 숨길 수 있다.") @@ -197,66 +199,55 @@ void findAll() { assertThat(actual).hasSize(1); } - private Menu createMenuRequest( - final String name, - final long price, - final UUID menuGroupId, - final boolean displayed, - final MenuProduct... menuProducts + private MenuRequest createMenuRequest( + final String name, + final long price, + final UUID menuGroupId, + final boolean displayed, + final MenuProductRequest... menuProducts ) { return createMenuRequest(name, BigDecimal.valueOf(price), menuGroupId, displayed, menuProducts); } - private Menu createMenuRequest( - final String name, - final BigDecimal price, - final UUID menuGroupId, - final boolean displayed, - final MenuProduct... menuProducts + private MenuRequest createMenuRequest( + final String name, + final BigDecimal price, + final UUID menuGroupId, + final boolean displayed, + final MenuProductRequest... menuProducts ) { return createMenuRequest(name, price, menuGroupId, displayed, Arrays.asList(menuProducts)); } - private Menu createMenuRequest( - final String name, - final long price, - final UUID menuGroupId, - final boolean displayed, - final List menuProducts + private MenuRequest createMenuRequest( + final String name, + final long price, + final UUID menuGroupId, + final boolean displayed, + final List menuProducts ) { return createMenuRequest(name, BigDecimal.valueOf(price), menuGroupId, displayed, menuProducts); } - private Menu createMenuRequest( - final String name, - final BigDecimal price, - final UUID menuGroupId, - final boolean displayed, - final List menuProducts + private MenuRequest createMenuRequest( + final String name, + final BigDecimal price, + final UUID menuGroupId, + final boolean displayed, + final List menuProducts ) { - final Menu menu = new Menu(); - menu.setName(name); - menu.setPrice(price); - menu.setMenuGroupId(menuGroupId); - menu.setDisplayed(displayed); - menu.setMenuProducts(menuProducts); - return menu; + return new MenuRequest(name, price, menuGroupId, displayed, menuProducts); } - private static MenuProduct createMenuProductRequest(final UUID productId, final long quantity) { - final MenuProduct menuProduct = new MenuProduct(); - menuProduct.setProductId(productId); - menuProduct.setQuantity(quantity); - return menuProduct; + private static MenuProductRequest createMenuProductRequest(final UUID productId, final long quantity) { + return new MenuProductRequest(productId, quantity); } - private Menu changePriceRequest(final long price) { + private MenuPriceRequest changePriceRequest(final long price) { return changePriceRequest(BigDecimal.valueOf(price)); } - private Menu changePriceRequest(final BigDecimal price) { - final Menu menu = new Menu(); - menu.setPrice(price); - return menu; + private MenuPriceRequest changePriceRequest(final BigDecimal price) { + return new MenuPriceRequest(price); } } diff --git a/src/test/java/kitchenpos/menus/domain/MenuGroupTest.java b/src/test/java/kitchenpos/menus/domain/MenuGroupTest.java new file mode 100644 index 000000000..74e675816 --- /dev/null +++ b/src/test/java/kitchenpos/menus/domain/MenuGroupTest.java @@ -0,0 +1,32 @@ +package kitchenpos.menus.domain; + +import org.assertj.core.api.Assertions; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThat; + +public class MenuGroupTest { + + @DisplayName("메뉴 그룹의 이름이 유효하지 않다") + @ParameterizedTest + @NullAndEmptySource + void invalid_group_name(String name){ + Assertions.assertThatThrownBy( + () -> new MenuGroup(UUID.randomUUID(), name) + ).isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("메뉴 그룹을 생성한다.") + @ParameterizedTest + @ValueSource(strings = {"추천메뉴"}) + void create_menu_group_name(String name){ + MenuGroup menuGroup = new MenuGroup(UUID.randomUUID(), name); + assertThat(menuGroup.getName()).isEqualTo(name); + } +} diff --git a/src/test/java/kitchenpos/menus/domain/MenuProductTest.java b/src/test/java/kitchenpos/menus/domain/MenuProductTest.java new file mode 100644 index 000000000..8efde4560 --- /dev/null +++ b/src/test/java/kitchenpos/menus/domain/MenuProductTest.java @@ -0,0 +1,40 @@ +package kitchenpos.menus.domain; + +import kitchenpos.products.domain.Product; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; + +import java.math.BigDecimal; + +import static kitchenpos.Fixtures.menuProduct; +import static kitchenpos.Fixtures.product; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class MenuProductTest { + + @DisplayName("메뉴 상품의 가격이 유효하지 않다") + @ParameterizedTest() + @ValueSource(longs = {-1L, -2L}) + void invalid_price_menu_product(long price){ + assertThatThrownBy(() -> new MenuProduct(product().getId(), BigDecimal.valueOf(price), 1L)) + .isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("메뉴 상품의 수량이 유효하지 않다") + @ParameterizedTest() + @ValueSource(longs = {-1L, -2L}) + void invalid_quantity_menu_product(long quantity){ + Product product = product(); + assertThatThrownBy(() -> new MenuProduct(product.getId(), product.getPrice(), quantity)) + .isInstanceOf(IllegalArgumentException.class); + } + + @Test + void menu_product_price_calculation() { + MenuProduct menuProduct = menuProduct(product("양념치킨", 16000L), 2L); + assertThat(menuProduct.calculatePrice().compareTo(BigDecimal.valueOf(32000L))).isEqualTo(0); + } +} diff --git a/src/test/java/kitchenpos/menus/domain/MenuTest.java b/src/test/java/kitchenpos/menus/domain/MenuTest.java new file mode 100644 index 000000000..df047cb66 --- /dev/null +++ b/src/test/java/kitchenpos/menus/domain/MenuTest.java @@ -0,0 +1,52 @@ +package kitchenpos.menus.domain; + +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.UUID; + +import static kitchenpos.Fixtures.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class MenuTest { + + @DisplayName("메뉴 생성") + @Test + void create_menu() { + Menu menu = new Menu(UUID.randomUUID(), "후라이드+후라이드", BigDecimal.valueOf(20000L), menuGroup(), true, Arrays.asList(menuProduct(product("양념치킨", 16000L), 1L))); + assertThat(menu).isNotNull(); + } + + @DisplayName("메뉴의 가격이 유효하지 않다") + @ParameterizedTest() + @ValueSource(longs = {-1L, -2L}) + void invalid_price_menu(long price) { + MenuProduct menuProduct = menuProduct(product("양념치킨", 16000L), 1L); + assertThatThrownBy( + () -> new Menu(UUID.randomUUID(), "후라이드+후라이드", BigDecimal.valueOf(price), menuGroup(), true, Arrays.asList(menuProduct(product("양념치킨", 16000L), 1L))) + ).isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("메뉴의 이름이 유효하지 않다") + @ParameterizedTest + @NullSource + void invalid_name_menu(String name) { + assertThatThrownBy( + () -> new Menu(UUID.randomUUID(), name, BigDecimal.valueOf(20000L), menuGroup(), true, Arrays.asList(menuProduct(product("양념치킨", 16000L), 1L))) + ).isInstanceOf(IllegalArgumentException.class); + } + + @Test + void menu_price_calculation() { + MenuProduct menuProduct = menuProduct(product("양념치킨", 16000L), 1L); + Menu menu = menu(10000L, menuProduct, menuProduct); + + assertThat(menu.calculateMenuProductsPrice().compareTo(BigDecimal.valueOf(32000L))).isEqualTo(0); + } +} diff --git a/src/test/java/kitchenpos/products/application/ProductServiceTest.java b/src/test/java/kitchenpos/products/application/ProductServiceTest.java index 58abd30f7..3f8c6d9f7 100644 --- a/src/test/java/kitchenpos/products/application/ProductServiceTest.java +++ b/src/test/java/kitchenpos/products/application/ProductServiceTest.java @@ -1,7 +1,5 @@ package kitchenpos.products.application; -import kitchenpos.menus.application.InMemoryMenuRepository; -import kitchenpos.menus.domain.Menu; import kitchenpos.menus.domain.MenuRepository; import kitchenpos.products.domain.Product; import kitchenpos.products.domain.ProductRepository; @@ -17,9 +15,8 @@ import java.math.BigDecimal; import java.util.List; -import java.util.UUID; -import static kitchenpos.Fixtures.*; +import static kitchenpos.Fixtures.product; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.assertAll; @@ -33,9 +30,9 @@ class ProductServiceTest { @BeforeEach void setUp() { productRepository = new InMemoryProductRepository(); - menuRepository = new InMemoryMenuRepository(); +// menuRepository = new ProductPriceChangeEventPublisher(new ApplicationEventPub); purgomalumClient = new FakePurgomalumClient(); - productService = new ProductService(productRepository, menuRepository, purgomalumClient); + productService = new ProductService(productRepository, null, purgomalumClient); } @DisplayName("상품을 등록할 수 있다.") @@ -71,35 +68,6 @@ void create(final String name) { .isInstanceOf(IllegalArgumentException.class); } - @DisplayName("상품의 가격을 변경할 수 있다.") - @Test - void changePrice() { - final UUID productId = productRepository.save(product("후라이드", 16_000L)).getId(); - final ProductPriceRequest expected = changePriceRequest(15_000L); - final Product actual = productService.changePrice(productId, expected); - assertThat(actual.getPrice()).isEqualTo(expected.getPrice()); - } - - @DisplayName("상품의 가격이 올바르지 않으면 변경할 수 없다.") - @ValueSource(strings = "-1000") - @NullSource - @ParameterizedTest - void changePrice(final BigDecimal price) { - final UUID productId = productRepository.save(product("후라이드", 16_000L)).getId(); - final ProductPriceRequest expected = changePriceRequest(price); - assertThatThrownBy(() -> productService.changePrice(productId, expected)) - .isInstanceOf(IllegalArgumentException.class); - } - - @DisplayName("상품의 가격이 변경될 때 메뉴의 가격이 메뉴에 속한 상품 금액의 합보다 크면 메뉴가 숨겨진다.") - @Test - void changePriceInMenu() { - final Product product = productRepository.save(product("후라이드", 16_000L)); - final Menu menu = menuRepository.save(menu(19_000L, true, menuProduct(product, 2L))); - productService.changePrice(product.getId(), changePriceRequest(8_000L)); - assertThat(menuRepository.findById(menu.getId()).get().isDisplayed()).isFalse(); - } - @DisplayName("상품의 목록을 조회할 수 있다.") @Test void findAll() { diff --git a/src/test/java/kitchenpos/products/domain/ProductTest.java b/src/test/java/kitchenpos/products/domain/ProductTest.java new file mode 100644 index 000000000..6082bc3ac --- /dev/null +++ b/src/test/java/kitchenpos/products/domain/ProductTest.java @@ -0,0 +1,71 @@ +package kitchenpos.products.domain; + +import kitchenpos.menus.domain.Menu; +import kitchenpos.menus.domain.MenuProduct; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.NullAndEmptySource; +import org.junit.jupiter.params.provider.NullSource; +import org.junit.jupiter.params.provider.ValueSource; + +import java.math.BigDecimal; +import java.util.Arrays; +import java.util.UUID; + +import static kitchenpos.Fixtures.*; +import static org.assertj.core.api.Assertions.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; + +public class ProductTest { + + @DisplayName("상품의 가격이 유효하지 않다") + @ParameterizedTest() + @ValueSource(longs = {-1L, -2L}) + void invalid_price_product(long price) { + assertThatThrownBy( + () -> new Product(UUID.randomUUID(), "후라이드", BigDecimal.valueOf(price)) + ).isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("상품의 이름이 유효하지 않다") + @ParameterizedTest + @NullAndEmptySource + void invalid_name_product(String name) { + assertThatThrownBy( + () -> new Product(UUID.randomUUID(), name, BigDecimal.valueOf(15000L)) + ).isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("상품의 가격을 null로 변경하는 것을 실패한다") + @ParameterizedTest + @NullSource + void invalid_change_price(BigDecimal price){ + Product product = product("후라이드", 15000L); + assertThatThrownBy( + () -> product.changePrice(price) + ).isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("상품의 가격 변경을 실패한다") + @ParameterizedTest + @ValueSource(longs = {-1L, -2L}) + void invalid_change_price(long price){ + Product product = product("후라이드", 15000L); + assertThatThrownBy( + () -> product.changePrice(BigDecimal.valueOf(price)) + ).isInstanceOf(IllegalArgumentException.class); + } + + @DisplayName("상품의 가격 변경 성공한다.") + @ParameterizedTest + @ValueSource(longs = {15000L, 20000L}) + void change_price_product(long price){ + Product product = product("후라이드", 14000L); + product.changePrice(BigDecimal.valueOf(price)); + + assertThat(product.getPrice()).isEqualTo(BigDecimal.valueOf(price)); + + } + +}