diff --git "a/docs/2\354\243\274\354\260\250 \352\270\260\353\212\245\353\252\205\354\204\270\354\204\234.md" "b/docs/2\354\243\274\354\260\250 \352\270\260\353\212\245\353\252\205\354\204\270\354\204\234.md" new file mode 100644 index 0000000000..b657033a6c --- /dev/null +++ "b/docs/2\354\243\274\354\260\250 \352\270\260\353\212\245\353\252\205\354\204\270\354\204\234.md" @@ -0,0 +1,86 @@ +# 2주차 기능 명세서 + +## [요구사항] + +--- + +### 1.입력 +> CLI (Command linke inteface)한 환경에서 실행되도록 한다. + +[ 프로그램 실행 구문 ] +``` +=== Manage Program === +Type customer to select customer menu. +Type voucher to select customer menu. +Type exit to exit the program. +``` + +[ voucher 실행 구문 ] +``` +=== Voucher Manage === +Type exit to exit the program. +Type create to create a new voucher. +Type list to list all vouchers. +``` + +[ customer 실행 구문 ] +``` +=== Customer Manage === +Type exit to exit the program. +Type create to create a new customer. +Type list to list all customers. +``` + +### 입력 예외 추가 기능 +- 입력시 `양쪽 공백`은 `삭제` +- `대소문자 상관없이` 동일한 메뉴 이름이면 사용가능 + +### 2. 회원 +> `create`와 `list` 커맨드를 통해 회원를 생성 및 조회할 수 있다. + +- `create`: 회원 생성 +- `list`: 회원 조회 +- customers 테이블 정의 및 추가 + - CustomerRepository 추가 및 `JdbcTemplate`을 사용해서 구현 + - CRUD 모든 기능 추가 + + +## 3. 바우처 +- `JdbcTemplate`을 사용해서 구현 +- CRUD 모든 기능 추가 + +## 4. 바우처 지갑 (심화) +- 특정 고객에게 바우처를 할당할 수 있습니다. +- 고객이 어떤 바우처를 보유하고 있는지 조회할 수 있어야 합니다. +- 고객이 보유한 바우처를 제거할 수 있어야 합니다. +- 특정 바우처를 보유한 고객을 조회할 수 있어야 합니다. + +
+ +## 기능 + +--- + +### 1. 입력 +- [x] 회원과 바우처 선택하는 기능 + +### 2. 바우처 +- [x] JdbcTemplate를 사용하여 Repostiroy 관리 +- [x] CRUD 기능 + +### 3. 회원 +- [x] 회원 CRUD 기능 + - [x] 회원 추가 + - [x] 회원 삭제 + - [x] 회원 업데이트 + - [x] 회원 조회 +- [x] JdbcTemplate를 사용하여 Repository 관리 + +### 4. 심화 +- [ ] 지갑 생성 +- [ ] 고객이 바우처 조회 기능 +- [ ] 고객이 보유한 바우처 제거 기능 +- [ ] 특정 바우처를 보유한 고객 조회 기능 + + + diff --git a/docs/README.md b/docs/README.md index 64979c406f..4d12d67ec3 100644 --- a/docs/README.md +++ b/docs/README.md @@ -34,7 +34,8 @@ Type list to list all vouchers. - 파일을 읽을 수 있고 리스트 조회 가능 -### 3. 기타 요구사항 + +### 기타 요구사항 - `YAML`프러퍼티를 만들고 어떤 설정을 만들 수 있을지 - 실행가능한 `jar`파일을 생성 - `logback`을 통한 에러 파일로 기록 diff --git a/pom.xml b/pom.xml index eed0ea226d..72e5a7a170 100644 --- a/pom.xml +++ b/pom.xml @@ -28,8 +28,39 @@ spring-boot-starter-test test + + + org.springframework.boot + spring-boot-starter-jdbc + + + + mysql + mysql-connector-java + 8.0.28 + + + + org.testcontainers + testcontainers + 1.18.3 + test + + + org.testcontainers + junit-jupiter + 1.18.3 + test + + + org.testcontainers + mysql + 1.18.3 + test + + diff --git a/src/main/java/org/weekly/weekly/VoucherManageApplication.java b/src/main/java/org/weekly/weekly/VoucherManageApplication.java index fad07c1435..7fe8ed5f34 100644 --- a/src/main/java/org/weekly/weekly/VoucherManageApplication.java +++ b/src/main/java/org/weekly/weekly/VoucherManageApplication.java @@ -3,14 +3,12 @@ import org.springframework.boot.SpringApplication; import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.context.ApplicationContext; -import org.weekly.weekly.voucher.controller.VoucherController; @SpringBootApplication public class VoucherManageApplication { - public static void main(String[] args) { + ApplicationContext context = SpringApplication.run(VoucherManageApplication.class, args); - context.getBean(VoucherController.class).start(); + context.getBean(VoucherManagementController.class).start(); } - } \ No newline at end of file diff --git a/src/main/java/org/weekly/weekly/VoucherManagementController.java b/src/main/java/org/weekly/weekly/VoucherManagementController.java new file mode 100644 index 0000000000..8cc14adbd3 --- /dev/null +++ b/src/main/java/org/weekly/weekly/VoucherManagementController.java @@ -0,0 +1,164 @@ +package org.weekly.weekly; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.stereotype.Component; +import org.weekly.weekly.customer.controller.CustomerController; +import org.weekly.weekly.customer.dto.request.CustomerCreationRequest; +import org.weekly.weekly.customer.dto.request.CustomerUpdateRequest; +import org.weekly.weekly.customer.dto.response.CustomerResponse; +import org.weekly.weekly.customer.dto.response.CustomersResponse; +import org.weekly.weekly.ui.CommandLineApplication; +import org.weekly.weekly.util.CustomerMenu; +import org.weekly.weekly.util.ManageMenu; +import org.weekly.weekly.util.PrintMessageType; +import org.weekly.weekly.util.VoucherMenu; +import org.weekly.weekly.voucher.controller.VoucherController; +import org.weekly.weekly.voucher.dto.Response; +import org.weekly.weekly.voucher.dto.request.VoucherCreationRequest; + +import java.util.List; + +@Component +public class VoucherManagementController { + private final Logger logger = LoggerFactory.getLogger(VoucherManagementController.class); + private final CommandLineApplication commandLineApplication; + private final VoucherController voucherController; + private final CustomerController customerController; + + public VoucherManagementController(CommandLineApplication commandLineApplication, VoucherController voucherController, CustomerController customerController) { + this.commandLineApplication = commandLineApplication; + this.voucherController = voucherController; + this.customerController = customerController; + } + + public void start() { + boolean isExit = false; + + while(!isExit) { + try { + ManageMenu manageMenu = commandLineApplication.readManageMenu(); + isExit = processManageMenuSelection(manageMenu); + } catch (RuntimeException runtimeException) { + commandLineApplication.printErrorMsg(runtimeException.getMessage()); + } + } + } + + private boolean processManageMenuSelection(ManageMenu manageMenu) { + if (ManageMenu.EXIT.equals(manageMenu)) { + return true; + } + + if (ManageMenu.VOUCHER.equals(manageMenu)) { + VoucherMenu voucherMenu = commandLineApplication.readVoucherMenu(); + processVoucherMenuSelection(voucherMenu); + return false; + } + + if (ManageMenu.CUSTOMER.equals(manageMenu)){ + CustomerMenu customerMenu = commandLineApplication.readCustomerMenu(); + processCustomerMenuSelection(customerMenu); + return false; + } + + return true; + } + + private boolean processVoucherMenuSelection(VoucherMenu selectMenu) { + if (VoucherMenu.CREATE.equals(selectMenu)) { + handleVoucherCreation(); + return false; + } + + if (VoucherMenu.LIST.equals(selectMenu)) { + handleVoucherSearch(); + return false; + } + + + return true; + } + + private void handleVoucherCreation() { + VoucherCreationRequest voucherCreationRequest = commandLineApplication.createVoucherFromInput(); + Response response = voucherController.createVoucher(voucherCreationRequest); + logger.info("{}{}", PrintMessageType.CREATE_VOUCHER_SUCCESS.getMessage(),response.getResult()); + commandLineApplication.printResult(response); + } + + private void handleVoucherSearch() { + Response response = voucherController.getVouchers(); + logger.info("{}{}", PrintMessageType.FIND_ALL_VOUCHER_SUCCESS.getMessage(), response.getResult()); + commandLineApplication.printResult(response); + } + + private boolean processCustomerMenuSelection(CustomerMenu selectMenu) { + if (CustomerMenu.CREATE.equals(selectMenu)) { + handleCustomerCreation(); + return false; + } + + if (CustomerMenu.DELETE.equals(selectMenu)) { + handleCustomerDelete(); + return false; + } + + if (CustomerMenu.DELETE_ALL.equals(selectMenu)) { + handleCustomerDeleteAll(); + return false; + } + + if (CustomerMenu.FIND_ALL.equals(selectMenu)) { + handleCustomerFindAll(); + return false; + } + + if (CustomerMenu.FIND_DETAIL.equals(selectMenu)) { + handleFindDetail(); + return false; + } + + if (CustomerMenu.UPDATE.equals(selectMenu)) { + handleUpdateCustomer(); + return false; + } + + return true; + } + + private void handleCustomerCreation() { + CustomerCreationRequest customerCreation = commandLineApplication.createCustomerFromInput(); + CustomerResponse customerDto = customerController.createCustomer(customerCreation); + commandLineApplication.printResult(customerDto); + } + + private void handleCustomerDelete() { + CustomerUpdateRequest customerUpdateRequest = commandLineApplication.customerDetailFromInput(); + customerController.deleteCustomer(customerUpdateRequest); + commandLineApplication.printDeleteMessage(); + } + + private void handleCustomerDeleteAll() { + customerController.deleteAllCustomer(); + commandLineApplication.printDeleteMessage(); + } + + private void handleCustomerFindAll() { + CustomersResponse customerResponses = customerController.findAllCustomer(); + commandLineApplication.printResult(customerResponses); + } + + private void handleFindDetail() { + CustomerUpdateRequest customerUpdateRequest = commandLineApplication.customerDetailFromInput(); + CustomerResponse customerResponse = customerController.findDetailCustomer(customerUpdateRequest); + commandLineApplication.printResult(customerResponse); + } + + private void handleUpdateCustomer() { + CustomerUpdateRequest customerUpdateRequest = commandLineApplication.customerUpdateRequest(); + CustomerResponse customerResponse = customerController.updateCustomer(customerUpdateRequest); + commandLineApplication.printResult(customerResponse); + } + +} diff --git a/src/main/java/org/weekly/weekly/customer/controller/CustomerController.java b/src/main/java/org/weekly/weekly/customer/controller/CustomerController.java new file mode 100644 index 0000000000..83abc90697 --- /dev/null +++ b/src/main/java/org/weekly/weekly/customer/controller/CustomerController.java @@ -0,0 +1,44 @@ +package org.weekly.weekly.customer.controller; + +import org.springframework.stereotype.Controller; +import org.weekly.weekly.customer.dto.request.CustomerCreationRequest; +import org.weekly.weekly.customer.dto.request.CustomerUpdateRequest; +import org.weekly.weekly.customer.dto.response.CustomerResponse; +import org.weekly.weekly.customer.dto.response.CustomersResponse; +import org.weekly.weekly.customer.service.CustomerService; + +import java.util.List; + +@Controller +public class CustomerController { + + private final CustomerService customerService; + + public CustomerController(CustomerService customerService) { + this.customerService = customerService; + } + + public CustomerResponse createCustomer(CustomerCreationRequest creationRequest) { + return customerService.createCustomer(creationRequest); + } + + public void deleteCustomer(CustomerUpdateRequest updateRequest) { + customerService.deleteCustomer(updateRequest); + } + + public void deleteAllCustomer() { + customerService.deleteAllCustomers(); + } + + public CustomerResponse findDetailCustomer(CustomerUpdateRequest updateRequest) { + return customerService.findDetailCustomer(updateRequest); + } + + public CustomersResponse findAllCustomer() { + return customerService.findAllCustomer(); + } + + public CustomerResponse updateCustomer(CustomerUpdateRequest updateRequest) { + return customerService.updateCustomer(updateRequest); + } +} diff --git a/src/main/java/org/weekly/weekly/customer/domain/Customer.java b/src/main/java/org/weekly/weekly/customer/domain/Customer.java new file mode 100644 index 0000000000..b6cdb8d6fb --- /dev/null +++ b/src/main/java/org/weekly/weekly/customer/domain/Customer.java @@ -0,0 +1,45 @@ +package org.weekly.weekly.customer.domain; + +import org.weekly.weekly.customer.exception.CustomerValidator; + +import java.time.LocalDateTime; +import java.util.UUID; + +public class Customer { + UUID customerId; + String name; + String email; + LocalDateTime createAt; + + public Customer(UUID customerId, String name, String email, LocalDateTime createAt) { + this.name = name; + this.email = email; + this.customerId = customerId; + this.createAt = createAt; + } + + public static Customer of(UUID uuid, String name, String email) { + CustomerValidator.validateEmailFormat(email); + return new Customer(uuid, name, email, LocalDateTime.now()); + } + + public String getName() { + return name; + } + + public LocalDateTime getCreateAt() { + return createAt; + } + + public String getEmail() { + return email; + } + + public UUID getCustomerId() { + return customerId; + } + + public void updateEmail(String email) { + this.email = email; + } +} diff --git a/src/main/java/org/weekly/weekly/customer/dto/request/CustomerCreationRequest.java b/src/main/java/org/weekly/weekly/customer/dto/request/CustomerCreationRequest.java new file mode 100644 index 0000000000..f21ac3cb5b --- /dev/null +++ b/src/main/java/org/weekly/weekly/customer/dto/request/CustomerCreationRequest.java @@ -0,0 +1,30 @@ +package org.weekly.weekly.customer.dto.request; + +import org.weekly.weekly.customer.domain.Customer; +import org.weekly.weekly.ui.exception.InputValidator; + +import java.util.UUID; + +public class CustomerCreationRequest { + private String email; + private String name; + + public CustomerCreationRequest(String email, String name) { + this.email = email; + this.name = name; + } + + public static CustomerCreationRequest of(String email, String name) { + InputValidator.isEmpty(email); + InputValidator.isEmpty(name); + return new CustomerCreationRequest(email, name); + } + + public Customer toCustomer() { + return Customer.of(UUID.randomUUID(), name, email); + } + + public String getEmail() { + return email; + } +} diff --git a/src/main/java/org/weekly/weekly/customer/dto/request/CustomerUpdateRequest.java b/src/main/java/org/weekly/weekly/customer/dto/request/CustomerUpdateRequest.java new file mode 100644 index 0000000000..5567f226b4 --- /dev/null +++ b/src/main/java/org/weekly/weekly/customer/dto/request/CustomerUpdateRequest.java @@ -0,0 +1,33 @@ +package org.weekly.weekly.customer.dto.request; + +import org.weekly.weekly.ui.exception.InputValidator; + +public class CustomerUpdateRequest { + private String email; + private String newEmail; + + private CustomerUpdateRequest(String email, String afterEmail) { + this.email = email; + this.newEmail = afterEmail; + } + private CustomerUpdateRequest(String email) { + this.email = email; + } + + public static CustomerUpdateRequest of(String email) { + InputValidator.isEmpty(email); + return new CustomerUpdateRequest(email); + } + + public static CustomerUpdateRequest of(String email, String afterEmail) { + InputValidator.isEmpty(email); + InputValidator.isEmpty(afterEmail); + return new CustomerUpdateRequest(email, afterEmail); + } + + public String email() { + return email; + } + + public String newEmail() {return newEmail;} +} diff --git a/src/main/java/org/weekly/weekly/customer/dto/response/CustomerResponse.java b/src/main/java/org/weekly/weekly/customer/dto/response/CustomerResponse.java new file mode 100644 index 0000000000..343af5edb7 --- /dev/null +++ b/src/main/java/org/weekly/weekly/customer/dto/response/CustomerResponse.java @@ -0,0 +1,28 @@ +package org.weekly.weekly.customer.dto.response; + +import org.weekly.weekly.customer.domain.Customer; +import org.weekly.weekly.voucher.dto.Response; + +import java.text.MessageFormat; +import java.time.LocalDateTime; + +public class CustomerResponse implements Response { + String name; + String email; + LocalDateTime createAt; + + private CustomerResponse(String name, String email, LocalDateTime createAt) { + this.name = name; + this.email = email; + this.createAt = createAt; + } + + public static CustomerResponse of(Customer customer) { + return new CustomerResponse(customer.getName(), customer.getEmail(), customer.getCreateAt()); + } + + @Override + public String getResult() { + return MessageFormat.format("[이름: {0}, 이메일: {1}, 생성 시기: {2}]", name, email, createAt); + } +} diff --git a/src/main/java/org/weekly/weekly/customer/dto/response/CustomersResponse.java b/src/main/java/org/weekly/weekly/customer/dto/response/CustomersResponse.java new file mode 100644 index 0000000000..eb601c9e82 --- /dev/null +++ b/src/main/java/org/weekly/weekly/customer/dto/response/CustomersResponse.java @@ -0,0 +1,31 @@ +package org.weekly.weekly.customer.dto.response; + +import org.weekly.weekly.customer.domain.Customer; +import org.weekly.weekly.util.PrintMessageType; +import org.weekly.weekly.voucher.domain.Voucher; +import org.weekly.weekly.voucher.dto.Response; +import org.weekly.weekly.voucher.dto.response.VoucherCreationResponse; + +import java.util.List; +import java.util.stream.Collectors; + +public class CustomersResponse implements Response { + List result; + + public CustomersResponse(List customers) { + result = customers.stream() + .map(CustomerResponse::of) + .collect(Collectors.toUnmodifiableList()); + } + + @Override + public String getResult() { + if (result.isEmpty()) { + return PrintMessageType.NO_VOUCHER_DATAS.getMessage(); + } + + StringBuilder resultBuilder = new StringBuilder(); + result.forEach(customerResponse-> resultBuilder.append(customerResponse.getResult()).append('\n')); + return resultBuilder.toString(); + } +} diff --git a/src/main/java/org/weekly/weekly/customer/exception/CustomerException.java b/src/main/java/org/weekly/weekly/customer/exception/CustomerException.java new file mode 100644 index 0000000000..cfda9e7fa5 --- /dev/null +++ b/src/main/java/org/weekly/weekly/customer/exception/CustomerException.java @@ -0,0 +1,9 @@ +package org.weekly.weekly.customer.exception; + +import org.weekly.weekly.util.ExceptionMsg; + +public class CustomerException extends RuntimeException{ + public CustomerException(ExceptionMsg exceptionMsg) { + super(exceptionMsg.getMsg()); + } +} diff --git a/src/main/java/org/weekly/weekly/customer/exception/CustomerValidator.java b/src/main/java/org/weekly/weekly/customer/exception/CustomerValidator.java new file mode 100644 index 0000000000..d30f721d32 --- /dev/null +++ b/src/main/java/org/weekly/weekly/customer/exception/CustomerValidator.java @@ -0,0 +1,18 @@ +package org.weekly.weekly.customer.exception; + +import org.weekly.weekly.util.ExceptionMsg; + +import java.util.regex.Pattern; + +public class CustomerValidator { + + public CustomerValidator() { + throw new CustomerException(ExceptionMsg.UTIL_CLASS); + } + private static final String EMAIL_FORMAT = "^[a-zA-Z0-9._%+-]+@[a-zA-Z0-9.-]+\\.[a-zA-Z]{2,3}$"; + public static void validateEmailFormat(String email) { + if (!Pattern.matches(EMAIL_FORMAT, email)) { + throw new CustomerException(ExceptionMsg.NOT_EMAIL_FORMAT); + } + } +} diff --git a/src/main/java/org/weekly/weekly/customer/repository/CustomerRepository.java b/src/main/java/org/weekly/weekly/customer/repository/CustomerRepository.java new file mode 100644 index 0000000000..a8a7352288 --- /dev/null +++ b/src/main/java/org/weekly/weekly/customer/repository/CustomerRepository.java @@ -0,0 +1,17 @@ +package org.weekly.weekly.customer.repository; + +import org.weekly.weekly.customer.domain.Customer; + +import java.util.List; +import java.util.Optional; + +public interface CustomerRepository { + Customer insert(Customer customer); + void deleteByEmail(String email); + void deleteAll(); + Optional findByEmail(String email); + List findAll(); + + Customer update(Customer customer, String newEmail); +} + diff --git a/src/main/java/org/weekly/weekly/customer/repository/JdbcCustomerRepository.java b/src/main/java/org/weekly/weekly/customer/repository/JdbcCustomerRepository.java new file mode 100644 index 0000000000..b332fa85f4 --- /dev/null +++ b/src/main/java/org/weekly/weekly/customer/repository/JdbcCustomerRepository.java @@ -0,0 +1,116 @@ +package org.weekly.weekly.customer.repository; + +import org.springframework.context.annotation.Profile; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; +import org.weekly.weekly.customer.domain.Customer; +import org.weekly.weekly.customer.exception.CustomerException; +import org.weekly.weekly.util.ExceptionMsg; +import org.weekly.weekly.voucher.domain.DiscountType; +import org.weekly.weekly.voucher.domain.Voucher; +import org.weekly.weekly.voucher.exception.VoucherException; + +import javax.sql.DataSource; +import java.nio.ByteBuffer; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.time.LocalDateTime; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Profile("!dev") +@Repository +public class JdbcCustomerRepository implements CustomerRepository{ + private final JdbcTemplate jdbcTemplate; + + public JdbcCustomerRepository(DataSource dataSource) { + this.jdbcTemplate = new JdbcTemplate(dataSource); + } + + + @Override + public Customer insert(Customer customer) { + String sql = "INSERT INTO customers(customer_id, name, email, create_at) VALUES(UUID_TO_BIN(?), ?, ?, ?)"; + int insert = jdbcTemplate.update(sql, + uuidToBytes(customer.getCustomerId()), + customer.getName(), + customer.getEmail(), + Timestamp.valueOf(customer.getCreateAt())); + + if (insert != 1) { + throw new CustomerException(ExceptionMsg.SQL_INSERT_ERROR); + } + return customer; + } + + @Override + public void deleteByEmail(String email) { + String sql = "DELETE FROM customers WHERE email = ?"; + jdbcTemplate.update(sql, email); + } + + @Override + public void deleteAll() { + String sql = "DELETE FROM customers"; + jdbcTemplate.update(sql); + } + + @Override + public Optional findByEmail(String email) { + String sql = "SELECT * FROM customers WHERE email = ?"; + + try { + return Optional.ofNullable(jdbcTemplate.queryForObject(sql, (rs, rowNum) -> mapToCustomer(rs), email)); + } catch (EmptyResultDataAccessException exception) { + return Optional.empty(); + } + } + + @Override + public List findAll() { + String sql = "SELECT * FROM customers"; + return jdbcTemplate.query(sql, (rs, rowNum) -> mapToCustomer(rs)); + } + + @Override + public Customer update(Customer customer, String newEmail) { + String sql = "UPDATE customers SET email = ? WHERE email = ?"; + + String beforeEmail = customer.getEmail(); + customer.updateEmail(newEmail); + + int update = jdbcTemplate.update(sql, + customer.getEmail(), + beforeEmail); + + if (update != 1) { + throw new CustomerException(ExceptionMsg.SQL_ERROR); + } + return customer; + } + + + private static UUID toUUID(byte[] bytes) { + var buffer = ByteBuffer.wrap(bytes); + return new UUID(buffer.getLong(), buffer.getLong()); + } + + private Customer mapToCustomer(ResultSet resultSet) throws SQLException { + UUID customerId = toUUID(resultSet.getBytes("customer_id")); + String name = resultSet.getString("name"); + String email = resultSet.getString("email"); + LocalDateTime createAt = resultSet.getTimestamp("create_at") == null? null : resultSet.getTimestamp("create_at").toLocalDateTime(); + + return new Customer(customerId, name, email, createAt); + } + + + private byte[] uuidToBytes(UUID voucherId) { + return voucherId.toString().getBytes(); + } +} diff --git a/src/main/java/org/weekly/weekly/customer/service/CustomerService.java b/src/main/java/org/weekly/weekly/customer/service/CustomerService.java new file mode 100644 index 0000000000..dcfa849365 --- /dev/null +++ b/src/main/java/org/weekly/weekly/customer/service/CustomerService.java @@ -0,0 +1,80 @@ +package org.weekly.weekly.customer.service; + +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import org.weekly.weekly.customer.domain.Customer; +import org.weekly.weekly.customer.dto.request.CustomerCreationRequest; +import org.weekly.weekly.customer.dto.request.CustomerUpdateRequest; +import org.weekly.weekly.customer.dto.response.CustomerResponse; +import org.weekly.weekly.customer.dto.response.CustomersResponse; +import org.weekly.weekly.customer.exception.CustomerException; +import org.weekly.weekly.customer.repository.CustomerRepository; +import org.weekly.weekly.util.ExceptionMsg; + +import java.util.List; +import java.util.Optional; + +@Service +public class CustomerService { + private final CustomerRepository customerRepository; + + public CustomerService(CustomerRepository customerRepository) { + this.customerRepository = customerRepository; + } + + @Transactional + public CustomerResponse createCustomer(CustomerCreationRequest creationRequest) { + validateCustomerNotExist(creationRequest.getEmail()); + + Customer customer = creationRequest.toCustomer(); + customerRepository.insert(customer); + return CustomerResponse.of(customer); + } + + @Transactional + public void deleteCustomer(CustomerUpdateRequest updateRequest) { + String email = updateRequest.email(); + customerRepository.deleteByEmail(email); + } + + public void deleteAllCustomers() { + customerRepository.deleteAll(); + } + + + public CustomerResponse findDetailCustomer(CustomerUpdateRequest updateRequest) { + String email = updateRequest.email(); + Customer customer = validateCustomerExistAndGet(email); + return CustomerResponse.of(customer); + } + + public CustomersResponse findAllCustomer() { + List customers = customerRepository.findAll(); + return new CustomersResponse(customers); + } + + @Transactional + public CustomerResponse updateCustomer(CustomerUpdateRequest updateRequest) { + validateCustomerNotExist(updateRequest.newEmail()); + + Customer customer = validateCustomerExistAndGet(updateRequest.email()); + + Customer updateCustomer = customerRepository.update(customer, updateRequest.newEmail()); + return CustomerResponse.of(updateCustomer); + } + + private void validateCustomerNotExist(String email) { + Optional findCustomer = customerRepository.findByEmail(email); + if (findCustomer.isPresent()) { + throw new CustomerException(ExceptionMsg.SQL_EXIST); + } + } + + private Customer validateCustomerExistAndGet(String email) { + Optional customer = customerRepository.findByEmail(email); + if (customer.isEmpty()) { + throw new CustomerException(ExceptionMsg.SQL_ERROR); + } + return customer.get(); + } +} diff --git a/src/main/java/org/weekly/weekly/ui/CommandLineApplication.java b/src/main/java/org/weekly/weekly/ui/CommandLineApplication.java index 9ec07581dd..5798232ca1 100644 --- a/src/main/java/org/weekly/weekly/ui/CommandLineApplication.java +++ b/src/main/java/org/weekly/weekly/ui/CommandLineApplication.java @@ -2,46 +2,96 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Component; +import org.weekly.weekly.customer.dto.request.CustomerCreationRequest; +import org.weekly.weekly.customer.dto.request.CustomerUpdateRequest; +import org.weekly.weekly.ui.exception.InputValidator; import org.weekly.weekly.ui.reader.CommandReader; -import org.weekly.weekly.ui.writer.CommandWriter; -import org.weekly.weekly.util.DiscountType; +import org.weekly.weekly.ui.writer.SystemWriter; +import org.weekly.weekly.util.CustomerMenu; +import org.weekly.weekly.util.ManageMenu; +import org.weekly.weekly.util.Menu; +import org.weekly.weekly.voucher.domain.DiscountType; import org.weekly.weekly.util.VoucherMenu; -import org.weekly.weekly.voucher.domain.Discount; -import org.weekly.weekly.voucher.dto.VoucherDto; import org.weekly.weekly.voucher.dto.Response; -import org.weekly.weekly.voucher.dto.VoucherInfoRequest; +import org.weekly.weekly.voucher.dto.request.VoucherInfoRequest; +import org.weekly.weekly.voucher.dto.request.VoucherCreationRequest; -import java.time.LocalDate; -import java.util.UUID; +import java.util.List; +import java.util.function.Supplier; @Component public class CommandLineApplication { private final CommandReader commandReader; - private final CommandWriter commandWriter; + private final SystemWriter commandWriter; @Autowired - public CommandLineApplication(CommandReader commandReader, CommandWriter commandWriter) { + public CommandLineApplication(CommandReader commandReader, SystemWriter commandWriter) { this.commandReader = commandReader; this.commandWriter = commandWriter; } - public VoucherMenu readMenu() { + public ManageMenu readManageMenu() { + return readMenu(()-> { + commandWriter.printMangeProgram(); + return ManageMenu.getMenu(readMenuInput()); + }); + } + + public VoucherMenu readVoucherMenu() { + return readMenu(()-> { + commandWriter.printVoucherProgram(); + return VoucherMenu.getMenu(readMenuInput()); + }); + } + + public CustomerMenu readCustomerMenu() { + return readMenu(()-> { + commandWriter.printCustomerProgram(); + return CustomerMenu.getMenu(readMenuInput()); + }); + } + + public VoucherCreationRequest createVoucherFromInput() { while(true) { try { - this.commandWriter.printVoucherProgram(); - return VoucherMenu.getMenu(this.commandReader.readLine()); + DiscountType discountType = readDiscountType(); + VoucherInfoRequest voucherInfoRequest = readVoucherInfo(discountType); + return new VoucherCreationRequest(voucherInfoRequest, discountType); } catch (Exception exception) { printErrorMsg(exception.getMessage()); } } } - public VoucherDto readVoucher() { + public CustomerCreationRequest createCustomerFromInput() { while(true) { try { - Discount discount = readDiscount(); - VoucherInfoRequest voucherInfoRequest = readVoucherInfo(); - return VoucherDto.parseDto(UUID.randomUUID(), voucherInfoRequest, discount, LocalDate.now()); + String email = processEmail(); + String name = processName(); + return CustomerCreationRequest.of(email, name); + } catch (Exception exception) { + printErrorMsg(exception.getMessage()); + } + } + } + + public CustomerUpdateRequest customerDetailFromInput() { + while(true) { + try { + String email = processEmail(); + return CustomerUpdateRequest.of(email); + } catch (Exception exception) { + printErrorMsg(exception.getMessage()); + } + } + } + + public CustomerUpdateRequest customerUpdateRequest(){ + while(true) { + try { + String email = processEmail(); + String newEmail = processNewEmail(); + return CustomerUpdateRequest.of(email, newEmail); } catch (Exception exception) { printErrorMsg(exception.getMessage()); } @@ -49,22 +99,64 @@ public VoucherDto readVoucher() { } public void printErrorMsg(String errorMsg) { - this.commandWriter.printErrorMsg(errorMsg); + commandWriter.printErrorMessage(errorMsg); } public void printResult(Response response) { - this.commandWriter.printReuslt(response.getResult()); + commandWriter.printReuslt(response.getResult()); + } + + public void printDeleteMessage() { + commandWriter.printDeleteMessage(); + } + + + private String readMenuInput() { + String userInput = commandReader.readLine(); + InputValidator.isEmpty(userInput); + return userInput.toUpperCase(); + } + + private String readUserInput() { + String userInput = commandReader.readLine(); + InputValidator.isEmpty(userInput); + return userInput; + } + + private T readMenu(Supplier menuSupplier) { + while(true) { + try { + return menuSupplier.get(); + } catch (Exception exception) { + printErrorMsg(exception.getMessage()); + } + } } - private Discount readDiscount() throws Exception { - this.commandWriter.printSelectDiscount(); - return DiscountType.getDiscountMap(this.commandReader.readLine()).getNewInstance(); + private DiscountType readDiscountType() { + commandWriter.printSelectDiscount(); + String no = readUserInput(); + return DiscountType.getDiscountTypeByNumber(no); } - private VoucherInfoRequest readVoucherInfo() throws Exception { - this.commandWriter.printCreateVoucher(); - return VoucherInfoRequest.of(this.commandReader.readLine()); + private VoucherInfoRequest readVoucherInfo(DiscountType discountType){ + commandWriter.printCreateVoucher(discountType); + String voucherInfo = readUserInput(); + return VoucherInfoRequest.of(voucherInfo); } + private String processEmail() { + commandWriter.printEmailInputMessage(); + return readUserInput(); + } + private String processNewEmail() { + commandWriter.printNewEmailInputMessage(); + return readUserInput(); + } + + private String processName() { + commandWriter.printNameInputMessage(); + return readUserInput(); + } } diff --git a/src/main/java/org/weekly/weekly/ui/exception/InputException.java b/src/main/java/org/weekly/weekly/ui/exception/InputException.java new file mode 100644 index 0000000000..6eb0516cfb --- /dev/null +++ b/src/main/java/org/weekly/weekly/ui/exception/InputException.java @@ -0,0 +1,10 @@ +package org.weekly.weekly.ui.exception; + +import org.weekly.weekly.util.ExceptionMsg; + +public class InputException extends RuntimeException{ + + public InputException(ExceptionMsg exception) { + super(exception.getMsg()); + } +} diff --git a/src/main/java/org/weekly/weekly/ui/exception/InputValidator.java b/src/main/java/org/weekly/weekly/ui/exception/InputValidator.java new file mode 100644 index 0000000000..0c4c3ef6c0 --- /dev/null +++ b/src/main/java/org/weekly/weekly/ui/exception/InputValidator.java @@ -0,0 +1,34 @@ +package org.weekly.weekly.ui.exception; + +import org.weekly.weekly.util.ExceptionMsg; +import org.weekly.weekly.voucher.exception.VoucherException; + +import java.util.Arrays; + +public class InputValidator { + public InputValidator() { + throw new InputException(ExceptionMsg.UTIL_CLASS); + } + + private static final int VOUCHER_INPUT_SIZE = 2; + public static void isEmpty(String userInput) { + if (userInput == null || userInput.isBlank()) { + throw new RuntimeException(ExceptionMsg.EMPTY.getMsg()); + } + } + + public static void notVoucherInputSize(String[] userInputs) { + if (userInputs.length != VOUCHER_INPUT_SIZE) { + throw new InputException(ExceptionMsg.NOT_SAME_PARAM_SIZE); + } + } + + public static void notNumber(String[] userInputs) { + try { + Arrays.stream(userInputs) + .peek(Long::parseLong); + } catch (NumberFormatException exception) { + throw new InputException(ExceptionMsg.NOT_INPUT_FORMAT); + } + } +} diff --git a/src/main/java/org/weekly/weekly/ui/exception/ReadException.java b/src/main/java/org/weekly/weekly/ui/exception/ReadException.java deleted file mode 100644 index 9ea5e87e53..0000000000 --- a/src/main/java/org/weekly/weekly/ui/exception/ReadException.java +++ /dev/null @@ -1,31 +0,0 @@ -package org.weekly.weekly.ui.exception; - -import org.weekly.weekly.util.ExceptionMsg; - -import java.util.Arrays; - -public class ReadException { - - public static void isEmpty(String userInput) { - if (userInput == null || userInput.isBlank()) { - throw new RuntimeException(ExceptionMsg.EMPTY.getMsg()); - } - } - - public static void notVoucherInputSize(String[] userInputs) { - if (userInputs.length != 2) { - throw new RuntimeException(ExceptionMsg.NOT_SAME_PARAM_SIZE.getMsg()); - } - } - - public static void notVoucherInputFormat(String[] userInputs) { - if (Arrays.stream(userInputs) - .anyMatch(input -> isDigit(input.trim()))) { - throw new RuntimeException(ExceptionMsg.NOT_INPUT_FORMAT.getMsg()); - } - } - - private static boolean isDigit(String userInput) { - return userInput.chars().anyMatch(value -> !Character.isDigit(value)); - } -} diff --git a/src/main/java/org/weekly/weekly/ui/reader/BufferedReaderWrap.java b/src/main/java/org/weekly/weekly/ui/reader/BufferedReaderWrap.java index a904e8262b..6542b3dc7c 100644 --- a/src/main/java/org/weekly/weekly/ui/reader/BufferedReaderWrap.java +++ b/src/main/java/org/weekly/weekly/ui/reader/BufferedReaderWrap.java @@ -1,21 +1,29 @@ package org.weekly.weekly.ui.reader; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; +import org.weekly.weekly.ui.exception.InputException; +import org.weekly.weekly.util.ExceptionMsg; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; @Component +@ConditionalOnProperty(value="command.read", havingValue = "buffer") public class BufferedReaderWrap implements CommandReader { private final BufferedReader bufferedReader; public BufferedReaderWrap() { - this.bufferedReader = new BufferedReader(new InputStreamReader(System.in)); + bufferedReader = new BufferedReader(new InputStreamReader(System.in)); } @Override - public String readLine() throws IOException { - return this.bufferedReader.readLine(); + public String readLine(){ + try { + return bufferedReader.readLine(); + } catch (IOException exception) { + throw new InputException(ExceptionMsg.NOT_INPUT_FORMAT); + } } } diff --git a/src/main/java/org/weekly/weekly/ui/reader/CommandReader.java b/src/main/java/org/weekly/weekly/ui/reader/CommandReader.java index d9954851d3..9ba5cf6055 100644 --- a/src/main/java/org/weekly/weekly/ui/reader/CommandReader.java +++ b/src/main/java/org/weekly/weekly/ui/reader/CommandReader.java @@ -3,5 +3,5 @@ import java.io.IOException; public interface CommandReader { - String readLine() throws IOException; + String readLine(); } diff --git a/src/main/java/org/weekly/weekly/ui/reader/ConsoleWrap.java b/src/main/java/org/weekly/weekly/ui/reader/ConsoleWrap.java index a64197e61f..b4e02e6bb8 100644 --- a/src/main/java/org/weekly/weekly/ui/reader/ConsoleWrap.java +++ b/src/main/java/org/weekly/weekly/ui/reader/ConsoleWrap.java @@ -1,15 +1,17 @@ package org.weekly.weekly.ui.reader; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; import java.io.Console; @Component +@ConditionalOnProperty(value="command.read", havingValue = "console") public class ConsoleWrap implements CommandReader{ private final Console consoleWrap; public ConsoleWrap() { - this.consoleWrap = System.console(); + consoleWrap = System.console(); } @Override diff --git a/src/main/java/org/weekly/weekly/ui/reader/ScannerWrap.java b/src/main/java/org/weekly/weekly/ui/reader/ScannerWrap.java index a3629575ac..35fb98e407 100644 --- a/src/main/java/org/weekly/weekly/ui/reader/ScannerWrap.java +++ b/src/main/java/org/weekly/weekly/ui/reader/ScannerWrap.java @@ -1,28 +1,28 @@ package org.weekly.weekly.ui.reader; -import org.springframework.context.annotation.Primary; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; import org.springframework.stereotype.Component; -import org.weekly.weekly.ui.exception.ReadException; +import org.weekly.weekly.ui.exception.InputValidator; import java.util.Scanner; @Component -@Primary +@ConditionalOnProperty(value="command.read", havingValue = "scanner") public class ScannerWrap implements CommandReader{ private final Scanner scanner; public ScannerWrap() { - this.scanner = new Scanner(System.in); + scanner = new Scanner(System.in); } @Override public String readLine() { - String userInput = this.scanner.nextLine().trim(); + String userInput = scanner.nextLine().trim(); checkException(userInput); - return userInput.toLowerCase(); + return userInput; } private void checkException(String userInput) { - ReadException.isEmpty(userInput); + InputValidator.isEmpty(userInput); } } diff --git a/src/main/java/org/weekly/weekly/ui/writer/CommandWriter.java b/src/main/java/org/weekly/weekly/ui/writer/CommandWriter.java index a31d25cfce..f26b91797c 100644 --- a/src/main/java/org/weekly/weekly/ui/writer/CommandWriter.java +++ b/src/main/java/org/weekly/weekly/ui/writer/CommandWriter.java @@ -1,49 +1,11 @@ package org.weekly.weekly.ui.writer; -import org.slf4j.Logger; -import org.slf4j.LoggerFactory; -import org.springframework.stereotype.Component; -import org.weekly.weekly.util.DiscountType; -import org.weekly.weekly.util.PrintMsg; -import org.weekly.weekly.util.VoucherMenu; - -import java.util.Arrays; - -@Component -public class CommandWriter { - private final Logger logger = LoggerFactory.getLogger(CommandWriter.class); - private void println(String msg) { - System.out.println(msg); - } - private void print(String msg) {System.out.print(msg);} - - public void printVoucherProgram() { - println(PrintMsg.EMPTY.getMsg()); - println(PrintMsg.PROGRAM.getMsg()); - Arrays.stream(VoucherMenu.values()) - .forEach(voucherMenu -> System.out.println(voucherMenu.getPrintMsg())); - } - - public void printErrorMsg(String errorMsg) { - logger.warn(errorMsg); - println(PrintMsg.EMPTY.getMsg()); - println(errorMsg); - } - - public void printCreateVoucher() { - println(PrintMsg.EMPTY.getMsg()); - print(PrintMsg.CREATE_VOUCHER.getMsg()); - } - - public void printSelectDiscount() { - println(PrintMsg.EMPTY.getMsg()); - println(PrintMsg.DISCOUNT_SELECT.getMsg()); - Arrays.stream(DiscountType.values()) - .forEach(discountMap -> System.out.println(discountMap.getMsg())); - } - - public void printReuslt(String result) { - println(PrintMsg.EMPTY.getMsg()); - println(result); - } +import org.weekly.weekly.voucher.domain.DiscountType; + +public interface CommandWriter { + void printVoucherProgram(); + void printErrorMsg(String message); + void printCreateVoucher(DiscountType discountType); + void printSelectDiscount(); + void printReuslt(String result); } diff --git a/src/main/java/org/weekly/weekly/ui/writer/SystemWriter.java b/src/main/java/org/weekly/weekly/ui/writer/SystemWriter.java new file mode 100644 index 0000000000..241130f9f4 --- /dev/null +++ b/src/main/java/org/weekly/weekly/ui/writer/SystemWriter.java @@ -0,0 +1,80 @@ +package org.weekly.weekly.ui.writer; + +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.boot.autoconfigure.condition.ConditionalOnProperty; +import org.springframework.stereotype.Component; +import org.weekly.weekly.util.*; +import org.weekly.weekly.voucher.domain.DiscountType; + +import java.util.Arrays; + +@Component +@ConditionalOnProperty(value="command.write", havingValue = "system") +public class SystemWriter { + private final Logger logger = LoggerFactory.getLogger(SystemWriter.class); + private void println(String msg) { + System.out.println(msg); + } + private void print(String msg) {System.out.print(msg);} + + public void printMangeProgram() { + printMenu(ManageMenu.values(), PrintMessageType.MANAGE_PROGRAM); + } + + public void printVoucherProgram() { + printMenu(VoucherMenu.values(), PrintMessageType.VOUCHER_PROGRAM); + } + + public void printCustomerProgram() { + printMenu(CustomerMenu.values(), PrintMessageType.CUSTOMER_PROGRAM); + } + + public void printErrorMessage(String errorMsg) { + logger.warn(errorMsg); + println(PrintMessageType.EMPTY.getMessage()); + println(errorMsg); + } + + public void printCreateVoucher(DiscountType discountType) { + println(PrintMessageType.EMPTY.getMessage()); + println(PrintMessageType.CREATE_VOUCHER.getMessage() + discountType.getInputExampleMessage()); + print(PrintMessageType.INPUT_MESSAGE.getMessage()); + } + + public void printSelectDiscount() { + println(PrintMessageType.EMPTY.getMessage()); + println(PrintMessageType.DISCOUNT_SELECT.getMessage()); + Arrays.stream(DiscountType.values()) + .forEach(discountMap -> System.out.println(discountMap.getSelectMessage())); + } + + public void printEmailInputMessage() { + println(PrintMessageType.EMAIL_INPUT.getMessage()); + } + + public void printNewEmailInputMessage() { + println(PrintMessageType.NEW_EMAIL_INPUT.getMessage()); + } + + public void printNameInputMessage() { + println(PrintMessageType.NAME_INPUT.getMessage()); + } + + public void printReuslt(String result) { + println(PrintMessageType.EMPTY.getMessage()); + println(result); + } + + public void printDeleteMessage() { + println(PrintMessageType.DELETE.getMessage()); + } + + + private void printMenu(Menu[] menus, PrintMessageType programMessage) { + println(PrintMessageType.EMPTY.getMessage()); + println(programMessage.getMessage()); + Arrays.stream(menus) + .forEach(menu -> System.out.println(menu.printMessage())); + } +} diff --git a/src/main/java/org/weekly/weekly/util/CustomerMenu.java b/src/main/java/org/weekly/weekly/util/CustomerMenu.java new file mode 100644 index 0000000000..14bcd473fe --- /dev/null +++ b/src/main/java/org/weekly/weekly/util/CustomerMenu.java @@ -0,0 +1,39 @@ +package org.weekly.weekly.util; + +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public enum CustomerMenu implements Menu { + EXIT("[Type: exit] to exit the program."), + CREATE("[Type: create] to create a new voucher."), + DELETE("[Type: list] to list all vouchers."), + DELETE_ALL("[Type: delete_all] 모든 유저 삭제"), + FIND_ALL("[Type: find_all] 모든 유저 검색"), + FIND_DETAIL("[Type: find_detail] 유저 상세 검색"), + UPDATE("[Type: update] 유저 정보 업데이트"); + + private final String printMessage; + private static final Map CUSTOMER_MENU_MAP; + static { + CUSTOMER_MENU_MAP = new ConcurrentHashMap<>(); + Arrays.stream(CustomerMenu.values()) + .forEach(customerMenu -> CUSTOMER_MENU_MAP.put(customerMenu.name(), customerMenu)); + } + + CustomerMenu(String printMessage) { + this.printMessage = printMessage; + } + + public static CustomerMenu getMenu(String userInput) { + if (CUSTOMER_MENU_MAP.containsKey(userInput)) { + return CUSTOMER_MENU_MAP.get(userInput); + } + throw new RuntimeException(ExceptionMsg.NOT_MENU.getMsg()); + } + + @Override + public String printMessage() { + return printMessage; + } +} diff --git a/src/main/java/org/weekly/weekly/util/DiscountType.java b/src/main/java/org/weekly/weekly/util/DiscountType.java deleted file mode 100644 index 367a4aa851..0000000000 --- a/src/main/java/org/weekly/weekly/util/DiscountType.java +++ /dev/null @@ -1,53 +0,0 @@ -package org.weekly.weekly.util; - -import org.weekly.weekly.voucher.domain.Discount; -import org.weekly.weekly.voucher.domain.FixedDiscount; -import org.weekly.weekly.voucher.domain.PercentDiscount; - -import java.util.Collections; -import java.util.HashMap; -import java.util.Map; - -public enum DiscountType { - FIXED("1", FixedDiscount.class, "1. Fixed Discount"), - PERCENT("2", PercentDiscount.class, "2. Percent Discount"); - - private String no; - private Class cls; - private String msg; - - private static final Map discuontMap; - - static { - Map map = new HashMap<>(); - for (DiscountType discount : DiscountType.values()) { - map.put(discount.no, discount); - } - discuontMap = Collections.unmodifiableMap(map); - } - - DiscountType(String no, Class cls, String msg) { - this.no = no; - this.cls = cls; - this.msg = msg; - } - - public static DiscountType getDiscountMap(String no) { - if (discuontMap.containsKey(no)) { - return discuontMap.get(no); - } - throw new RuntimeException(ExceptionMsg.NOT_DISCOUNT.getMsg()); - } - - public Discount getNewInstance() throws Exception { - return this.cls.getDeclaredConstructor().newInstance(); - } - - public Class getCls() { - return cls; - } - - public String getMsg() { - return msg; - } -} diff --git a/src/main/java/org/weekly/weekly/util/ExceptionMsg.java b/src/main/java/org/weekly/weekly/util/ExceptionMsg.java index 0bbbb47ea6..c77ec9ab02 100644 --- a/src/main/java/org/weekly/weekly/util/ExceptionMsg.java +++ b/src/main/java/org/weekly/weekly/util/ExceptionMsg.java @@ -2,6 +2,7 @@ public enum ExceptionMsg { ERROR("[ERROR]: "), + UTIL_CLASS("유틸 클래스입니다."), EMPTY("사용자가 아무 값도 입력하지 않았습니다."), NOT_INPUT_FORMAT("입력 형식에 맞지 않습니다."), NOT_MENU("해당 메뉴는 존재하지 않습니다."), @@ -11,9 +12,14 @@ public enum ExceptionMsg { EXPIRATION_ERROR("유효기간이 등록기간보다 빠릅니다."), VOUCHER_EXIST("이미 존재하는 바우처입니다."), NOT_FOUND("해당 종류의 할인정보를 찾을 수 없습니다."), - NOT_SAME_PARAM_SIZE("입력 파라미터 개수가 맞지 않습니다."); + NOT_SAME_PARAM_SIZE("입력 파라미터 개수가 맞지 않습니다."), + NOT_EMAIL_FORMAT("이메일형식이 아닙니다."), + SQL_ERROR("값을 가져오기 실패"), + SQL_INSERT_ERROR("값 추가 실패"), + SQL_EXIST("이미 존재합니다."), + SQL_DELETE_ERROR("삭제 실패"); - private String msg; + private final String msg; ExceptionMsg(String msg) { this.msg = msg; diff --git a/src/main/java/org/weekly/weekly/util/ManageMenu.java b/src/main/java/org/weekly/weekly/util/ManageMenu.java new file mode 100644 index 0000000000..fcd829a672 --- /dev/null +++ b/src/main/java/org/weekly/weekly/util/ManageMenu.java @@ -0,0 +1,36 @@ +package org.weekly.weekly.util; + +import java.util.Arrays; +import java.util.Map; +import java.util.concurrent.ConcurrentHashMap; + +public enum ManageMenu implements Menu { + EXIT("Type exit to exit the program."), + VOUCHER("Type voucher to voucher a new voucher."), + CUSTOMER("Type customer to customer all vouchers."); + + private final String printMessage; + private static final Map MANAGE_MENU_MAP; + + static { + MANAGE_MENU_MAP = new ConcurrentHashMap<>(); + Arrays.stream(ManageMenu.values()) + .forEach(manageMenu -> MANAGE_MENU_MAP.put(manageMenu.name(), manageMenu)); + } + + ManageMenu(String printMessage) { + this.printMessage = printMessage; + } + + public static ManageMenu getMenu(String userInput) { + if (MANAGE_MENU_MAP.containsKey(userInput)) { + return MANAGE_MENU_MAP.get(userInput); + } + throw new RuntimeException(ExceptionMsg.NOT_MENU.getMsg()); + } + + @Override + public String printMessage() { + return printMessage; + } +} diff --git a/src/main/java/org/weekly/weekly/util/Menu.java b/src/main/java/org/weekly/weekly/util/Menu.java new file mode 100644 index 0000000000..40981c0f93 --- /dev/null +++ b/src/main/java/org/weekly/weekly/util/Menu.java @@ -0,0 +1,5 @@ +package org.weekly.weekly.util; + +public interface Menu { + String printMessage(); +} diff --git a/src/main/java/org/weekly/weekly/util/PrintMessageType.java b/src/main/java/org/weekly/weekly/util/PrintMessageType.java new file mode 100644 index 0000000000..c42e69ef39 --- /dev/null +++ b/src/main/java/org/weekly/weekly/util/PrintMessageType.java @@ -0,0 +1,29 @@ +package org.weekly.weekly.util; + +public enum PrintMessageType { + MANAGE_PROGRAM("=== Manage Program ==="), + VOUCHER_PROGRAM("=== Voucher Program ==="), + CUSTOMER_PROGRAM("=== Customer Program ==="), + CREATE_VOUCHER("바우처를 생성합니다.\n" + + "입력예시 => "), + INPUT_MESSAGE("입력하세요: "), + DISCOUNT_SELECT("할인 종류중 하나를 선택하세요"), + EMAIL_INPUT("이메일을 입력하세요: "), + NEW_EMAIL_INPUT("새 이메일을 입력하세요: "), + NAME_INPUT("이름을 입력하세요"), + NO_VOUCHER_DATAS("저장소에 데이터가 없습니다."), + EMPTY(""), + CREATE_VOUCHER_SUCCESS("[바우처 생성에 성공]: "), + FIND_ALL_VOUCHER_SUCCESS("[모든 바우처 조회 성공]: "), + DELETE("삭제에 성공했습니다!"); + + private final String message; + + PrintMessageType(String msg) { + this.message = msg; + } + + public String getMessage() { + return message; + } +} diff --git a/src/main/java/org/weekly/weekly/util/PrintMsg.java b/src/main/java/org/weekly/weekly/util/PrintMsg.java deleted file mode 100644 index a89c6bbbae..0000000000 --- a/src/main/java/org/weekly/weekly/util/PrintMsg.java +++ /dev/null @@ -1,25 +0,0 @@ -package org.weekly.weekly.util; - -public enum PrintMsg { - PROGRAM("=== Voucher Program ==="), - CREATE_VOUCHER("바우처를 생성합니다.\n" + - "입력 예시\n" + - " 1. 고정: 바우처 할인 금액,바우처 유효 개월 수\n" + - " 2. 퍼센트: 바우처 퍼센트(0~100으로만), 바우처 유효 개월 수\n" + - "(,)콤마를 기준으로 입력하세요 :"), - DISCOUNT_SELECT("할인 종류중 하나를 선택하세요"), - NO_VOUCHER_DATAS("저장소에 데이터가 없습니다."), - EMPTY(""), - CREATE_VOUCHER_SUCCESS("[바우처 생성에 성공]: "), - FIND_ALL_VOUCHER_SUCCESS("[모든 바우처 조회 성공]: "); - - private String msg; - - PrintMsg(String msg) { - this.msg = msg; - } - - public String getMsg() { - return msg; - } -} diff --git a/src/main/java/org/weekly/weekly/util/VoucherMenu.java b/src/main/java/org/weekly/weekly/util/VoucherMenu.java index c5da7f8eb0..a9b4e621ce 100644 --- a/src/main/java/org/weekly/weekly/util/VoucherMenu.java +++ b/src/main/java/org/weekly/weekly/util/VoucherMenu.java @@ -1,28 +1,24 @@ package org.weekly.weekly.util; import java.util.Arrays; -import java.util.Collections; import java.util.Map; import java.util.concurrent.ConcurrentHashMap; -public enum VoucherMenu { - EXIT("exit", "Type exit to exit the program."), - CREATE("create", "Type create to create a new voucher."), - LIST("list", "Type list to list all vouchers."); - - private String input; - private String printMsg; +public enum VoucherMenu implements Menu { + EXIT("Type exit to exit the program."), + CREATE("Type create to create a new voucher."), + LIST("Type list to list all vouchers."); + private final String printMessage; private final static Map VOUCHER_MENU_MAP; static { VOUCHER_MENU_MAP = new ConcurrentHashMap<>(); Arrays.stream(VoucherMenu.values()) - .forEach(menu -> VOUCHER_MENU_MAP.put(menu.input, menu)); + .forEach(menu -> VOUCHER_MENU_MAP.put(menu.name(), menu)); } - VoucherMenu(String input, String printMsg) { - this.input = input; - this.printMsg = printMsg; + VoucherMenu(String printMsg) { + this.printMessage = printMsg; } public static VoucherMenu getMenu(String userInput) { @@ -32,7 +28,8 @@ public static VoucherMenu getMenu(String userInput) { throw new RuntimeException(ExceptionMsg.NOT_MENU.getMsg()); } - public String getPrintMsg() { - return printMsg; + @Override + public String printMessage() { + return printMessage; } } \ No newline at end of file diff --git a/src/main/java/org/weekly/weekly/voucher/controller/VoucherController.java b/src/main/java/org/weekly/weekly/voucher/controller/VoucherController.java index f1c148a667..5e9c1e2804 100644 --- a/src/main/java/org/weekly/weekly/voucher/controller/VoucherController.java +++ b/src/main/java/org/weekly/weekly/voucher/controller/VoucherController.java @@ -3,60 +3,26 @@ import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.stereotype.Controller; -import org.weekly.weekly.ui.CommandLineApplication; -import org.weekly.weekly.util.PrintMsg; -import org.weekly.weekly.util.VoucherMenu; -import org.weekly.weekly.voucher.dto.VoucherDto; import org.weekly.weekly.voucher.dto.Response; +import org.weekly.weekly.voucher.dto.request.VoucherCreationRequest; import org.weekly.weekly.voucher.service.VoucherService; @Controller public class VoucherController { private final Logger logger = LoggerFactory.getLogger(VoucherController.class); private final VoucherService voucherService; - private final CommandLineApplication commandLineApplication; - public VoucherController(VoucherService voucherService, CommandLineApplication commandLineApplication) { - this.voucherService = voucherService; - this.commandLineApplication = commandLineApplication; - } - public void start() { - boolean isStop = false; - - while(!isStop) { - try { - isStop = selectMenu(this.commandLineApplication.readMenu()); - } catch (RuntimeException runtimeException) { - this.commandLineApplication.printErrorMsg(runtimeException.getMessage()); - } - } + public VoucherController(VoucherService voucherService) { + this.voucherService = voucherService; } - private boolean selectMenu(VoucherMenu voucherMenu) { - if (VoucherMenu.CREATE.equals(voucherMenu)) { - createVoucher(); - return false; - } - - if (VoucherMenu.LIST.equals(voucherMenu)) { - getList(); - return false; - } - - return true; - } - private void createVoucher() { - VoucherDto voucherDto = this.commandLineApplication.readVoucher(); - Response response = this.voucherService.insertVoucher(voucherDto); - logger.info("{}{}",PrintMsg.CREATE_VOUCHER_SUCCESS.getMsg(),response.getResult()); - this.commandLineApplication.printResult(response); + public Response createVoucher(VoucherCreationRequest voucherCreationRequest) { + return voucherService.insertVoucher(voucherCreationRequest); } - private void getList() { - Response response = this.voucherService.getVouchers(); - logger.info("{}{}",PrintMsg.FIND_ALL_VOUCHER_SUCCESS.getMsg()); - this.commandLineApplication.printResult(response); + public Response getVouchers() { + return voucherService.getVouchers(); } } diff --git a/src/main/java/org/weekly/weekly/voucher/domain/Discount.java b/src/main/java/org/weekly/weekly/voucher/domain/Discount.java index d823a44342..c7dc3708a2 100644 --- a/src/main/java/org/weekly/weekly/voucher/domain/Discount.java +++ b/src/main/java/org/weekly/weekly/voucher/domain/Discount.java @@ -1,7 +1,5 @@ package org.weekly.weekly.voucher.domain; -import org.weekly.weekly.util.DiscountType; - public interface Discount { long applyDiscount(long beforeAmount, long discountAmount); DiscountType discountType(); diff --git a/src/main/java/org/weekly/weekly/voucher/domain/DiscountType.java b/src/main/java/org/weekly/weekly/voucher/domain/DiscountType.java new file mode 100644 index 0000000000..d27a2b602f --- /dev/null +++ b/src/main/java/org/weekly/weekly/voucher/domain/DiscountType.java @@ -0,0 +1,70 @@ +package org.weekly.weekly.voucher.domain; + +import org.weekly.weekly.util.ExceptionMsg; +import org.weekly.weekly.voucher.exception.VoucherException; + +import java.lang.reflect.InvocationTargetException; +import java.util.Collections; +import java.util.HashMap; +import java.util.Map; + +public enum DiscountType { + FIXED("1", FixedDiscount.class, "1. Fixed Discount", "바우처 할인 금액, 바우처 유효 개월 수"), + PERCENT("2", PercentDiscount.class, "2. Percent Discount", "0~100사이의 바우처 할인율, 바우처 유효 개월 수"); + + private final String no; + private final Class cls; + private final String selectMessage; + private final String inputExampleMessage; + + private static final Map discuontTypeMap; + + static { + Map map = new HashMap<>(); + for (DiscountType discount : DiscountType.values()) { + map.put(discount.no, discount); + } + discuontTypeMap = Collections.unmodifiableMap(map); + } + + DiscountType(String no, Class cls, String msg, String exMessage) { + this.no = no; + this.cls = cls; + this.selectMessage = msg; + this.inputExampleMessage = exMessage; + } + + public static DiscountType getDiscountTypeByNumber(String no) { + if (discuontTypeMap.containsKey(no)) { + return discuontTypeMap.get(no); + } + throw new VoucherException(ExceptionMsg.NOT_DISCOUNT); + } + + public static DiscountType getDiscountTypeByName(String name) { + for (DiscountType discount : DiscountType.values()) { + if (name.equals(discount.name())) { + return discount; + } + } + throw new VoucherException(ExceptionMsg.NOT_DISCOUNT); + } + + public Discount getNewInstance() { + try { + return this.cls.getDeclaredConstructor().newInstance(); + } catch (NoSuchMethodException | InstantiationException | IllegalAccessException | InvocationTargetException exception) { + throw new VoucherException(ExceptionMsg.NOT_FOUND); + } + + } + + + public String getSelectMessage() { + return selectMessage; + } + + public String getInputExampleMessage() { + return inputExampleMessage; + } +} diff --git a/src/main/java/org/weekly/weekly/voucher/domain/FixedDiscount.java b/src/main/java/org/weekly/weekly/voucher/domain/FixedDiscount.java index 9dcf21e072..b8621eb46f 100644 --- a/src/main/java/org/weekly/weekly/voucher/domain/FixedDiscount.java +++ b/src/main/java/org/weekly/weekly/voucher/domain/FixedDiscount.java @@ -1,7 +1,5 @@ package org.weekly.weekly.voucher.domain; -import org.weekly.weekly.util.DiscountType; - public class FixedDiscount implements Discount{ @Override public long applyDiscount(long beforeAmount, long discountAmount) { diff --git a/src/main/java/org/weekly/weekly/voucher/domain/PercentDiscount.java b/src/main/java/org/weekly/weekly/voucher/domain/PercentDiscount.java index d368157df3..8670ab08ca 100644 --- a/src/main/java/org/weekly/weekly/voucher/domain/PercentDiscount.java +++ b/src/main/java/org/weekly/weekly/voucher/domain/PercentDiscount.java @@ -1,7 +1,5 @@ package org.weekly.weekly.voucher.domain; -import org.weekly.weekly.util.DiscountType; - public class PercentDiscount implements Discount{ private final int PERCENT = 100; @Override diff --git a/src/main/java/org/weekly/weekly/voucher/domain/Voucher.java b/src/main/java/org/weekly/weekly/voucher/domain/Voucher.java index 22be047c67..f5242f721f 100644 --- a/src/main/java/org/weekly/weekly/voucher/domain/Voucher.java +++ b/src/main/java/org/weekly/weekly/voucher/domain/Voucher.java @@ -1,5 +1,9 @@ package org.weekly.weekly.voucher.domain; +import org.weekly.weekly.util.ExceptionMsg; +import org.weekly.weekly.voucher.exception.VoucherException; +import org.weekly.weekly.voucher.exception.VoucherValidator; + import java.text.MessageFormat; import java.time.LocalDate; import java.util.UUID; @@ -15,6 +19,7 @@ public class Voucher { private Discount discount; + public Voucher(UUID voucherId, long amount, LocalDate registrationDate, LocalDate expirationDate, Discount discount) { this.voucherId = voucherId; this.amount = amount; @@ -23,8 +28,14 @@ public Voucher(UUID voucherId, long amount, LocalDate registrationDate, LocalDat this.discount = discount; } - public UUID getVoucherId() { - return voucherId; + public static Voucher of(UUID id, long amount, LocalDate now, long expiration, DiscountType discountType) { + validation(now, expiration, discountType, amount); + return new Voucher(id, amount, now, now.plusMonths(expiration), discountType.getNewInstance()); + } + + private static void validation(LocalDate registrationDate, long expiration, DiscountType discountType, long amount) { + VoucherValidator.validateExpiration(registrationDate, expiration); + VoucherValidator.validateAmount(discountType, amount); } public long applyDiscount(long beforeAmount) { @@ -34,12 +45,27 @@ public long applyDiscount(long beforeAmount) { return afterAmount; } - this.amount -= afterAmount; + this.amount += afterAmount; return 0L; } - @Override - public String toString() { - return MessageFormat.format("[ID: {0}, 금액: {1}, 등록일자: {2}, 유효기간: {3}]", voucherId,amount, registrationDate, expirationDate); + public UUID getVoucherId() { + return voucherId; + } + + public LocalDate getExpirationDate() { + return expirationDate; + } + + public LocalDate getRegistrationDate() { + return registrationDate; + } + + public long getAmount() { + return amount; + } + + public DiscountType getDiscountType() { + return discount.discountType(); } } diff --git a/src/main/java/org/weekly/weekly/voucher/dto/CreateResponse.java b/src/main/java/org/weekly/weekly/voucher/dto/CreateResponse.java deleted file mode 100644 index e845075c8f..0000000000 --- a/src/main/java/org/weekly/weekly/voucher/dto/CreateResponse.java +++ /dev/null @@ -1,16 +0,0 @@ -package org.weekly.weekly.voucher.dto; - -import org.weekly.weekly.voucher.domain.Voucher; - -public class CreateResponse implements Response{ - private final String result; - - public CreateResponse(Voucher voucher) { - this.result = voucher.toString(); - } - - @Override - public String getResult() { - return result; - } -} diff --git a/src/main/java/org/weekly/weekly/voucher/dto/ListResponse.java b/src/main/java/org/weekly/weekly/voucher/dto/ListResponse.java deleted file mode 100644 index b2a5f08aff..0000000000 --- a/src/main/java/org/weekly/weekly/voucher/dto/ListResponse.java +++ /dev/null @@ -1,27 +0,0 @@ -package org.weekly.weekly.voucher.dto; - -import org.weekly.weekly.util.PrintMsg; -import org.weekly.weekly.voucher.domain.Voucher; - -import java.util.List; -import java.util.stream.Collectors; - -public class ListResponse implements Response{ - private final List result; - - public ListResponse(List vouchers) { - this.result = vouchers.stream() - .map(Voucher::toString) - .collect(Collectors.toUnmodifiableList()); - } - - public String getResult() { - if (result.isEmpty()) { - return PrintMsg.NO_VOUCHER_DATAS.getMsg(); - } - - StringBuilder sb = new StringBuilder(); - result.forEach(sb::append); - return sb.toString(); - } -} diff --git a/src/main/java/org/weekly/weekly/voucher/dto/Response.java b/src/main/java/org/weekly/weekly/voucher/dto/Response.java index 163a86e2be..86c2795d86 100644 --- a/src/main/java/org/weekly/weekly/voucher/dto/Response.java +++ b/src/main/java/org/weekly/weekly/voucher/dto/Response.java @@ -1,5 +1,8 @@ package org.weekly.weekly.voucher.dto; -public interface Response { +/** + * 모든 반환 값에 대해 동일한 동작을 적용시키고 싶어서 이렇게 했습니다. + */ +public interface Response { String getResult(); } diff --git a/src/main/java/org/weekly/weekly/voucher/dto/VoucherDto.java b/src/main/java/org/weekly/weekly/voucher/dto/VoucherDto.java deleted file mode 100644 index 1b12a70f30..0000000000 --- a/src/main/java/org/weekly/weekly/voucher/dto/VoucherDto.java +++ /dev/null @@ -1,57 +0,0 @@ -package org.weekly.weekly.voucher.dto; - -import org.weekly.weekly.util.DiscountType; -import org.weekly.weekly.voucher.domain.Discount; -import org.weekly.weekly.voucher.domain.Voucher; -import org.weekly.weekly.voucher.exception.VoucherException; - -import java.time.LocalDate; -import java.util.UUID; - -public class VoucherDto { - private static final int RANGE_START = 0; - private static final int RANGE_END = 100; - - private final UUID voucherId; - private final long amount; - private final LocalDate registrationDate; - private final LocalDate expirationDate; - private final Discount discount; - - private VoucherDto(UUID voucherId, long amount, LocalDate registrationDate, LocalDate expirationDate, Discount discount) { - this.voucherId = voucherId; - this.amount = amount; - this.registrationDate = registrationDate; - this.expirationDate = expirationDate; - this.discount = discount; - } - - public static VoucherDto parseDto(UUID voucherId, VoucherInfoRequest voucherInfoRequest, Discount discount, LocalDate registrationDate) { - checkException(voucherInfoRequest, registrationDate, discount); - return new VoucherDto(voucherId - , Long.parseLong(voucherInfoRequest.getAmount()) - , registrationDate - , registrationDate.plusMonths(Long.parseLong(voucherInfoRequest.getExpiration())) - , discount); - } - - private static void checkException(VoucherInfoRequest voucherInfoRequest, LocalDate registrationDate, Discount discount) { - VoucherException.expirationError(registrationDate, voucherInfoRequest.getExpiration()); - VoucherException.notNumberFormat(voucherInfoRequest.getExpiration(), input -> Long.parseLong(input) <= RANGE_START); - - if (discount.getClass().equals(DiscountType.PERCENT.getCls())) { - VoucherException.notNumberFormat(voucherInfoRequest.getAmount(), input -> Long.parseLong(input) < RANGE_START || Long.parseLong(input) > RANGE_END); - return; - } - VoucherException.notNumberFormat(voucherInfoRequest.getAmount(), input -> Long.parseLong(input) < RANGE_START); - - } - - public Voucher parseToVoucher() { - return new Voucher(this.voucherId, this.amount, this.registrationDate, this.expirationDate, this.discount); - } - - public UUID getVoucherId() { - return voucherId; - } -} \ No newline at end of file diff --git a/src/main/java/org/weekly/weekly/voucher/dto/request/VoucherCreationRequest.java b/src/main/java/org/weekly/weekly/voucher/dto/request/VoucherCreationRequest.java new file mode 100644 index 0000000000..76c137ed6e --- /dev/null +++ b/src/main/java/org/weekly/weekly/voucher/dto/request/VoucherCreationRequest.java @@ -0,0 +1,25 @@ +package org.weekly.weekly.voucher.dto.request; + +import org.weekly.weekly.voucher.domain.DiscountType; +import org.weekly.weekly.voucher.domain.Voucher; +import org.weekly.weekly.voucher.exception.VoucherException; + +import java.time.LocalDate; +import java.util.UUID; + +public class VoucherCreationRequest { + private final VoucherInfoRequest voucherInfoRequest; + private final DiscountType discountType; + + public VoucherCreationRequest(VoucherInfoRequest voucherInfoRequest, DiscountType discountType) { + this.voucherInfoRequest = voucherInfoRequest; + this.discountType = discountType; + } + + public Voucher toVoucher() { + UUID id = UUID.randomUUID(); + long amount = voucherInfoRequest.getAmount(); + LocalDate now = LocalDate.now(); + return Voucher.of(id, amount, now, voucherInfoRequest.getExpiration(), discountType); + } +} diff --git a/src/main/java/org/weekly/weekly/voucher/dto/VoucherInfoRequest.java b/src/main/java/org/weekly/weekly/voucher/dto/request/VoucherInfoRequest.java similarity index 50% rename from src/main/java/org/weekly/weekly/voucher/dto/VoucherInfoRequest.java rename to src/main/java/org/weekly/weekly/voucher/dto/request/VoucherInfoRequest.java index b4401d7a97..3aecb596c7 100644 --- a/src/main/java/org/weekly/weekly/voucher/dto/VoucherInfoRequest.java +++ b/src/main/java/org/weekly/weekly/voucher/dto/request/VoucherInfoRequest.java @@ -1,17 +1,17 @@ -package org.weekly.weekly.voucher.dto; +package org.weekly.weekly.voucher.dto.request; -import org.weekly.weekly.ui.exception.ReadException; +import org.weekly.weekly.ui.exception.InputValidator; public class VoucherInfoRequest { private static final String SPLIT_FORMAT = ","; private static final int AMOUNT_NO = 0; private static final int EXPIRATION = 1; - private final String amount; - private final String expiration; + private final long amount; + private final long expiration; - private VoucherInfoRequest(String amount, String expiration) { + private VoucherInfoRequest(long amount, long expiration) { this.amount = amount; this.expiration = expiration; } @@ -20,21 +20,21 @@ public static VoucherInfoRequest of(String userInput) { String[] inputs = userInput.split(SPLIT_FORMAT); checkReadVoucherException(inputs); - return new VoucherInfoRequest(inputs[AMOUNT_NO].trim(), inputs[EXPIRATION].trim()); + return new VoucherInfoRequest( + Long.parseLong(inputs[AMOUNT_NO].trim()), + Long.parseLong(inputs[EXPIRATION].trim())); } - public String getAmount() { + public long getAmount() { return amount; } - public String getExpiration() { + public long getExpiration() { return expiration; } private static void checkReadVoucherException(String[] inputs) { - ReadException.notVoucherInputSize(inputs); - ReadException.notVoucherInputFormat(inputs); + InputValidator.notVoucherInputSize(inputs); + InputValidator.notNumber(inputs); } - - } diff --git a/src/main/java/org/weekly/weekly/voucher/dto/response/VoucherCreationResponse.java b/src/main/java/org/weekly/weekly/voucher/dto/response/VoucherCreationResponse.java new file mode 100644 index 0000000000..f02d0703a6 --- /dev/null +++ b/src/main/java/org/weekly/weekly/voucher/dto/response/VoucherCreationResponse.java @@ -0,0 +1,27 @@ +package org.weekly.weekly.voucher.dto.response; + +import org.weekly.weekly.voucher.domain.Voucher; +import org.weekly.weekly.voucher.dto.Response; + +import java.text.MessageFormat; +import java.time.LocalDate; +import java.util.UUID; + +public class VoucherCreationResponse implements Response { + private final UUID id; + private final LocalDate registrationDate; + private final LocalDate expirationDate; + private final long amount; + + public VoucherCreationResponse(Voucher voucher) { + this.id = voucher.getVoucherId(); + this.registrationDate = voucher.getRegistrationDate(); + this.expirationDate = voucher.getExpirationDate(); + this.amount = voucher.getAmount(); + } + + @Override + public String getResult() { + return MessageFormat.format("[ID: {0}, 금액: {1}, 등록일자: {2}, 유효기간: {3}]", id, amount, registrationDate, expirationDate); + } +} diff --git a/src/main/java/org/weekly/weekly/voucher/dto/response/VouchersResponse.java b/src/main/java/org/weekly/weekly/voucher/dto/response/VouchersResponse.java new file mode 100644 index 0000000000..64b16be1ce --- /dev/null +++ b/src/main/java/org/weekly/weekly/voucher/dto/response/VouchersResponse.java @@ -0,0 +1,29 @@ +package org.weekly.weekly.voucher.dto.response; + +import org.weekly.weekly.util.PrintMessageType; +import org.weekly.weekly.voucher.domain.Voucher; +import org.weekly.weekly.voucher.dto.Response; + +import java.util.List; +import java.util.stream.Collectors; + +public class VouchersResponse implements Response { + private final List result; + + public VouchersResponse(List vouchers) { + this.result = vouchers.stream() + .map(VoucherCreationResponse::new) + .collect(Collectors.toUnmodifiableList()); + } + + public String getResult() { + if (result.isEmpty()) { + return PrintMessageType.NO_VOUCHER_DATAS.getMessage(); + } + + + StringBuilder resultBuilder = new StringBuilder(); + result.forEach(voucherResponse-> resultBuilder.append(voucherResponse.getResult()).append('\n')); + return resultBuilder.toString(); + } +} diff --git a/src/main/java/org/weekly/weekly/voucher/exception/VoucherException.java b/src/main/java/org/weekly/weekly/voucher/exception/VoucherException.java index 7d895df2b2..a9743019c0 100644 --- a/src/main/java/org/weekly/weekly/voucher/exception/VoucherException.java +++ b/src/main/java/org/weekly/weekly/voucher/exception/VoucherException.java @@ -2,36 +2,8 @@ import org.weekly.weekly.util.ExceptionMsg; -import java.time.LocalDate; -import java.util.function.Predicate; - -public class VoucherException { - private static boolean isNumber(String userInput) { - return userInput.chars().allMatch(value -> Character.isDigit(value)); - } - - private static void notNumber(String userInput) { - if (!isNumber(userInput)) { - throw new RuntimeException(ExceptionMsg.NOT_NUMBER_FORMAT.getMsg()); - } - } - - private static void notRange(String userInput, Predicate ifCase) { - if (ifCase.test(userInput)) { - throw new RuntimeException(ExceptionMsg.NOT_NUMBER_FORMAT.getMsg()); - } - } - - public static void notNumberFormat(String userInput, Predicate ifCase) { - notNumber(userInput); - notRange(userInput, ifCase); - } - - public static void expirationError(LocalDate registrationDate, String expirationMonth) { - LocalDate expirationDate = registrationDate.plusMonths(Long.parseLong(expirationMonth)); - - if (registrationDate.isEqual(expirationDate) || registrationDate.isAfter(expirationDate)) { - throw new RuntimeException(ExceptionMsg.EXPIRATION_ERROR.getMsg()); - } +public class VoucherException extends RuntimeException{ + public VoucherException(ExceptionMsg exceptionMsg) { + super(exceptionMsg.getMsg()); } -} \ No newline at end of file +} diff --git a/src/main/java/org/weekly/weekly/voucher/exception/VoucherValidator.java b/src/main/java/org/weekly/weekly/voucher/exception/VoucherValidator.java new file mode 100644 index 0000000000..872558d980 --- /dev/null +++ b/src/main/java/org/weekly/weekly/voucher/exception/VoucherValidator.java @@ -0,0 +1,38 @@ +package org.weekly.weekly.voucher.exception; + +import org.weekly.weekly.util.ExceptionMsg; +import org.weekly.weekly.voucher.domain.DiscountType; + +import java.time.LocalDate; +import java.util.function.LongPredicate; + +public class VoucherValidator { + private static final int RANGE_START = 0; + private static final int RANGE_END = 100; + + private VoucherValidator() { + throw new VoucherException(ExceptionMsg.UTIL_CLASS); + } + + public static void validateExpiration(LocalDate registrationDate, long expirationMonth) { + LocalDate expirationDate = registrationDate.plusMonths(expirationMonth); + + if (registrationDate.isEqual(expirationDate) || registrationDate.isAfter(expirationDate)) { + throw new VoucherException(ExceptionMsg.EXPIRATION_ERROR); + } + } + public static void validateAmount(DiscountType discountType, long amount) { + if (DiscountType.FIXED.equals(discountType)) { + notRange(amount, input -> input < RANGE_START); + return; + } + + notRange(amount, input -> input < RANGE_START || input > RANGE_END); + } + + private static void notRange(long userInput, LongPredicate ifCase) { + if (ifCase.test(userInput)) { + throw new VoucherException(ExceptionMsg.NOT_NUMBER_FORMAT); + } + } +} diff --git a/src/main/java/org/weekly/weekly/voucher/repository/JdbcVoucherRepository.java b/src/main/java/org/weekly/weekly/voucher/repository/JdbcVoucherRepository.java new file mode 100644 index 0000000000..8892e68ef2 --- /dev/null +++ b/src/main/java/org/weekly/weekly/voucher/repository/JdbcVoucherRepository.java @@ -0,0 +1,123 @@ +package org.weekly.weekly.voucher.repository; + +import org.springframework.context.annotation.Profile; +import org.springframework.dao.DataAccessException; +import org.springframework.dao.EmptyResultDataAccessException; +import org.springframework.jdbc.core.JdbcTemplate; +import org.springframework.stereotype.Repository; +import org.weekly.weekly.util.ExceptionMsg; +import org.weekly.weekly.voucher.domain.DiscountType; +import org.weekly.weekly.voucher.domain.Voucher; +import org.weekly.weekly.voucher.exception.VoucherException; + +import javax.sql.DataSource; +import java.nio.ByteBuffer; +import java.sql.ResultSet; +import java.sql.SQLException; +import java.sql.Timestamp; +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +@Profile("!dev") +@Repository +public class JdbcVoucherRepository implements VoucherRepository{ + private final JdbcTemplate jdbcTemplate; + + public JdbcVoucherRepository(DataSource dataSource) { + jdbcTemplate = new JdbcTemplate(dataSource); + } + + @Override + public Optional findById(UUID voucherId) { + String sql = "SELECT * FROM vouchers WHERE voucher_id = UUID_TO_BIN(?)"; + try { + return Optional.ofNullable(jdbcTemplate.queryForObject(sql, (rs, rowNum) -> mapToVoucher(rs), uuidToBytes(voucherId))); + } catch (EmptyResultDataAccessException e) { + return Optional.empty(); + } + } + + @Override + public List findAll() { + String sql = "SELECT * FROM vouchers"; + return jdbcTemplate.query(sql, (rs, rowNum) -> mapToVoucher(rs)); + } + + @Override + public List findByDiscountType(DiscountType discountType) { + String sql = "SELECT * FROM vouchers WHERE discount = ?"; + return jdbcTemplate.query(sql, (rs, rowNum) -> mapToVoucher(rs), discountType.name()); + } + + @Override + public Voucher insert(Voucher voucher) { + String sql = "INSERT INTO vouchers(voucher_id, amount, discount, registration_date, expiration_date) VALUES (UUID_TO_BIN(?), ?, ?, ?, ?)"; + int update = 0; + try { + update = jdbcTemplate.update(sql, + uuidToBytes(voucher.getVoucherId()), + voucher.getAmount(), + voucher.getDiscountType().name(), + Timestamp.valueOf(voucher.getRegistrationDate().atStartOfDay()), + Timestamp.valueOf(voucher.getExpirationDate().atStartOfDay())); + } catch(DataAccessException dataAccessException) { + throw new VoucherException(ExceptionMsg.SQL_INSERT_ERROR); + } + + if (update != 1) { + throw new VoucherException(ExceptionMsg.SQL_ERROR); + } + return voucher; + } + + @Override + public Voucher update(Voucher voucher) { + String sql = "UPDATE vouchers SET amount = ?, discount = ?, expiration_date = ? WHERE voucher_id = UUID_TO_BIN(?)"; + + int update = jdbcTemplate.update(sql, + voucher.getAmount(), + voucher.getDiscountType().name(), + Timestamp.valueOf(voucher.getExpirationDate().atStartOfDay()), + uuidToBytes(voucher.getVoucherId())); + if (update != 1) { + throw new VoucherException(ExceptionMsg.SQL_ERROR); + } + return voucher; + } + + @Override + public void deleteById(UUID voucherId) { + String sql = "DELETE FROM vouchers WHERE voucher_id = UUID_TO_BIN(?)"; + jdbcTemplate.update(sql, uuidToBytes(voucherId)); + } + + @Override + public void deleteAll() { + String sql = "DELETE FROM vouchers"; + jdbcTemplate.update(sql); + } + + public static UUID toUUID(byte[] bytes) { + var buffer = ByteBuffer.wrap(bytes); + return new UUID(buffer.getLong(), buffer.getLong()); + } + + private byte[] uuidToBytes(UUID voucherId) { + return voucherId.toString().getBytes(); + } + + private Voucher mapToVoucher(ResultSet resultSet) throws SQLException { + UUID voucherId = toUUID(resultSet.getBytes("voucher_id")); + long amount = resultSet.getLong("amount"); + DiscountType discountType = DiscountType.getDiscountTypeByName(resultSet.getString("discount")); + LocalDate registrationDate = resultSet.getTimestamp("registration_date") == null ? null : resultSet.getTimestamp("registration_date").toLocalDateTime().toLocalDate(); + LocalDate expirationDate = resultSet.getTimestamp("expiration_date") == null ? null : resultSet.getTimestamp("expiration_date").toLocalDateTime().toLocalDate(); + + return new Voucher(voucherId,amount, registrationDate, expirationDate, discountType.getNewInstance()); + } + + + +} diff --git a/src/main/java/org/weekly/weekly/voucher/repository/MemoryVoucherRepository.java b/src/main/java/org/weekly/weekly/voucher/repository/MemoryVoucherRepository.java new file mode 100644 index 0000000000..e1b8d1bc70 --- /dev/null +++ b/src/main/java/org/weekly/weekly/voucher/repository/MemoryVoucherRepository.java @@ -0,0 +1,62 @@ +package org.weekly.weekly.voucher.repository; + +import org.springframework.context.annotation.Profile; +import org.springframework.stereotype.Repository; +import org.weekly.weekly.util.ExceptionMsg; +import org.weekly.weekly.voucher.domain.DiscountType; +import org.weekly.weekly.voucher.domain.Voucher; +import org.weekly.weekly.voucher.exception.VoucherException; + +import java.util.List; +import java.util.Map; +import java.util.Optional; +import java.util.UUID; +import java.util.concurrent.ConcurrentHashMap; + +@Profile("dev") +@Repository +public class MemoryVoucherRepository implements VoucherRepository{ + private final Map storages = new ConcurrentHashMap<>(); + + public Voucher insert(Voucher voucher) { + validateUUID(voucher.getVoucherId()); + storages.put(voucher.getVoucherId(), voucher); + return voucher; + } + + public Optional findById(UUID voucherId) { + return Optional.ofNullable(storages.get(voucherId)); + } + + public List findAll() { + return storages.values().stream() + .toList(); + } + + @Override + public List findByDiscountType(DiscountType discountType) { + return null; + } + + @Override + public Voucher update(Voucher voucher) { + return null; + } + + @Override + public void deleteById(UUID voucherId) { + + } + + @Override + public void deleteAll() { + + } + + private void validateUUID(UUID uuid) { + Optional voucherOptional = findById(uuid); + if (voucherOptional.isPresent()) { + throw new VoucherException(ExceptionMsg.VOUCHER_EXIST); + } + } +} diff --git a/src/main/java/org/weekly/weekly/voucher/repository/VoucherRepository.java b/src/main/java/org/weekly/weekly/voucher/repository/VoucherRepository.java index a1a8c0aa38..0e794a4f2f 100644 --- a/src/main/java/org/weekly/weekly/voucher/repository/VoucherRepository.java +++ b/src/main/java/org/weekly/weekly/voucher/repository/VoucherRepository.java @@ -1,26 +1,22 @@ package org.weekly.weekly.voucher.repository; -import org.springframework.stereotype.Repository; +import org.weekly.weekly.voucher.domain.DiscountType; import org.weekly.weekly.voucher.domain.Voucher; -import java.util.*; -import java.util.concurrent.ConcurrentHashMap; +import java.util.List; +import java.util.Optional; +import java.util.UUID; -@Repository -public class VoucherRepository { - private final Map storages = new ConcurrentHashMap<>(); +public interface VoucherRepository { + Voucher insert(Voucher voucher); + Optional findById(UUID voucherId); + List findAll(); - public Voucher insert(Voucher voucher) { - storages.put(voucher.getVoucherId(), voucher); - return voucher; - } + List findByDiscountType(DiscountType discountType); - public Optional findById(UUID voucherId) { - return Optional.ofNullable(storages.get(voucherId)); - } + Voucher update(Voucher voucher); - public List findAll() { - return storages.values().stream() - .toList(); - } + void deleteById(UUID voucherId); + + void deleteAll(); } diff --git a/src/main/java/org/weekly/weekly/voucher/service/VoucherService.java b/src/main/java/org/weekly/weekly/voucher/service/VoucherService.java index e1893b22a1..a79e89cd54 100644 --- a/src/main/java/org/weekly/weekly/voucher/service/VoucherService.java +++ b/src/main/java/org/weekly/weekly/voucher/service/VoucherService.java @@ -1,15 +1,13 @@ package org.weekly.weekly.voucher.service; import org.springframework.stereotype.Service; -import org.weekly.weekly.util.ExceptionMsg; import org.weekly.weekly.voucher.domain.Voucher; -import org.weekly.weekly.voucher.dto.VoucherDto; -import org.weekly.weekly.voucher.dto.CreateResponse; -import org.weekly.weekly.voucher.dto.ListResponse; +import org.weekly.weekly.voucher.dto.request.VoucherCreationRequest; +import org.weekly.weekly.voucher.dto.response.VoucherCreationResponse; +import org.weekly.weekly.voucher.dto.response.VouchersResponse; import org.weekly.weekly.voucher.repository.VoucherRepository; import java.util.List; -import java.util.Optional; @Service public class VoucherService { @@ -19,22 +17,16 @@ public VoucherService(VoucherRepository voucherRepository) { this.voucherRepository = voucherRepository; } - public CreateResponse insertVoucher(VoucherDto voucherDto) { - validateVoucher(voucherDto); - Voucher voucher = voucherDto.parseToVoucher(); - this.voucherRepository.insert(voucher); - return new CreateResponse(voucher); + public VoucherCreationResponse insertVoucher(VoucherCreationRequest voucherCreationRequest) { + Voucher voucher = voucherCreationRequest.toVoucher(); + voucherRepository.insert(voucher); + return new VoucherCreationResponse(voucher); } - public ListResponse getVouchers() { - List vouchers = this.voucherRepository.findAll(); - return new ListResponse(vouchers); + public VouchersResponse getVouchers() { + List vouchers = voucherRepository.findAll(); + return new VouchersResponse(vouchers); } - private void validateVoucher(VoucherDto voucherDto) { - Optional voucherOptional = this.voucherRepository.findById(voucherDto.getVoucherId()); - if (voucherOptional.isPresent()) { - throw new RuntimeException(ExceptionMsg.VOUCHER_EXIST.getMsg()); - } - } + } 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..90244d6551 --- /dev/null +++ b/src/main/resources/application.yaml @@ -0,0 +1,46 @@ +spring.config.activate.on-profile: default +command: + write: system + read: scanner + +spring: + datasource: + driver-class-name: com.mysql.cj.jdbc.Driver + url: jdbc:mysql://localhost:3306/voucher_mgmt?createDatabaseIfNotExist=true + username: "root" + password: "qwe123" + + sql: + init: + mode: always + +--- + +spring.config.activate.on-profile: dev +command: + write: system + read: console + +--- + +spring.config.activate.on-profile: local +command: + write: system + read: buffer + +--- + +spring.config.activate.on-profile: test +command: + write: system + read: scanner + +spring: + datasource: + url: jdbc:tc:mysql:8://test + + sql: + init: + mode: always + + diff --git a/src/main/resources/logback.xml b/src/main/resources/logback.xml index c28bb18107..41b5312d80 100644 --- a/src/main/resources/logback.xml +++ b/src/main/resources/logback.xml @@ -18,4 +18,4 @@ - \ 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..0b88d26b44 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,29 @@ +DROP TABLE IF EXISTS customers; +CREATE TABLE customers ( + customer_id BINARY(16) PRIMARY KEY, + name varchar(20) NOT NULL, + email varchar(50) NOT NULL, + create_at datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + CONSTRAINT unq_user_email UNIQUE (email) +); + +INSERT INTO customers(customer_id, name, email) +VALUES (uuid_to_bin(UUID()), 'tester00', 'test00@gmail.com'), + (uuid_to_bin(UUID()), 'tester01', 'test01@gmail.com'), + (uuid_to_bin(UUID()), 'tester02', 'test02@gmail.com'); + + + +DROP TABLE IF EXISTS vouchers; +CREATE TABLE vouchers ( + voucher_id BINARY(16) PRIMARY KEY, + amount bigint NOT NULL, + discount enum('FIXED', 'PERCENT') NOT NULL, + registration_date datetime(6) NOT NULL DEFAULT CURRENT_TIMESTAMP(6), + expiration_date datetime(6) NOT NULL +); + +INSERT INTO vouchers (voucher_id, amount, discount, expiration_date) +VALUES (uuid_to_bin(UUID()), 50, 'PERCENT', DATE_ADD(CURRENT_TIMESTAMP(6), INTERVAL 7 DAY)), + (uuid_to_bin(UUID()), 100000, 'FIXED', DATE_ADD(CURRENT_TIMESTAMP(6), INTERVAL 7 DAY)), + (uuid_to_bin(UUID()), 80, 'PERCENT', DATE_ADD(CURRENT_TIMESTAMP(6), INTERVAL 7 DAY)); \ No newline at end of file diff --git a/src/test/java/org/weekly/weekly/customer/CustomerTest.java b/src/test/java/org/weekly/weekly/customer/CustomerTest.java new file mode 100644 index 0000000000..a809768083 --- /dev/null +++ b/src/test/java/org/weekly/weekly/customer/CustomerTest.java @@ -0,0 +1,27 @@ +package org.weekly.weekly.customer; + +import org.junit.jupiter.api.Test; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.ValueSource; +import org.weekly.weekly.customer.domain.Customer; +import org.weekly.weekly.customer.exception.CustomerException; + +import java.util.UUID; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hamcrest.Matchers.*; + +public class CustomerTest { + + @Test + void 회원생성_성공_테스트() { + assertThat(Customer.of(UUID.randomUUID(), "hello", "email@gmail.com"), instanceOf(Customer.class)); + } + + @ParameterizedTest + @ValueSource(strings = {"123", "123@", "213@n", "abc@naver.", "abc@naver.c", "abc@naver.comc"}) + void 회원생성_실패_테스트(String email) { + assertThatThrownBy(()-> Customer.of(UUID.randomUUID(), "hello", email)).isInstanceOf(CustomerException.class); + } +} diff --git a/src/test/java/org/weekly/weekly/customer/JdbcCustomerRepositoryTest.java b/src/test/java/org/weekly/weekly/customer/JdbcCustomerRepositoryTest.java new file mode 100644 index 0000000000..54b4f22fed --- /dev/null +++ b/src/test/java/org/weekly/weekly/customer/JdbcCustomerRepositoryTest.java @@ -0,0 +1,128 @@ +package org.weekly.weekly.customer; + +import org.junit.jupiter.api.*; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.weekly.weekly.customer.domain.Customer; +import org.weekly.weekly.customer.exception.CustomerException; +import org.weekly.weekly.customer.repository.JdbcCustomerRepository; + +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import static org.assertj.core.api.Assertions.assertThatCode; +import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.samePropertyValuesAs; + +@Testcontainers +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@ActiveProfiles("test") +@SpringBootTest +class JdbcCustomerRepositoryTest { + @Autowired + private JdbcCustomerRepository jdbcCustomerRepository; + + Customer customer; + + @BeforeEach + void setUp() { + customer = Customer.of(UUID.randomUUID(), "tester", "test@naver.com"); + } + + @AfterEach + void deleteCustomer() { + assertThatCode(() -> jdbcCustomerRepository.deleteByEmail(customer.getEmail())); + } + + @Test + void 전체_회원_검색_테스트() { + // Given + Customer insertCustomer = jdbcCustomerRepository.insert(customer); + + // When + Then + List customers = jdbcCustomerRepository.findAll(); + assertThat(customers.isEmpty(), is(false)); + } + + @Test + void 회원_등록성공_테스트() { + // Given + Customer insertCustomer = jdbcCustomerRepository.insert(customer); + + // When + Optional findCustomer = jdbcCustomerRepository.findByEmail(insertCustomer.getEmail()); + + // Then + assertThat(findCustomer.isEmpty(), is(false)); + assertThat(findCustomer.get(), samePropertyValuesAs(insertCustomer)); + } + + @Test + void 이메일_회원_검색_성공_테스트() { + // Given + Customer insertCustomer = jdbcCustomerRepository.insert(customer); + + // When + Optional customers = jdbcCustomerRepository.findByEmail(insertCustomer.getEmail()); + + // Then + assertThat(customers.isEmpty(), is(false)); + } + + @Test + void 이메일_회원_검색_실패_테스트() { + Optional customers = jdbcCustomerRepository.findByEmail(customer.getEmail()); + assertThat(customers.isEmpty(), is(true)); + } + + @Test + void 회원_삭제_성공_테스트() { + // Given + Customer insertCustomer = jdbcCustomerRepository.insert(customer); + Optional findCustomer = jdbcCustomerRepository.findByEmail(insertCustomer.getEmail()); + assertThat(findCustomer.isPresent(), is(true)); + + // When + jdbcCustomerRepository.deleteByEmail(findCustomer.get().getEmail()); + + // THen + Optional deleteCustomer = jdbcCustomerRepository.findByEmail(insertCustomer.getEmail()); + assertThat(deleteCustomer.isEmpty(), is(true)); + } + + @Test + void 회원_삭제_실패_테스트() { + // when + jdbcCustomerRepository.deleteByEmail(customer.getEmail()); + + } + + @Test + void 전체_회원_삭제_테스트() { + // Given + jdbcCustomerRepository.deleteAll(); + + List customers = jdbcCustomerRepository.findAll(); + assertThat(customers.isEmpty(), is(true)); + } + + @Test + void 회원_닉네임_업데이트_테스트() { + // Given + String newName = "newName"; + Customer insertCusomter = jdbcCustomerRepository.insert(customer); + + // When + jdbcCustomerRepository.update(insertCusomter,newName); + + // Then + Optional updateCustomer = jdbcCustomerRepository.findByEmail(newName); + assertThat(updateCustomer.isPresent(), is(true)); + assertThat(updateCustomer.get().getEmail(), is(newName)); + } +} diff --git a/src/test/java/org/weekly/weekly/ui/ReadExceptionTest.java b/src/test/java/org/weekly/weekly/ui/ReadExceptionTest.java index 27020b2670..ec1ee12655 100644 --- a/src/test/java/org/weekly/weekly/ui/ReadExceptionTest.java +++ b/src/test/java/org/weekly/weekly/ui/ReadExceptionTest.java @@ -2,7 +2,7 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; -import org.weekly.weekly.ui.exception.ReadException; +import org.weekly.weekly.ui.exception.InputValidator; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -10,7 +10,7 @@ public class ReadExceptionTest { @ParameterizedTest @ValueSource(strings = {"", " "}) void 사용자가_빈값이나_입력오류났을때_예외발생(String userInput) { - assertThatThrownBy(()-> ReadException.isEmpty(userInput)) + assertThatThrownBy(()-> InputValidator.isEmpty(userInput)) .isInstanceOf(RuntimeException.class); } } diff --git a/src/test/java/org/weekly/weekly/util/DiscountMapTest.java b/src/test/java/org/weekly/weekly/util/DiscountMapTest.java index 56bbb7d0e7..3443ee7c77 100644 --- a/src/test/java/org/weekly/weekly/util/DiscountMapTest.java +++ b/src/test/java/org/weekly/weekly/util/DiscountMapTest.java @@ -3,6 +3,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.ValueSource; +import org.weekly.weekly.voucher.domain.DiscountType; import static org.assertj.core.api.Assertions.assertThat; import static org.assertj.core.api.Assertions.assertThatThrownBy; @@ -17,7 +18,7 @@ public class DiscountMapTest { }) void 사용자_입력이_할인_맵에_없으면_예외발생(String userInput) { // when + then - assertThatThrownBy(()-> DiscountType.getDiscountMap(userInput)) + assertThatThrownBy(()-> DiscountType.getDiscountTypeByNumber(userInput)) .isInstanceOf(RuntimeException.class); } @@ -29,8 +30,8 @@ public class DiscountMapTest { String percentUserInput = "2"; // when - DiscountType fixedDiscount = DiscountType.getDiscountMap(fixedUserInput); - DiscountType percentDiscount = DiscountType.getDiscountMap(percentUserInput); + DiscountType fixedDiscount = DiscountType.getDiscountTypeByNumber(fixedUserInput); + DiscountType percentDiscount = DiscountType.getDiscountTypeByNumber(percentUserInput); // then assertThat(fixedDiscount).isEqualTo(DiscountType.FIXED); diff --git a/src/test/java/org/weekly/weekly/voucher/JdbcVoucherRepositoryTest.java b/src/test/java/org/weekly/weekly/voucher/JdbcVoucherRepositoryTest.java new file mode 100644 index 0000000000..6a47bcc496 --- /dev/null +++ b/src/test/java/org/weekly/weekly/voucher/JdbcVoucherRepositoryTest.java @@ -0,0 +1,157 @@ +package org.weekly.weekly.voucher; + +import org.junit.jupiter.api.*; +import org.junit.jupiter.params.ParameterizedTest; +import org.junit.jupiter.params.provider.CsvSource; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.boot.test.context.SpringBootTest; +import org.springframework.test.context.ActiveProfiles; +import org.testcontainers.junit.jupiter.Testcontainers; +import org.weekly.weekly.voucher.domain.DiscountType; +import org.weekly.weekly.voucher.domain.Voucher; +import org.weekly.weekly.voucher.repository.JdbcVoucherRepository; + +import java.time.LocalDate; +import java.util.List; +import java.util.Optional; +import java.util.UUID; + +import static org.hamcrest.MatcherAssert.assertThat; +import static org.hamcrest.Matchers.is; +import static org.hamcrest.Matchers.samePropertyValuesAs; + +@TestMethodOrder(MethodOrderer.OrderAnnotation.class) +@ActiveProfiles("test") +@Testcontainers +@SpringBootTest +class JdbcVoucherRepositoryTest{ + @Autowired + private JdbcVoucherRepository jdbcVoucherRepository; + + Voucher fixedVoucher; + Voucher percentVoucher; + + @BeforeEach + void setUp() { + fixedVoucher = Voucher.of(UUID.randomUUID(), 10000, LocalDate.now(), 7, DiscountType.FIXED); + percentVoucher = Voucher.of(UUID.randomUUID(), 50, LocalDate.now(), 7, DiscountType.PERCENT); + } + + @Test + @Order(1) + void 전체_바우처_검색_테스트() { + List vouchers = jdbcVoucherRepository.findAll(); + assertThat(vouchers.isEmpty(), is(false)); + } + + @Test + @Order(1) + void 아이디를_통한_검색_실패_테스트() { + Optional voucher = jdbcVoucherRepository.findById(fixedVoucher.getVoucherId()); + assertThat(voucher.isEmpty(), is(true)); + } + + @Test + @Order(1) + void 아이디를_통한_검색_성공_테스트() { + Optional voucher = jdbcVoucherRepository.findById(fixedVoucher.getVoucherId()); + assertThat(voucher.isPresent(), is(false)); + } + + @Test + @Order(1) + void 할인정책을_통한_검색_테스트() { + List vouchers = jdbcVoucherRepository.findByDiscountType(percentVoucher.getDiscountType()); + + assertThat(vouchers.isEmpty(), is(false)); + + vouchers.stream() + .forEach(voucher -> assertThat(voucher.getDiscountType(), is(DiscountType.PERCENT))); + } + + @Test + @Order(2) + void 할인바우처_등록성공_테스트() { + Voucher voucher = jdbcVoucherRepository.insert(percentVoucher); + + Optional findVoucher = jdbcVoucherRepository.findById(percentVoucher.getVoucherId()); + + assertThat(findVoucher.isEmpty(), is(false)); + assertThat(findVoucher.get(), samePropertyValuesAs(voucher)); + } + + @Test + @Order(2) + void 고정바우처_등록성공_테스트() { + Voucher voucher = jdbcVoucherRepository.insert(fixedVoucher); + + Optional findVoucher = jdbcVoucherRepository.findById(fixedVoucher.getVoucherId()); + + assertThat(findVoucher.isEmpty(), is(false)); + assertThat(findVoucher.get(), samePropertyValuesAs(voucher)); + } + + + @ParameterizedTest + @CsvSource({"5000, 0", "15000, 5000"}) + @Order(3) + void 고정바우처_정보_업데이트_테스트(int amount, long reaminExpected) { + // Given + jdbcVoucherRepository.insert(fixedVoucher); + + // When + long remain = fixedVoucher.applyDiscount(amount); + jdbcVoucherRepository.update(fixedVoucher); + Optional voucher = jdbcVoucherRepository.findById(fixedVoucher.getVoucherId()); + + // Then + assertThat(remain, is(reaminExpected)); + assertThat(voucher.isEmpty(), is(false)); + assertThat(voucher.get(), samePropertyValuesAs(fixedVoucher)); + } + + @ParameterizedTest + @CsvSource({"3000, 1500", "1000, 500"}) + @Order(3) + void 퍼센트바우처_정보_업데이트_테스트(int amount, long reaminExpected) { + // Given + jdbcVoucherRepository.insert(percentVoucher); + + // When + long remain = percentVoucher.applyDiscount(amount); + jdbcVoucherRepository.update(percentVoucher); + Optional voucher = jdbcVoucherRepository.findById(percentVoucher.getVoucherId()); + + // Then + assertThat(remain, is(reaminExpected)); + assertThat(voucher.isEmpty(), is(false)); + assertThat(voucher.get(), samePropertyValuesAs(percentVoucher)); + } + + @Test + @Order(4) + void 바우처_삭제_테스트() { + // Given + Voucher voucher = jdbcVoucherRepository.insert(percentVoucher); + Optional findVoucher = jdbcVoucherRepository.findById(voucher.getVoucherId()); + assertThat(findVoucher.isPresent(), is(true)); + + // when + jdbcVoucherRepository.deleteById(voucher.getVoucherId()); + + // Then + Optional deleteVoucher = jdbcVoucherRepository.findById(voucher.getVoucherId()); + assertThat(deleteVoucher.isEmpty(), is(true)); + } + + @Test + @Order(5) + void 전체_바우처_삭제_테스트() { + // Given + jdbcVoucherRepository.deleteAll(); + + // when + List vouchers = jdbcVoucherRepository.findAll(); + assertThat(vouchers.isEmpty(), is(true)); + } +} diff --git a/src/test/java/org/weekly/weekly/voucher/VoucherTest.java b/src/test/java/org/weekly/weekly/voucher/VoucherTest.java index 0947d69ba0..d786525500 100644 --- a/src/test/java/org/weekly/weekly/voucher/VoucherTest.java +++ b/src/test/java/org/weekly/weekly/voucher/VoucherTest.java @@ -5,14 +5,11 @@ import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; import org.junit.jupiter.params.provider.ValueSource; -import org.weekly.weekly.util.DiscountType; -import org.weekly.weekly.voucher.domain.Discount; -import org.weekly.weekly.voucher.domain.FixedDiscount; -import org.weekly.weekly.voucher.domain.PercentDiscount; +import org.weekly.weekly.voucher.domain.DiscountType; import org.weekly.weekly.voucher.domain.Voucher; -import org.weekly.weekly.voucher.dto.VoucherDto; -import org.weekly.weekly.voucher.dto.VoucherInfoRequest; -import org.weekly.weekly.voucher.repository.VoucherRepository; +import org.weekly.weekly.voucher.dto.request.VoucherCreationRequest; +import org.weekly.weekly.voucher.dto.request.VoucherInfoRequest; +import org.weekly.weekly.voucher.repository.MemoryVoucherRepository; import java.time.LocalDate; import java.util.UUID; @@ -20,11 +17,11 @@ import static org.assertj.core.api.Assertions.*; public class VoucherTest { - private VoucherRepository voucherRepository; + private MemoryVoucherRepository voucherRepository; @BeforeEach void setVoucherRepository() { - voucherRepository = new VoucherRepository(); + voucherRepository = new MemoryVoucherRepository(); } @ParameterizedTest @@ -33,20 +30,18 @@ void setVoucherRepository() { "10,1: 1" }, delimiter = ':') void 바우처가_이미_존재하면_예외발생(String userInput, String no) { - assertThatCode(()-> { - // Given - UUID voucherId = UUID.randomUUID(); - VoucherInfoRequest voucherInfo = VoucherInfoRequest.of(userInput); - Discount discount = DiscountType.getDiscountMap(no).getCls().getDeclaredConstructor().newInstance(); - LocalDate localDate = LocalDate.now(); + // Given + UUID voucherId = UUID.randomUUID(); + VoucherInfoRequest voucherInfo = VoucherInfoRequest.of(userInput); + DiscountType discount = DiscountType.getDiscountTypeByNumber(no); + LocalDate now = LocalDate.now(); + Voucher voucher = Voucher.of(voucherId, voucherInfo.getAmount(), now, voucherInfo.getExpiration(), discount); - // when - VoucherDto voucherDto = VoucherDto.parseDto(voucherId, voucherInfo, discount, localDate); - voucherRepository.insert(voucherDto.parseToVoucher()); + // when + voucherRepository.insert(voucher); - // then - assertThat(voucherRepository.findById(voucherId).isPresent()).isTrue(); - }).doesNotThrowAnyException(); + // then + assertThat(voucherRepository.findById(voucherId).isPresent()).isTrue(); } @ParameterizedTest @@ -57,13 +52,15 @@ void setVoucherRepository() { }) void 바우처_발행시간이_유효시간보다_느리면_예외발생(String userInput) { // Given - UUID voucherId = UUID.randomUUID(); - LocalDate localDate = LocalDate.now(); - Discount discount = new FixedDiscount(); +// UUID voucherId = UUID.randomUUID(); +// LocalDate localDate = LocalDate.now(); + DiscountType discount = DiscountType.FIXED; VoucherInfoRequest voucherInfo = VoucherInfoRequest.of(userInput); + VoucherCreationRequest request = new VoucherCreationRequest(voucherInfo, discount); + // when + then - assertThatThrownBy(()->VoucherDto.parseDto(voucherId, voucherInfo, discount, localDate)) + assertThatThrownBy(()->request.toVoucher()) .isInstanceOf(RuntimeException.class); } @@ -72,17 +69,19 @@ class 고정바우처_테스트 { @ParameterizedTest @ValueSource(strings = { "-1,12", - " asfd, 1" + " -50, 1" }) void 바우처_금액이_자연수가_아니면_예외발생(String userInput) { // Given - UUID voucherId = UUID.randomUUID(); - LocalDate localDate = LocalDate.now(); - Discount discount = new FixedDiscount(); - VoucherInfoRequest voucherInfo = VoucherInfoRequest.of(userInput); + DiscountType discountType = DiscountType.FIXED; - // when + then - assertThatThrownBy(()->VoucherDto.parseDto(voucherId, voucherInfo, discount, localDate)) + VoucherInfoRequest voucherInfoRequest = VoucherInfoRequest.of(userInput); + + // when + VoucherCreationRequest request = new VoucherCreationRequest(voucherInfoRequest, discountType); + + // then + assertThatThrownBy(()-> request.toVoucher()) .isInstanceOf(RuntimeException.class); } @@ -93,10 +92,8 @@ class 고정바우처_테스트 { }) void 고정_할인금액_적용하여_결과확인(int userInput, int discountMoney, int result) { // Given - Discount discount = new FixedDiscount(); LocalDate current = LocalDate.now(); - LocalDate next = current.plusMonths(1); - Voucher voucher = new Voucher(UUID.randomUUID(), discountMoney, current, next, discount); + Voucher voucher = Voucher.of(UUID.randomUUID(), discountMoney, current, 1, DiscountType.FIXED); // when long afterApply = voucher.applyDiscount(userInput); @@ -116,12 +113,12 @@ class 퍼센트바우처_테스트 { void 바우처_퍼센트값이_자연수가_아니면_예외발생(String userInput) { // Given UUID voucherId = UUID.randomUUID(); - LocalDate localDate = LocalDate.now(); - Discount discount = new PercentDiscount(); + LocalDate now = LocalDate.now(); VoucherInfoRequest voucherInfo = VoucherInfoRequest.of(userInput); + // when + then - assertThatThrownBy(()->VoucherDto.parseDto(voucherId, voucherInfo, discount, localDate)) + assertThatThrownBy(()->Voucher.of(voucherId, voucherInfo.getAmount(), now, 1, DiscountType.PERCENT)) .isInstanceOf(RuntimeException.class); } @@ -134,9 +131,7 @@ class 퍼센트바우처_테스트 { void 퍼센트_할인금액_적용하여_결과확인(int userInput, int discountMoney, int result) { // Given LocalDate current = LocalDate.now(); - LocalDate next = current.plusMonths(1); - Discount discount = new PercentDiscount(); - Voucher voucher = new Voucher(UUID.randomUUID(), discountMoney, current, next, discount); + Voucher voucher = Voucher.of(UUID.randomUUID(), discountMoney, current, 1, DiscountType.PERCENT); // when long afterApply = voucher.applyDiscount(userInput);