Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
27 commits
Select commit Hold shift + click to select a range
f13e6e2
docs(README.md) : 기능 목록 정리
daeGULLL Mar 3, 2025
6d7e27b
chore(directory) : 파일 구조 설정
daeGULLL Mar 3, 2025
53b621a
fix(directory) : ProductInitializeService
daeGULLL Mar 3, 2025
bb1cfc6
feat(model) : Promotion 완성
daeGULLL Mar 5, 2025
2ed549a
fix(README.md) : 기능 목록 수정
daeGULLL Apr 3, 2025
cff31ba
fix(model) : Promotion 수정
daeGULLL Apr 3, 2025
74a7d10
feat(model) : Product 완성
daeGULLL Apr 3, 2025
7449fac
feat(model) : Stock 완성
daeGULLL Apr 3, 2025
b832ccd
fix(model) : RepositoryProvider(Interface)
daeGULLL Apr 3, 2025
5df430a
fix(directory, service) : ProductInitializeService -> StoreInitialize…
daeGULLL Apr 3, 2025
5dcca0a
feat(utility) : FileReadTool, Parser
daeGULLL Apr 3, 2025
b378c06
feat(model) : User 완성
daeGULLL Apr 3, 2025
ae57307
feat(utility) : 상수 값 관리를 위한 enum과 global var
daeGULLL Apr 3, 2025
d44b24f
feat(view) : InputView 완성
daeGULLL Apr 3, 2025
c70919b
feat(model) : Membership, Receipt 완성
daeGULLL Apr 3, 2025
9ecfe26
feat(utility) : InputFormatter
daeGULLL Apr 3, 2025
ae85d76
feat(service) : RepositoryService
daeGULLL Apr 3, 2025
b796256
feat(service) : PurchaseService
daeGULLL Apr 3, 2025
e60d112
fix(service) : PurchaseService
daeGULLL Apr 3, 2025
007d26a
feat(controller) : StoreController
daeGULLL Apr 3, 2025
b62f86e
feat(view) : OutputView
daeGULLL Apr 3, 2025
c59eca9
feat(test) : TestInputProvider
daeGULLL Apr 3, 2025
25e302f
feat(main) : Application
daeGULLL Apr 3, 2025
f961174
feat(main) : AppConfig
daeGULLL Apr 3, 2025
38fb9d0
fix(main) : ApplicationTest
daeGULLL Apr 3, 2025
483b9eb
chore(main) : removed unused imports
daeGULLL Apr 3, 2025
da0fde5
fix(controller) : removed debugging message
daeGULLL Apr 3, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
@@ -1 +1,15 @@
# java-convenience-store-precourse
# java-convenience-stock-precourse

# 기능 목록

1. 확장, 축소 가능한 상품, 프로모션, 재고
2. 영수증 : 구매 내역 저장 및 계산
3. 유저 관리 : 유저(멤버십, 영수증 인스턴스 관리), 유저 생성 및 식별 코드 생성
4. 편의점 초기 설정 기능 : resource 읽고 초기 세팅
5. 구매 처리 : 재고, 프로모션, 영수증 모델 연관
6. 입력 view : 테스트 목적으로 inputProvider를 inject
7. 출력 view
8. 편의점 전체 로직 controller
9. 시스템 메세지 enum
10. 에러 메세지 enum
11. 시스템 상수 : 편의점 이름, Y/N문자 설정
119 changes: 119 additions & 0 deletions src/main/java/store/AppConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,119 @@
package store;

import java.util.function.Consumer;
import store.controller.StoreController;
import store.model.repository.ProductRepository;
import store.model.repository.PromotionRepository;
import store.model.repository.StockRepository;
import store.model.repository.UserRepository;
import store.service.PurchaseService;
import store.service.RepositoryService;
import store.service.StoreInitializeService;
import store.service.UserService;
import store.utility.ErrorMessage;
import store.view.InputView;
import store.view.OutputView;

public class AppConfig {
private ProductRepository productRepository;
private PromotionRepository promotionRepository;
private StockRepository stockRepository;
private UserRepository userRepository;

private RepositoryService repositoryService;
private UserService userService;
private StoreInitializeService storeInitializeService;
private PurchaseService purchaseService;

private InputView inputView;
private OutputView outputView;

private StoreController storeController;

public MockSetting mock;

public AppConfig(){
setRepository();
setService();
setView();
setContoller();

mock = new MockSetting(this);
}

public StoreController getStoreController(){
return storeController;
}

public static class MockSetting {
public SingleVariable<InputView> InputView;
public SingleVariable<ProductRepository> ProductRepository;
public SingleVariable<PromotionRepository> PromotionRepository;
public SingleVariable<StockRepository> StockRepository;

public MockSetting(AppConfig parent){
this.InputView = new SingleVariable<>(v -> parent.inputView = v, parent::setContoller);
this.ProductRepository = new SingleVariable<>(v -> parent.productRepository = v, ()->{parent.setService(); parent.setContoller();});
this.StockRepository = new SingleVariable<>(v -> parent.stockRepository = v, ()->{parent.setService(); parent.setContoller();});
this.PromotionRepository = new SingleVariable<>(v -> parent.promotionRepository = v, ()->{parent.setService(); parent.setContoller();});
}

static class SingleVariable<T> implements Injectable {
private final Consumer<T> setter;
private final Refresh func;
public SingleVariable(Consumer<T> setter, Refresh func){
this.setter = setter;
this.func = func;
}
@Override
public void inject(Object mockedInstance){
try{
setter.accept((T) mockedInstance);
func.refresh();
} catch (Exception e){
throw new IllegalStateException(ErrorMessage.errorHeader+
String.format(ErrorMessage.PARAMETER_NOT_ACCEPTABLE.getMessage(),
"입력된 타입의 mockInstance",setter.getClass().getName()));
}
}
}
}

@FunctionalInterface
interface Injectable {
void inject(Object mockedInstance);
}

@FunctionalInterface
interface Refresh{
void refresh();
}

private void setRepository(){
productRepository = new ProductRepository();
promotionRepository = new PromotionRepository();
stockRepository = new StockRepository();
userRepository = new UserRepository();
}

private void setService(){
repositoryService = new RepositoryService(promotionRepository, productRepository, stockRepository);
userService = new UserService(userRepository);
storeInitializeService = new StoreInitializeService(repositoryService);
purchaseService = new PurchaseService(userService, repositoryService);
}

private void setView(){
inputView = new InputView();
outputView = new OutputView();
}

private void setContoller(){
storeController = new StoreController(inputView, outputView, storeInitializeService, purchaseService, repositoryService, userService);
}

//for debugging
// public void myInputView(){
// System.out.println("Injected InputView: " + inputView.myIdentity());
// }
}
64 changes: 64 additions & 0 deletions src/main/java/store/Application.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,71 @@
package store;

import java.io.PrintStream;
import java.nio.charset.StandardCharsets;
import store.controller.StoreController;

public class Application {
public static void main(String[] args) {
// TODO: 프로그램 구현
System.setOut(new PrintStream(System.out, true, StandardCharsets.UTF_8));
AppConfig appConfig = new AppConfig();
new Application().run(appConfig);
}

public void run(AppConfig appConfig){
boolean repeatInput = true;
boolean repeatProgram = true;
StoreController storeController = appConfig.getStoreController();
String accessingUserID = storeController.initializing();

while(repeatProgram){
storeController.startPurchase();
while(repeatInput){
repeatInput = storeController.purchasing(accessingUserID);
}

repeatInput = true;

while(repeatInput){
int result = storeController.endPurchase(accessingUserID);
if(result < 1) repeatInput = false;
if(result < 0) repeatProgram = false;
}
repeatInput = true;
}
storeController.disconnectUser(accessingUserID);
}

public void testRun(AppConfig appConfig, Breakpoint breakpoint){
boolean repeatInput = true;
boolean repeatProgram = true;
StoreController storeController = appConfig.getStoreController();
String accessingUserID = storeController.initializing();

while(repeatProgram){
storeController.startPurchase();
if(breakpoint==Breakpoint.PRODUCT_GUIDE) break;
while(repeatInput){
repeatInput = storeController.purchasing(accessingUserID);
if(breakpoint==Breakpoint.FIRST_PURCHASE_ATTEMPT) break;
}

if(breakpoint==Breakpoint.FIRST_PURCHASE_ATTEMPT) break;
repeatInput = true;

while(repeatInput){
int result = storeController.endPurchase(accessingUserID);
if(result < 1) repeatInput = false;
if(result < 0) repeatProgram = false;
}
repeatInput = true;
}
storeController.disconnectUser(accessingUserID);
}

public enum Breakpoint{
PRODUCT_GUIDE,
FIRST_PURCHASE_ATTEMPT,
NONE
}
}
121 changes: 121 additions & 0 deletions src/main/java/store/controller/StoreController.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,121 @@
package store.controller;

import java.util.List;
import java.util.Objects;
import store.model.domain.Stock;
import store.model.dto.CheckPromotionDTO;
import store.model.dto.CheckPromotionDTO.Type;
import store.service.PurchaseService;
import store.service.RepositoryService;
import store.service.StoreInitializeService;
import store.service.UserService;
import store.utility.ErrorMessage;
import store.utility.InputFormatter;
import store.utility.Parser;
import store.view.InputView;
import store.view.OutputView;

public class StoreController {
InputView inputView;
OutputView outputView;
StoreInitializeService storeInitializeService;
PurchaseService purchaseService;
RepositoryService repositoryService;
UserService userService;

public StoreController(InputView inputView, OutputView outputView,
StoreInitializeService storeInitializeService,
PurchaseService purchaseService,
RepositoryService repositoryService, UserService userService){
this.inputView = inputView;
this.outputView = outputView;
this.storeInitializeService = storeInitializeService;
this.purchaseService = purchaseService;
this.repositoryService = repositoryService;
this.userService = userService;
}

public String initializing(){
try{
storeInitializeService.setPromotions();
storeInitializeService.setProductsAndStocks();
return userService.createUser();
}catch (Exception e){
outputView.printError(ErrorMessage.errorHeader + e.getMessage());
throw e;
}
}

public void startPurchase(){
outputView.printGreeting();
List<String> currentStock = repositoryService.getCurrentStockStringList();
outputView.printCurrentStock(currentStock);
}

public boolean purchasing(String userKey){
try{
String inputLine = inputView.readPurchaseItems();
String[] splittedInput = InputFormatter.isStockFormat(inputLine);
purchaseEachItem(userKey, splittedInput);
return false;
}catch (Exception e){
outputView.printError(ErrorMessage.errorHeader + e.getMessage());
return true;
}
}

public int endPurchase(String userKey){
try{
String inputLine = inputView.readIfMembershipDiscount();
purchaseService.useMembership(userKey, Objects.equals(inputLine, "Y"));
String receipt = purchaseService.getReceipt(userKey);
outputView.printReceipt(receipt);
purchaseService.resetReceipt(userKey);
inputLine = inputView.readDismissal();
if(Objects.equals(inputLine, "N")) return -1;
return 0;
}catch (Exception e){
outputView.printError(ErrorMessage.errorHeader + e.getMessage());
return 1;
}
}

public void disconnectUser(String userKey){
userService.removeUser(userKey);
}




//------------------------------------------------------------------------------------------------//
// private methods from here

//정가 질문에 N 답할 시 정가 상품만 구매하지 않는 것으로 이해하고 코드 짰습니다.
private CheckPromotionDTO readAccordingToCheckPromotionDTO(CheckPromotionDTO result, int customerQuantity) {
if (!result.isPurchasable()) throw new IllegalArgumentException(String.format(ErrorMessage.PURCHASE_NOT_ABLE.getMessage(), customerQuantity));
String productName = result.getProduct().getName();
if (result.isRelatedStock() && result.isAdditionSuggest()) {
String input = inputView.readIfAdditionalPurchase(productName, result.getInfoQuantity());
if(Objects.equals(input, "N")){ result.changeQuantity(Type.PROMOTION,-1*result.getInfoQuantity());}
}
if (result.isRelatedStock() && result.getInfoQuantity() != 0) {
String input = inputView.readIfRegularPricePurchase(productName, result.getInfoQuantity());
if(Objects.equals(input, "N")){ result.changeQuantity(Type.NORMAL,-1*result.getInfoQuantity()); }
}

repositoryService.deductStock(productName, result.getPromotionQuantity(),result.getNormalQuantity());
return result;
}

private void purchaseEachItem(String userKey, String[] splittedInput){
for(int i=0; i<splittedInput.length/2; i++){
int customerQuantity = Parser.NumberParse(splittedInput[i*2+1]);
CheckPromotionDTO result = purchaseService.checkPromotionForCustomerInput(splittedInput[i*2], customerQuantity);
result = readAccordingToCheckPromotionDTO(result, customerQuantity);
purchaseService.confirmPurchaseToReceipt(userKey,
Stock.createStore(result.getProduct(), result.getPromotion(), result.getPromotionQuantity()),
Stock.createStore(result.getProduct(), result.getPromotion(), result.getNormalQuantity()+result.getPromotionQuantity())
);
}
}
}
30 changes: 30 additions & 0 deletions src/main/java/store/model/domain/Membership.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package store.model.domain;

public class Membership {
private Type type;

Membership(Type type){
this.type = type;
}

public int discount(int price){
int result = (int) Math.round(price * type.DISCOUNT_PERCENTAGE);
return Math.min(result, type.DISCOUNT_LIMIT);
}

public enum Type{
NORMAL(0.3, 8000),;

Type(double discount_percentage,int discount_limit){
this.DISCOUNT_PERCENTAGE = discount_percentage;
this.DISCOUNT_LIMIT = discount_limit;
}

final double DISCOUNT_PERCENTAGE;
final int DISCOUNT_LIMIT;

public Membership create(){
return new Membership(this);
}
}
}
Loading