diff --git a/README.md b/README.md index 02f0ec0004..3e26e6964b 100644 --- a/README.md +++ b/README.md @@ -1,163 +1,10 @@ -# SpringBoot Basic Weekly Mission 1 -> 바우처 관리 애플리케이션 만들기 1 - 김영주 - - - -# 1. 요구사항 - -## 프로젝트 요구사항 - -1. Gradle로 프로젝트를 빌드하고, SpringBoot 프로젝트 환경을 설정한다. -2. Web 기능 없이 Command-line Application으로 만든다. -3. logback을 통해 로그를 띄우고, 특히 에러는 별도의 파일로 기록한다. -4. 실행 가능한 jar 파일을 생성한다. - - - -## 기능 요구사항 - -### 1. 프로그램 시작(랜딩 화면) - - ``` - [할인권 프로그램 v1.0] - 1. 새 할인권 생성 - 2. 할인권 조회 - 3. 프로그램 종료 - - 입력 : 1 - ``` - -사용자는 숫자를 입력해서 원하는 메뉴를 선택할 수 있다. -- `1` → 새 할인권 생성 메뉴 실행 -- `2` → 저장된 할인권 조회 메뉴 실행 -- `3` → 프로그램 종료 - - - -### 2. 새 할인권 생성 - -사용자는 숫자를 입력해서 원하는 할인 방식을 선택할 수 있다. - -- `1` → 고정 할인 방식 -- `2` → 비율 할인 방식 - - - -할인권 생성이 완료되면 생성된 할인권의 `UUID, 할인 방식, 금액/비율` 내용을 보여준다. - - - `고정 할인 방식` - - ``` - 할인 방식을 선택하세요. - 1. 고정 할인 - 2. 비율 할인 - 입력 : 1 - - 고정 할인 금액을 입력하세요. - 1이상의 자연수만 입력하세요. 단위는 원입니다. - 입력 : 1000 - - 할인권 생성이 완료되었습니다. - sf5e8400-53m2-nn53-me09-456292156231 | 고정 할인 | 1,000원 - ``` - - - 사용자는 1 이상의 자연수를 입력하여, 고정 할인 금액을 설정할 수 있다. - - - `비율 할인 방식` - - ``` - 할인 방식을 선택하세요. - 1. 고정 할인 - 2. 비율 할인 - 입력 : 2 - - 비율 할인 퍼센트를 입력하세요. - 1이상 100이하의 자연수만 입력하세요. 단위는 %입니다. - 입력 : 40 - - 할인권 생성이 완료되었습니다. - 550e8400-e29b-41d4-a716-446655440000 | 비율 할인 | 40% - ``` - - - 사용자는 1 이상 100 이하의 자연수를 입력하여, 비율 할인 퍼센트를 설정할 수 있다. - - - -### 3. 할인권 조회 - - ``` - 현재까지 생성된 할인권 목록입니다. - 550e8400-e29b-41d4-a716-446655440000 | 비율 할인 | 40% - sf5e8400-53m2-nn53-me09-456292156231 | 고정 할인 | 1,000원 - ``` - -저장된 할인권들의 `UUID, 할인 방식, 금액/비율` 목록을 조회한다. - - - -### 4. 프로그램 종료 - - ``` - [할인권 프로그램 v1.0] - 1. 새 할인권 생성 - 2. 할인권 조회 - 3. 프로그램 종료 - - 입력 : 3 - - 프로그램을 종료합니다. - ``` - -실행 중인 프로그램을 종료한다. - - - ------- - -# 2. 설계 - -## 주요 객체 목록 - -1. **클라이언트** - - 입력(InputView.java) : 각종 메뉴, 할인 금액/비율에 대해 사용자 입력을 받는다. - - 출력(OutputView.java) : 각종 결과 및 예외메시지를 출력한다. -2. **컨트롤러(VoucherController.java)** - - 사용자의 메뉴 입력에 따라 각 메뉴를 실행한다. -3. **서비스(VoucherService.java)** - - 할인권과 관련된 기능을 수행한다. - - 새로운 할인권(고정, 비율)을 생성한다. - - 저장된 모든 할인권 목록을 조회한다. -4. **할인권(Voucher.java)** - - 실제 발행되는 할인권 객체 - - 할인권의 UUID, 할인 금액/비율 수치 정보를 갖는다. - - 고정 할인 방식(FixDiscountVoucher)과 비율 할인 방식(PercentDiscountVoucher)이 존재한다. -5. **저장소(VoucherRepository.java)** - - 할인권 정보를 저장한다. - - 저장된 할인권 목록을 조회하여 전달한다. -6. **메뉴(Menu.java)** - - 사용자가 실행할 수 있는 메뉴 정보를 가진다. -7. **할인권 종류(VoucherType.java)** - - 발행할 수 있는 할인권의 종류 정보를 가진다. - - 각 종류에 맞는 실제 할인권을 발행한다. - - - -## 예외처리 - -1. 메뉴 입력 예외 - - 없는 메뉴 번호인 경우 -2. 할인권 생성 예외 - - 할인 방식을 선택할 때, 없는 방식 번호를 선택한 경우 - - 고정 할인권 생성할 때, 금액의 범위를 벗어난 경우 - - 고정 할인권 생성할 때, 자연수 이외의 숫자나 문자를 입력한 경우 - - 비율 할인권 생성할 때, 퍼센트 범위를 벗어난 경우 - - 비율 할인권 생성할 때, 자연수 이외의 숫자나 문자를 입력한 경우 - - +# SpringBoot Basic Weekly Mission 2 +> 바우처 관리 애플리케이션 만들기 2 - 김영주 +# 1. 설계 ## 프로그램 구조도 -![](https://github.com/prgrms-be-devcourse/java-calculator/assets/49775540/6d4e6aba-824e-4e8e-b5fe-9c05589cdce7) +![image](https://github.com/prgrms-be-devcourse/springboot-basic/assets/49775540/1b4f4d03-b56b-4c2a-9cf6-410db2bdffb7) @@ -170,20 +17,29 @@ src/main/java/com.devcourse.voucherapp/ - VoucherController.java - entity/ + - dto/ + - VoucherCreateRequestDto.java + - VoucherUpdateRequestDto.java + - VoucherResponseDto.java + - VouchersResponseDto.java - voucher/ - Voucher.java - FixDiscountVoucher.java - PercentDiscountVoucher.java - Menu.java - VoucherType.java + - TriFunction.java - exception/ - MenuInputException.java - - VoucherInputException.java + - NotFoundVoucherException.java + - DiscountAmountException.java + - VoucherTypeInputException.java - repository/ - VoucherRepository.java - MemoryVoucherRepository.java + - JdbcVoucherRepository.java - service/ - VoucherService.java @@ -193,7 +49,9 @@ src/main/java/com.devcourse.voucherapp/ - OutputView.java - ConsoleInputView.java - ConsoleOutputView.java + - ViewManager.java +- CommandLineApplication.java - VoucherappApplication.java ``` @@ -201,9 +59,9 @@ src/main/java/com.devcourse.voucherapp/ ------ -## 3. 컨벤션 +# 2. 컨벤션 -### Git commit convention +## Git commit convention > [Angular JS commit convention](https://velog.io/@outstandingboy/Git-커밋-메시지-규약-정리-the-AngularJS-commit-conventions)를 참고 @@ -234,79 +92,71 @@ src/main/java/com.devcourse.voucherapp/ ------ -# 4. 기능 구현 및 실행 화면 +# 3. 기능 구현 및 실행 화면 -## 기능 구현 +## 구현 사항 -- [x] 메뉴 선택 화면 -- [x] 메뉴 입력 및 예외처리 -- [x] 프로그램 종료 -- [x] 고정 할인권 생성 -- [x] 비율 할인권 생성 -- [x] 할인권 조회 -- [x] logback 이용한 로그 파일 생성 -- [x] 실행 가능한 jar 파일 생성 +- [x] H2 데이터베이스를 적용한다. +- [x] 프로필을 이용해 개발과 로컬 환경을 구분한다. +- [x] 할인권 수정 기능을 추가한다. +- [x] 할인권 삭제 기능을 추가한다. ## 실행 화면 +> 할인권 수정, 삭제 기능 추가 + ``` [할인권 프로그램 v1.0] 1. 새 할인권 생성 2. 할인권 조회 -3. 프로그램 종료 +3. 할인권 수정 +4. 할인권 삭제 +5. 프로그램 종료 +입력 : 3 + +현재까지 생성된 할인권 목록입니다. +62f590c0-61c0-461d-90ce-ebafd031a4d8 | 고정 할인 | 1,000원 +38de7fec-e170-49ee-b5bd-30d768a11fd5 | 비율 할인 | 20% -입력 : 1 +수정할 할인권의 ID를 입력하세요. +입력 : 62f590c0-61c0-461d-90ce-ebafd031a4d8 -할인 방식을 선택하세요. -1. 고정 할인 -2. 비율 할인 -입력 : 1 +선택하신 할인권의 정보를 수정합니다. +62f590c0-61c0-461d-90ce-ebafd031a4d8 | 고정 할인 | 1,000원 -고정 할인 금액을 입력하세요. -1이상의 자연수만 입력하세요. 단위는 원입니다. -입력 : 1000 +고정 할인 금액을 입력하세요. (1이상의 자연수, 단위: 원) +입력 : 3000 -할인권 생성이 완료되었습니다. -0437e68e-7fc1-4136-9090-e8a4c3f50ed1 | 고정 할인 | 1,000원 +할인권 수정이 완료되었습니다. +62f590c0-61c0-461d-90ce-ebafd031a4d8 | 고정 할인 | 3,000원 [할인권 프로그램 v1.0] 1. 새 할인권 생성 2. 할인권 조회 -3. 프로그램 종료 +3. 할인권 수정 +4. 할인권 삭제 +5. 프로그램 종료 +입력 : 4 -입력 : 1 - -할인 방식을 선택하세요. -1. 고정 할인 -2. 비율 할인 -입력 : 2 +현재까지 생성된 할인권 목록입니다. +62f590c0-61c0-461d-90ce-ebafd031a4d8 | 고정 할인 | 3,000원 +38de7fec-e170-49ee-b5bd-30d768a11fd5 | 비율 할인 | 20% -비율 할인 퍼센트를 입력하세요. -1이상 100이하의 자연수만 입력하세요. 단위는 %입니다. -입력 : 40 +삭제할 할인권의 ID를 입력하세요. +입력 : 62f590c0-61c0-461d-90ce-ebafd031a4d8 -할인권 생성이 완료되었습니다. -71a70469-3e03-49a6-ad53-e8ec37531dce | 비율 할인 | 40% +할인권이 정상적으로 삭제되었습니다. [할인권 프로그램 v1.0] 1. 새 할인권 생성 2. 할인권 조회 -3. 프로그램 종료 - +3. 할인권 수정 +4. 할인권 삭제 +5. 프로그램 종료 입력 : 2 현재까지 생성된 할인권 목록입니다. -71a70469-3e03-49a6-ad53-e8ec37531dce | 비율 할인 | 40% -0437e68e-7fc1-4136-9090-e8a4c3f50ed1 | 고정 할인 | 1,000원 - -[할인권 프로그램 v1.0] -1. 새 할인권 생성 -2. 할인권 조회 -3. 프로그램 종료 - -입력 : 3 - -프로그램을 종료합니다. +38de7fec-e170-49ee-b5bd-30d768a11fd5 | 비율 할인 | 20% ``` diff --git a/build.gradle b/build.gradle index b10c7b38ec..bbcc557d80 100644 --- a/build.gradle +++ b/build.gradle @@ -16,9 +16,13 @@ repositories { } dependencies { - implementation 'org.springframework.boot:spring-boot-starter' + implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'org.springframework.boot:spring-boot-starter-jdbc' + implementation 'org.apache.commons:commons-lang3:3.12.0' testImplementation 'org.springframework.boot:spring-boot-starter-test' + runtimeOnly 'com.h2database:h2' + compileOnly 'org.projectlombok:lombok:1.18.28' annotationProcessor 'org.projectlombok:lombok:1.18.28' diff --git a/src/main/java/com/devcourse/voucherapp/CommandLineApplication.java b/src/main/java/com/devcourse/voucherapp/CommandLineApplication.java index c4a67234c4..f0cd5227e4 100644 --- a/src/main/java/com/devcourse/voucherapp/CommandLineApplication.java +++ b/src/main/java/com/devcourse/voucherapp/CommandLineApplication.java @@ -3,7 +3,9 @@ import com.devcourse.voucherapp.controller.VoucherController; import com.devcourse.voucherapp.entity.Menu; import com.devcourse.voucherapp.entity.VoucherType; -import com.devcourse.voucherapp.entity.voucher.Voucher; +import com.devcourse.voucherapp.entity.dto.VoucherCreateRequestDto; +import com.devcourse.voucherapp.entity.dto.VoucherResponseDto; +import com.devcourse.voucherapp.entity.dto.VoucherUpdateRequestDto; import com.devcourse.voucherapp.view.ViewManager; import java.util.List; import lombok.RequiredArgsConstructor; @@ -25,7 +27,7 @@ public void run(String... args) { while (isRunning) { try { String menuNumber = viewManager.readMenuNumber(); - Menu selectedMenu = Menu.of(menuNumber); + Menu selectedMenu = Menu.from(menuNumber); executeMenu(selectedMenu); } catch (Exception e) { String message = e.getMessage(); @@ -38,25 +40,51 @@ public void run(String... args) { private void executeMenu(Menu selectedMenu) { switch (selectedMenu) { case CREATE -> createVoucher(); - case LIST -> listAllVouchers(); + case READ -> readAllVouchers(); + case UPDATE -> updateVoucher(); + case DELETE -> deleteVoucher(); case QUIT -> quitApplication(); } } private void createVoucher() { - String voucherTypeNumber = viewManager.readVoucherTypeNumber(); - VoucherType voucherType = VoucherType.of(voucherTypeNumber); + String typeNumber = viewManager.readVoucherTypeNumber(); + VoucherType voucherType = VoucherType.from(typeNumber); String message = voucherType.getMessage(); String discountAmount = viewManager.readDiscountAmount(message); - Voucher voucher = voucherController.createVoucher(voucherType, discountAmount); - viewManager.showVoucherCreationSuccessMessage(voucher); + VoucherCreateRequestDto request = new VoucherCreateRequestDto(voucherType, discountAmount); + VoucherResponseDto response = voucherController.create(request); + + viewManager.showVoucherCreationSuccessMessage(response); + } + + private void readAllVouchers() { + List response = voucherController.findAllVouchers(); + viewManager.showAllVouchers(response); + } + + private void updateVoucher() { + readAllVouchers(); + + String id = viewManager.readVoucherIdToUpdate(); + VoucherResponseDto findResponse = voucherController.findVoucherById(id); + + String discountAmount = viewManager.readVoucherDiscountAmountToUpdate(findResponse); + VoucherUpdateRequestDto request = new VoucherUpdateRequestDto(findResponse.getId(), findResponse.getType(), discountAmount); + VoucherResponseDto response = voucherController.update(request); + + viewManager.showVoucherUpdateSuccessMessage(response); } - private void listAllVouchers() { - List vouchers = voucherController.findAllVouchers(); - viewManager.showAllVouchers(vouchers); + private void deleteVoucher() { + readAllVouchers(); + + String id = viewManager.readVoucherIdToDelete(); + voucherController.deleteById(id); + + viewManager.showVoucherDeleteSuccessMessage(); } private void quitApplication() { diff --git a/src/main/java/com/devcourse/voucherapp/controller/VoucherController.java b/src/main/java/com/devcourse/voucherapp/controller/VoucherController.java index 5a98171738..3c8025af1f 100644 --- a/src/main/java/com/devcourse/voucherapp/controller/VoucherController.java +++ b/src/main/java/com/devcourse/voucherapp/controller/VoucherController.java @@ -1,7 +1,8 @@ package com.devcourse.voucherapp.controller; -import com.devcourse.voucherapp.entity.VoucherType; -import com.devcourse.voucherapp.entity.voucher.Voucher; +import com.devcourse.voucherapp.entity.dto.VoucherCreateRequestDto; +import com.devcourse.voucherapp.entity.dto.VoucherResponseDto; +import com.devcourse.voucherapp.entity.dto.VoucherUpdateRequestDto; import com.devcourse.voucherapp.service.VoucherService; import java.util.List; import lombok.RequiredArgsConstructor; @@ -13,11 +14,23 @@ public class VoucherController { private final VoucherService voucherService; - public Voucher createVoucher(VoucherType voucherType, String discountAmount) { - return voucherService.create(voucherType, discountAmount); + public VoucherResponseDto create(VoucherCreateRequestDto request) { + return voucherService.create(request); } - public List findAllVouchers() { + public List findAllVouchers() { return voucherService.findAllVouchers(); } + + public VoucherResponseDto findVoucherById(String id) { + return voucherService.findVoucherById(id); + } + + public VoucherResponseDto update(VoucherUpdateRequestDto request) { + return voucherService.update(request); + } + + public void deleteById(String id) { + voucherService.deleteById(id); + } } diff --git a/src/main/java/com/devcourse/voucherapp/entity/Menu.java b/src/main/java/com/devcourse/voucherapp/entity/Menu.java index 4c4f0cd6f6..ce28f30ffa 100644 --- a/src/main/java/com/devcourse/voucherapp/entity/Menu.java +++ b/src/main/java/com/devcourse/voucherapp/entity/Menu.java @@ -12,8 +12,10 @@ public enum Menu { CREATE("1", "새 할인권 생성"), - LIST("2", "할인권 조회"), - QUIT("3", "프로그램 종료"); + READ("2", "할인권 조회"), + UPDATE("3", "할인권 수정"), + DELETE("4", "할인권 삭제"), + QUIT("5", "프로그램 종료"); private static final Map MENUS = Collections.unmodifiableMap(Stream.of(values()) .collect(Collectors.toMap(Menu::getNumber, Function.identity()))); @@ -28,7 +30,7 @@ public enum Menu { this.name = name; } - public static Menu of(String menuNumber) { + public static Menu from(String menuNumber) { if (MENUS.containsKey(menuNumber)) { return MENUS.get(menuNumber); } diff --git a/src/main/java/com/devcourse/voucherapp/entity/VoucherType.java b/src/main/java/com/devcourse/voucherapp/entity/VoucherType.java index 8e37537c93..477f2ede52 100644 --- a/src/main/java/com/devcourse/voucherapp/entity/VoucherType.java +++ b/src/main/java/com/devcourse/voucherapp/entity/VoucherType.java @@ -8,24 +8,30 @@ import com.devcourse.voucherapp.exception.VoucherTypeInputException; import java.util.Collections; import java.util.Map; +import java.util.UUID; import java.util.function.Function; import java.util.stream.Collectors; import java.util.stream.Stream; import lombok.Getter; +import org.apache.commons.lang3.function.TriFunction; @Getter public enum VoucherType { - FIX( - "1", - "고정 할인", - "\n고정 할인 금액을 입력하세요. (1이상의 자연수, 단위: 원)", - FixDiscountVoucher::new + FIX(VoucherTypeInfo.builder() + .number("1") + .name("고정 할인") + .message("\n고정 할인 금액을 입력하세요. (1이상의 자연수, 단위: 원)") + .unit("원") + .voucherGenerator(FixDiscountVoucher::new) + .build() ), - PERCENT( - "2", - "비율 할인", - "\n비율 할인 퍼센트를 입력하세요. (1이상 100이하의 자연수, 단위: %)", - PercentDiscountVoucher::new + PERCENT(VoucherTypeInfo.builder() + .number("2") + .name("비율 할인") + .message("\n비율 할인 퍼센트를 입력하세요. (1이상 100이하의 자연수, 단위: %)") + .unit("%") + .voucherGenerator(PercentDiscountVoucher::new) + .build() ); private static final Map VOUCHER_TYPES = Collections.unmodifiableMap(Stream.of(values()) @@ -34,16 +40,18 @@ public enum VoucherType { private final String number; private final String name; private final String message; - private final Function voucherGenerator; + private final String unit; + private final TriFunction voucherGenerator; - VoucherType(String number, String name, String message, Function voucherGenerator) { - this.number = number; - this.name = name; - this.message = message; - this.voucherGenerator = voucherGenerator; + VoucherType(VoucherTypeInfo voucherTypeInfo) { + this.number = voucherTypeInfo.getNumber(); + this.name = voucherTypeInfo.getName(); + this.message = voucherTypeInfo.getMessage(); + this.unit = voucherTypeInfo.getUnit(); + this.voucherGenerator = voucherTypeInfo.getVoucherGenerator(); } - public static VoucherType of(String voucherTypeNumber) { + public static VoucherType from(String voucherTypeNumber) { if (VOUCHER_TYPES.containsKey(voucherTypeNumber)) { return VOUCHER_TYPES.get(voucherTypeNumber); } @@ -51,8 +59,8 @@ public static VoucherType of(String voucherTypeNumber) { throw new VoucherTypeInputException(voucherTypeNumber); } - public Voucher makeVoucher(String discountAmount) { - return voucherGenerator.apply(discountAmount); + public Voucher makeVoucher(UUID id, String discountAmount) { + return voucherGenerator.apply(id, this, discountAmount); } @Override diff --git a/src/main/java/com/devcourse/voucherapp/entity/VoucherTypeInfo.java b/src/main/java/com/devcourse/voucherapp/entity/VoucherTypeInfo.java new file mode 100644 index 0000000000..488c500bfd --- /dev/null +++ b/src/main/java/com/devcourse/voucherapp/entity/VoucherTypeInfo.java @@ -0,0 +1,18 @@ +package com.devcourse.voucherapp.entity; + +import com.devcourse.voucherapp.entity.voucher.Voucher; +import java.util.UUID; +import lombok.Builder; +import lombok.Getter; +import org.apache.commons.lang3.function.TriFunction; + +@Builder +@Getter +public class VoucherTypeInfo { + + private final String number; + private final String name; + private final String message; + private final String unit; + private final TriFunction voucherGenerator; +} diff --git a/src/main/java/com/devcourse/voucherapp/entity/dto/VoucherCreateRequestDto.java b/src/main/java/com/devcourse/voucherapp/entity/dto/VoucherCreateRequestDto.java new file mode 100644 index 0000000000..30cc6af96a --- /dev/null +++ b/src/main/java/com/devcourse/voucherapp/entity/dto/VoucherCreateRequestDto.java @@ -0,0 +1,13 @@ +package com.devcourse.voucherapp.entity.dto; + +import com.devcourse.voucherapp.entity.VoucherType; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class VoucherCreateRequestDto { + + private final VoucherType type; + private final String discountAmount; +} diff --git a/src/main/java/com/devcourse/voucherapp/entity/dto/VoucherResponseDto.java b/src/main/java/com/devcourse/voucherapp/entity/dto/VoucherResponseDto.java new file mode 100644 index 0000000000..53d5be7694 --- /dev/null +++ b/src/main/java/com/devcourse/voucherapp/entity/dto/VoucherResponseDto.java @@ -0,0 +1,27 @@ +package com.devcourse.voucherapp.entity.dto; + +import static java.text.MessageFormat.format; + +import com.devcourse.voucherapp.entity.VoucherType; +import com.devcourse.voucherapp.entity.voucher.Voucher; +import java.util.UUID; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class VoucherResponseDto { + + private final UUID id; + private final VoucherType type; + private final int discountAmount; + + public static VoucherResponseDto from(Voucher voucher) { + return new VoucherResponseDto(voucher.getId(), voucher.getType(), voucher.getDiscountAmount()); + } + + @Override + public String toString() { + return format("{0} | {1} | {2}{3}", id, type.getName(), discountAmount, type.getUnit()); + } +} diff --git a/src/main/java/com/devcourse/voucherapp/entity/dto/VoucherUpdateRequestDto.java b/src/main/java/com/devcourse/voucherapp/entity/dto/VoucherUpdateRequestDto.java new file mode 100644 index 0000000000..b2238f1e50 --- /dev/null +++ b/src/main/java/com/devcourse/voucherapp/entity/dto/VoucherUpdateRequestDto.java @@ -0,0 +1,15 @@ +package com.devcourse.voucherapp.entity.dto; + +import com.devcourse.voucherapp.entity.VoucherType; +import java.util.UUID; +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@RequiredArgsConstructor +@Getter +public class VoucherUpdateRequestDto { + + private final UUID id; + private final VoucherType type; + private final String discountAmount; +} diff --git a/src/main/java/com/devcourse/voucherapp/entity/voucher/FixDiscountVoucher.java b/src/main/java/com/devcourse/voucherapp/entity/voucher/FixDiscountVoucher.java index 53dda6214a..12287085dc 100644 --- a/src/main/java/com/devcourse/voucherapp/entity/voucher/FixDiscountVoucher.java +++ b/src/main/java/com/devcourse/voucherapp/entity/voucher/FixDiscountVoucher.java @@ -2,38 +2,35 @@ import static java.text.MessageFormat.format; +import com.devcourse.voucherapp.entity.VoucherType; import com.devcourse.voucherapp.exception.DiscountAmountException; import java.util.UUID; import lombok.Getter; +@Getter public class FixDiscountVoucher implements Voucher { private static final String FIX_DISCOUNT_PRICE_REGEX = "^[1-9][0-9]*$"; - @Getter - private final UUID voucherId; + private final UUID id; + private final VoucherType type; + private final int discountAmount; - private final int discountPrice; - - public FixDiscountVoucher(String discountPrice) { - this.voucherId = UUID.randomUUID(); - this.discountPrice = getValidPrice(discountPrice); - } - - @Override - public String toString() { - return format("{0} | 고정 할인 | {1}원", voucherId, discountPrice); + public FixDiscountVoucher(UUID id, VoucherType type, String discountAmount) { + this.id = id; + this.type = type; + this.discountAmount = getValidPrice(discountAmount); } - private int getValidPrice(String discountPrice) { - if (isNotValid(discountPrice)) { - throw new DiscountAmountException(discountPrice); + private int getValidPrice(String discountAmount) { + if (isNotValid(discountAmount)) { + throw new DiscountAmountException(discountAmount); } - return Integer.parseInt(discountPrice); + return Integer.parseInt(discountAmount); } - private boolean isNotValid(String discountPrice) { - return !discountPrice.matches(FIX_DISCOUNT_PRICE_REGEX); + private boolean isNotValid(String discountAmount) { + return !discountAmount.matches(FIX_DISCOUNT_PRICE_REGEX); } } diff --git a/src/main/java/com/devcourse/voucherapp/entity/voucher/PercentDiscountVoucher.java b/src/main/java/com/devcourse/voucherapp/entity/voucher/PercentDiscountVoucher.java index e9e9013b6e..8545ec1447 100644 --- a/src/main/java/com/devcourse/voucherapp/entity/voucher/PercentDiscountVoucher.java +++ b/src/main/java/com/devcourse/voucherapp/entity/voucher/PercentDiscountVoucher.java @@ -2,38 +2,35 @@ import static java.text.MessageFormat.format; +import com.devcourse.voucherapp.entity.VoucherType; import com.devcourse.voucherapp.exception.DiscountAmountException; import java.util.UUID; import lombok.Getter; +@Getter public class PercentDiscountVoucher implements Voucher { private static final String PERCENT_DISCOUNT_RATE_REGEX = "^[1-9]|[1-9][0-9]|100$"; - @Getter - private final UUID voucherId; + private final UUID id; + private final VoucherType type; + private final int discountAmount; - private final int discountRate; - - public PercentDiscountVoucher(String discountRate) { - this.voucherId = UUID.randomUUID(); - this.discountRate = getValidRate(discountRate); - } - - @Override - public String toString() { - return format("{0} | 비율 할인 | {1}%", voucherId, discountRate); + public PercentDiscountVoucher(UUID id, VoucherType type, String discountAmount) { + this.id = id; + this.type = type; + this.discountAmount = getValidRate(discountAmount); } - private int getValidRate(String discountRate) { - if (isNotValid(discountRate)) { - throw new DiscountAmountException(discountRate); + private int getValidRate(String discountAmount) { + if (isNotValid(discountAmount)) { + throw new DiscountAmountException(discountAmount); } - return Integer.parseInt(discountRate); + return Integer.parseInt(discountAmount); } - private boolean isNotValid(String discountRate) { - return !discountRate.matches(PERCENT_DISCOUNT_RATE_REGEX); + private boolean isNotValid(String discountAmount) { + return !discountAmount.matches(PERCENT_DISCOUNT_RATE_REGEX); } } diff --git a/src/main/java/com/devcourse/voucherapp/entity/voucher/Voucher.java b/src/main/java/com/devcourse/voucherapp/entity/voucher/Voucher.java index 4550240816..b94e6428db 100644 --- a/src/main/java/com/devcourse/voucherapp/entity/voucher/Voucher.java +++ b/src/main/java/com/devcourse/voucherapp/entity/voucher/Voucher.java @@ -1,8 +1,13 @@ package com.devcourse.voucherapp.entity.voucher; +import com.devcourse.voucherapp.entity.VoucherType; import java.util.UUID; public interface Voucher { - UUID getVoucherId(); + UUID getId(); + + VoucherType getType(); + + int getDiscountAmount(); } diff --git a/src/main/java/com/devcourse/voucherapp/exception/NotFoundVoucherException.java b/src/main/java/com/devcourse/voucherapp/exception/NotFoundVoucherException.java new file mode 100644 index 0000000000..85091de575 --- /dev/null +++ b/src/main/java/com/devcourse/voucherapp/exception/NotFoundVoucherException.java @@ -0,0 +1,12 @@ +package com.devcourse.voucherapp.exception; + +import static java.text.MessageFormat.format; + +public class NotFoundVoucherException extends RuntimeException { + + private static final String NOT_FOUND_VOUCHER_MESSAGE = "입력하신 ID에 해당하는 할인권이 없습니다."; + + public NotFoundVoucherException(String id) { + super(format("{0} | 입력 : {1}", NOT_FOUND_VOUCHER_MESSAGE, id)); + } +} diff --git a/src/main/java/com/devcourse/voucherapp/repository/JdbcVoucherRepository.java b/src/main/java/com/devcourse/voucherapp/repository/JdbcVoucherRepository.java new file mode 100644 index 0000000000..be66a5af8e --- /dev/null +++ b/src/main/java/com/devcourse/voucherapp/repository/JdbcVoucherRepository.java @@ -0,0 +1,91 @@ +package com.devcourse.voucherapp.repository; + +import com.devcourse.voucherapp.entity.VoucherType; +import com.devcourse.voucherapp.entity.voucher.Voucher; +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import javax.sql.DataSource; +import org.springframework.context.annotation.Profile; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.RowMapper; +import org.springframework.jdbc.core.namedparam.MapSqlParameterSource; +import org.springframework.jdbc.core.namedparam.NamedParameterJdbcTemplate; +import org.springframework.stereotype.Repository; + +@Repository +@Profile("dev") +public class JdbcVoucherRepository implements VoucherRepository { + + private final NamedParameterJdbcTemplate template; + + public JdbcVoucherRepository(DataSource dataSource) { + this.template = new NamedParameterJdbcTemplate(dataSource); + } + + @Override + public Voucher save(Voucher voucher) { + String sql = "insert into voucher(id, type, discount_amount) values (:id, :typeNumber, :discountAmount)"; + template.update(sql, getParameterSource(voucher)); + + return voucher; + } + + @Override + public List findAllVouchers() { + String sql = "select id, type, discount_amount from voucher"; + + return template.query(sql, getVoucherRowMapper()); + } + + @Override + public Optional findVoucherById(String id) { + String sql = "select id, type, discount_amount from voucher where id = :id"; + + try { + Voucher voucher = template.queryForObject(sql, getParameterMap(id), getVoucherRowMapper()); + + return Optional.of(voucher); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } + } + + @Override + public Voucher update(Voucher voucher) { + String sql = "update voucher set type = :typeNumber, discount_amount = :discountAmount where id = :id"; + template.update(sql, getParameterSource(voucher)); + + return voucher; + } + + @Override + public int deleteById(String id) { + String sql = "delete from voucher where id = :id"; + + return template.update(sql, getParameterMap(id)); + } + + private MapSqlParameterSource getParameterSource(Voucher voucher) { + return new MapSqlParameterSource() + .addValue("id", voucher.getId().toString()) + .addValue("typeNumber", voucher.getType().getNumber()) + .addValue("discountAmount", voucher.getDiscountAmount()); + } + + private Map getParameterMap(String id) { + return Map.of("id", id); + } + + private RowMapper getVoucherRowMapper() { + return (resultSet, rowNum) -> { + String id = resultSet.getString("id"); + String typeNumber = resultSet.getString("type"); + int discountAmount = resultSet.getInt("discount_amount"); + + return VoucherType.from(typeNumber) + .makeVoucher(UUID.fromString(id), String.valueOf(discountAmount)); + }; + } +} diff --git a/src/main/java/com/devcourse/voucherapp/repository/MemoryVoucherRepository.java b/src/main/java/com/devcourse/voucherapp/repository/MemoryVoucherRepository.java index f52cfaf945..e4bddf6638 100644 --- a/src/main/java/com/devcourse/voucherapp/repository/MemoryVoucherRepository.java +++ b/src/main/java/com/devcourse/voucherapp/repository/MemoryVoucherRepository.java @@ -1,20 +1,24 @@ package com.devcourse.voucherapp.repository; import com.devcourse.voucherapp.entity.voucher.Voucher; +import com.devcourse.voucherapp.exception.NotFoundVoucherException; import java.util.LinkedHashMap; import java.util.List; import java.util.Map; +import java.util.Optional; import java.util.UUID; +import org.springframework.context.annotation.Profile; import org.springframework.stereotype.Repository; @Repository +@Profile("local") public class MemoryVoucherRepository implements VoucherRepository { private final Map storage = new LinkedHashMap<>(); @Override public Voucher save(Voucher voucher) { - storage.put(voucher.getVoucherId(), voucher); + storage.put(voucher.getId(), voucher); return voucher; } @@ -23,4 +27,32 @@ public Voucher save(Voucher voucher) { public List findAllVouchers() { return List.copyOf(storage.values()); } + + @Override + public Optional findVoucherById(String id) { + UUID voucherId = convertToUuid(id); + + return Optional.ofNullable(storage.get(voucherId)); + } + + @Override + public Voucher update(Voucher voucher) { + return save(voucher); + } + + @Override + public int deleteById(String id) { + UUID voucherId = convertToUuid(id); + Voucher deletedVoucher = storage.remove(voucherId); + + return deletedVoucher == null ? 0 : 1; + } + + private UUID convertToUuid(String id) { + try { + return UUID.fromString(id); + } catch (IllegalArgumentException e) { + throw new NotFoundVoucherException(id); + } + } } diff --git a/src/main/java/com/devcourse/voucherapp/repository/VoucherRepository.java b/src/main/java/com/devcourse/voucherapp/repository/VoucherRepository.java index afb0b8cb1a..a541fc7b7f 100644 --- a/src/main/java/com/devcourse/voucherapp/repository/VoucherRepository.java +++ b/src/main/java/com/devcourse/voucherapp/repository/VoucherRepository.java @@ -2,10 +2,17 @@ import com.devcourse.voucherapp.entity.voucher.Voucher; import java.util.List; +import java.util.Optional; public interface VoucherRepository { Voucher save(Voucher voucher); List findAllVouchers(); + + Optional findVoucherById(String id); + + Voucher update(Voucher voucher); + + int deleteById(String id); } diff --git a/src/main/java/com/devcourse/voucherapp/service/VoucherService.java b/src/main/java/com/devcourse/voucherapp/service/VoucherService.java index 65c784b82c..370fdd6409 100644 --- a/src/main/java/com/devcourse/voucherapp/service/VoucherService.java +++ b/src/main/java/com/devcourse/voucherapp/service/VoucherService.java @@ -1,9 +1,14 @@ package com.devcourse.voucherapp.service; import com.devcourse.voucherapp.entity.VoucherType; +import com.devcourse.voucherapp.entity.dto.VoucherCreateRequestDto; +import com.devcourse.voucherapp.entity.dto.VoucherResponseDto; +import com.devcourse.voucherapp.entity.dto.VoucherUpdateRequestDto; import com.devcourse.voucherapp.entity.voucher.Voucher; +import com.devcourse.voucherapp.exception.NotFoundVoucherException; import com.devcourse.voucherapp.repository.VoucherRepository; import java.util.List; +import java.util.UUID; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; @@ -13,13 +18,44 @@ public class VoucherService { private final VoucherRepository voucherRepository; - public Voucher create(VoucherType voucherType, String discountAmount) { - Voucher voucher = voucherType.makeVoucher(discountAmount); + public VoucherResponseDto create(VoucherCreateRequestDto request) { + VoucherType voucherType = request.getType(); + Voucher newVoucher = voucherType.makeVoucher(UUID.randomUUID(), request.getDiscountAmount()); + Voucher voucher = voucherRepository.save(newVoucher); - return voucherRepository.save(voucher); + return VoucherResponseDto.from(voucher); } - public List findAllVouchers() { - return voucherRepository.findAllVouchers(); + public List findAllVouchers() { + return voucherRepository.findAllVouchers().stream() + .map(VoucherResponseDto::from) + .toList(); + } + + public VoucherResponseDto findVoucherById(String id) { + Voucher voucher = voucherRepository.findVoucherById(id) + .orElseThrow(() -> new NotFoundVoucherException(id)); + + return VoucherResponseDto.from(voucher); + } + + public VoucherResponseDto update(VoucherUpdateRequestDto request) { + VoucherType voucherType = request.getType(); + Voucher updatedVoucher = voucherType.makeVoucher(request.getId(), request.getDiscountAmount()); + Voucher voucher = voucherRepository.update(updatedVoucher); + + return VoucherResponseDto.from(voucher); + } + + public void deleteById(String id) { + int deleteCounts = voucherRepository.deleteById(id); + + if (isEmptyDeleteResult(deleteCounts)) { + throw new NotFoundVoucherException(id); + } + } + + private boolean isEmptyDeleteResult(int deleteCounts) { + return deleteCounts == 0; } } diff --git a/src/main/java/com/devcourse/voucherapp/view/ViewManager.java b/src/main/java/com/devcourse/voucherapp/view/ViewManager.java index cbe0481840..a556b5c8b4 100644 --- a/src/main/java/com/devcourse/voucherapp/view/ViewManager.java +++ b/src/main/java/com/devcourse/voucherapp/view/ViewManager.java @@ -2,7 +2,7 @@ import com.devcourse.voucherapp.entity.Menu; import com.devcourse.voucherapp.entity.VoucherType; -import com.devcourse.voucherapp.entity.voucher.Voucher; +import com.devcourse.voucherapp.entity.dto.VoucherResponseDto; import java.util.List; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Component; @@ -17,6 +17,11 @@ public class ViewManager { private static final String VOUCHER_TYPE_SELECTION_MESSAGE = "\n할인 방식을 선택하세요."; private static final String VOUCHER_CREATION_SUCCESS_MESSAGE = "\n할인권 생성이 완료되었습니다."; private static final String ALL_VOUCHERS_LIST_MESSAGE = "\n현재까지 생성된 할인권 목록입니다."; + private static final String UPDATE_VOUCHER_ID_INPUT_MESSAGE = "\n수정할 할인권의 ID를 입력하세요."; + private static final String UPDATE_VOUCHER_INFORMATION_MESSAGE = "\n선택하신 할인권의 정보를 수정합니다."; + private static final String VOUCHER_UPDATE_SUCCESS_MESSAGE = "\n할인권 수정이 완료되었습니다."; + private static final String DELETE_VOUCHER_ID_INPUT_MESSAGE = "\n삭제할 할인권의 ID를 입력하세요."; + private static final String VOUCHER_DELETE_SUCCESS_MESSAGE = "\n할인권이 정상적으로 삭제되었습니다."; private final InputView inputView; private final OutputView outputView; @@ -26,9 +31,8 @@ public String readMenuNumber() { for (Menu menu : Menu.values()) { outputView.printWithLineBreak(menu); } - outputView.printWithoutLineBreak(INPUT_MESSAGE); - return inputView.inputWithTrimming(); + return readUserInput(); } public String readVoucherTypeNumber() { @@ -36,16 +40,34 @@ public String readVoucherTypeNumber() { for (VoucherType voucherType : VoucherType.values()) { outputView.printWithLineBreak(voucherType); } - outputView.printWithoutLineBreak(INPUT_MESSAGE); - return inputView.inputWithTrimming(); + return readUserInput(); } public String readDiscountAmount(String message) { outputView.printWithLineBreak(message); - outputView.printWithoutLineBreak(INPUT_MESSAGE); - return inputView.inputWithTrimming(); + return readUserInput(); + } + + public String readVoucherIdToUpdate() { + outputView.printWithLineBreak(UPDATE_VOUCHER_ID_INPUT_MESSAGE); + + return readUserInput(); + } + + public String readVoucherDiscountAmountToUpdate(VoucherResponseDto findResponse) { + outputView.printWithLineBreak(UPDATE_VOUCHER_INFORMATION_MESSAGE); + outputView.printWithLineBreak(findResponse); + VoucherType voucherType = findResponse.getType(); + + return readDiscountAmount(voucherType.getMessage()); + } + + public String readVoucherIdToDelete() { + outputView.printWithLineBreak(DELETE_VOUCHER_ID_INPUT_MESSAGE); + + return readUserInput(); } public void showExceptionMessage(String message) { @@ -56,15 +78,30 @@ public void showQuitMessage() { outputView.printWithLineBreak(QUIT_MESSAGE); } - public void showVoucherCreationSuccessMessage(Voucher voucher) { + public void showVoucherCreationSuccessMessage(VoucherResponseDto response) { outputView.printWithLineBreak(VOUCHER_CREATION_SUCCESS_MESSAGE); - outputView.printWithLineBreak(voucher); + outputView.printWithLineBreak(response); } - public void showAllVouchers(List vouchers) { + public void showAllVouchers(List response) { outputView.printWithLineBreak(ALL_VOUCHERS_LIST_MESSAGE); - for (Voucher voucher : vouchers) { + for (VoucherResponseDto voucher : response) { outputView.printWithLineBreak(voucher); } } + + public void showVoucherUpdateSuccessMessage(VoucherResponseDto response) { + outputView.printWithLineBreak(VOUCHER_UPDATE_SUCCESS_MESSAGE); + outputView.printWithLineBreak(response); + } + + public void showVoucherDeleteSuccessMessage() { + outputView.printWithLineBreak(VOUCHER_DELETE_SUCCESS_MESSAGE); + } + + private String readUserInput() { + outputView.printWithoutLineBreak(INPUT_MESSAGE); + + return inputView.inputWithTrimming(); + } } diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties deleted file mode 100644 index 8b13789179..0000000000 --- a/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ - diff --git a/src/main/resources/application.yaml b/src/main/resources/application.yaml new file mode 100644 index 0000000000..96ed0446b4 --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,26 @@ +spring: + profiles: + active: dev + +--- + +spring: + config: + activate: + on-profile: local + +--- + +spring: + config: + activate: + on-profile: dev + h2: + console: + enabled: true + path: /h2-console + datasource: + driver-class-name: org.h2.Driver + url: jdbc:h2:mem:testDB + username: sa + password: \ No newline at end of file diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 0000000000..fef45f8da8 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,7 @@ +DROP TABLE IF EXISTS voucher; + +CREATE TABLE voucher ( + id VARCHAR(36) PRIMARY KEY, + type VARCHAR(5) NOT NULL, + discount_amount INTEGER NOT NULL +); \ No newline at end of file diff --git a/src/test/java/com/devcourse/voucherapp/entity/MenuTest.java b/src/test/java/com/devcourse/voucherapp/entity/MenuTest.java index 6b881b58c2..0bdb27bf34 100644 --- a/src/test/java/com/devcourse/voucherapp/entity/MenuTest.java +++ b/src/test/java/com/devcourse/voucherapp/entity/MenuTest.java @@ -5,26 +5,23 @@ import com.devcourse.voucherapp.exception.MenuInputException; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.junit.jupiter.params.provider.ValueSource; class MenuTest { @DisplayName("존재하는 메뉴 번호 입력 시, 해당 메뉴 객체가 반환된다.") - @Test - void selectExistedMenuTest() { - Menu createMenu = Menu.of("1"); - Menu listMenu = Menu.of("2"); - Menu quitMenu = Menu.of("3"); - - assertEquals(Menu.CREATE, createMenu); - assertEquals(Menu.LIST, listMenu); - assertEquals(Menu.QUIT, quitMenu); + @ParameterizedTest + @CsvSource(value = {"1,CREATE", "2,READ", "3,UPDATE", "4,DELETE", "5,QUIT"}) + void selectExistedMenuTest(String menuNumber, Menu menu) { + assertEquals(menu, Menu.from(menuNumber)); } @DisplayName("존재하지 않는 메뉴 번호 입력 시, MenuInputException 예외가 발생한다.") - @Test - void selectNotExistedMenuTest() { - assertThrows(MenuInputException.class, () -> Menu.of("10000")); - assertThrows(MenuInputException.class, () -> Menu.of("string")); + @ParameterizedTest + @ValueSource(strings = {"10000", "string"}) + void selectNotExistedMenuTest(String invalidMenuNumber) { + assertThrows(MenuInputException.class, () -> Menu.from(invalidMenuNumber)); } } diff --git a/src/test/java/com/devcourse/voucherapp/entity/VoucherTypeTest.java b/src/test/java/com/devcourse/voucherapp/entity/VoucherTypeTest.java index 940f50fc41..04d43a6182 100644 --- a/src/test/java/com/devcourse/voucherapp/entity/VoucherTypeTest.java +++ b/src/test/java/com/devcourse/voucherapp/entity/VoucherTypeTest.java @@ -5,41 +5,39 @@ import com.devcourse.voucherapp.exception.DiscountAmountException; import com.devcourse.voucherapp.exception.VoucherTypeInputException; +import java.util.UUID; import org.junit.jupiter.api.DisplayName; -import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; class VoucherTypeTest { @DisplayName("존재하는 할인권 방식 입력 시, 해당 할인권 방식 객체가 반환된다.") - @Test - void selectExistedVoucherTypeTest() { - VoucherType fixType = VoucherType.of("1"); - VoucherType percentType = VoucherType.of("2"); - - assertEquals(VoucherType.FIX, fixType); - assertEquals(VoucherType.PERCENT, percentType); + @ParameterizedTest + @CsvSource(value = {"1,FIX", "2,PERCENT"}) + void selectExistedVoucherTypeTest(String typeNumber, VoucherType type) { + assertEquals(type, VoucherType.from(typeNumber)); } @DisplayName("존재하지 않는 할인권 방식 입력 시, VoucherInputException 예외가 발생한다.") - @Test - void selectNotExistedVoucherTypeTest() { - assertThrows(VoucherTypeInputException.class, () -> VoucherType.of("10000")); - assertThrows(VoucherTypeInputException.class, () -> VoucherType.of("string")); + @ParameterizedTest + @ValueSource(strings = {"10000", "string"}) + void selectNotExistedVoucherTypeTest(String invalidTypeNumber) { + assertThrows(VoucherTypeInputException.class, () -> VoucherType.from(invalidTypeNumber)); } @DisplayName("고정 금액 할인권 생성 시, 잘못된 금액을 입력한 경우 VoucherInputException 예외가 발생한다.") @ParameterizedTest @ValueSource(strings = {"0", "-1", "10.1", "string", "", " ", "\n"}) void invalidFixDiscountPriceTest(String discountPrice) { - assertThrows(DiscountAmountException.class, () -> VoucherType.FIX.makeVoucher(discountPrice)); + assertThrows(DiscountAmountException.class, () -> VoucherType.FIX.makeVoucher(UUID.randomUUID(), discountPrice)); } @DisplayName("비율 퍼센트 할인권 생성 시, 잘못된 퍼센트를 입력한 경우 VoucherInputException 예외가 발생한다.") @ParameterizedTest @ValueSource(strings = {"0", "-1", "10.1", "string", "101", "", " ", "\n"}) void invalidPercentDiscountRateTest(String discountRate) { - assertThrows(DiscountAmountException.class, () -> VoucherType.PERCENT.makeVoucher(discountRate)); + assertThrows(DiscountAmountException.class, () -> VoucherType.PERCENT.makeVoucher(UUID.randomUUID(), discountRate)); } }