Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[크리스마스 프로모션] 김민겸 미션 제출합니다. #17

Open
wants to merge 22 commits into
base: main
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
22 commits
Select commit Hold shift + click to select a range
85a8a4d
docs: 기능 구현 목록 작성
Mingyum-Kim Dec 15, 2023
01f2d9a
feat: 커스텀 예외 처리 클래스 생성
Mingyum-Kim Dec 15, 2023
7b1b151
feat: 할인 및 증정 헤택을 Benefit 인터페이스의 구현체로 정의
Mingyum-Kim Dec 15, 2023
95093ed
feat: 메뉴와 메뉴 카테고리를 enum class로 정의
Mingyum-Kim Dec 15, 2023
67b1db5
feat: 이벤트를 적용한 결과를 담아 전달할 DTO 클래스 생성
Mingyum-Kim Dec 15, 2023
f04f984
feat: 콘솔 입출력을 위한 클래스 생성
Mingyum-Kim Dec 15, 2023
392fb67
feat: 사용자의 메뉴 주문을 저장할 클래스 생성
Mingyum-Kim Dec 15, 2023
db7086a
feat: 크리스마스 디데이 할인 이벤트를 적용하는 기능 구현
Mingyum-Kim Dec 15, 2023
bb35f8a
feat: 평일 이벤트 적용 기능 구현
Mingyum-Kim Dec 15, 2023
d6cf841
feat: 주말 할인 이벤트를 적용하는 기능 구현
Mingyum-Kim Dec 15, 2023
ac0c33a
feat: 특별 할인 이벤트를 적용하는 기능 구현
Mingyum-Kim Dec 15, 2023
2371260
feat: 증정 이벤트를 적용하는 기능 구현
Mingyum-Kim Dec 15, 2023
ba6da56
feat: 사용자가 주문한 금액을 계산하는 기능 구현
Mingyum-Kim Dec 15, 2023
9510eeb
dosc: 기능 구현 목록 추가
Mingyum-Kim Dec 15, 2023
fb611ed
feat: 식당 방문 날짜를 입력하는 기능 구현
Mingyum-Kim Dec 15, 2023
fbcb5ef
feat: 메뉴 주문이 올바른 형식임을 확인
Mingyum-Kim Dec 15, 2023
eab0a38
feat: 메뉴 주문의 중복, 개수, 음료 주문 여부를 검증
Mingyum-Kim Dec 15, 2023
77dd1d1
feat: 모든 이벤트의 함수를 호출하는 기능 구현
Mingyum-Kim Dec 15, 2023
27d13d3
feat: 주문 메뉴와 할인 전 총 주문 금액을 출력하는 기능 구현
Mingyum-Kim Dec 15, 2023
73218f1
feat: 이벤트 적용 결과를 출력하는 기능 구현
Mingyum-Kim Dec 15, 2023
2c86716
feat: 배지를 출력하는 기능 구현
Mingyum-Kim Dec 15, 2023
53c653a
fix: 사소한 에러사항 수정
Mingyum-Kim Dec 15, 2023
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
56 changes: 56 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,56 @@
# 💪 프로젝트 개요

# 📝 구현 기능 목록

### 식당 방문 날짜를 입력하는 기능

- [x] `안녕하세요! 우테코 식당 12월 이벤트 플래너입니다.`를 출력한다.
- [x] `12월 중 식당 예상 방문 날짜는 언제인가요? (숫자만 입력해 주세요!)`를 출력한다.
- [x] 식당 예상 방문 날짜를 입력한다.
- [x] 1 이상 31 이하의 숫자 입력임을 검증한다.

### 주문할 메뉴와 개수를 입력하는 기능

- [x] `주문하실 메뉴를 메뉴와 개수를 알려 주세요. (e.g. 해산물파스타-2,레드와인-1,초코케이크-1)`를 출력한다.
- [x] `타파스-1,제로콜라-1`와 같이 메뉴와 개수를 입력한다.
- [x] 올바른 형식으로 입력되었음을 검증한다.
- [x] 음료만 주문된 경우가 아님을 검증한다.
- [x] 중복되는 메뉴가 입력되지 않았음을 검증한다.
- [x] 20개 이하의 메뉴임을 검증한다.

### 이벤트를 적용하는 기능

- [ ] 총 주문 금액이 10,000원 이상인 경우에만 이벤트를 적용한다.
- [x] 크리스마스 디데이 할인 이벤트를 적용하는 기능을 구현한다.
- [x] 조건은 이벤트 기간이 `2023.12.1 ~ 2023.12.25`인 경우이다.
- [x] 할인 금액은 1000원으로 시작하여 100원 씩 증가한다.
- [x] 평일 할인 이벤트를 적용하는 기능을 구현한다.
- [x] 조건은 일요일 ~ 목요일인 경우이다.
- [x] 디저트 메뉴 하나 당 2,023원 할인한다.
- [x] 주말 할인 이벤트를 적용하는 기능을 구현한다.
- [x] 조건은 금요일, 토요일인 경우이다.
- [x] 메인 메뉴 하나 당 2,023원 할인한다.
- [x] 특별 이벤트를 구현한다.
- [x] 조건은 달력에 별이 표시된 날인 경우이다.
- [x] 총 주문 금액에서 1000원을 할인한다.
- [x] 증정이벤트를 구현한다.
- [x] 조건은 할인 전 총 주문 금액이 12만원 이상인 경우이다.
- [x] 샴페인 1개를 증정한다.

### 주문 메뉴와 할인 전 총 주문 금액을 출력하는 기능

- [x] `12월 26일에 우테코 식당에서 받을 이벤트 혜택 미리 보기!`를 출력한다.
- [x] `<주문 메뉴>`를 출력하고, 주문한 메뉴와 개수를 출력한다.
- [x] `<할인 전 총주문 금액>`를 출력하고, 총 주문 금액을 출력한다.

### 이벤트 적용 결과를 출력하는 기능

- [x] 증정 메뉴의 이름과 개수를 출력한다.
- [x] 이벤트 별 혜택 내역을 출력한다.
- [x] 총 혜택 금액을 출력한다.
- [x] 할인 후 예상 결제 금액을 출력한다.

### 배지를 출력하는 기능

- [x] 총 할인 금액으로 배지를 반환한다.
- [x] 배지의 이름을 출력한다.
12 changes: 11 additions & 1 deletion src/main/java/christmas/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,17 @@
package christmas;

import camp.nextstep.edu.missionutils.Console;
import christmas.controller.PromotionManager;
import christmas.view.InputView;
import christmas.view.OutputView;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
PromotionManager promotionManager = new PromotionManager(
new InputView(),
new OutputView()
);
promotionManager.run();
Console.close();
}
}
112 changes: 112 additions & 0 deletions src/main/java/christmas/controller/PromotionManager.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,112 @@
package christmas.controller;

import christmas.controller.dto.PromotionResult;
import christmas.controller.dto.PromotionsResult;
import christmas.domain.Badge;
import christmas.domain.Benefit;
import christmas.domain.Discount;
import christmas.domain.Orders;
import christmas.domain.constants.Promotion;
import christmas.view.InputView;
import christmas.view.OutputView;
import christmas.view.console.ConsoleWriter;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.function.Supplier;

public class PromotionManager {
private final InputView inputView;
private final OutputView outputView;

public PromotionManager(InputView inputView, OutputView outputView) {
this.inputView = inputView;
this.outputView = outputView;
this.outputView.start();
}

public void run() {
int day = retry(inputView::readDay);
Orders orders = retry(inputView::readOrders);
int amount = printBeforePromotion(orders);
Optional<PromotionsResult> result = applyPromotion(day, orders);
printAfterPromotion(amount, result);
}

private int printBeforePromotion(Orders orders) {
outputView.printMenu(orders);
int amount = orders.calculateTotalPrice();
outputView.printPrice(amount);
return amount;
}

private void printAfterPromotion(int amount, Optional<PromotionsResult> result) {
outputView.printGiftAndBenefit(result);
int benefit = calculateTotalBenefit(result);
outputView.printBenefitAmount(benefit);
outputView.printFinalPrice(calculateFinalPrice(amount, result));
outputView.printBadge(calculateBadge(benefit));
}

private Badge calculateBadge(int benefit) {
return Badge.from(benefit);
}

private int calculateFinalPrice(int amount, Optional<PromotionsResult> result) {
if (result.isEmpty()) {
return amount;
}
int discount = (int) result.get().promotionResults()
.stream()
.map(PromotionResult::benefit)
.filter(element -> element instanceof Discount)
.mapToInt(element -> element.calculate())
.sum();
return amount - discount;
}

private int calculateTotalBenefit(Optional<PromotionsResult> result) {
if (result.isEmpty()) {
return 0;
}
return result.get().promotionResults()
.stream()
.mapToInt(element -> element.benefit().calculate())
.sum();
}

private Optional<PromotionsResult> applyPromotion(int day, Orders orders) {
if (orders.calculateTotalPrice() > 10000) {
return Optional.of(
getPromotionResult(day, orders)
);
}
return Optional.empty();
}

private PromotionsResult getPromotionResult(int day, Orders orders) {
List<PromotionResult> promotionResults = new ArrayList<>();
for (Promotion promotion : Promotion.values()) {
Optional<Benefit> benefit = promotion.getInstance().apply(day, orders);
if (benefit.isPresent()) {
promotionResults.add(
new PromotionResult(
promotion,
benefit.get()
)
);
}
}
return new PromotionsResult(promotionResults);
}

private static <T> T retry(Supplier<T> supplier) {
while (true) {
try {
return supplier.get();
} catch (IllegalArgumentException e) {
ConsoleWriter.printlnMessage(e.getMessage());
}
}
}
}
10 changes: 10 additions & 0 deletions src/main/java/christmas/controller/dto/PromotionResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package christmas.controller.dto;

import christmas.domain.Benefit;
import christmas.domain.constants.Promotion;

public record PromotionResult(
Promotion promotion,
Benefit benefit
) {
}
8 changes: 8 additions & 0 deletions src/main/java/christmas/controller/dto/PromotionsResult.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
package christmas.controller.dto;

import java.util.List;

public record PromotionsResult(
List<PromotionResult> promotionResults
) {
}
29 changes: 29 additions & 0 deletions src/main/java/christmas/domain/Badge.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,29 @@
package christmas.domain;

import java.util.Arrays;
import java.util.Comparator;

public enum Badge {
STAR("별", 5000),
TREE("트리", 10000),
SANTA("산타", 20000);

private final String name;
private final int threshold;

Badge(String name, int threshold) {
this.name = name;
this.threshold = threshold;
}

public static Badge from(int price) {
return Arrays.stream(Badge.values())
.filter(b -> price >= b.threshold)
.max(Comparator.comparingInt(b -> b.threshold))
.orElse(null);
}

public String getName() {
return name;
}
}
5 changes: 5 additions & 0 deletions src/main/java/christmas/domain/Benefit.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package christmas.domain;

public interface Benefit {
int calculate();
}
15 changes: 15 additions & 0 deletions src/main/java/christmas/domain/Discount.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package christmas.domain;

public class Discount implements Benefit {
private final int discount;

public Discount(int discount) {
this.discount = discount;

}

@Override
public int calculate() {
return discount;
}
}
26 changes: 26 additions & 0 deletions src/main/java/christmas/domain/Gift.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package christmas.domain;

import christmas.domain.constants.Menu;

public class Gift implements Benefit {
private Menu menu;
private int count;

public Gift(Menu menu, int count) {
this.menu = menu;
this.count = count;
}

@Override
public int calculate() {
return menu.getPrice() * count;
}

public Menu getMenu() {
return menu;
}

public int getCount() {
return count;
}
}
30 changes: 30 additions & 0 deletions src/main/java/christmas/domain/Order.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package christmas.domain;

import christmas.domain.constants.Menu;
import christmas.domain.constants.MenuCategory;

public class Order {
private Menu menu;
private int count;

public Order(Menu menu, int count) {
this.menu = menu;
this.count = count;
}

public int calculatePrice() {
return menu.getPrice() * count;
}

public boolean isCategory(MenuCategory category) {
return menu.getCategory().equals(category);
}

public Menu getMenu() {
return menu;
}

public int getCount() {
return count;
}
}
78 changes: 78 additions & 0 deletions src/main/java/christmas/domain/Orders.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,78 @@
package christmas.domain;

import christmas.domain.constants.MenuCategory;
import christmas.global.exception.CustomException;
import christmas.global.exception.ErrorMessage;
import java.util.Collections;
import java.util.List;

public class Orders {
private List<Order> orders;

public Orders(List<Order> orders) {
Validator.validate(orders);
this.orders = orders;
}

public int getCountOfCategory(MenuCategory category) {
int count = 0;
for (Order order : orders) {
if (order.isCategory(category)) {
count += order.getCount();
}
}
return count;
}

public int calculateTotalPrice() {
return orders.stream()
.mapToInt(order -> order.calculatePrice())
.sum();
}

public List<Order> getOrders() {
return Collections.unmodifiableList(orders);
}

private static class Validator {
private static void validate(List<Order> orders) {
validateDuplicatedItem(orders);
validateAllDrinkMenu(orders);
validateMenuCount(orders);
}

private static int validateMenuCount(List<Order> orders) {
return (int) orders.stream()
.mapToInt(order -> order.getCount())
.count();
}

private static void validateAllDrinkMenu(List<Order> orders) {
if (isAllDrinkMenu(orders)) {
throw CustomException.from(ErrorMessage.INVALID_ORDER_ERROR);
}
}

private static boolean isAllDrinkMenu(List<Order> orders) {
return orders.stream()
.allMatch(order -> order.isCategory(MenuCategory.DRINK));
}

private static void validateDuplicatedItem(List<Order> items) {
if (hasDuplicatedItem(items)) {
throw CustomException.from(ErrorMessage.INVALID_ORDER_ERROR);
}
}

private static boolean hasDuplicatedItem(List<Order> items) {
return items.size() != calculateUniqueItemsCount(items);
}

private static int calculateUniqueItemsCount(List<Order> items) {
return (int) items.stream()
.map(Order::getMenu)
.distinct()
.count();
}
}
}
Loading