From f71fa9a98c62a95146d95ee68620baebd45963b2 Mon Sep 17 00:00:00 2001 From: khcho96 Date: Wed, 10 Dec 2025 18:55:02 +0900 Subject: [PATCH 01/18] =?UTF-8?q?docs:=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=AA=A9=EB=A1=9D=20=EC=9E=91=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- README.md | 56 +++++++++++++++++++++++++++++++++++++++++++++---------- 1 file changed, 46 insertions(+), 10 deletions(-) diff --git a/README.md b/README.md index 8cb9b8eb..70bb3a28 100644 --- a/README.md +++ b/README.md @@ -1,10 +1,46 @@ -![문제 원문1](description-img/4.편의점_page-0001.jpg) -![문제 원문2](description-img/4.편의점_page-0002.jpg) -![문제 원문3](description-img/4.편의점_page-0003.jpg) -![문제 원문4](description-img/4.편의점_page-0004.jpg) -![문제 원문5](description-img/4.편의점_page-0005.jpg) -![문제 원문6](description-img/4.편의점_page-0006.jpg) -![문제 원문7](description-img/4.편의점_page-0007.jpg) -![문제 원문8](description-img/4.편의점_page-0008.jpg) -![문제 원문9](description-img/4.편의점_page-0009.jpg) -![문제 원문10](description-img/4.편의점_page-0010.jpg) +# 기능 구현 목록 +1. 환영 인사와 재고 상태를 출력한다. + 1. 프로모션 파일을 읽어서 저장한다. + 2. 재고 파일을 읽어서 저장 후 출력한다. +2. 구매 상품명과 수량을 입력받는다. + - 예외 사항 + 1. 빈 문자열 입력 시 예외 발생 + 2. 형식에 맞지 않으면 예외 발생 +3. 구매 상품명과 수량을 각각 문자열과 숫자로 변환한다. + - 예외 사항 + 1. 수량이 1 이상이 아니면 예외 발생 + 2. 존재하지 않는 상품을 입력하면 예외 발생 + 3. 구매 수량이 재고 수량을 초과하면 예외 발생 +4. 프로모션 적용이 가능한 상품에 대해 고객이 해당 수량보다 적게 가져온 경우, 필요한 수량을 추가로 가져오면 혜택을 받을 수 있음을 안내한다.(해당 상품 종류의 개수만큼 안내 내용을 출력한다.) + - Y + 1. 무료로 더 받을 수 있는 개수만큼 구매 및 증정 상품 목록에 추가한다. + - N + - 다음 안내로 넘어간다. +5. 프로모션 재고가 부족하여 일부 수령을 프로모션 혜택 없이 결제해야 하는 경우, 일부 수량에 대해 정가로 결제하게 됨을 안내한다.(해당 상품 종류의 개수만큼 안내 내용을 출력한다.) + - Y + 1. 프로모션 혜택 없이 일부 수량에 대해 정가로 계산한다. + 2. 구매 및 증정 상품 목록에 추가한다. + - N + 1. 프로모션 혜택 없는 일부 수량을 제외한 나머지를 구매 및 증정 상품 목록에 추가한다. +6. 5, 6번에 해당하지 않는 프로모션 상품을 구매 및 증정 상품 목록에 추가한다. +7. 프로모션에 해당하지 않는 상품을 구매 상품 목록에 추가한다. + - 프로모션 해당하지 않는 상품: 프로모션 값이 null이거나 프로모션 기간에 오늘 날짜가 포함되지 않는 경우 +8. 멤버십 할인을 받을지 유무를 입력받는다. + - Y + 1. 프로모션 적용되지 않은 상품에 대한 총 금액의 30%를 멤버십 할인 금액으로 저장한다. + 2. 만약 멤버십 할인 금액이 8,000원 초과면 멤버십 할인 금액을 8,000원으로 저장한다. + - N + 1. 다음으로 넘어간다. +9. 구매 및 증정 목록에 기반하여 재고를 업데이트한다. +10. 영수증을 출력한다. + - 전체 구매 상품명 및 수량 + - 증정 상품명 및 수량 + - 총 구매액 + - 행사 할인 금액 + - 멤버십 할인 금액 + - 내실돈 +11. 다른 상품을 구매할지 여부를 입력받는다. + - Y + 1. 1번부터 다시 시작 + - N + 1. 프로그램 종료 \ No newline at end of file From 19d76df506f9c12f150886c52f6c6aab35f63bb5 Mon Sep 17 00:00:00 2001 From: khcho96 Date: Wed, 10 Dec 2025 19:40:08 +0900 Subject: [PATCH 02/18] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EB=AA=A8?= =?UTF-8?q?=EC=85=98=20=ED=8C=8C=EC=9D=BC=EC=9D=84=20=EC=9D=BD=EC=96=B4?= =?UTF-8?q?=EC=84=9C=20=EC=A0=80=EC=9E=A5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/store/Application.java | 29 ++++++++++++++++++++-- src/main/java/store/domain/Promotion.java | 24 ++++++++++++++++++ src/main/java/store/domain/Promotions.java | 22 ++++++++++++++++ 3 files changed, 73 insertions(+), 2 deletions(-) create mode 100644 src/main/java/store/domain/Promotion.java create mode 100644 src/main/java/store/domain/Promotions.java diff --git a/src/main/java/store/Application.java b/src/main/java/store/Application.java index ec4afd8b..337921c3 100644 --- a/src/main/java/store/Application.java +++ b/src/main/java/store/Application.java @@ -1,7 +1,32 @@ package store; +import java.io.IOException; +import java.time.LocalDate; +import java.util.List; +import store.domain.Promotions; +import store.util.file.FileReader; + public class Application { - public static void main(String[] args) { - // TODO: 프로그램 구현 + + public static void main(String[] args) throws IOException { + FileReader promotionReader = new FileReader("src/main/resources/promotions.md"); + List readPromotions = promotionReader.readLines(); + Promotions promotions = Promotions.newInstance(); + readPromotions.removeFirst(); + for (String readPromotion : readPromotions) { + String[] split = readPromotion.split(","); + String name = split[0]; + int buy = Integer.parseInt(split[1]); + int get = Integer.parseInt(split[2]); + LocalDate startDate = LocalDate.parse(split[3]); + LocalDate endDate = LocalDate.parse(split[3]); + promotions.addPromotion(name, buy, get, startDate, endDate); + } + + + +// +// OutputView.printStock(); +// InputView.readPurchaseProducts(); } } diff --git a/src/main/java/store/domain/Promotion.java b/src/main/java/store/domain/Promotion.java new file mode 100644 index 00000000..972d1022 --- /dev/null +++ b/src/main/java/store/domain/Promotion.java @@ -0,0 +1,24 @@ +package store.domain; + +import java.time.LocalDate; + +public class Promotion { + + private final String name; + private final int buy; + private final int get; + private final LocalDate startDate; + private final LocalDate endDate; + + private Promotion(String name, int buy, int get, LocalDate startDate, LocalDate endDate) { + this.name = name; + this.buy = buy; + this.get = get; + this.startDate = startDate; + this.endDate = endDate; + } + + public static Promotion of(String name, int buy, int get, LocalDate startDate, LocalDate endDate) { + return new Promotion(name, buy, get, startDate, endDate); + } +} diff --git a/src/main/java/store/domain/Promotions.java b/src/main/java/store/domain/Promotions.java new file mode 100644 index 00000000..c4e69c38 --- /dev/null +++ b/src/main/java/store/domain/Promotions.java @@ -0,0 +1,22 @@ +package store.domain; + +import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; + +public class Promotions { + + private final List promotions; + + private Promotions() { + this.promotions = new ArrayList<>(); + } + + public static Promotions newInstance() { + return new Promotions(); + } + + public void addPromotion(String name, int buy, int get, LocalDate startDate, LocalDate endDate) { + + } +} From c2acd1b38b1ec95bb20f32a88ccee0dbba25eeea Mon Sep 17 00:00:00 2001 From: khcho96 Date: Wed, 10 Dec 2025 20:01:26 +0900 Subject: [PATCH 03/18] =?UTF-8?q?feat:=20=EC=9E=AC=EA=B3=A0=20=ED=8C=8C?= =?UTF-8?q?=EC=9D=BC=20=EC=97=B4=EC=96=B4=EC=84=9C=20=EC=A0=80=EC=9E=A5?= =?UTF-8?q?=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/store/Application.java | 17 +++++- src/main/java/store/domain/Product.java | 69 ++++++++++++++++++++++ src/main/java/store/domain/Products.java | 27 +++++++++ src/main/java/store/domain/Promotion.java | 19 ++++++ src/main/java/store/domain/Promotions.java | 9 +++ 5 files changed, 139 insertions(+), 2 deletions(-) create mode 100644 src/main/java/store/domain/Product.java create mode 100644 src/main/java/store/domain/Products.java diff --git a/src/main/java/store/Application.java b/src/main/java/store/Application.java index 337921c3..32e64d4c 100644 --- a/src/main/java/store/Application.java +++ b/src/main/java/store/Application.java @@ -3,6 +3,8 @@ import java.io.IOException; import java.time.LocalDate; import java.util.List; +import store.domain.Products; +import store.domain.Promotion; import store.domain.Promotions; import store.util.file.FileReader; @@ -11,8 +13,8 @@ public class Application { public static void main(String[] args) throws IOException { FileReader promotionReader = new FileReader("src/main/resources/promotions.md"); List readPromotions = promotionReader.readLines(); - Promotions promotions = Promotions.newInstance(); readPromotions.removeFirst(); + Promotions promotions = Promotions.newInstance(); for (String readPromotion : readPromotions) { String[] split = readPromotion.split(","); String name = split[0]; @@ -23,7 +25,18 @@ public static void main(String[] args) throws IOException { promotions.addPromotion(name, buy, get, startDate, endDate); } - + FileReader fr = new FileReader("src/main/resources/products.md"); + List readProducts = fr.readLines(); + readProducts.removeFirst(); + Products products = Products.newInstance(); + for (String readProduct : readProducts) { + String[] split = readProduct.split(","); + String name = split[0]; + int price = Integer.parseInt(split[1]); + int quantity = Integer.parseInt(split[2]); + Promotion promotion = promotions.get(split[3]); + products.addProduct(name, price, quantity, promotion); + } // // OutputView.printStock(); diff --git a/src/main/java/store/domain/Product.java b/src/main/java/store/domain/Product.java new file mode 100644 index 00000000..19336cb7 --- /dev/null +++ b/src/main/java/store/domain/Product.java @@ -0,0 +1,69 @@ +package store.domain; + +import java.util.Objects; + +public class Product { + + private final String name; + private final int price; + private int promotionQuantity; + private int normalQuantity; + private final Promotion promotion; + + private Product(String name, int price, int promotionQuantity, int normalQuantity, Promotion promotion) { + this.name = name; + this.price = price; + this.promotionQuantity = promotionQuantity; + this.normalQuantity = normalQuantity; + this.promotion = promotion; + } + + public static Product of(String name, int price, int quantity, Promotion promotion) { + if (promotion == null) { + return new Product(name, price, 0, quantity, promotion); + } + return new Product(name, price, quantity, 0, promotion); + } + + public void setQuantity(int quantity, Promotion promotion) { + if (promotion == null) { + normalQuantity = quantity; + return; + } + promotionQuantity = quantity; + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) { + return false; + } + Product product = (Product) object; + return Objects.equals(name, product.name); + } + + @Override + public int hashCode() { + return Objects.hashCode(name); + } + + public String getName() { + return name; + } + + public int getNormalQuantity() { + return normalQuantity; + } + + public int getPrice() { + return price; + } + + public Promotion getPromotion() { + return promotion; + } + + public int getPromotionQuantity() { + return promotionQuantity; + } +} diff --git a/src/main/java/store/domain/Products.java b/src/main/java/store/domain/Products.java new file mode 100644 index 00000000..a8608c17 --- /dev/null +++ b/src/main/java/store/domain/Products.java @@ -0,0 +1,27 @@ +package store.domain; + +import java.util.ArrayList; +import java.util.List; + +public class Products { + + private final List products; + + public Products() { + this.products = new ArrayList<>(); + } + + public static Products newInstance() { + return new Products(); + } + + public void addProduct(String name, int price, int quantity, Promotion promotion) { + for (Product product : products) { + if (product.getName().equals(name)) { + product.setQuantity(quantity, promotion); + return; + } + } + products.add(Product.of(name, price, quantity, promotion)); + } +} diff --git a/src/main/java/store/domain/Promotion.java b/src/main/java/store/domain/Promotion.java index 972d1022..8629479e 100644 --- a/src/main/java/store/domain/Promotion.java +++ b/src/main/java/store/domain/Promotion.java @@ -1,6 +1,7 @@ package store.domain; import java.time.LocalDate; +import java.util.Objects; public class Promotion { @@ -21,4 +22,22 @@ private Promotion(String name, int buy, int get, LocalDate startDate, LocalDate public static Promotion of(String name, int buy, int get, LocalDate startDate, LocalDate endDate) { return new Promotion(name, buy, get, startDate, endDate); } + + public boolean equalsName(String name) { + return this.name.equals(name); + } + + @Override + public boolean equals(Object object) { + if (object == null || getClass() != object.getClass()) { + return false; + } + Promotion promotion = (Promotion) object; + return Objects.equals(name, promotion.name); + } + + @Override + public int hashCode() { + return Objects.hashCode(name); + } } diff --git a/src/main/java/store/domain/Promotions.java b/src/main/java/store/domain/Promotions.java index c4e69c38..480c81d3 100644 --- a/src/main/java/store/domain/Promotions.java +++ b/src/main/java/store/domain/Promotions.java @@ -17,6 +17,15 @@ public static Promotions newInstance() { } public void addPromotion(String name, int buy, int get, LocalDate startDate, LocalDate endDate) { + promotions.add(Promotion.of(name, buy, get, startDate, endDate)); + } + public Promotion get(String promotionName) { + for (Promotion promotion : promotions) { + if (promotion.equalsName(promotionName)) { + return promotion; + } + } + return null; } } From 7980531a3124e5d65309c2d648c463e82af839d6 Mon Sep 17 00:00:00 2001 From: khcho96 Date: Wed, 10 Dec 2025 20:38:32 +0900 Subject: [PATCH 04/18] =?UTF-8?q?feat:=20=ED=99=98=EC=98=81=20=EC=9D=B8?= =?UTF-8?q?=EC=82=AC=EC=99=80=20=EC=9E=AC=EA=B3=A0=20=EC=83=81=ED=83=9C?= =?UTF-8?q?=EB=A5=BC=20=EC=B6=9C=EB=A0=A5=ED=95=98=EB=8A=94=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/store/Application.java | 9 ++++-- src/main/java/store/domain/Product.java | 37 ++++++++++++++++++---- src/main/java/store/domain/Products.java | 33 +++++++++++++++++++ src/main/java/store/domain/Promotion.java | 20 ++++++++++++ src/main/java/store/domain/Promotions.java | 1 + src/main/java/store/dto/StockDto.java | 4 +++ src/main/java/store/view/OutputView.java | 21 ++++++++++++ 7 files changed, 116 insertions(+), 9 deletions(-) create mode 100644 src/main/java/store/dto/StockDto.java create mode 100644 src/main/java/store/view/OutputView.java diff --git a/src/main/java/store/Application.java b/src/main/java/store/Application.java index 32e64d4c..1a37bb18 100644 --- a/src/main/java/store/Application.java +++ b/src/main/java/store/Application.java @@ -6,7 +6,9 @@ import store.domain.Products; import store.domain.Promotion; import store.domain.Promotions; +import store.dto.StockDto; import store.util.file.FileReader; +import store.view.OutputView; public class Application { @@ -27,7 +29,7 @@ public static void main(String[] args) throws IOException { FileReader fr = new FileReader("src/main/resources/products.md"); List readProducts = fr.readLines(); - readProducts.removeFirst(); + String initStock = readProducts.removeFirst() + "\n"; Products products = Products.newInstance(); for (String readProduct : readProducts) { String[] split = readProduct.split(","); @@ -38,8 +40,9 @@ public static void main(String[] args) throws IOException { products.addProduct(name, price, quantity, promotion); } -// -// OutputView.printStock(); + StockDto stockDto = products.getStockDto(); + OutputView.printStock(stockDto); + // InputView.readPurchaseProducts(); } } diff --git a/src/main/java/store/domain/Product.java b/src/main/java/store/domain/Product.java index 19336cb7..2b4dc366 100644 --- a/src/main/java/store/domain/Product.java +++ b/src/main/java/store/domain/Product.java @@ -51,19 +51,44 @@ public String getName() { return name; } - public int getNormalQuantity() { - return normalQuantity; - } - public int getPrice() { return price; } + public int getPromotionQuantity() { + return promotionQuantity; + } + + public int getNormalQuantity() { + return normalQuantity; + } + public Promotion getPromotion() { return promotion; } - public int getPromotionQuantity() { - return promotionQuantity; + public String getNormalQuantityForOutput() { + if (normalQuantity == 0) { + return "재고 없음"; + } + return normalQuantity + "개"; + } + + public String getPriceForOutput() { + return String.format("%,d원", price); + } + + public String getPromotionForOutput() { + if (promotion == null) { + return ""; + } + return promotion.getName(); + } + + public String getPromotionQuantityForOutput() { + if (promotionQuantity == 0) { + return "재고 없음"; + } + return promotionQuantity + "개"; } } diff --git a/src/main/java/store/domain/Products.java b/src/main/java/store/domain/Products.java index a8608c17..e6930469 100644 --- a/src/main/java/store/domain/Products.java +++ b/src/main/java/store/domain/Products.java @@ -2,6 +2,7 @@ import java.util.ArrayList; import java.util.List; +import store.dto.StockDto; public class Products { @@ -15,6 +16,22 @@ public static Products newInstance() { return new Products(); } + public String getStock(String stock) { + StringBuilder st = new StringBuilder(stock); + for (Product product : products) { + if (product.getPromotion() == null) { + st.append(product.getName() + "," + product.getPrice() + "," + product.getNormalQuantity() + "," + + "null" + "\n"); + continue; + } + st.append(product.getName() + "," + product.getPrice() + "," + product.getPromotionQuantity() + "," + + product.getPromotion().getName() + "\n"); + st.append(product.getName() + "," + product.getPrice() + "," + product.getNormalQuantity() + "," + "null" + + "\n"); + } + return st.toString(); + } + public void addProduct(String name, int price, int quantity, Promotion promotion) { for (Product product : products) { if (product.getName().equals(name)) { @@ -24,4 +41,20 @@ public void addProduct(String name, int price, int quantity, Promotion promotion } products.add(Product.of(name, price, quantity, promotion)); } + + public StockDto getStockDto() { + StringBuilder st = new StringBuilder(); + for (Product product : products) { + if (product.getPromotion() == null) { + st.append("- " + product.getName() + " " + product.getPriceForOutput() + " " + product.getNormalQuantityForOutput() + " " + + "\n"); + continue; + } + st.append("- " + product.getName() + " " + product.getPriceForOutput() + " " + product.getPromotionQuantityForOutput() + " " + + product.getPromotionForOutput() + "\n"); + st.append("- " + product.getName() + " " + product.getPriceForOutput() + " " + product.getNormalQuantityForOutput() + " " + + "\n"); + } + return new StockDto(st.toString()); + } } diff --git a/src/main/java/store/domain/Promotion.java b/src/main/java/store/domain/Promotion.java index 8629479e..bcffb76e 100644 --- a/src/main/java/store/domain/Promotion.java +++ b/src/main/java/store/domain/Promotion.java @@ -40,4 +40,24 @@ public boolean equals(Object object) { public int hashCode() { return Objects.hashCode(name); } + + public int getBuy() { + return buy; + } + + public LocalDate getEndDate() { + return endDate; + } + + public int getGet() { + return get; + } + + public String getName() { + return name; + } + + public LocalDate getStartDate() { + return startDate; + } } diff --git a/src/main/java/store/domain/Promotions.java b/src/main/java/store/domain/Promotions.java index 480c81d3..bf80a6cd 100644 --- a/src/main/java/store/domain/Promotions.java +++ b/src/main/java/store/domain/Promotions.java @@ -3,6 +3,7 @@ import java.time.LocalDate; import java.util.ArrayList; import java.util.List; +import store.time.DateTime; public class Promotions { diff --git a/src/main/java/store/dto/StockDto.java b/src/main/java/store/dto/StockDto.java new file mode 100644 index 00000000..3ae797ae --- /dev/null +++ b/src/main/java/store/dto/StockDto.java @@ -0,0 +1,4 @@ +package store.dto; + +public record StockDto(String stock) { +} diff --git a/src/main/java/store/view/OutputView.java b/src/main/java/store/view/OutputView.java new file mode 100644 index 00000000..c5d11a9b --- /dev/null +++ b/src/main/java/store/view/OutputView.java @@ -0,0 +1,21 @@ +package store.view; + +import store.dto.StockDto; + +public class OutputView { + + private static final String WELL_COME_MESSAGE = "안녕하세요. W편의점입니다.\n현재 보유하고 있는 상품입니다.\n"; + + public static void printStock(StockDto stock) { + System.out.println(WELL_COME_MESSAGE); + System.out.println(stock.stock()); + } + + public static void printResult() { + + } + + public static void printErrorMessage(IllegalArgumentException e) { + System.out.println(e.getMessage()); + } +} From e15774d4d7abfdd027ae46449d7a52693d6fc428 Mon Sep 17 00:00:00 2001 From: khcho96 Date: Wed, 10 Dec 2025 21:17:31 +0900 Subject: [PATCH 05/18] =?UTF-8?q?feat:=20=EA=B5=AC=EB=A7=A4=20=EC=83=81?= =?UTF-8?q?=ED=92=88=EB=AA=85=EA=B3=BC=20=EC=88=98=EB=9F=89=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=EA=B3=BC=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=B0=8F=20=ED=8C=8C=EC=8B=B1=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/store/constant/ErrorMessage.java | 20 +++++++++ src/main/java/store/domain/Products.java | 18 ++++++++ src/main/java/store/domain/Promotions.java | 1 - .../store/domain/vo/PurchaseProducts.java | 44 ++++++++++++++++++ src/main/java/store/util/InputParser.java | 38 ++++++++++++++++ src/main/java/store/util/NumberConvertor.java | 12 +++++ src/main/java/store/util/Validator.java | 29 ++++++++++++ src/main/java/store/util/file/FileReader.java | 45 +++++++++++++++++++ src/main/java/store/util/file/FileWriter.java | 43 ++++++++++++++++++ src/main/java/store/view/InputView.java | 13 ++++++ 10 files changed, 262 insertions(+), 1 deletion(-) create mode 100644 src/main/java/store/constant/ErrorMessage.java create mode 100644 src/main/java/store/domain/vo/PurchaseProducts.java create mode 100644 src/main/java/store/util/InputParser.java create mode 100644 src/main/java/store/util/NumberConvertor.java create mode 100644 src/main/java/store/util/Validator.java create mode 100644 src/main/java/store/util/file/FileReader.java create mode 100644 src/main/java/store/util/file/FileWriter.java create mode 100644 src/main/java/store/view/InputView.java diff --git a/src/main/java/store/constant/ErrorMessage.java b/src/main/java/store/constant/ErrorMessage.java new file mode 100644 index 00000000..424e0689 --- /dev/null +++ b/src/main/java/store/constant/ErrorMessage.java @@ -0,0 +1,20 @@ +package store.constant; + +public enum ErrorMessage { + + FORMAT_ERROR("올바르지 않은 형식으로 입력했습니다. 다시 입력해 주세요."), + NO_EXIST_ERROR("존재하지 않는 상품입니다. 다시 입력해 주세요."), + EXCEED_ERROR("재고 수량을 초과하여 구매할 수 없습니다. 다시 입력해 주세요."), + INVALID_ERROR("잘못된 입력입니다. 다시 입력해 주세요."); + + private static final String ERROR_MESSAGE_PREFIX = "[ERROR] "; + private final String errorMessage; + + ErrorMessage(String errorMessage) { + this.errorMessage = errorMessage; + } + + public String getErrorMessage() { + return ERROR_MESSAGE_PREFIX + errorMessage; + } +} diff --git a/src/main/java/store/domain/Products.java b/src/main/java/store/domain/Products.java index e6930469..fc7487fa 100644 --- a/src/main/java/store/domain/Products.java +++ b/src/main/java/store/domain/Products.java @@ -57,4 +57,22 @@ public StockDto getStockDto() { } return new StockDto(st.toString()); } + + public boolean contains(String productName) { + for (Product product : products) { + if (product.getName().equals(productName)) { + return true; + } + } + return false; + } + + public int getQuantityForProduct(String productName) { + for (Product product : products) { + if (product.getName().equals(productName)) { + return product.getPromotionQuantity() + product.getNormalQuantity(); + } + } + return 0; + } } diff --git a/src/main/java/store/domain/Promotions.java b/src/main/java/store/domain/Promotions.java index bf80a6cd..480c81d3 100644 --- a/src/main/java/store/domain/Promotions.java +++ b/src/main/java/store/domain/Promotions.java @@ -3,7 +3,6 @@ import java.time.LocalDate; import java.util.ArrayList; import java.util.List; -import store.time.DateTime; public class Promotions { diff --git a/src/main/java/store/domain/vo/PurchaseProducts.java b/src/main/java/store/domain/vo/PurchaseProducts.java new file mode 100644 index 00000000..b706a7f6 --- /dev/null +++ b/src/main/java/store/domain/vo/PurchaseProducts.java @@ -0,0 +1,44 @@ +package store.domain.vo; + +import static store.constant.ErrorMessage.EXCEED_ERROR; +import static store.constant.ErrorMessage.NO_EXIST_ERROR; + +import java.util.HashMap; +import java.util.Map; +import store.domain.Products; + +public class PurchaseProducts { + + private final Map purchaseProducts; + + private PurchaseProducts() { + this.purchaseProducts = new HashMap<>(); + } + + public static PurchaseProducts newInstance() { + return new PurchaseProducts(); + } + + public void addProduct(Products products, String productName) { + validateNoExist(products, productName); + validateMax(products, productName); + purchaseProducts.put(productName, purchaseProducts.getOrDefault(productName, 0) + 1); + } + + private void validateMax(Products products, String productName) { + if (products.getQuantityForProduct(productName) <= purchaseProducts.getOrDefault(productName, 0)) { + throw new IllegalArgumentException(EXCEED_ERROR.getErrorMessage()); + } + } + + private void validateNoExist(Products products, String productName) { + if (!products.contains(productName)) { + throw new IllegalArgumentException(NO_EXIST_ERROR.getErrorMessage()); + } + } + + @Override + public String toString() { + return purchaseProducts.toString(); + } +} diff --git a/src/main/java/store/util/InputParser.java b/src/main/java/store/util/InputParser.java new file mode 100644 index 00000000..181446a9 --- /dev/null +++ b/src/main/java/store/util/InputParser.java @@ -0,0 +1,38 @@ +package store.util; + +import java.util.ArrayList; +import java.util.List; +import java.util.stream.Stream; + +public final class InputParser { + + private static final String DELIMITER = ","; + private static final String FIRST_DELIMITER = ","; + private static final String SECOND_DELIMITER = "-"; + + private InputParser() { + } + + public static List parsePurchaseProducts(String rawInput) { + Validator.validateNullOrBlank(rawInput); + rawInput = rawInput.strip(); + + Validator.validateCsvFormat(rawInput); + + List purchaseProducts = new ArrayList<>(); + String[] split = rawInput.split(FIRST_DELIMITER); + for (String s : split) { + String[] order = s.strip().substring(1, s.length() - 1).split(SECOND_DELIMITER); + String name = order[0]; + int quantity = NumberConvertor.convertToNumber(order[1]); + + Validator.validateQuantity(quantity); + + for (int i = 0; i < quantity; i++) { + purchaseProducts.add(name); + } + } + + return purchaseProducts; + } +} diff --git a/src/main/java/store/util/NumberConvertor.java b/src/main/java/store/util/NumberConvertor.java new file mode 100644 index 00000000..45682207 --- /dev/null +++ b/src/main/java/store/util/NumberConvertor.java @@ -0,0 +1,12 @@ +package store.util; + +public final class NumberConvertor { + + public static Integer convertToNumber(String input) { + try { + return Integer.parseInt(input); + } catch (NumberFormatException e) { + throw new IllegalArgumentException(); + } + } +} diff --git a/src/main/java/store/util/Validator.java b/src/main/java/store/util/Validator.java new file mode 100644 index 00000000..e76ee0e2 --- /dev/null +++ b/src/main/java/store/util/Validator.java @@ -0,0 +1,29 @@ +package store.util; + +import static store.constant.ErrorMessage.FORMAT_ERROR; +import static store.constant.ErrorMessage.INVALID_ERROR; + +public final class Validator { + + private static final String CSV_FORMAT = "^ *(\\[[가-힣a-zA-Z]+-\\d+])+ *(, *(\\[[가-힣a-zA-Z]+-\\d+])+ *)*$"; + + private Validator() {} + + public static void validateNullOrBlank(String input) { + if (input == null || input.isBlank()) { + throw new IllegalArgumentException(INVALID_ERROR.getErrorMessage()); + } + } + + public static void validateCsvFormat(String input) { + if (!input.matches(CSV_FORMAT)) { + throw new IllegalArgumentException(FORMAT_ERROR.getErrorMessage()); + } + } + + public static void validateQuantity(int quantity) { + if (quantity <= 0) { + throw new IllegalArgumentException(INVALID_ERROR.getErrorMessage()); + } + } +} diff --git a/src/main/java/store/util/file/FileReader.java b/src/main/java/store/util/file/FileReader.java new file mode 100644 index 00000000..438e9a1b --- /dev/null +++ b/src/main/java/store/util/file/FileReader.java @@ -0,0 +1,45 @@ +package store.util.file; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.BufferedReader; +import java.io.IOException; +import java.util.ArrayList; +import java.util.List; + +public class FileReader { + + private static final int BUFFER_SIZE = 8192; + + private final java.io.FileReader fr; + + public FileReader(String fileName) throws IOException { + fr = new java.io.FileReader(fileName, UTF_8); + } + + // 파일 전체를 한 번에 읽기 + public String readAll() throws IOException { + StringBuilder contents = new StringBuilder(); + int ch; + while ((ch = fr.read()) != -1) { + contents.append((char) ch); + } + fr.close(); + + return contents.toString(); + } + + // 파일 전체를 한 줄 씩 나눠서 읽기 + public List readLines() throws IOException { + List contents = new ArrayList<>(); + BufferedReader br = new BufferedReader(fr, BUFFER_SIZE); + + String line; + while ((line = br.readLine()) != null) { + contents.add(line); + } + br.close(); + + return new ArrayList<>(contents); + } +} diff --git a/src/main/java/store/util/file/FileWriter.java b/src/main/java/store/util/file/FileWriter.java new file mode 100644 index 00000000..c69bc15d --- /dev/null +++ b/src/main/java/store/util/file/FileWriter.java @@ -0,0 +1,43 @@ +package store.util.file; + +import static java.nio.charset.StandardCharsets.UTF_8; + +import java.io.IOException; + +public class FileWriter { + + private final String fileName; + private java.io.FileWriter fw; + + public FileWriter(String fileName) { + this.fileName = fileName; + } + + // 파일에 내용 한 번에 쓰기 - append 기본값(false) + public void writeAll(String contents) throws IOException { + fw = new java.io.FileWriter(fileName, UTF_8, false); + fw.write(contents); + fw.close(); + } + + // 파일에 내용 한 번에 쓰기 - append 선택 + public void writeAll(String contents, boolean append) throws IOException { + fw = new java.io.FileWriter(fileName, UTF_8, append); + fw.write(contents); + fw.close(); + } + + // 파일에 내용 한 줄씩 쓰기 - append 기본값(false) + public void writeLine(String contents) throws IOException { + fw = new java.io.FileWriter(fileName, UTF_8, false); + fw.write(contents); + fw.close(); + } + + // 파일에 내용 한 줄씩 쓰기 - append 선택 + public void writeLine(String contents, boolean append) throws IOException { + fw = new java.io.FileWriter(fileName, UTF_8, append); + fw.write(contents); + fw.close(); + } +} diff --git a/src/main/java/store/view/InputView.java b/src/main/java/store/view/InputView.java new file mode 100644 index 00000000..5d2e3b14 --- /dev/null +++ b/src/main/java/store/view/InputView.java @@ -0,0 +1,13 @@ +package store.view; + +import camp.nextstep.edu.missionutils.Console; + +public class InputView { + + private static final String PURCHASE_PRODUCTS_REQUEST = "구매하실 상품명과 수량을 입력해주세요. (예: [사이다-2],[감자칩-1])"; + + public static String readPurchaseProducts() { + System.out.println(PURCHASE_PRODUCTS_REQUEST); + return Console.readLine(); + } +} From 03a607d0fb7a0526dd6eb6acbda691cbef9ea0d2 Mon Sep 17 00:00:00 2001 From: khcho96 Date: Wed, 10 Dec 2025 21:18:13 +0900 Subject: [PATCH 06/18] =?UTF-8?q?feat:=20=EA=B5=AC=EB=A7=A4=20=EC=83=81?= =?UTF-8?q?=ED=92=88=EB=AA=85=EA=B3=BC=20=EC=88=98=EB=9F=89=20=EC=9E=85?= =?UTF-8?q?=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=EA=B3=BC=20=EA=B2=80=EC=A6=9D=20?= =?UTF-8?q?=EB=B0=8F=20=ED=8C=8C=EC=8B=B1=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/store/Application.java | 14 +++++++++++++- 1 file changed, 13 insertions(+), 1 deletion(-) diff --git a/src/main/java/store/Application.java b/src/main/java/store/Application.java index 1a37bb18..7b42af62 100644 --- a/src/main/java/store/Application.java +++ b/src/main/java/store/Application.java @@ -1,13 +1,18 @@ package store; +import static store.constant.ErrorMessage.NO_EXIST_ERROR; + import java.io.IOException; import java.time.LocalDate; import java.util.List; import store.domain.Products; import store.domain.Promotion; import store.domain.Promotions; +import store.domain.vo.PurchaseProducts; import store.dto.StockDto; +import store.util.InputParser; import store.util.file.FileReader; +import store.view.InputView; import store.view.OutputView; public class Application { @@ -43,6 +48,13 @@ public static void main(String[] args) throws IOException { StockDto stockDto = products.getStockDto(); OutputView.printStock(stockDto); -// InputView.readPurchaseProducts(); + String rawPurchaseProducts = InputView.readPurchaseProducts(); + List parsePurchaseProducts = InputParser.parsePurchaseProducts(rawPurchaseProducts); + PurchaseProducts purchaseProducts = PurchaseProducts.newInstance(); + for (String productName : parsePurchaseProducts) { + purchaseProducts.addProduct(products, productName); + } + + } } From c4f213e28d083d857242de19750a6c5e7d6820eb Mon Sep 17 00:00:00 2001 From: khcho96 Date: Fri, 12 Dec 2025 17:04:08 +0900 Subject: [PATCH 07/18] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EB=AA=A8?= =?UTF-8?q?=EC=85=98=20=EC=A0=81=EC=9A=A9=EC=9D=B4=20=EA=B0=80=EB=8A=A5?= =?UTF-8?q?=ED=95=9C=20=EC=83=81=ED=92=88=EC=97=90=20=EB=8C=80=ED=95=B4=20?= =?UTF-8?q?=EA=B3=A0=EA=B0=9D=EC=9D=B4=20=ED=95=B4=EB=8B=B9=20=EC=88=98?= =?UTF-8?q?=EB=9F=89=EB=B3=B4=EB=8B=A4=20=EC=A0=81=EA=B2=8C=20=EA=B0=80?= =?UTF-8?q?=EC=A0=B8=EC=98=A8=20=EA=B2=BD=EC=9A=B0=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/store/Application.java | 33 ++++++++++++++- src/main/java/store/domain/Customer.java | 41 +++++++++++++++++++ src/main/java/store/domain/Product.java | 32 +++++++++++++++ src/main/java/store/domain/Products.java | 4 ++ .../store/domain/vo/PurchaseProducts.java | 21 ++++++++++ src/main/java/store/time/DateTime.java | 11 +++++ src/main/java/store/util/InputParser.java | 9 +++- src/main/java/store/util/Validator.java | 7 ++++ src/main/java/store/view/InputView.java | 6 +++ src/main/resources/promotions.md | 6 +-- 10 files changed, 164 insertions(+), 6 deletions(-) create mode 100644 src/main/java/store/domain/Customer.java create mode 100644 src/main/java/store/time/DateTime.java diff --git a/src/main/java/store/Application.java b/src/main/java/store/Application.java index 7b42af62..ae17342e 100644 --- a/src/main/java/store/Application.java +++ b/src/main/java/store/Application.java @@ -5,6 +5,9 @@ import java.io.IOException; import java.time.LocalDate; import java.util.List; +import java.util.Map; +import store.domain.Customer; +import store.domain.Product; import store.domain.Products; import store.domain.Promotion; import store.domain.Promotions; @@ -28,13 +31,13 @@ public static void main(String[] args) throws IOException { int buy = Integer.parseInt(split[1]); int get = Integer.parseInt(split[2]); LocalDate startDate = LocalDate.parse(split[3]); - LocalDate endDate = LocalDate.parse(split[3]); + LocalDate endDate = LocalDate.parse(split[4]); promotions.addPromotion(name, buy, get, startDate, endDate); } FileReader fr = new FileReader("src/main/resources/products.md"); List readProducts = fr.readLines(); - String initStock = readProducts.removeFirst() + "\n"; + readProducts.removeFirst(); Products products = Products.newInstance(); for (String readProduct : readProducts) { String[] split = readProduct.split(","); @@ -55,6 +58,32 @@ public static void main(String[] args) throws IOException { purchaseProducts.addProduct(products, productName); } + // 4 + Customer customer = Customer.newInstance(); + Map lessPromotionProducts = purchaseProducts.getLessPromotionProducts(products); + for (Product product : lessPromotionProducts.keySet()) { + int free = product.getFreeProductQuantity(); + + String rawChoice = InputView.readFreeProductChoice(product.getName(), free); + boolean choice = InputParser.parseChoice(rawChoice); + int purchaseQuantity = lessPromotionProducts.get(product); + if (choice) { + customer.addProduct(product, purchaseQuantity + free); + continue; + } + customer.addProduct(product, purchaseQuantity); + } + + // 5 +// purchaseProducts.getMorePromotionProducts(products); + + // 6 +// purchaseProducts.getExactPromotionProducts(products); + + // 7 +// purchaseProducts.getNoPromotionProducts(products); + + // 8 } } diff --git a/src/main/java/store/domain/Customer.java b/src/main/java/store/domain/Customer.java new file mode 100644 index 00000000..2984e2a2 --- /dev/null +++ b/src/main/java/store/domain/Customer.java @@ -0,0 +1,41 @@ +package store.domain; + +import java.util.HashMap; +import java.util.List; +import java.util.Map; + +public class Customer { + + private final Map> purchaseProducts; + private final Map> presentProducts; + + private Customer() { + purchaseProducts = new HashMap<>(); + presentProducts = new HashMap<>(); + } + + public static Customer newInstance() { + return new Customer(); + } + + public void addProduct(Product product, int purchaseQuantity) { + // 구매 상품 + int buy = product.getPromotion().getBuy(); + int get = product.getPromotion().getGet(); + int setSize = buy + get; + int setQuantity = purchaseQuantity / setSize; + + int presentQuantity = get * setQuantity; + purchaseProducts.put(product, List.of(purchaseQuantity, presentQuantity)); + + product.updateStock(purchaseQuantity ,0); + } + + public Map> getPresentProducts() { + return presentProducts; + } + + public Map> getPurchaseProducts() { + return purchaseProducts; + } +} diff --git a/src/main/java/store/domain/Product.java b/src/main/java/store/domain/Product.java index 2b4dc366..74fbe166 100644 --- a/src/main/java/store/domain/Product.java +++ b/src/main/java/store/domain/Product.java @@ -1,6 +1,8 @@ package store.domain; +import java.time.LocalDate; import java.util.Objects; +import store.time.DateTime; public class Product { @@ -91,4 +93,34 @@ public String getPromotionQuantityForOutput() { } return promotionQuantity + "개"; } + + public boolean isPromotion() { + LocalDate now = DateTime.now(); + return promotion != null + && !now.isBefore(promotion.getStartDate()) + && !now.isAfter(promotion.getEndDate()); + } + + public boolean isLessPromotionProduct(int purchaseQuantity) { + int buy = promotion.getBuy(); + int get = promotion.getGet(); + int mod = purchaseQuantity % (buy + get); + + if (purchaseQuantity >= promotionQuantity) { + return false; + } + + int maxPurchaseQuantity = purchaseQuantity + (buy + get - mod); + + return buy <= mod && maxPurchaseQuantity <= promotionQuantity; + } + + public int getFreeProductQuantity() { + return promotion.getGet(); + } + + public void updateStock(int promotionQuantity, int normalQuantity) { + this.promotionQuantity -= promotionQuantity; + this.normalQuantity -= normalQuantity; + } } diff --git a/src/main/java/store/domain/Products.java b/src/main/java/store/domain/Products.java index fc7487fa..df258ae7 100644 --- a/src/main/java/store/domain/Products.java +++ b/src/main/java/store/domain/Products.java @@ -75,4 +75,8 @@ public int getQuantityForProduct(String productName) { } return 0; } + + public List getProducts() { + return products; + } } diff --git a/src/main/java/store/domain/vo/PurchaseProducts.java b/src/main/java/store/domain/vo/PurchaseProducts.java index b706a7f6..adbc0061 100644 --- a/src/main/java/store/domain/vo/PurchaseProducts.java +++ b/src/main/java/store/domain/vo/PurchaseProducts.java @@ -5,6 +5,7 @@ import java.util.HashMap; import java.util.Map; +import store.domain.Product; import store.domain.Products; public class PurchaseProducts { @@ -41,4 +42,24 @@ private void validateNoExist(Products products, String productName) { public String toString() { return purchaseProducts.toString(); } + + public Map getPurchaseProducts() { + return purchaseProducts; + } + + public Map getLessPromotionProducts(Products products) { + Map lessProducts = new HashMap<>(); + for (Product product : products.getProducts()) { + // 프로모션인지 또는 구매 상품인지 판단 + if (!product.isPromotion() || !purchaseProducts.containsKey(product.getName())) { + continue; + } + + // 주문 개수 < 프로모션 적용 개수인 상품은 (주문 개수, 증정 개수)를 값으로 저장 + if (product.isLessPromotionProduct(purchaseProducts.get(product.getName()))) { + lessProducts.put(product, purchaseProducts.get(product.getName())); + } + } + return lessProducts; + } } diff --git a/src/main/java/store/time/DateTime.java b/src/main/java/store/time/DateTime.java new file mode 100644 index 00000000..2647ab6a --- /dev/null +++ b/src/main/java/store/time/DateTime.java @@ -0,0 +1,11 @@ +package store.time; + +import camp.nextstep.edu.missionutils.DateTimes; +import java.time.LocalDate; + +public class DateTime { + + public static LocalDate now() { + return DateTimes.now().toLocalDate(); + } +} diff --git a/src/main/java/store/util/InputParser.java b/src/main/java/store/util/InputParser.java index 181446a9..c107aab0 100644 --- a/src/main/java/store/util/InputParser.java +++ b/src/main/java/store/util/InputParser.java @@ -6,9 +6,9 @@ public final class InputParser { - private static final String DELIMITER = ","; private static final String FIRST_DELIMITER = ","; private static final String SECOND_DELIMITER = "-"; + private static final String YES = "Y"; private InputParser() { } @@ -35,4 +35,11 @@ public static List parsePurchaseProducts(String rawInput) { return purchaseProducts; } + + public static boolean parseChoice(String rawChoice) { + Validator.validateNullOrBlank(rawChoice); + rawChoice = rawChoice.strip(); + Validator.validateChoiceFormat(rawChoice); + return rawChoice.equals(YES); + } } diff --git a/src/main/java/store/util/Validator.java b/src/main/java/store/util/Validator.java index e76ee0e2..1ddb8bdd 100644 --- a/src/main/java/store/util/Validator.java +++ b/src/main/java/store/util/Validator.java @@ -6,6 +6,7 @@ public final class Validator { private static final String CSV_FORMAT = "^ *(\\[[가-힣a-zA-Z]+-\\d+])+ *(, *(\\[[가-힣a-zA-Z]+-\\d+])+ *)*$"; + private static final String YES_NO = "[YN]"; private Validator() {} @@ -26,4 +27,10 @@ public static void validateQuantity(int quantity) { throw new IllegalArgumentException(INVALID_ERROR.getErrorMessage()); } } + + public static void validateChoiceFormat(String rawChoice) { + if (!rawChoice.matches(YES_NO)) { + throw new IllegalArgumentException(INVALID_ERROR.getErrorMessage()); + } + } } diff --git a/src/main/java/store/view/InputView.java b/src/main/java/store/view/InputView.java index 5d2e3b14..cbff565d 100644 --- a/src/main/java/store/view/InputView.java +++ b/src/main/java/store/view/InputView.java @@ -5,9 +5,15 @@ public class InputView { private static final String PURCHASE_PRODUCTS_REQUEST = "구매하실 상품명과 수량을 입력해주세요. (예: [사이다-2],[감자칩-1])"; + private static final String FREE_PRODUCTS_REQUEST = "\n현재 %s은(는) %d개를 무료로 더 받을 수 있습니다. 추가하시겠습니까? (Y/N)"; public static String readPurchaseProducts() { System.out.println(PURCHASE_PRODUCTS_REQUEST); return Console.readLine(); } + + public static String readFreeProductChoice(String name, int free) { + System.out.printf(FREE_PRODUCTS_REQUEST + "\n", name, free); + return Console.readLine(); + } } diff --git a/src/main/resources/promotions.md b/src/main/resources/promotions.md index d89887ce..6398c046 100644 --- a/src/main/resources/promotions.md +++ b/src/main/resources/promotions.md @@ -1,4 +1,4 @@ name,buy,get,start_date,end_date -탄산2+1,2,1,2024-01-01,2024-12-31 -MD추천상품,1,1,2024-01-01,2024-12-31 -반짝할인,1,1,2024-11-01,2024-11-30 +탄산2+1,2,1,2025-01-01,2025-12-31 +MD추천상품,1,1,2025-01-01,2025-12-31 +반짝할인,1,1,2025-11-01,2025-11-30 From abfe25326679b857c52f1677da528bde772111fe Mon Sep 17 00:00:00 2001 From: khcho96 Date: Fri, 12 Dec 2025 18:12:43 +0900 Subject: [PATCH 08/18] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EB=AA=A8?= =?UTF-8?q?=EC=85=98=20=EC=9E=AC=EA=B3=A0=EA=B0=80=20=EB=B6=80=EC=A1=B1?= =?UTF-8?q?=ED=95=98=EC=97=AC=20=EC=9D=BC=EB=B6=80=20=EC=88=98=EB=A0=B9?= =?UTF-8?q?=EC=9D=84=20=ED=94=84=EB=A1=9C=EB=AA=A8=EC=85=98=20=ED=98=9C?= =?UTF-8?q?=ED=83=9D=20=EC=97=86=EC=9D=B4=20=EA=B2=B0=EC=A0=9C=ED=95=B4?= =?UTF-8?q?=EC=95=BC=20=ED=95=98=EB=8A=94=20=EA=B2=BD=EC=9A=B0=20=EA=B8=B0?= =?UTF-8?q?=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/store/Application.java | 22 +++++++++++-- src/main/java/store/domain/Customer.java | 8 +++-- src/main/java/store/domain/Product.java | 33 +++++++++++++++++-- .../store/domain/vo/PurchaseProducts.java | 21 +++++++++++- src/main/java/store/view/InputView.java | 10 ++++-- 5 files changed, 83 insertions(+), 11 deletions(-) diff --git a/src/main/java/store/Application.java b/src/main/java/store/Application.java index ae17342e..e319fcab 100644 --- a/src/main/java/store/Application.java +++ b/src/main/java/store/Application.java @@ -1,7 +1,5 @@ package store; -import static store.constant.ErrorMessage.NO_EXIST_ERROR; - import java.io.IOException; import java.time.LocalDate; import java.util.List; @@ -67,6 +65,7 @@ public static void main(String[] args) throws IOException { String rawChoice = InputView.readFreeProductChoice(product.getName(), free); boolean choice = InputParser.parseChoice(rawChoice); + int purchaseQuantity = lessPromotionProducts.get(product); if (choice) { customer.addProduct(product, purchaseQuantity + free); @@ -76,11 +75,28 @@ public static void main(String[] args) throws IOException { } // 5 -// purchaseProducts.getMorePromotionProducts(products); + Map morePromotionProducts = purchaseProducts.getMorePromotionProducts(products); + for (Product product : morePromotionProducts.keySet()) { + int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); + int impossiblePromotionQuantity = product.getImpossiblePromotionQuantity(purchaseQuantity); + + String rawChoice = InputView.readImpossiblePromotionChoice(product.getName(), impossiblePromotionQuantity); + boolean choice = InputParser.parseChoice(rawChoice); + + if (choice) { + customer.addProduct(product, purchaseQuantity); + continue; + } + customer.addProduct(product, purchaseQuantity - impossiblePromotionQuantity); + } // 6 // purchaseProducts.getExactPromotionProducts(products); +// OutputView.printStock(products.getStockDto()); +// System.out.println(customer.getPurchaseProducts()); +// System.out.println(customer.getPresentProducts()); + // 7 // purchaseProducts.getNoPromotionProducts(products); diff --git a/src/main/java/store/domain/Customer.java b/src/main/java/store/domain/Customer.java index 2984e2a2..cfe45e34 100644 --- a/src/main/java/store/domain/Customer.java +++ b/src/main/java/store/domain/Customer.java @@ -24,11 +24,15 @@ public void addProduct(Product product, int purchaseQuantity) { int get = product.getPromotion().getGet(); int setSize = buy + get; int setQuantity = purchaseQuantity / setSize; - + int setQuantityNotEqual = product.getPromotionQuantity() / setSize; + if (setQuantity > setQuantityNotEqual) { + setQuantity = setQuantityNotEqual; + } int presentQuantity = get * setQuantity; + purchaseProducts.put(product, List.of(purchaseQuantity, presentQuantity)); - product.updateStock(purchaseQuantity ,0); + product.updateStock(purchaseQuantity); } public Map> getPresentProducts() { diff --git a/src/main/java/store/domain/Product.java b/src/main/java/store/domain/Product.java index 74fbe166..b9107c08 100644 --- a/src/main/java/store/domain/Product.java +++ b/src/main/java/store/domain/Product.java @@ -115,12 +115,39 @@ public boolean isLessPromotionProduct(int purchaseQuantity) { return buy <= mod && maxPurchaseQuantity <= promotionQuantity; } + public boolean isMorePromotionProduct(int purchaseQuantity) { + int buy = promotion.getBuy(); + int get = promotion.getGet(); + int setSize = buy + get; + int mod = purchaseQuantity % setSize; + int minPromotionQuantity = purchaseQuantity - mod; + + return minPromotionQuantity > promotionQuantity; + } + public int getFreeProductQuantity() { return promotion.getGet(); } - public void updateStock(int promotionQuantity, int normalQuantity) { - this.promotionQuantity -= promotionQuantity; - this.normalQuantity -= normalQuantity; + public void updateStock(int quantity) { + if (this.promotionQuantity >= quantity) { + this.promotionQuantity -= quantity; + return; + } + + quantity -= this.promotionQuantity; + this.promotionQuantity = 0; + + this.normalQuantity -= quantity; + } + + public int getImpossiblePromotionQuantity(int purchaseQuantity) { + int buy = promotion.getBuy(); + int get = promotion.getGet(); + int setSize = buy + get; + int mod = promotionQuantity % setSize; + int maxPromotionQuantity = promotionQuantity - mod; + + return purchaseQuantity - maxPromotionQuantity; } } diff --git a/src/main/java/store/domain/vo/PurchaseProducts.java b/src/main/java/store/domain/vo/PurchaseProducts.java index adbc0061..11490a13 100644 --- a/src/main/java/store/domain/vo/PurchaseProducts.java +++ b/src/main/java/store/domain/vo/PurchaseProducts.java @@ -55,11 +55,30 @@ public Map getLessPromotionProducts(Products products) { continue; } - // 주문 개수 < 프로모션 적용 개수인 상품은 (주문 개수, 증정 개수)를 값으로 저장 + // 주문 개수 < 프로모션 적용 개수인 상품은 주문 개수를 값으로 저장 if (product.isLessPromotionProduct(purchaseProducts.get(product.getName()))) { lessProducts.put(product, purchaseProducts.get(product.getName())); } } return lessProducts; } + + public Map getMorePromotionProducts(Products products) { + Map moreProducts = new HashMap<>(); + for (Product product : products.getProducts()) { + // 프로모션인지 또는 구매 상품인지 판단 + if (!product.isPromotion() || !purchaseProducts.containsKey(product.getName())) { + continue; + } + + if (product.isMorePromotionProduct(purchaseProducts.get(product.getName()))) { + moreProducts.put(product, purchaseProducts.get(product.getName())); + } + } + return moreProducts; + } + + public int getPurchaseQuantity(Product product) { + return purchaseProducts.get(product.getName()); + } } diff --git a/src/main/java/store/view/InputView.java b/src/main/java/store/view/InputView.java index cbff565d..3ce1f590 100644 --- a/src/main/java/store/view/InputView.java +++ b/src/main/java/store/view/InputView.java @@ -5,7 +5,8 @@ public class InputView { private static final String PURCHASE_PRODUCTS_REQUEST = "구매하실 상품명과 수량을 입력해주세요. (예: [사이다-2],[감자칩-1])"; - private static final String FREE_PRODUCTS_REQUEST = "\n현재 %s은(는) %d개를 무료로 더 받을 수 있습니다. 추가하시겠습니까? (Y/N)"; + private static final String FREE_PRODUCTS_REQUEST = "\n현재 %s은(는) %d개를 무료로 더 받을 수 있습니다. 추가하시겠습니까? (Y/N)\n"; + private static final String IMPOSSIBLE_PROMOTION_REQUEST = "\n현재 %s %d개는 프로모션 할인이 적용되지 않습니다. 그래도 구매하시겠습니까? (Y/N)\n"; public static String readPurchaseProducts() { System.out.println(PURCHASE_PRODUCTS_REQUEST); @@ -13,7 +14,12 @@ public static String readPurchaseProducts() { } public static String readFreeProductChoice(String name, int free) { - System.out.printf(FREE_PRODUCTS_REQUEST + "\n", name, free); + System.out.printf(FREE_PRODUCTS_REQUEST, name, free); + return Console.readLine(); + } + + public static String readImpossiblePromotionChoice(String name, int impossiblePromotionQuantity) { + System.out.printf(IMPOSSIBLE_PROMOTION_REQUEST, name, impossiblePromotionQuantity); return Console.readLine(); } } From 230d2dd9e96f8fc3ae0da6ab894efdba36144e6c Mon Sep 17 00:00:00 2001 From: khcho96 Date: Fri, 12 Dec 2025 18:24:50 +0900 Subject: [PATCH 09/18] =?UTF-8?q?feat:=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20=EB=AA=A9=EB=A1=9D=205,6=EB=B2=88=EC=97=90=20?= =?UTF-8?q?=ED=95=B4=EB=8B=B9=ED=95=98=EC=A7=80=20=EC=95=8A=EB=8A=94=20?= =?UTF-8?q?=ED=94=84=EB=A1=9C=EB=AA=A8=EC=85=98=20=EC=83=81=ED=92=88?= =?UTF-8?q?=EC=9D=84=20=EA=B5=AC=EB=A7=A4=20=EB=B0=8F=20=EC=A6=9D=EC=A0=95?= =?UTF-8?q?=20=EC=83=81=ED=92=88=20=EB=AA=A9=EB=A1=9D=EC=97=90=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=ED=95=9C=EB=8B=A4.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/store/Application.java | 22 ++++++++------ .../store/domain/vo/PurchaseProducts.java | 30 +++++++++++++++---- 2 files changed, 37 insertions(+), 15 deletions(-) diff --git a/src/main/java/store/Application.java b/src/main/java/store/Application.java index e319fcab..e858480e 100644 --- a/src/main/java/store/Application.java +++ b/src/main/java/store/Application.java @@ -59,14 +59,14 @@ public static void main(String[] args) throws IOException { // 4 Customer customer = Customer.newInstance(); - Map lessPromotionProducts = purchaseProducts.getLessPromotionProducts(products); - for (Product product : lessPromotionProducts.keySet()) { + List lessPromotionProducts = purchaseProducts.getLessPromotionProducts(products); + for (Product product : lessPromotionProducts) { + int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); int free = product.getFreeProductQuantity(); String rawChoice = InputView.readFreeProductChoice(product.getName(), free); boolean choice = InputParser.parseChoice(rawChoice); - int purchaseQuantity = lessPromotionProducts.get(product); if (choice) { customer.addProduct(product, purchaseQuantity + free); continue; @@ -75,8 +75,8 @@ public static void main(String[] args) throws IOException { } // 5 - Map morePromotionProducts = purchaseProducts.getMorePromotionProducts(products); - for (Product product : morePromotionProducts.keySet()) { + List morePromotionProducts = purchaseProducts.getMorePromotionProducts(products); + for (Product product : morePromotionProducts) { int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); int impossiblePromotionQuantity = product.getImpossiblePromotionQuantity(purchaseQuantity); @@ -91,15 +91,19 @@ public static void main(String[] args) throws IOException { } // 6 -// purchaseProducts.getExactPromotionProducts(products); + List normalPromotionProducts = purchaseProducts.getNormalPromotionProducts(products); + for (Product product : normalPromotionProducts) { + int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); + customer.addProduct(product, purchaseQuantity); + } + // 7 +// purchaseProducts.getNoPromotionProducts(products); +// // OutputView.printStock(products.getStockDto()); // System.out.println(customer.getPurchaseProducts()); // System.out.println(customer.getPresentProducts()); - // 7 -// purchaseProducts.getNoPromotionProducts(products); - // 8 } } diff --git a/src/main/java/store/domain/vo/PurchaseProducts.java b/src/main/java/store/domain/vo/PurchaseProducts.java index 11490a13..f20e0ccd 100644 --- a/src/main/java/store/domain/vo/PurchaseProducts.java +++ b/src/main/java/store/domain/vo/PurchaseProducts.java @@ -3,7 +3,9 @@ import static store.constant.ErrorMessage.EXCEED_ERROR; import static store.constant.ErrorMessage.NO_EXIST_ERROR; +import java.util.ArrayList; import java.util.HashMap; +import java.util.List; import java.util.Map; import store.domain.Product; import store.domain.Products; @@ -47,8 +49,8 @@ public Map getPurchaseProducts() { return purchaseProducts; } - public Map getLessPromotionProducts(Products products) { - Map lessProducts = new HashMap<>(); + public List getLessPromotionProducts(Products products) { + List lessProducts = new ArrayList<>(); for (Product product : products.getProducts()) { // 프로모션인지 또는 구매 상품인지 판단 if (!product.isPromotion() || !purchaseProducts.containsKey(product.getName())) { @@ -57,14 +59,14 @@ public Map getLessPromotionProducts(Products products) { // 주문 개수 < 프로모션 적용 개수인 상품은 주문 개수를 값으로 저장 if (product.isLessPromotionProduct(purchaseProducts.get(product.getName()))) { - lessProducts.put(product, purchaseProducts.get(product.getName())); + lessProducts.add(product); } } return lessProducts; } - public Map getMorePromotionProducts(Products products) { - Map moreProducts = new HashMap<>(); + public List getMorePromotionProducts(Products products) { + List moreProducts = new ArrayList<>(); for (Product product : products.getProducts()) { // 프로모션인지 또는 구매 상품인지 판단 if (!product.isPromotion() || !purchaseProducts.containsKey(product.getName())) { @@ -72,7 +74,7 @@ public Map getMorePromotionProducts(Products products) { } if (product.isMorePromotionProduct(purchaseProducts.get(product.getName()))) { - moreProducts.put(product, purchaseProducts.get(product.getName())); + moreProducts.add(product); } } return moreProducts; @@ -81,4 +83,20 @@ public Map getMorePromotionProducts(Products products) { public int getPurchaseQuantity(Product product) { return purchaseProducts.get(product.getName()); } + + public List getNormalPromotionProducts(Products products) { + List moreProducts = new ArrayList<>(); + for (Product product : products.getProducts()) { + // 프로모션인지 또는 구매 상품인지 판단 + if (!product.isPromotion() || !purchaseProducts.containsKey(product.getName())) { + continue; + } + + if (!product.isLessPromotionProduct(purchaseProducts.get(product.getName())) + && !product.isMorePromotionProduct(purchaseProducts.get(product.getName()))) { + moreProducts.add(product); + } + } + return moreProducts; + } } From 2d43b16619af56ae4b603fa88adffc8e43814c8d Mon Sep 17 00:00:00 2001 From: khcho96 Date: Fri, 12 Dec 2025 19:11:28 +0900 Subject: [PATCH 10/18] =?UTF-8?q?feat:=20=ED=94=84=EB=A1=9C=EB=AA=A8?= =?UTF-8?q?=EC=85=98=EC=97=90=20=ED=95=B4=EB=8B=B9=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EB=8A=94=20=EC=83=81=ED=92=88=EC=9D=84=20=EA=B5=AC?= =?UTF-8?q?=EB=A7=A4=20=EC=83=81=ED=92=88=20=EB=AA=A9=EB=A1=9D=EC=97=90=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/store/Application.java | 133 ++++++++++++------ src/main/java/store/domain/Customer.java | 8 +- .../store/domain/vo/PurchaseProducts.java | 16 ++- 3 files changed, 107 insertions(+), 50 deletions(-) diff --git a/src/main/java/store/Application.java b/src/main/java/store/Application.java index e858480e..067fb39c 100644 --- a/src/main/java/store/Application.java +++ b/src/main/java/store/Application.java @@ -3,7 +3,6 @@ import java.io.IOException; import java.time.LocalDate; import java.util.List; -import java.util.Map; import store.domain.Customer; import store.domain.Product; import store.domain.Products; @@ -18,11 +17,16 @@ public class Application { + static PurchaseProducts purchaseProducts; + static Products products; + static Promotions promotions; + static Customer customer; + public static void main(String[] args) throws IOException { FileReader promotionReader = new FileReader("src/main/resources/promotions.md"); List readPromotions = promotionReader.readLines(); readPromotions.removeFirst(); - Promotions promotions = Promotions.newInstance(); + promotions = Promotions.newInstance(); for (String readPromotion : readPromotions) { String[] split = readPromotion.split(","); String name = split[0]; @@ -36,7 +40,7 @@ public static void main(String[] args) throws IOException { FileReader fr = new FileReader("src/main/resources/products.md"); List readProducts = fr.readLines(); readProducts.removeFirst(); - Products products = Products.newInstance(); + products = Products.newInstance(); for (String readProduct : readProducts) { String[] split = readProduct.split(","); String name = split[0]; @@ -46,63 +50,102 @@ public static void main(String[] args) throws IOException { products.addProduct(name, price, quantity, promotion); } - StockDto stockDto = products.getStockDto(); - OutputView.printStock(stockDto); - - String rawPurchaseProducts = InputView.readPurchaseProducts(); - List parsePurchaseProducts = InputParser.parsePurchaseProducts(rawPurchaseProducts); - PurchaseProducts purchaseProducts = PurchaseProducts.newInstance(); - for (String productName : parsePurchaseProducts) { - purchaseProducts.addProduct(products, productName); + while (true) { + try { + StockDto stockDto = products.getStockDto(); + OutputView.printStock(stockDto); + String rawPurchaseProducts = InputView.readPurchaseProducts(); + List parsePurchaseProducts = InputParser.parsePurchaseProducts(rawPurchaseProducts); + purchaseProducts = PurchaseProducts.newInstance(); + for (String productName : parsePurchaseProducts) { + purchaseProducts.addProduct(products, productName); + } + break; + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e); + } } // 4 - Customer customer = Customer.newInstance(); - List lessPromotionProducts = purchaseProducts.getLessPromotionProducts(products); - for (Product product : lessPromotionProducts) { - int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); - int free = product.getFreeProductQuantity(); - - String rawChoice = InputView.readFreeProductChoice(product.getName(), free); - boolean choice = InputParser.parseChoice(rawChoice); - - if (choice) { - customer.addProduct(product, purchaseQuantity + free); - continue; + while (true) { + try { + customer = Customer.newInstance(); + List lessPromotionProducts = purchaseProducts.getLessPromotionProducts(products); + for (Product product : lessPromotionProducts) { + int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); + int free = product.getFreeProductQuantity(); + + String rawChoice = InputView.readFreeProductChoice(product.getName(), free); + boolean choice = InputParser.parseChoice(rawChoice); + + if (choice) { + customer.addProductForPromotion(product, purchaseQuantity + free); + continue; + } + customer.addProductForPromotion(product, purchaseQuantity); + } + break; + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e); } - customer.addProduct(product, purchaseQuantity); } // 5 - List morePromotionProducts = purchaseProducts.getMorePromotionProducts(products); - for (Product product : morePromotionProducts) { - int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); - int impossiblePromotionQuantity = product.getImpossiblePromotionQuantity(purchaseQuantity); - - String rawChoice = InputView.readImpossiblePromotionChoice(product.getName(), impossiblePromotionQuantity); - boolean choice = InputParser.parseChoice(rawChoice); - - if (choice) { - customer.addProduct(product, purchaseQuantity); - continue; + while (true) { + try { + List morePromotionProducts = purchaseProducts.getMorePromotionProducts(products); + for (Product product : morePromotionProducts) { + int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); + int impossiblePromotionQuantity = product.getImpossiblePromotionQuantity(purchaseQuantity); + + String rawChoice = InputView.readImpossiblePromotionChoice(product.getName(), + impossiblePromotionQuantity); + boolean choice = InputParser.parseChoice(rawChoice); + + if (choice) { + customer.addProductForPromotion(product, purchaseQuantity); + continue; + } + customer.addProductForPromotion(product, purchaseQuantity - impossiblePromotionQuantity); + } + break; + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e); } - customer.addProduct(product, purchaseQuantity - impossiblePromotionQuantity); } // 6 - List normalPromotionProducts = purchaseProducts.getNormalPromotionProducts(products); - for (Product product : normalPromotionProducts) { - int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); - customer.addProduct(product, purchaseQuantity); + while (true) { + try { + List normalPromotionProducts = purchaseProducts.getNormalPromotionProducts(products); + for (Product product : normalPromotionProducts) { + int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); + customer.addProductForPromotion(product, purchaseQuantity); + } + break; + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e); + } } // 7 -// purchaseProducts.getNoPromotionProducts(products); -// -// OutputView.printStock(products.getStockDto()); -// System.out.println(customer.getPurchaseProducts()); -// System.out.println(customer.getPresentProducts()); + while (true) { + try { + List noPromotionProducts = purchaseProducts.getNoPromotionProducts(products); + for (Product product : noPromotionProducts) { + int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); + customer.addProductForNormal(product, purchaseQuantity); + } + break; + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e); + } + } + + OutputView.printStock(products.getStockDto()); + System.out.println(customer.getPurchaseProducts()); + System.out.println(customer.getPresentProducts()); // 8 } diff --git a/src/main/java/store/domain/Customer.java b/src/main/java/store/domain/Customer.java index cfe45e34..3d245c84 100644 --- a/src/main/java/store/domain/Customer.java +++ b/src/main/java/store/domain/Customer.java @@ -18,7 +18,7 @@ public static Customer newInstance() { return new Customer(); } - public void addProduct(Product product, int purchaseQuantity) { + public void addProductForPromotion(Product product, int purchaseQuantity) { // 구매 상품 int buy = product.getPromotion().getBuy(); int get = product.getPromotion().getGet(); @@ -31,7 +31,6 @@ public void addProduct(Product product, int purchaseQuantity) { int presentQuantity = get * setQuantity; purchaseProducts.put(product, List.of(purchaseQuantity, presentQuantity)); - product.updateStock(purchaseQuantity); } @@ -42,4 +41,9 @@ public Map> getPresentProducts() { public Map> getPurchaseProducts() { return purchaseProducts; } + + public void addProductForNormal(Product product, int purchaseQuantity) { + purchaseProducts.put(product, List.of(purchaseQuantity, 0)); + product.updateStock(purchaseQuantity); + } } diff --git a/src/main/java/store/domain/vo/PurchaseProducts.java b/src/main/java/store/domain/vo/PurchaseProducts.java index f20e0ccd..dc10df6f 100644 --- a/src/main/java/store/domain/vo/PurchaseProducts.java +++ b/src/main/java/store/domain/vo/PurchaseProducts.java @@ -85,7 +85,7 @@ public int getPurchaseQuantity(Product product) { } public List getNormalPromotionProducts(Products products) { - List moreProducts = new ArrayList<>(); + List normalPromotionProducts = new ArrayList<>(); for (Product product : products.getProducts()) { // 프로모션인지 또는 구매 상품인지 판단 if (!product.isPromotion() || !purchaseProducts.containsKey(product.getName())) { @@ -94,9 +94,19 @@ public List getNormalPromotionProducts(Products products) { if (!product.isLessPromotionProduct(purchaseProducts.get(product.getName())) && !product.isMorePromotionProduct(purchaseProducts.get(product.getName()))) { - moreProducts.add(product); + normalPromotionProducts.add(product); } } - return moreProducts; + return normalPromotionProducts; + } + + public List getNoPromotionProducts(Products products) { + List noPromotionProducts = new ArrayList<>(); + for (Product product : products.getProducts()) { + if (!product.isPromotion() && purchaseProducts.containsKey(product.getName())) { + noPromotionProducts.add(product); + } + } + return noPromotionProducts; } } From bb8c9dcaab66d80fd4dd3004b6f9ecbc53044ba6 Mon Sep 17 00:00:00 2001 From: khcho96 Date: Fri, 12 Dec 2025 19:45:24 +0900 Subject: [PATCH 11/18] =?UTF-8?q?feat:=20=EB=A9=A4=EB=B2=84=EC=8B=AD=20?= =?UTF-8?q?=ED=95=A0=EC=9D=B8=EC=9D=84=20=EB=B0=9B=EC=9D=84=EC=A7=80=20?= =?UTF-8?q?=EC=9C=A0=EB=AC=B4=20=EC=9E=85=EB=A0=A5=20=EB=B0=8F=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/store/Application.java | 39 ++++++++++++------------ src/main/java/store/domain/Customer.java | 34 +++++++++++++++------ src/main/java/store/view/InputView.java | 6 ++++ 3 files changed, 50 insertions(+), 29 deletions(-) diff --git a/src/main/java/store/Application.java b/src/main/java/store/Application.java index 067fb39c..c1603bf5 100644 --- a/src/main/java/store/Application.java +++ b/src/main/java/store/Application.java @@ -116,26 +116,27 @@ public static void main(String[] args) throws IOException { } // 6 - while (true) { - try { - List normalPromotionProducts = purchaseProducts.getNormalPromotionProducts(products); - for (Product product : normalPromotionProducts) { - int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); - customer.addProductForPromotion(product, purchaseQuantity); - } - break; - } catch (IllegalArgumentException e) { - OutputView.printErrorMessage(e); - } + List normalPromotionProducts = purchaseProducts.getNormalPromotionProducts(products); + for (Product product : normalPromotionProducts) { + int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); + customer.addProductForPromotion(product, purchaseQuantity); } // 7 + List noPromotionProducts = purchaseProducts.getNoPromotionProducts(products); + for (Product product : noPromotionProducts) { + int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); + customer.addProductForNormal(product, purchaseQuantity); + } + + // 8 while (true) { try { - List noPromotionProducts = purchaseProducts.getNoPromotionProducts(products); - for (Product product : noPromotionProducts) { - int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); - customer.addProductForNormal(product, purchaseQuantity); + String rawChoice = InputView.readMembershipChoice(); + boolean choice = InputParser.parseChoice(rawChoice); + + if (choice) { + customer.setMembershipDiscountAmount(); } break; } catch (IllegalArgumentException e) { @@ -143,10 +144,8 @@ public static void main(String[] args) throws IOException { } } - OutputView.printStock(products.getStockDto()); - System.out.println(customer.getPurchaseProducts()); - System.out.println(customer.getPresentProducts()); - - // 8 +// OutputView.printStock(products.getStockDto()); +// System.out.println(customer.getPurchaseProducts()); +// System.out.println(customer.getMembershipDiscountAmount()); } } diff --git a/src/main/java/store/domain/Customer.java b/src/main/java/store/domain/Customer.java index 3d245c84..085b1518 100644 --- a/src/main/java/store/domain/Customer.java +++ b/src/main/java/store/domain/Customer.java @@ -7,11 +7,10 @@ public class Customer { private final Map> purchaseProducts; - private final Map> presentProducts; + private int membershipDiscountAmount; private Customer() { purchaseProducts = new HashMap<>(); - presentProducts = new HashMap<>(); } public static Customer newInstance() { @@ -19,7 +18,6 @@ public static Customer newInstance() { } public void addProductForPromotion(Product product, int purchaseQuantity) { - // 구매 상품 int buy = product.getPromotion().getBuy(); int get = product.getPromotion().getGet(); int setSize = buy + get; @@ -29,21 +27,39 @@ public void addProductForPromotion(Product product, int purchaseQuantity) { setQuantity = setQuantityNotEqual; } int presentQuantity = get * setQuantity; + int noPromotionQuantity = purchaseQuantity - setSize * setQuantity; - purchaseProducts.put(product, List.of(purchaseQuantity, presentQuantity)); + purchaseProducts.put(product, List.of(purchaseQuantity, presentQuantity, noPromotionQuantity)); product.updateStock(purchaseQuantity); } - public Map> getPresentProducts() { - return presentProducts; + public void addProductForNormal(Product product, int purchaseQuantity) { + purchaseProducts.put(product, List.of(purchaseQuantity, 0, purchaseQuantity)); + product.updateStock(purchaseQuantity); + } + + public void setMembershipDiscountAmount() { + membershipDiscountAmount = calculateMembershipDiscountAmount(); + if (membershipDiscountAmount > 8000) { + membershipDiscountAmount = 8000; + } + } + + private int calculateMembershipDiscountAmount() { + int sum = 0; + for (Product product : purchaseProducts.keySet()) { + int price = product.getPrice(); + int noPromotionQuantity = purchaseProducts.get(product).get(2); + sum += price * noPromotionQuantity; + } + return (int) (sum * 0.3); } public Map> getPurchaseProducts() { return purchaseProducts; } - public void addProductForNormal(Product product, int purchaseQuantity) { - purchaseProducts.put(product, List.of(purchaseQuantity, 0)); - product.updateStock(purchaseQuantity); + public int getMembershipDiscountAmount() { + return membershipDiscountAmount; } } diff --git a/src/main/java/store/view/InputView.java b/src/main/java/store/view/InputView.java index 3ce1f590..ddf687ee 100644 --- a/src/main/java/store/view/InputView.java +++ b/src/main/java/store/view/InputView.java @@ -7,6 +7,7 @@ public class InputView { private static final String PURCHASE_PRODUCTS_REQUEST = "구매하실 상품명과 수량을 입력해주세요. (예: [사이다-2],[감자칩-1])"; private static final String FREE_PRODUCTS_REQUEST = "\n현재 %s은(는) %d개를 무료로 더 받을 수 있습니다. 추가하시겠습니까? (Y/N)\n"; private static final String IMPOSSIBLE_PROMOTION_REQUEST = "\n현재 %s %d개는 프로모션 할인이 적용되지 않습니다. 그래도 구매하시겠습니까? (Y/N)\n"; + private static final String MEMBERSHIP_REQUEST = "\n멤버십 할인을 받으시겠습니까? (Y/N)"; public static String readPurchaseProducts() { System.out.println(PURCHASE_PRODUCTS_REQUEST); @@ -22,4 +23,9 @@ public static String readImpossiblePromotionChoice(String name, int impossiblePr System.out.printf(IMPOSSIBLE_PROMOTION_REQUEST, name, impossiblePromotionQuantity); return Console.readLine(); } + + public static String readMembershipChoice() { + System.out.println(MEMBERSHIP_REQUEST); + return Console.readLine(); + } } From 1b89ae2c6e99ee1127bd3dd2938e65a14fe64c00 Mon Sep 17 00:00:00 2001 From: khcho96 Date: Fri, 12 Dec 2025 20:35:19 +0900 Subject: [PATCH 12/18] =?UTF-8?q?feat:=20=EA=B5=AC=EB=A7=A4=20=EB=B0=8F=20?= =?UTF-8?q?=EC=A6=9D=EC=A0=95=20=EB=AA=A9=EB=A1=9D=EC=97=90=20=EA=B8=B0?= =?UTF-8?q?=EB=B0=98=ED=95=98=EC=97=AC=20=EC=9E=AC=EA=B3=A0=EB=A5=BC=20?= =?UTF-8?q?=EC=97=85=EB=8D=B0=EC=9D=B4=ED=8A=B8(=ED=8C=8C=EC=9D=BC=20?= =?UTF-8?q?=EC=88=98=EC=A0=95)=ED=95=98=EB=8A=94=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/store/Application.java | 6 +++--- src/main/java/store/domain/Products.java | 5 +++-- 2 files changed, 6 insertions(+), 5 deletions(-) diff --git a/src/main/java/store/Application.java b/src/main/java/store/Application.java index c1603bf5..2984dfa5 100644 --- a/src/main/java/store/Application.java +++ b/src/main/java/store/Application.java @@ -12,6 +12,7 @@ import store.dto.StockDto; import store.util.InputParser; import store.util.file.FileReader; +import store.util.file.FileWriter; import store.view.InputView; import store.view.OutputView; @@ -144,8 +145,7 @@ public static void main(String[] args) throws IOException { } } -// OutputView.printStock(products.getStockDto()); -// System.out.println(customer.getPurchaseProducts()); -// System.out.println(customer.getMembershipDiscountAmount()); + FileWriter fw = new FileWriter("src/main/resources/temp products.md"); + fw.writeAll(products.getStockForFile()); } } diff --git a/src/main/java/store/domain/Products.java b/src/main/java/store/domain/Products.java index df258ae7..e645be01 100644 --- a/src/main/java/store/domain/Products.java +++ b/src/main/java/store/domain/Products.java @@ -16,8 +16,9 @@ public static Products newInstance() { return new Products(); } - public String getStock(String stock) { - StringBuilder st = new StringBuilder(stock); + public String getStockForFile() { + StringBuilder st = new StringBuilder(); + st.append("name,price,quantity,promotion\n"); for (Product product : products) { if (product.getPromotion() == null) { st.append(product.getName() + "," + product.getPrice() + "," + product.getNormalQuantity() + "," From bd6d27745be6ef3ef1853690d0fc9fc8eab9d8bb Mon Sep 17 00:00:00 2001 From: khcho96 Date: Fri, 12 Dec 2025 21:29:20 +0900 Subject: [PATCH 13/18] =?UTF-8?q?feat:=20=EC=98=81=EC=88=98=EC=A6=9D?= =?UTF-8?q?=EC=9D=84=20=EC=B6=9C=EB=A0=A5=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/store/Application.java | 8 +++- src/main/java/store/domain/Customer.java | 22 ++++++++-- src/main/java/store/domain/Product.java | 13 ++++++ .../store/domain/vo/PurchaseProducts.java | 28 +++++++++++- src/main/java/store/dto/ReceiptDto.java | 9 ++++ src/main/java/store/view/OutputView.java | 43 +++++++++++++++++++ 6 files changed, 116 insertions(+), 7 deletions(-) create mode 100644 src/main/java/store/dto/ReceiptDto.java diff --git a/src/main/java/store/Application.java b/src/main/java/store/Application.java index 2984dfa5..37302b9b 100644 --- a/src/main/java/store/Application.java +++ b/src/main/java/store/Application.java @@ -2,6 +2,7 @@ import java.io.IOException; import java.time.LocalDate; +import java.time.Year; import java.util.List; import store.domain.Customer; import store.domain.Product; @@ -9,6 +10,7 @@ import store.domain.Promotion; import store.domain.Promotions; import store.domain.vo.PurchaseProducts; +import store.dto.ReceiptDto; import store.dto.StockDto; import store.util.InputParser; import store.util.file.FileReader; @@ -68,7 +70,6 @@ public static void main(String[] args) throws IOException { } // 4 - while (true) { try { customer = Customer.newInstance(); @@ -147,5 +148,10 @@ public static void main(String[] args) throws IOException { FileWriter fw = new FileWriter("src/main/resources/temp products.md"); fw.writeAll(products.getStockForFile()); + + ReceiptDto receiptDto = customer.getReceipt(); + OutputView.printReceipt(receiptDto); + + OutputView.printStock(products.getStockDto()); } } diff --git a/src/main/java/store/domain/Customer.java b/src/main/java/store/domain/Customer.java index 085b1518..402d843d 100644 --- a/src/main/java/store/domain/Customer.java +++ b/src/main/java/store/domain/Customer.java @@ -1,8 +1,9 @@ package store.domain; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import store.dto.ReceiptDto; public class Customer { @@ -10,7 +11,7 @@ public class Customer { private int membershipDiscountAmount; private Customer() { - purchaseProducts = new HashMap<>(); + purchaseProducts = new LinkedHashMap<>(); } public static Customer newInstance() { @@ -59,7 +60,20 @@ public Map> getPurchaseProducts() { return purchaseProducts; } - public int getMembershipDiscountAmount() { - return membershipDiscountAmount; + public ReceiptDto getReceipt() { + Map purchaseProducts = new LinkedHashMap<>(); + Map presentProducts = new LinkedHashMap<>(); + for (Product product : this.purchaseProducts.keySet()) { + List values = this.purchaseProducts.get(product); + + int purchaseQuantity = values.get(0); + purchaseProducts.put(product, purchaseQuantity); + + int presentQuantity = values.get(1); + if (presentQuantity > 0) { + presentProducts.put(product, presentQuantity); + } + } + return new ReceiptDto(purchaseProducts, presentProducts, membershipDiscountAmount); } } diff --git a/src/main/java/store/domain/Product.java b/src/main/java/store/domain/Product.java index b9107c08..2c660d69 100644 --- a/src/main/java/store/domain/Product.java +++ b/src/main/java/store/domain/Product.java @@ -11,6 +11,7 @@ public class Product { private int promotionQuantity; private int normalQuantity; private final Promotion promotion; + private boolean isChecked; private Product(String name, int price, int promotionQuantity, int normalQuantity, Promotion promotion) { this.name = name; @@ -57,6 +58,10 @@ public int getPrice() { return price; } + public int getPrice(int quantity) { + return price * quantity; + } + public int getPromotionQuantity() { return promotionQuantity; } @@ -150,4 +155,12 @@ public int getImpossiblePromotionQuantity(int purchaseQuantity) { return purchaseQuantity - maxPromotionQuantity; } + + public boolean isChecked() { + return isChecked; + } + + public void setCheckedTrue() { + isChecked = true; + } } diff --git a/src/main/java/store/domain/vo/PurchaseProducts.java b/src/main/java/store/domain/vo/PurchaseProducts.java index dc10df6f..17be86cd 100644 --- a/src/main/java/store/domain/vo/PurchaseProducts.java +++ b/src/main/java/store/domain/vo/PurchaseProducts.java @@ -4,7 +4,7 @@ import static store.constant.ErrorMessage.NO_EXIST_ERROR; import java.util.ArrayList; -import java.util.HashMap; +import java.util.LinkedHashMap; import java.util.List; import java.util.Map; import store.domain.Product; @@ -15,7 +15,7 @@ public class PurchaseProducts { private final Map purchaseProducts; private PurchaseProducts() { - this.purchaseProducts = new HashMap<>(); + this.purchaseProducts = new LinkedHashMap<>(); } public static PurchaseProducts newInstance() { @@ -52,6 +52,11 @@ public Map getPurchaseProducts() { public List getLessPromotionProducts(Products products) { List lessProducts = new ArrayList<>(); for (Product product : products.getProducts()) { + // 이미 계산한 상품인지 확인 + if (product.isChecked()) { + continue; + } + // 프로모션인지 또는 구매 상품인지 판단 if (!product.isPromotion() || !purchaseProducts.containsKey(product.getName())) { continue; @@ -60,6 +65,7 @@ public List getLessPromotionProducts(Products products) { // 주문 개수 < 프로모션 적용 개수인 상품은 주문 개수를 값으로 저장 if (product.isLessPromotionProduct(purchaseProducts.get(product.getName()))) { lessProducts.add(product); + product.setCheckedTrue(); } } return lessProducts; @@ -68,6 +74,11 @@ public List getLessPromotionProducts(Products products) { public List getMorePromotionProducts(Products products) { List moreProducts = new ArrayList<>(); for (Product product : products.getProducts()) { + // 이미 계산한 상품인지 확인 + if (product.isChecked()) { + continue; + } + // 프로모션인지 또는 구매 상품인지 판단 if (!product.isPromotion() || !purchaseProducts.containsKey(product.getName())) { continue; @@ -75,6 +86,7 @@ public List getMorePromotionProducts(Products products) { if (product.isMorePromotionProduct(purchaseProducts.get(product.getName()))) { moreProducts.add(product); + product.setCheckedTrue(); } } return moreProducts; @@ -87,6 +99,11 @@ public int getPurchaseQuantity(Product product) { public List getNormalPromotionProducts(Products products) { List normalPromotionProducts = new ArrayList<>(); for (Product product : products.getProducts()) { + // 이미 계산한 상품인지 확인 + if (product.isChecked()) { + continue; + } + // 프로모션인지 또는 구매 상품인지 판단 if (!product.isPromotion() || !purchaseProducts.containsKey(product.getName())) { continue; @@ -95,6 +112,7 @@ public List getNormalPromotionProducts(Products products) { if (!product.isLessPromotionProduct(purchaseProducts.get(product.getName())) && !product.isMorePromotionProduct(purchaseProducts.get(product.getName()))) { normalPromotionProducts.add(product); + product.setCheckedTrue(); } } return normalPromotionProducts; @@ -103,8 +121,14 @@ public List getNormalPromotionProducts(Products products) { public List getNoPromotionProducts(Products products) { List noPromotionProducts = new ArrayList<>(); for (Product product : products.getProducts()) { + // 이미 계산한 상품인지 확인 + if (product.isChecked()) { + continue; + } + if (!product.isPromotion() && purchaseProducts.containsKey(product.getName())) { noPromotionProducts.add(product); + product.setCheckedTrue(); } } return noPromotionProducts; diff --git a/src/main/java/store/dto/ReceiptDto.java b/src/main/java/store/dto/ReceiptDto.java new file mode 100644 index 00000000..5f885ec5 --- /dev/null +++ b/src/main/java/store/dto/ReceiptDto.java @@ -0,0 +1,9 @@ +package store.dto; + +import java.util.Map; +import store.domain.Product; + +public record ReceiptDto(Map purchaseProducts, Map presentProducts, + int membershipDiscountAmount) { + +} diff --git a/src/main/java/store/view/OutputView.java b/src/main/java/store/view/OutputView.java index c5d11a9b..9e7e26cb 100644 --- a/src/main/java/store/view/OutputView.java +++ b/src/main/java/store/view/OutputView.java @@ -1,5 +1,8 @@ package store.view; +import java.util.Map; +import store.domain.Product; +import store.dto.ReceiptDto; import store.dto.StockDto; public class OutputView { @@ -18,4 +21,44 @@ public static void printResult() { public static void printErrorMessage(IllegalArgumentException e) { System.out.println(e.getMessage()); } + + public static void printReceipt(ReceiptDto receiptDto) { + Map purchaseProducts = receiptDto.purchaseProducts(); + Map presentProducts = receiptDto.presentProducts(); + int membershipDiscountAmount = receiptDto.membershipDiscountAmount(); + + int totalPurchaseQuantity = 0; + int totalPurchasePrice = 0; + int totalPresentQuantity = 0; + int totalPresentPrice = 0; + + + System.out.println("\n============W 편의점============"); + System.out.println("상품명 수량 금액"); + for (Product product : presentProducts.keySet()) { + int quantity = purchaseProducts.get(product); + int price = product.getPrice(quantity); + System.out.printf("%-16s%-8s%,d\n", product.getName(), quantity, price); + + totalPurchaseQuantity += quantity; + totalPurchasePrice += price; + } + + System.out.println("============증 정============"); + for (Product product : presentProducts.keySet()) { + int quantity = presentProducts.get(product); + int price = product.getPrice(quantity); + System.out.printf("%-16s%d\n", product.getName(), quantity); + + totalPresentQuantity += quantity; + totalPresentPrice += price; + } + + int finalPrice = totalPurchasePrice - totalPresentPrice - membershipDiscountAmount; + System.out.println("=============================="); + System.out.printf("%-15s%-8s%,d\n", "총구매액", totalPurchaseQuantity, totalPurchasePrice); + System.out.printf("%-23s-%,d\n", "행사할인", totalPresentPrice); + System.out.printf("%-23s-%,d\n", "멤버십할인", membershipDiscountAmount); + System.out.printf("%-23s%,d\n", "내실돈", finalPrice); + } } From 1504a5713d1c8303656ffd08af7969d0a6b67cb9 Mon Sep 17 00:00:00 2001 From: khcho96 Date: Fri, 12 Dec 2025 22:01:31 +0900 Subject: [PATCH 14/18] =?UTF-8?q?feat:=20=EC=BB=A8=ED=8A=B8=EB=A1=A4?= =?UTF-8?q?=EB=9F=AC=20=EB=B0=8F=20=EC=84=9C=EB=B9=84=EC=8A=A4=20=EB=8F=84?= =?UTF-8?q?=EC=9E=85?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/store/Application.java | 153 +----------------- .../store/controller/StoreController.java | 132 +++++++++++++++ src/main/java/store/service/StoreService.java | 128 +++++++++++++++ 3 files changed, 265 insertions(+), 148 deletions(-) create mode 100644 src/main/java/store/controller/StoreController.java create mode 100644 src/main/java/store/service/StoreService.java diff --git a/src/main/java/store/Application.java b/src/main/java/store/Application.java index 37302b9b..ce5b6b18 100644 --- a/src/main/java/store/Application.java +++ b/src/main/java/store/Application.java @@ -1,157 +1,14 @@ package store; import java.io.IOException; -import java.time.LocalDate; -import java.time.Year; -import java.util.List; -import store.domain.Customer; -import store.domain.Product; -import store.domain.Products; -import store.domain.Promotion; -import store.domain.Promotions; -import store.domain.vo.PurchaseProducts; -import store.dto.ReceiptDto; -import store.dto.StockDto; -import store.util.InputParser; -import store.util.file.FileReader; -import store.util.file.FileWriter; -import store.view.InputView; -import store.view.OutputView; +import store.controller.StoreController; +import store.service.StoreService; public class Application { - static PurchaseProducts purchaseProducts; - static Products products; - static Promotions promotions; - static Customer customer; - public static void main(String[] args) throws IOException { - FileReader promotionReader = new FileReader("src/main/resources/promotions.md"); - List readPromotions = promotionReader.readLines(); - readPromotions.removeFirst(); - promotions = Promotions.newInstance(); - for (String readPromotion : readPromotions) { - String[] split = readPromotion.split(","); - String name = split[0]; - int buy = Integer.parseInt(split[1]); - int get = Integer.parseInt(split[2]); - LocalDate startDate = LocalDate.parse(split[3]); - LocalDate endDate = LocalDate.parse(split[4]); - promotions.addPromotion(name, buy, get, startDate, endDate); - } - - FileReader fr = new FileReader("src/main/resources/products.md"); - List readProducts = fr.readLines(); - readProducts.removeFirst(); - products = Products.newInstance(); - for (String readProduct : readProducts) { - String[] split = readProduct.split(","); - String name = split[0]; - int price = Integer.parseInt(split[1]); - int quantity = Integer.parseInt(split[2]); - Promotion promotion = promotions.get(split[3]); - products.addProduct(name, price, quantity, promotion); - } - - while (true) { - try { - StockDto stockDto = products.getStockDto(); - OutputView.printStock(stockDto); - String rawPurchaseProducts = InputView.readPurchaseProducts(); - List parsePurchaseProducts = InputParser.parsePurchaseProducts(rawPurchaseProducts); - purchaseProducts = PurchaseProducts.newInstance(); - for (String productName : parsePurchaseProducts) { - purchaseProducts.addProduct(products, productName); - } - break; - } catch (IllegalArgumentException e) { - OutputView.printErrorMessage(e); - } - } - - // 4 - while (true) { - try { - customer = Customer.newInstance(); - List lessPromotionProducts = purchaseProducts.getLessPromotionProducts(products); - for (Product product : lessPromotionProducts) { - int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); - int free = product.getFreeProductQuantity(); - - String rawChoice = InputView.readFreeProductChoice(product.getName(), free); - boolean choice = InputParser.parseChoice(rawChoice); - - if (choice) { - customer.addProductForPromotion(product, purchaseQuantity + free); - continue; - } - customer.addProductForPromotion(product, purchaseQuantity); - } - break; - } catch (IllegalArgumentException e) { - OutputView.printErrorMessage(e); - } - } - - // 5 - while (true) { - try { - List morePromotionProducts = purchaseProducts.getMorePromotionProducts(products); - for (Product product : morePromotionProducts) { - int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); - int impossiblePromotionQuantity = product.getImpossiblePromotionQuantity(purchaseQuantity); - - String rawChoice = InputView.readImpossiblePromotionChoice(product.getName(), - impossiblePromotionQuantity); - boolean choice = InputParser.parseChoice(rawChoice); - - if (choice) { - customer.addProductForPromotion(product, purchaseQuantity); - continue; - } - customer.addProductForPromotion(product, purchaseQuantity - impossiblePromotionQuantity); - } - break; - } catch (IllegalArgumentException e) { - OutputView.printErrorMessage(e); - } - } - - // 6 - List normalPromotionProducts = purchaseProducts.getNormalPromotionProducts(products); - for (Product product : normalPromotionProducts) { - int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); - customer.addProductForPromotion(product, purchaseQuantity); - } - - // 7 - List noPromotionProducts = purchaseProducts.getNoPromotionProducts(products); - for (Product product : noPromotionProducts) { - int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); - customer.addProductForNormal(product, purchaseQuantity); - } - - // 8 - while (true) { - try { - String rawChoice = InputView.readMembershipChoice(); - boolean choice = InputParser.parseChoice(rawChoice); - - if (choice) { - customer.setMembershipDiscountAmount(); - } - break; - } catch (IllegalArgumentException e) { - OutputView.printErrorMessage(e); - } - } - - FileWriter fw = new FileWriter("src/main/resources/temp products.md"); - fw.writeAll(products.getStockForFile()); - - ReceiptDto receiptDto = customer.getReceipt(); - OutputView.printReceipt(receiptDto); - - OutputView.printStock(products.getStockDto()); + StoreService storeService = new StoreService(); + StoreController storeController = new StoreController(storeService); + storeController.run(); } } diff --git a/src/main/java/store/controller/StoreController.java b/src/main/java/store/controller/StoreController.java new file mode 100644 index 00000000..cef79e7b --- /dev/null +++ b/src/main/java/store/controller/StoreController.java @@ -0,0 +1,132 @@ +package store.controller; + +import java.io.IOException; +import java.util.List; +import store.dto.ReceiptDto; +import store.dto.StockDto; +import store.service.StoreService; +import store.util.InputParser; +import store.util.file.FileReader; +import store.util.file.FileWriter; +import store.view.InputView; +import store.view.OutputView; + +public class StoreController { + + private final StoreService storeService; + + public StoreController(StoreService storeService) { + this.storeService = storeService; + } + + public void run() throws IOException { + readFiles(); + + StockDto stockDto = storeService.getStockDto(); + OutputView.printStock(stockDto); + + registerPurchaseProducts(); + + handleFreeProducts(); + handleOverProducts(); + handleNormalProducts(); + handleNoPromotionProducts(); + setMembershipDiscountAmount(); + + writeStockFile(); + printResult(); + } + + private void readFiles() throws IOException { + FileReader promotionReader = new FileReader("src/main/resources/promotions.md"); + List readPromotions = promotionReader.readLines(); + storeService.readPromotionFile(readPromotions); + + FileReader fr = new FileReader("src/main/resources/products.md"); + List readProducts = fr.readLines(); + storeService.readProductsFile(readProducts); + } + + private void registerPurchaseProducts() { + while (true) { + try { + storeService.registerPurchaseProducts(); + return; + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e); + } + } + } + + private void handleFreeProducts() { + while (true) { + try { + storeService.handleFreeProducts(); + return; + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e); + } + } + } + + private void handleOverProducts() { + while (true) { + try { + storeService.handleOverProducts(); + return; + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e); + } + } + } + + private void handleNormalProducts() { + while (true) { + try { + storeService.handleNormalProducts(); + return; + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e); + } + } + } + + private void handleNoPromotionProducts() { + while (true) { + try { + storeService.handleNoPromotionProducts(); + return; + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e); + } + } + } + + private void setMembershipDiscountAmount() { + while (true) { + try { + String rawChoice = InputView.readMembershipChoice(); + boolean choice = InputParser.parseChoice(rawChoice); + + if (choice) { + storeService.setMembershipDiscountAmount(); + } + break; + } catch (IllegalArgumentException e) { + OutputView.printErrorMessage(e); + } + } + } + + private void writeStockFile() throws IOException { + FileWriter fw = new FileWriter("src/main/resources/products.md"); + + String stockResult = storeService.getStockResult(); + fw.writeAll(stockResult); + } + + private void printResult() { + ReceiptDto receiptDto = storeService.getReceiptResult(); + OutputView.printReceipt(receiptDto); + } +} \ No newline at end of file diff --git a/src/main/java/store/service/StoreService.java b/src/main/java/store/service/StoreService.java new file mode 100644 index 00000000..42db7ee6 --- /dev/null +++ b/src/main/java/store/service/StoreService.java @@ -0,0 +1,128 @@ +package store.service; + +import java.time.LocalDate; +import java.util.List; +import store.domain.Customer; +import store.domain.Product; +import store.domain.Products; +import store.domain.Promotion; +import store.domain.Promotions; +import store.domain.vo.PurchaseProducts; +import store.dto.ReceiptDto; +import store.dto.StockDto; +import store.util.InputParser; +import store.view.InputView; + +public class StoreService { + + private PurchaseProducts purchaseProducts; + private Products products; + private Promotions promotions; + private Customer customer; + + public void readPromotionFile(List readPromotions) { + readPromotions.removeFirst(); + promotions = Promotions.newInstance(); + for (String readPromotion : readPromotions) { + String[] split = readPromotion.split(","); + String name = split[0]; + int buy = Integer.parseInt(split[1]); + int get = Integer.parseInt(split[2]); + LocalDate startDate = LocalDate.parse(split[3]); + LocalDate endDate = LocalDate.parse(split[4]); + promotions.addPromotion(name, buy, get, startDate, endDate); + } + } + + public void readProductsFile(List readProducts) { + readProducts.removeFirst(); + products = Products.newInstance(); + for (String readProduct : readProducts) { + String[] split = readProduct.split(","); + String name = split[0]; + int price = Integer.parseInt(split[1]); + int quantity = Integer.parseInt(split[2]); + Promotion promotion = promotions.get(split[3]); + products.addProduct(name, price, quantity, promotion); + } + } + + public StockDto getStockDto() { + return products.getStockDto(); + } + + public void registerPurchaseProducts() { + String rawPurchaseProducts = InputView.readPurchaseProducts(); + List parsePurchaseProducts = InputParser.parsePurchaseProducts(rawPurchaseProducts); + purchaseProducts = PurchaseProducts.newInstance(); + for (String productName : parsePurchaseProducts) { + purchaseProducts.addProduct(products, productName); + } + } + + public void handleFreeProducts() { + customer = Customer.newInstance(); + + List lessPromotionProducts = purchaseProducts.getLessPromotionProducts(products); + for (Product product : lessPromotionProducts) { + int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); + int free = product.getFreeProductQuantity(); + + String rawChoice = InputView.readFreeProductChoice(product.getName(), free); + boolean choice = InputParser.parseChoice(rawChoice); + + if (choice) { + customer.addProductForPromotion(product, purchaseQuantity + free); + continue; + } + customer.addProductForPromotion(product, purchaseQuantity); + } + } + + public void handleOverProducts() { + List morePromotionProducts = purchaseProducts.getMorePromotionProducts(products); + for (Product product : morePromotionProducts) { + int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); + int impossiblePromotionQuantity = product.getImpossiblePromotionQuantity(purchaseQuantity); + + String rawChoice = InputView.readImpossiblePromotionChoice(product.getName(), + impossiblePromotionQuantity); + boolean choice = InputParser.parseChoice(rawChoice); + + if (choice) { + customer.addProductForPromotion(product, purchaseQuantity); + continue; + } + customer.addProductForPromotion(product, purchaseQuantity - impossiblePromotionQuantity); + } + } + + + public void handleNormalProducts() { + List normalPromotionProducts = purchaseProducts.getNormalPromotionProducts(products); + for (Product product : normalPromotionProducts) { + int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); + customer.addProductForPromotion(product, purchaseQuantity); + } + } + + public void handleNoPromotionProducts() { + List noPromotionProducts = purchaseProducts.getNoPromotionProducts(products); + for (Product product : noPromotionProducts) { + int purchaseQuantity = purchaseProducts.getPurchaseQuantity(product); + customer.addProductForNormal(product, purchaseQuantity); + } + } + + public void setMembershipDiscountAmount() { + customer.setMembershipDiscountAmount(); + } + + public String getStockResult() { + return products.getStockForFile(); + } + + public ReceiptDto getReceiptResult() { + return customer.getReceipt(); + } +} From 2123af392ab81134c53b305b5730dfedfabbe038 Mon Sep 17 00:00:00 2001 From: khcho96 Date: Fri, 12 Dec 2025 22:11:55 +0900 Subject: [PATCH 15/18] =?UTF-8?q?feat:=20=EB=8B=A4=EB=A5=B8=20=EC=83=81?= =?UTF-8?q?=ED=92=88=EC=9D=84=20=EA=B5=AC=EB=A7=A4=ED=95=A0=EC=A7=80=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=EC=9E=85=EB=A0=A5=20=EB=B0=8F=20=EC=B2=98?= =?UTF-8?q?=EB=A6=AC=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../store/controller/StoreController.java | 38 +++++++++++-------- src/main/java/store/view/InputView.java | 8 +++- src/main/java/store/view/OutputView.java | 10 +---- 3 files changed, 32 insertions(+), 24 deletions(-) diff --git a/src/main/java/store/controller/StoreController.java b/src/main/java/store/controller/StoreController.java index cef79e7b..8b3d1331 100644 --- a/src/main/java/store/controller/StoreController.java +++ b/src/main/java/store/controller/StoreController.java @@ -20,21 +20,29 @@ public StoreController(StoreService storeService) { } public void run() throws IOException { - readFiles(); - - StockDto stockDto = storeService.getStockDto(); - OutputView.printStock(stockDto); - - registerPurchaseProducts(); - - handleFreeProducts(); - handleOverProducts(); - handleNormalProducts(); - handleNoPromotionProducts(); - setMembershipDiscountAmount(); - - writeStockFile(); - printResult(); + while (true) { + readFiles(); + + StockDto stockDto = storeService.getStockDto(); + OutputView.printStock(stockDto); + + registerPurchaseProducts(); + + handleFreeProducts(); + handleOverProducts(); + handleNormalProducts(); + handleNoPromotionProducts(); + setMembershipDiscountAmount(); + + writeStockFile(); + printResult(); + + String rawChoice = InputView.readMorePurchaseChoice(); + boolean choice = InputParser.parseChoice(rawChoice); + if (!choice) { + break; + } + } } private void readFiles() throws IOException { diff --git a/src/main/java/store/view/InputView.java b/src/main/java/store/view/InputView.java index ddf687ee..2a4387ce 100644 --- a/src/main/java/store/view/InputView.java +++ b/src/main/java/store/view/InputView.java @@ -7,7 +7,8 @@ public class InputView { private static final String PURCHASE_PRODUCTS_REQUEST = "구매하실 상품명과 수량을 입력해주세요. (예: [사이다-2],[감자칩-1])"; private static final String FREE_PRODUCTS_REQUEST = "\n현재 %s은(는) %d개를 무료로 더 받을 수 있습니다. 추가하시겠습니까? (Y/N)\n"; private static final String IMPOSSIBLE_PROMOTION_REQUEST = "\n현재 %s %d개는 프로모션 할인이 적용되지 않습니다. 그래도 구매하시겠습니까? (Y/N)\n"; - private static final String MEMBERSHIP_REQUEST = "\n멤버십 할인을 받으시겠습니까? (Y/N)"; + private static final String MEMBERSHIP_REQUEST = "\n멤버십 할인을 받으시겠습니까? (Y/N)"; + private static final String MORE_PURCHASE_REQUEST = "\n감사합니다. 구매하고 싶은 다른 상품이 있나요? (Y/N)"; public static String readPurchaseProducts() { System.out.println(PURCHASE_PRODUCTS_REQUEST); @@ -28,4 +29,9 @@ public static String readMembershipChoice() { System.out.println(MEMBERSHIP_REQUEST); return Console.readLine(); } + + public static String readMorePurchaseChoice() { + System.out.println(MORE_PURCHASE_REQUEST); + return Console.readLine(); + } } diff --git a/src/main/java/store/view/OutputView.java b/src/main/java/store/view/OutputView.java index 9e7e26cb..adc755f6 100644 --- a/src/main/java/store/view/OutputView.java +++ b/src/main/java/store/view/OutputView.java @@ -7,17 +7,13 @@ public class OutputView { - private static final String WELL_COME_MESSAGE = "안녕하세요. W편의점입니다.\n현재 보유하고 있는 상품입니다.\n"; + private static final String WELL_COME_MESSAGE = "\n안녕하세요. W편의점입니다.\n현재 보유하고 있는 상품입니다.\n"; public static void printStock(StockDto stock) { System.out.println(WELL_COME_MESSAGE); System.out.println(stock.stock()); } - public static void printResult() { - - } - public static void printErrorMessage(IllegalArgumentException e) { System.out.println(e.getMessage()); } @@ -29,7 +25,6 @@ public static void printReceipt(ReceiptDto receiptDto) { int totalPurchaseQuantity = 0; int totalPurchasePrice = 0; - int totalPresentQuantity = 0; int totalPresentPrice = 0; @@ -50,7 +45,6 @@ public static void printReceipt(ReceiptDto receiptDto) { int price = product.getPrice(quantity); System.out.printf("%-16s%d\n", product.getName(), quantity); - totalPresentQuantity += quantity; totalPresentPrice += price; } @@ -59,6 +53,6 @@ public static void printReceipt(ReceiptDto receiptDto) { System.out.printf("%-15s%-8s%,d\n", "총구매액", totalPurchaseQuantity, totalPurchasePrice); System.out.printf("%-23s-%,d\n", "행사할인", totalPresentPrice); System.out.printf("%-23s-%,d\n", "멤버십할인", membershipDiscountAmount); - System.out.printf("%-23s%,d\n", "내실돈", finalPrice); + System.out.printf("%-24s%,d\n", "내실돈", finalPrice); } } From 76a0af65f724faa4bce7efcbb7f1e7d56d7e74c7 Mon Sep 17 00:00:00 2001 From: khcho96 Date: Fri, 12 Dec 2025 22:25:53 +0900 Subject: [PATCH 16/18] =?UTF-8?q?fix:=20=EC=B6=9C=EB=A0=A5=20=EB=82=B4?= =?UTF-8?q?=EC=9A=A9=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/store/view/OutputView.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/store/view/OutputView.java b/src/main/java/store/view/OutputView.java index adc755f6..0e0b2fd2 100644 --- a/src/main/java/store/view/OutputView.java +++ b/src/main/java/store/view/OutputView.java @@ -30,7 +30,7 @@ public static void printReceipt(ReceiptDto receiptDto) { System.out.println("\n============W 편의점============"); System.out.println("상품명 수량 금액"); - for (Product product : presentProducts.keySet()) { + for (Product product : purchaseProducts.keySet()) { int quantity = purchaseProducts.get(product); int price = product.getPrice(quantity); System.out.printf("%-16s%-8s%,d\n", product.getName(), quantity, price); From 3c22f23cead096b27c3811f763b046fadeed2e6c Mon Sep 17 00:00:00 2001 From: khcho96 Date: Fri, 12 Dec 2025 22:32:31 +0900 Subject: [PATCH 17/18] =?UTF-8?q?test:=20IOException=20=EC=B2=98=EB=A6=AC?= =?UTF-8?q?=20=EC=BD=94=EB=93=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/test/java/store/ApplicationTest.java | 7 ++++++- 1 file changed, 6 insertions(+), 1 deletion(-) diff --git a/src/test/java/store/ApplicationTest.java b/src/test/java/store/ApplicationTest.java index 7eac2290..d89fe652 100644 --- a/src/test/java/store/ApplicationTest.java +++ b/src/test/java/store/ApplicationTest.java @@ -1,6 +1,7 @@ package store; import camp.nextstep.edu.missionutils.test.NsTest; +import java.io.IOException; import org.junit.jupiter.api.Test; import java.time.LocalDate; @@ -63,6 +64,10 @@ class ApplicationTest extends NsTest { @Override public void runMain() { - Application.main(new String[]{}); + try { + Application.main(new String[]{}); + } catch (IOException e) { + throw new RuntimeException(e); + } } } From 64881694d6e4e5fe9133a7799a707071774cdf33 Mon Sep 17 00:00:00 2001 From: khcho96 Date: Mon, 5 Jan 2026 15:59:46 +0900 Subject: [PATCH 18/18] =?UTF-8?q?refactor:=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/store/constant/Constant.java | 7 +++++++ src/main/java/store/controller/StoreController.java | 2 +- src/main/java/store/domain/vo/PurchaseProducts.java | 4 ---- src/main/java/store/util/file/FileReader.java | 4 +--- 4 files changed, 9 insertions(+), 8 deletions(-) create mode 100644 src/main/java/store/constant/Constant.java diff --git a/src/main/java/store/constant/Constant.java b/src/main/java/store/constant/Constant.java new file mode 100644 index 00000000..2920f810 --- /dev/null +++ b/src/main/java/store/constant/Constant.java @@ -0,0 +1,7 @@ +package store.constant; + +public final class Constant { + +// public static final int = ; +// public static final String = ; +} diff --git a/src/main/java/store/controller/StoreController.java b/src/main/java/store/controller/StoreController.java index 8b3d1331..b6897770 100644 --- a/src/main/java/store/controller/StoreController.java +++ b/src/main/java/store/controller/StoreController.java @@ -137,4 +137,4 @@ private void printResult() { ReceiptDto receiptDto = storeService.getReceiptResult(); OutputView.printReceipt(receiptDto); } -} \ No newline at end of file +} diff --git a/src/main/java/store/domain/vo/PurchaseProducts.java b/src/main/java/store/domain/vo/PurchaseProducts.java index 17be86cd..08bdc7e1 100644 --- a/src/main/java/store/domain/vo/PurchaseProducts.java +++ b/src/main/java/store/domain/vo/PurchaseProducts.java @@ -45,10 +45,6 @@ public String toString() { return purchaseProducts.toString(); } - public Map getPurchaseProducts() { - return purchaseProducts; - } - public List getLessPromotionProducts(Products products) { List lessProducts = new ArrayList<>(); for (Product product : products.getProducts()) { diff --git a/src/main/java/store/util/file/FileReader.java b/src/main/java/store/util/file/FileReader.java index 438e9a1b..107cba3b 100644 --- a/src/main/java/store/util/file/FileReader.java +++ b/src/main/java/store/util/file/FileReader.java @@ -9,8 +9,6 @@ public class FileReader { - private static final int BUFFER_SIZE = 8192; - private final java.io.FileReader fr; public FileReader(String fileName) throws IOException { @@ -32,7 +30,7 @@ public String readAll() throws IOException { // 파일 전체를 한 줄 씩 나눠서 읽기 public List readLines() throws IOException { List contents = new ArrayList<>(); - BufferedReader br = new BufferedReader(fr, BUFFER_SIZE); + BufferedReader br = new BufferedReader(fr); String line; while ((line = br.readLine()) != null) {