forked from pierront/mylibrary-template
🎉 Push every file from windaube computers
This commit is contained in:
10
.idea/.gitignore
generated
vendored
Normal file
10
.idea/.gitignore
generated
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
# Default ignored files
|
||||
/shelf/
|
||||
/workspace.xml
|
||||
# Editor-based HTTP Client requests
|
||||
/httpRequests/
|
||||
# Environment-dependent path to Maven home directory
|
||||
/mavenHomeManager.xml
|
||||
# Datasource local storage ignored files
|
||||
/dataSources/
|
||||
/dataSources.local.xml
|
7
.idea/encodings.xml
generated
Normal file
7
.idea/encodings.xml
generated
Normal file
@@ -0,0 +1,7 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="Encoding">
|
||||
<file url="file://$PROJECT_DIR$/src/main/java" charset="UTF-8" />
|
||||
<file url="file://$PROJECT_DIR$/src/main/resources" charset="UTF-8" />
|
||||
</component>
|
||||
</project>
|
12
.idea/misc.xml
generated
Normal file
12
.idea/misc.xml
generated
Normal file
@@ -0,0 +1,12 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="ExternalStorageConfigurationManager" enabled="true" />
|
||||
<component name="MavenProjectsManager">
|
||||
<option name="originalFiles">
|
||||
<list>
|
||||
<option value="$PROJECT_DIR$/pom.xml" />
|
||||
</list>
|
||||
</option>
|
||||
</component>
|
||||
<component name="ProjectRootManager" version="2" languageLevel="JDK_24" default="true" project-jdk-name="24" project-jdk-type="JavaSDK" />
|
||||
</project>
|
6
.idea/vcs.xml
generated
Normal file
6
.idea/vcs.xml
generated
Normal file
@@ -0,0 +1,6 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<project version="4">
|
||||
<component name="VcsDirectoryMappings">
|
||||
<mapping directory="" vcs="Git" />
|
||||
</component>
|
||||
</project>
|
@@ -0,0 +1,24 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.book;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class BookDTO {
|
||||
private final String isbn;
|
||||
private final String title;
|
||||
private final String author;
|
||||
private final String publisher;
|
||||
private final LocalDate publicationDate;
|
||||
private final double price;
|
||||
private final int stock;
|
||||
private final List<Category> categories;
|
||||
private final String description;
|
||||
private final String language;
|
||||
}
|
@@ -0,0 +1,17 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.book;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
|
||||
public record BookInfo(
|
||||
String isbn,
|
||||
String title,
|
||||
String author,
|
||||
String publisher,
|
||||
LocalDate publicationDate,
|
||||
double price,
|
||||
int stock,
|
||||
List<Category> categories,
|
||||
String description,
|
||||
String language
|
||||
) {}
|
@@ -0,0 +1,24 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.book;
|
||||
|
||||
public enum Category {
|
||||
FICTION,
|
||||
NON_FICTION,
|
||||
SCIENCE_FICTION,
|
||||
FANTASY,
|
||||
MYSTERY,
|
||||
THRILLER,
|
||||
ROMANCE,
|
||||
BIOGRAPHY,
|
||||
HISTORY,
|
||||
POETRY,
|
||||
CHILDREN,
|
||||
YOUNG_ADULT,
|
||||
SCIENCE,
|
||||
PHILOSOPHY,
|
||||
SELF_HELP,
|
||||
TRAVEL,
|
||||
COOKING,
|
||||
ART,
|
||||
RELIGION,
|
||||
REFERENCE
|
||||
}
|
@@ -0,0 +1,40 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.book.converter;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.BookDTO;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.BookInfo;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book;
|
||||
|
||||
public final class BookConverter {
|
||||
|
||||
private BookConverter() {}
|
||||
|
||||
public static Book toDomain(BookInfo info) {
|
||||
return Book.builder()
|
||||
.isbn(info.isbn())
|
||||
.title(info.title())
|
||||
.author(info.author())
|
||||
.publisher(info.publisher())
|
||||
.publicationDate(info.publicationDate())
|
||||
.price(info.price())
|
||||
.stock(info.stock())
|
||||
.categories(info.categories())
|
||||
.description(info.description())
|
||||
.language(info.language())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static BookDTO toDTO(Book book) {
|
||||
return BookDTO.builder()
|
||||
.isbn(book.getIsbn())
|
||||
.title(book.getTitle())
|
||||
.author(book.getAuthor())
|
||||
.publisher(book.getPublisher())
|
||||
.publicationDate(book.getPublicationDate())
|
||||
.price(book.getPrice())
|
||||
.stock(book.getStock())
|
||||
.categories(book.getCategories())
|
||||
.description(book.getDescription())
|
||||
.language(book.getLanguage())
|
||||
.build();
|
||||
}
|
||||
}
|
@@ -0,0 +1,33 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.book.entity;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.Category;
|
||||
import java.time.LocalDate;
|
||||
import java.util.List;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class Book {
|
||||
private String isbn;
|
||||
private String title;
|
||||
private String author;
|
||||
private String publisher;
|
||||
private LocalDate publicationDate;
|
||||
private double price;
|
||||
private int stock;
|
||||
private List<Category> categories;
|
||||
private String description;
|
||||
private String language;
|
||||
|
||||
public void addStock(int quantityToAdd) {
|
||||
this.stock += quantityToAdd;
|
||||
}
|
||||
|
||||
public void removeStock(int quantityToRemove) {
|
||||
if (quantityToRemove > this.stock) {
|
||||
throw new IllegalArgumentException("Pas assez de stock pour retirer : " + quantityToRemove);
|
||||
}
|
||||
this.stock -= quantityToRemove;
|
||||
}
|
||||
}
|
@@ -0,0 +1,13 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.book.exception;
|
||||
|
||||
public class BookNotFoundException extends RuntimeException {
|
||||
public static final String THE_BOOK_WITH_ISBN_DOES_NOT_EXIST_MESSAGE = "The book with isbn %s does not exist";
|
||||
|
||||
public BookNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
|
||||
public static BookNotFoundException forIsbn(String isbn) {
|
||||
return new BookNotFoundException(String.format(THE_BOOK_WITH_ISBN_DOES_NOT_EXIST_MESSAGE, isbn));
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.book.exception;
|
||||
|
||||
public class NotValidBookException extends RuntimeException {
|
||||
public NotValidBookException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,78 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.book.repository;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.Category;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor
|
||||
public final class BookRepository {
|
||||
private final List<Book> books = new ArrayList<>();
|
||||
|
||||
public List<Book> findAll() {
|
||||
return books;
|
||||
}
|
||||
|
||||
public void deleteAll() {
|
||||
books.clear();
|
||||
}
|
||||
|
||||
public Book save(Book newBook) {
|
||||
Optional<Book> optionalBookWithSameIsbn = this.findByIsbn(newBook.getIsbn());
|
||||
optionalBookWithSameIsbn.ifPresent(books::remove);
|
||||
books.add(newBook);
|
||||
return newBook;
|
||||
}
|
||||
|
||||
public Optional<Book> findByIsbn(String isbn) {
|
||||
return books.stream()
|
||||
.filter(book -> Objects.equals(book.getIsbn(), isbn))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public boolean existsByIsbn(String isbn) {
|
||||
return books.stream()
|
||||
.anyMatch(book -> Objects.equals(book.getIsbn(), isbn));
|
||||
}
|
||||
|
||||
public void delete(Book book) {
|
||||
books.remove(book);
|
||||
}
|
||||
|
||||
public List<Book> findByTitleContaining(String titlePart) {
|
||||
return books.stream()
|
||||
.filter(book -> book.getTitle() != null && book.getTitle().toLowerCase().contains(titlePart.toLowerCase()))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Book> findByAuthor(String author) {
|
||||
return books.stream()
|
||||
.filter(book -> book.getAuthor() != null && book.getAuthor().equalsIgnoreCase(author))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Book> findByPublisher(String publisher) {
|
||||
return books.stream()
|
||||
.filter(book -> book.getPublisher() != null && book.getPublisher().equalsIgnoreCase(publisher))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Book> findByCategory(Category category) {
|
||||
return books.stream()
|
||||
.filter(book -> book.getCategories() != null && book.getCategories().contains(category))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Book> findPublishedAfter(java.time.LocalDate date) {
|
||||
return books.stream()
|
||||
.filter(book -> book.getPublicationDate() != null && book.getPublicationDate().isAfter(date))
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Book> findByPriceRange(double min, double max) {
|
||||
return books.stream()
|
||||
.filter(book -> book.getPrice() >= min && book.getPrice() <= max)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
}
|
@@ -0,0 +1,163 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.book.usecase;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.*;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.converter.BookConverter;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.BookNotFoundException;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.NotValidBookException;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.validator.BookValidator;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class BookUseCase {
|
||||
|
||||
private final BookRepository repository;
|
||||
|
||||
public BookUseCase(BookRepository repository) {
|
||||
this.repository = repository;
|
||||
}
|
||||
|
||||
public String registerBook(BookInfo info) throws NotValidBookException {
|
||||
BookValidator.validate(info);
|
||||
|
||||
if (repository.existsByIsbn(info.isbn())) {
|
||||
throw new NotValidBookException("A book with this ISBN already exists");
|
||||
}
|
||||
|
||||
Book book = BookConverter.toDomain(info);
|
||||
Book stored = repository.save(book);
|
||||
return stored.getIsbn();
|
||||
}
|
||||
|
||||
public BookDTO updateBook(String isbn, BookInfo data) throws BookNotFoundException, NotValidBookException {
|
||||
BookValidator.validate(data);
|
||||
ensureBookExists(isbn);
|
||||
|
||||
Book updated = Book.builder()
|
||||
.isbn(isbn)
|
||||
.title(data.title())
|
||||
.author(data.author())
|
||||
.publisher(data.publisher())
|
||||
.publicationDate(data.publicationDate())
|
||||
.price(data.price())
|
||||
.stock(data.stock())
|
||||
.categories(data.categories())
|
||||
.description(data.description())
|
||||
.language(data.language())
|
||||
.build();
|
||||
|
||||
return BookConverter.toDTO(repository.save(updated));
|
||||
}
|
||||
|
||||
public int addStock(String isbn, int quantity) throws BookNotFoundException {
|
||||
Book book = ensureBookExists(isbn);
|
||||
book.addStock(quantity);
|
||||
repository.save(book);
|
||||
return book.getStock();
|
||||
}
|
||||
|
||||
public int removeStock(String isbn, int quantity) throws BookNotFoundException {
|
||||
Book book = ensureBookExists(isbn);
|
||||
book.removeStock(quantity);
|
||||
repository.save(book);
|
||||
return book.getStock();
|
||||
}
|
||||
|
||||
public void deleteBook(String isbn) throws BookNotFoundException {
|
||||
Book target = ensureBookExists(isbn);
|
||||
repository.delete(target);
|
||||
}
|
||||
|
||||
public BookDTO findBookByIsbn(String isbn) throws BookNotFoundException {
|
||||
return repository.findByIsbn(isbn)
|
||||
.map(BookConverter::toDTO)
|
||||
.orElseThrow(() -> new BookNotFoundException(isbn));
|
||||
}
|
||||
|
||||
public List<BookDTO> findBooksByAuthor(String author) {
|
||||
return repository.findByAuthor(author)
|
||||
.stream()
|
||||
.map(BookConverter::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<BookDTO> findBooksByCategory(Category category) {
|
||||
return repository.findByCategory(category)
|
||||
.stream()
|
||||
.map(BookConverter::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<BookDTO> findBooksByTitleContaining(String title) {
|
||||
return repository.findByTitleContaining(title)
|
||||
.stream()
|
||||
.map(BookConverter::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<BookDTO> findBooksByPublisher(String publisher) {
|
||||
return repository.findByPublisher(publisher)
|
||||
.stream()
|
||||
.map(BookConverter::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<BookDTO> findBooksPublishedAfter(LocalDate date) {
|
||||
return repository.findPublishedAfter(date)
|
||||
.stream()
|
||||
.map(BookConverter::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<BookDTO> findBooksByPriceRange(double min, double max) {
|
||||
return repository.findByPriceRange(min, max)
|
||||
.stream()
|
||||
.map(BookConverter::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public Map<String, Object> findBooksPageSorted(int page, int size, String sortBy) {
|
||||
List<Book> books = repository.findAll();
|
||||
|
||||
books.sort(Comparator.comparing(book -> {
|
||||
switch (sortBy.toLowerCase()) {
|
||||
case "title":
|
||||
case "titre":
|
||||
return book.getTitle() == null ? "" : book.getTitle().toLowerCase();
|
||||
case "author":
|
||||
case "auteur":
|
||||
return book.getAuthor() == null ? "" : book.getAuthor().toLowerCase();
|
||||
case "publisher":
|
||||
case "editeur":
|
||||
return book.getPublisher() == null ? "" : book.getPublisher().toLowerCase();
|
||||
default:
|
||||
return book.getTitle() == null ? "" : book.getTitle().toLowerCase();
|
||||
}
|
||||
}));
|
||||
|
||||
int total = books.size();
|
||||
int pages = (int) Math.ceil((double) total / size);
|
||||
int start = Math.min(page * size, total);
|
||||
int end = Math.min(start + size, total);
|
||||
|
||||
List<BookDTO> content = books.subList(start, end)
|
||||
.stream()
|
||||
.map(BookConverter::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("content", content);
|
||||
result.put("totalElements", total);
|
||||
result.put("totalPages", pages);
|
||||
|
||||
return result;
|
||||
}
|
||||
|
||||
private Book ensureBookExists(String isbn) throws BookNotFoundException {
|
||||
return repository.findByIsbn(isbn)
|
||||
.orElseThrow(() -> new BookNotFoundException(isbn));
|
||||
}
|
||||
}
|
@@ -0,0 +1,65 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.book.validator;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.BookInfo;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.NotValidBookException;
|
||||
|
||||
public final class BookValidator {
|
||||
|
||||
public static final String ERROR_ISBN_NULL = "ISBN cannot be null";
|
||||
public static final String ERROR_INVALID_ISBN = "ISBN is not valid";
|
||||
public static final String ERROR_TITLE_EMPTY = "Title cannot be empty";
|
||||
public static final String ERROR_AUTHOR_EMPTY = "Author cannot be empty";
|
||||
public static final String ERROR_NEGATIVE_PRICE = "Price must be positive";
|
||||
public static final String ERROR_NEGATIVE_STOCK = "Stock cannot be negative";
|
||||
public static final String ERROR_PUBLISHER_EMPTY = "Publisher cannot be empty";
|
||||
public static final String ERROR_PUBLICATION_DATE_NULL = "Publication date cannot be null";
|
||||
public static final String ERROR_CATEGORIES_EMPTY = "Categories list cannot be empty";
|
||||
public static final String ERROR_DESCRIPTION_EMPTY = "Description cannot be empty";
|
||||
public static final String ERROR_LANGUAGE_EMPTY = "Language cannot be empty";
|
||||
|
||||
private BookValidator() {
|
||||
// Prevent instantiation
|
||||
}
|
||||
|
||||
public static void validate(BookInfo info) throws NotValidBookException {
|
||||
checkPrice(info);
|
||||
checkIsbn(info);
|
||||
checkTitle(info);
|
||||
checkAuthor(info);
|
||||
checkStock(info);
|
||||
}
|
||||
|
||||
private static void checkIsbn(BookInfo info) throws NotValidBookException {
|
||||
String isbn = info.isbn();
|
||||
if (isbn == null) {
|
||||
throw new NotValidBookException(ERROR_ISBN_NULL);
|
||||
}
|
||||
if (!isbn.matches("\\d{13}")) {
|
||||
throw new NotValidBookException(ERROR_INVALID_ISBN);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkTitle(BookInfo info) throws NotValidBookException {
|
||||
if (info.title() == null || info.title().isBlank()) {
|
||||
throw new NotValidBookException(ERROR_TITLE_EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkAuthor(BookInfo info) throws NotValidBookException {
|
||||
if (info.author() == null || info.author().isBlank()) {
|
||||
throw new NotValidBookException(ERROR_AUTHOR_EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkPrice(BookInfo info) throws NotValidBookException {
|
||||
if (info.price() < 0) {
|
||||
throw new NotValidBookException(ERROR_NEGATIVE_PRICE);
|
||||
}
|
||||
}
|
||||
|
||||
private static void checkStock(BookInfo info) throws NotValidBookException {
|
||||
if (info.stock() < 0) {
|
||||
throw new NotValidBookException(ERROR_NEGATIVE_STOCK);
|
||||
}
|
||||
}
|
||||
}
|
@@ -3,14 +3,16 @@ package fr.iut_fbleau.but3.dev62.mylibrary.customer.converter;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.customer.CustomerDTO;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.customer.CustomerInfo;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer;
|
||||
import java.util.UUID;
|
||||
|
||||
public final class CustomerConverter {
|
||||
private CustomerConverter(){
|
||||
|
||||
private CustomerConverter() {
|
||||
// util class
|
||||
}
|
||||
|
||||
public static Customer toDomain(CustomerInfo newCustomer) {
|
||||
return Customer.builder()
|
||||
.id(UUID.randomUUID()) // ← génération automatique
|
||||
.firstName(newCustomer.firstName())
|
||||
.lastName(newCustomer.lastName())
|
||||
.phoneNumber(newCustomer.phoneNumber())
|
||||
|
@@ -14,68 +14,58 @@ import java.util.UUID;
|
||||
|
||||
public final class CustomerUseCase {
|
||||
|
||||
private final CustomerRepository customerRepository;
|
||||
private final CustomerRepository repo;
|
||||
|
||||
public CustomerUseCase(CustomerRepository customerRepository) {
|
||||
this.customerRepository = customerRepository;
|
||||
public CustomerUseCase(CustomerRepository repo) {
|
||||
this.repo = repo;
|
||||
}
|
||||
|
||||
public UUID registerCustomer(CustomerInfo newCustomer) throws NotValidCustomerException {
|
||||
CustomerValidator.validate(newCustomer);
|
||||
Customer customerToRegister = CustomerConverter.toDomain(newCustomer);
|
||||
Customer customerToRegistered = customerRepository.save(customerToRegister);
|
||||
return customerToRegistered.getId();
|
||||
public Optional<CustomerDTO> getByPhone(String phone) {
|
||||
return repo.findByPhoneNumber(phone).map(CustomerConverter::toDTO);
|
||||
}
|
||||
|
||||
public Optional<CustomerDTO> findCustomerByPhoneNumber(String phoneNumber) {
|
||||
Optional<Customer> optionalCustomer = customerRepository.findByPhoneNumber(phoneNumber);
|
||||
return optionalCustomer.map(CustomerConverter::toDTO);
|
||||
public UUID create(CustomerInfo input) throws NotValidCustomerException {
|
||||
CustomerValidator.validate(input);
|
||||
Customer newCustomer = CustomerConverter.toDomain(input);
|
||||
Customer saved = repo.save(newCustomer);
|
||||
return saved.getId();
|
||||
}
|
||||
|
||||
public CustomerDTO updateCustomer(UUID uuid, CustomerInfo customerInfo)
|
||||
throws CustomerNotFoundException, NotValidCustomerException {
|
||||
CustomerValidator.validate(customerInfo);
|
||||
Customer customerByUUID = getCustomerIfDoesNotExistThrowCustomerNotFoundException(
|
||||
uuid);
|
||||
Customer customer = Customer.builder()
|
||||
.id(uuid)
|
||||
.firstName(customerInfo.firstName())
|
||||
.lastName(customerInfo.lastName())
|
||||
.phoneNumber(customerInfo.phoneNumber())
|
||||
.loyaltyPoints(customerByUUID.getLoyaltyPoints())
|
||||
.build();
|
||||
Customer updatedCustomer = customerRepository.save(customer);
|
||||
return CustomerConverter.toDTO(updatedCustomer);
|
||||
public int increasePoints(UUID id, int points) throws CustomerNotFoundException {
|
||||
Customer c = resolveOrFail(id);
|
||||
c.addLoyaltyPoints(points);
|
||||
repo.save(c);
|
||||
return c.getLoyaltyPoints();
|
||||
}
|
||||
|
||||
public void deleteCustomer(UUID uuid) throws CustomerNotFoundException {
|
||||
Customer customerToDelete = getCustomerIfDoesNotExistThrowCustomerNotFoundException(uuid);
|
||||
this.customerRepository.delete(customerToDelete);
|
||||
}
|
||||
|
||||
public int addLoyaltyPoints(UUID uuid, int loyaltyPointToAdd) throws CustomerNotFoundException {
|
||||
Customer customerToAddLoyaltyPoints = getCustomerIfDoesNotExistThrowCustomerNotFoundException(
|
||||
uuid);
|
||||
customerToAddLoyaltyPoints.addLoyaltyPoints(loyaltyPointToAdd);
|
||||
customerRepository.save(customerToAddLoyaltyPoints);
|
||||
return customerToAddLoyaltyPoints.getLoyaltyPoints();
|
||||
}
|
||||
|
||||
public int subtractLoyaltyPoints(UUID uuid, int loyaltyPointToRemove)
|
||||
public int decreasePoints(UUID id, int points)
|
||||
throws CustomerNotFoundException, IllegalCustomerPointException {
|
||||
Customer customerToSubtractLoyaltyPoints = getCustomerIfDoesNotExistThrowCustomerNotFoundException(
|
||||
uuid);
|
||||
customerToSubtractLoyaltyPoints.removeLoyaltyPoints(loyaltyPointToRemove);
|
||||
customerRepository.save(customerToSubtractLoyaltyPoints);
|
||||
return customerToSubtractLoyaltyPoints.getLoyaltyPoints();
|
||||
Customer c = resolveOrFail(id);
|
||||
c.removeLoyaltyPoints(points);
|
||||
repo.save(c);
|
||||
return c.getLoyaltyPoints();
|
||||
}
|
||||
|
||||
private Customer getCustomerIfDoesNotExistThrowCustomerNotFoundException(UUID uuid)
|
||||
throws CustomerNotFoundException {
|
||||
Optional<Customer> optionalCustomerById = customerRepository.findById(uuid);
|
||||
if (optionalCustomerById.isEmpty()) {
|
||||
throw new CustomerNotFoundException(uuid);
|
||||
public void remove(UUID id) throws CustomerNotFoundException {
|
||||
Customer c = resolveOrFail(id);
|
||||
repo.delete(c);
|
||||
}
|
||||
return optionalCustomerById.get();
|
||||
|
||||
public CustomerDTO modify(UUID id, CustomerInfo input)
|
||||
throws CustomerNotFoundException, NotValidCustomerException {
|
||||
CustomerValidator.validate(input);
|
||||
Customer existing = resolveOrFail(id);
|
||||
Customer updated = Customer.builder()
|
||||
.id(id)
|
||||
.firstName(input.firstName())
|
||||
.lastName(input.lastName())
|
||||
.phoneNumber(input.phoneNumber())
|
||||
.loyaltyPoints(existing.getLoyaltyPoints())
|
||||
.build();
|
||||
return CustomerConverter.toDTO(repo.save(updated));
|
||||
}
|
||||
|
||||
private Customer resolveOrFail(UUID id) throws CustomerNotFoundException {
|
||||
return repo.findById(id).orElseThrow(() -> new CustomerNotFoundException(id));
|
||||
}
|
||||
}
|
||||
|
@@ -5,40 +5,37 @@ import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.NotValidCustomerExc
|
||||
|
||||
public final class CustomerValidator {
|
||||
|
||||
public static final String PHONE_NUMBER_IS_NOT_VALID = "Phone number is not valid";
|
||||
public static final String LAST_NAME_CANNOT_BE_BLANK = "Last name cannot be blank";
|
||||
public static final String FIRST_NAME_CANNOT_BE_BLANK = "First name cannot be blank";
|
||||
public static final String PHONE_NUMBER_REGEX = "0([67])\\d{8}";
|
||||
public static final String ERROR_PHONE_INVALID = "Phone number is not valid";
|
||||
public static final String ERROR_LASTNAME_EMPTY = "Last name cannot be blank";
|
||||
public static final String ERROR_FIRSTNAME_EMPTY = "First name cannot be blank";
|
||||
public static final String PHONE_PATTERN = "0([67])\\d{8}";
|
||||
|
||||
private CustomerValidator() {
|
||||
private CustomerValidator() {}
|
||||
|
||||
public static void check(CustomerInfo input) throws NotValidCustomerException {
|
||||
ensureFirstName(input);
|
||||
ensureLastName(input);
|
||||
ensurePhone(input);
|
||||
}
|
||||
|
||||
public static void validate(CustomerInfo newCustomer) throws NotValidCustomerException {
|
||||
validateFirstName(newCustomer);
|
||||
validateLastName(newCustomer);
|
||||
validatePhoneNumber(newCustomer);
|
||||
}
|
||||
|
||||
private static void validatePhoneNumber(CustomerInfo newCustomer)
|
||||
throws NotValidCustomerException {
|
||||
if (newCustomer.phoneNumber().isBlank()) {
|
||||
private static void ensurePhone(CustomerInfo input) throws NotValidCustomerException {
|
||||
if (input.phoneNumber().isBlank()) {
|
||||
throw new NotValidCustomerException("Phone number cannot be blank");
|
||||
}
|
||||
if (!newCustomer.phoneNumber().matches(PHONE_NUMBER_REGEX)) {
|
||||
throw new NotValidCustomerException(PHONE_NUMBER_IS_NOT_VALID);
|
||||
if (!input.phoneNumber().matches(PHONE_PATTERN)) {
|
||||
throw new NotValidCustomerException(ERROR_PHONE_INVALID);
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateLastName(CustomerInfo newCustomer) throws NotValidCustomerException {
|
||||
if (newCustomer.lastName().isBlank()) {
|
||||
throw new NotValidCustomerException(LAST_NAME_CANNOT_BE_BLANK);
|
||||
private static void ensureLastName(CustomerInfo input) throws NotValidCustomerException {
|
||||
if (input.lastName().isBlank()) {
|
||||
throw new NotValidCustomerException(ERROR_LASTNAME_EMPTY);
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateFirstName(CustomerInfo newCustomer) throws NotValidCustomerException {
|
||||
if (newCustomer.firstName().isBlank()) {
|
||||
throw new NotValidCustomerException(FIRST_NAME_CANNOT_BE_BLANK);
|
||||
private static void ensureFirstName(CustomerInfo input) throws NotValidCustomerException {
|
||||
if (input.firstName().isBlank()) {
|
||||
throw new NotValidCustomerException(ERROR_FIRSTNAME_EMPTY);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,14 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class AddressDTO {
|
||||
private final String street;
|
||||
private final String city;
|
||||
private final String postalCode;
|
||||
private final String country;
|
||||
}
|
||||
|
@@ -0,0 +1,19 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class OrderDTO {
|
||||
private final UUID id;
|
||||
private final UUID customerId;
|
||||
private final String paymentMethod;
|
||||
private final List<OrderLineDTO> orderLines;
|
||||
private final AddressDTO shippingAddress;
|
||||
private final double totalPrice;
|
||||
private final double totalPriceToPay;
|
||||
}
|
||||
|
@@ -0,0 +1,16 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
import java.util.List;
|
||||
|
||||
@Getter
|
||||
@Setter
|
||||
@Builder
|
||||
public class OrderInfo {
|
||||
private String customerId;
|
||||
private String paymentMethod;
|
||||
private List<OrderLineDTO> orderLines;
|
||||
private AddressDTO address;
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class OrderLineDTO {
|
||||
private final String bookId;
|
||||
private final int quantity;
|
||||
}
|
||||
|
@@ -0,0 +1,6 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order;
|
||||
|
||||
public enum PaymentMethod {
|
||||
CREDIT_CARD,
|
||||
LOYALTY_POINTS;
|
||||
}
|
@@ -0,0 +1,97 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order.converter;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.order.*;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Address;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.OrderLine;
|
||||
|
||||
import java.util.UUID;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
|
||||
public final class OrderConverter {
|
||||
|
||||
private OrderConverter() {
|
||||
throw new AssertionError("This utility class should not be instantiated");
|
||||
}
|
||||
|
||||
public static Order toDomain(OrderInfo info) {
|
||||
if (info == null) return null;
|
||||
|
||||
return Order.builder()
|
||||
.customerId(Optional.ofNullable(info.getCustomerId())
|
||||
.map(UUID::fromString)
|
||||
.orElse(null))
|
||||
.paymentMethod(Optional.ofNullable(info.getPaymentMethod())
|
||||
.map(PaymentMethod::valueOf)
|
||||
.orElse(null))
|
||||
.orderLines(convertOrderLines(info.getOrderLines()))
|
||||
.shippingAddress(convertAddress(info.getAddress()))
|
||||
.totalPrice(0.0)
|
||||
.build();
|
||||
}
|
||||
|
||||
public static OrderDTO toDTO(Order order) {
|
||||
if (order == null) return null;
|
||||
|
||||
return OrderDTO.builder()
|
||||
.id(order.getId())
|
||||
.customerId(order.getCustomerId())
|
||||
.paymentMethod(Optional.ofNullable(order.getPaymentMethod())
|
||||
.map(PaymentMethod::name)
|
||||
.orElse(null))
|
||||
.orderLines(convertOrderLinesToDTO(order.getOrderLines()))
|
||||
.shippingAddress(convertAddressToDTO(order.getShippingAddress()))
|
||||
.totalPrice(order.getTotalPrice())
|
||||
.totalPriceToPay(order.getTotalPriceToPay())
|
||||
.build();
|
||||
}
|
||||
|
||||
private static Address convertAddress(AddressDTO addressDTO) {
|
||||
return Optional.ofNullable(addressDTO)
|
||||
.map(addr -> Address.builder()
|
||||
.street(addr.getStreet())
|
||||
.city(addr.getCity())
|
||||
.postalCode(addr.getPostalCode())
|
||||
.country(addr.getCountry())
|
||||
.build())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private static AddressDTO convertAddressToDTO(Address address) {
|
||||
return Optional.ofNullable(address)
|
||||
.map(addr -> AddressDTO.builder()
|
||||
.street(addr.getStreet())
|
||||
.city(addr.getCity())
|
||||
.postalCode(addr.getPostalCode())
|
||||
.country(addr.getCountry())
|
||||
.build())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private static List<OrderLineDTO> convertOrderLinesToDTO(List<OrderLine> orderLines) {
|
||||
return Optional.ofNullable(orderLines)
|
||||
.map(lines -> lines.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(line -> OrderLineDTO.builder()
|
||||
.bookId(line.getBookId())
|
||||
.quantity(line.getQuantity())
|
||||
.build())
|
||||
.toList())
|
||||
.orElse(null);
|
||||
}
|
||||
|
||||
private static List<OrderLine> convertOrderLines(List<OrderLineDTO> orderLines) {
|
||||
return Optional.ofNullable(orderLines)
|
||||
.map(lines -> lines.stream()
|
||||
.filter(Objects::nonNull)
|
||||
.map(line -> OrderLine.builder()
|
||||
.bookId(line.getBookId())
|
||||
.quantity(line.getQuantity())
|
||||
.build())
|
||||
.toList())
|
||||
.orElse(new ArrayList<>());
|
||||
}
|
||||
}
|
@@ -0,0 +1,14 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order.entity;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.AllArgsConstructor;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class Address {
|
||||
private final String street;
|
||||
private final String city;
|
||||
private final String postalCode;
|
||||
private final String country;
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order.entity;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.order.PaymentMethod;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
@Getter
|
||||
@Builder
|
||||
public class Order {
|
||||
private final UUID id;
|
||||
private final UUID customerId;
|
||||
private final PaymentMethod paymentMethod;
|
||||
private final List<OrderLine> orderLines;
|
||||
private final Address shippingAddress;
|
||||
private final double totalPrice;
|
||||
private final double totalPriceToPay;
|
||||
}
|
@@ -0,0 +1,12 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order.entity;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.NoArgsConstructor;
|
||||
@Getter
|
||||
@Builder
|
||||
public class OrderLine {
|
||||
private final String bookId;
|
||||
private final int quantity;
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
|
||||
|
||||
public class IllegalOrderPointException extends RuntimeException {
|
||||
public IllegalOrderPointException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
|
||||
|
||||
public class InvalidPaymentMethodException extends RuntimeException {
|
||||
public InvalidPaymentMethodException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
|
||||
|
||||
public class NotValidOrderException extends RuntimeException {
|
||||
public NotValidOrderException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
|
||||
|
||||
|
||||
public class OrderNotFoundException extends RuntimeException {
|
||||
public OrderNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
|
||||
|
||||
public class OrderQuantityException extends RuntimeException {
|
||||
public OrderQuantityException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order.exception;
|
||||
|
||||
public class WrongAddressException extends RuntimeException {
|
||||
public WrongAddressException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
@@ -0,0 +1,60 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order.repository;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order;
|
||||
import java.util.*;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
@NoArgsConstructor
|
||||
public final class OrderRepository {
|
||||
private final List<Order> orders = new ArrayList<>();
|
||||
|
||||
public void deleteAll() {
|
||||
orders.clear();
|
||||
}
|
||||
|
||||
public Optional<Order> findById(UUID id) {
|
||||
return this.orders.stream()
|
||||
.filter(order -> Objects.equals(order.getId(), id))
|
||||
.findFirst();
|
||||
}
|
||||
|
||||
public boolean existsById(UUID id) {
|
||||
return this.orders.stream()
|
||||
.anyMatch(order -> Objects.equals(order.getId(), id));
|
||||
}
|
||||
|
||||
public void delete(Order order) {
|
||||
this.orders.remove(order);
|
||||
}
|
||||
|
||||
public Order save(Order newOrder) {
|
||||
if (newOrder.getId() == null) {
|
||||
Order orderWithId = Order.builder()
|
||||
.id(UUID.randomUUID())
|
||||
.customerId(newOrder.getCustomerId())
|
||||
.paymentMethod(newOrder.getPaymentMethod())
|
||||
.orderLines(newOrder.getOrderLines())
|
||||
.shippingAddress(newOrder.getShippingAddress())
|
||||
.totalPrice(newOrder.getTotalPrice())
|
||||
.totalPriceToPay(newOrder.getTotalPriceToPay())
|
||||
.build();
|
||||
this.orders.add(orderWithId);
|
||||
return orderWithId;
|
||||
}
|
||||
|
||||
Optional<Order> optionalOrderWithSameId = this.findById(newOrder.getId());
|
||||
optionalOrderWithSameId.ifPresent(orders::remove);
|
||||
this.orders.add(newOrder);
|
||||
return newOrder;
|
||||
}
|
||||
|
||||
public List<Order> findByCustomerId(UUID customerId) {
|
||||
return this.orders.stream()
|
||||
.filter(order -> Objects.equals(order.getCustomerId(), customerId))
|
||||
.toList();
|
||||
}
|
||||
|
||||
public List<Order> findAll() {
|
||||
return orders;
|
||||
}
|
||||
}
|
@@ -0,0 +1,158 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order.usecase;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.BookNotFoundException;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.CustomerNotFoundException;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.IllegalCustomerPointException;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.customer.repository.CustomerRepository;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.customer.entity.Customer;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.order.*;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.order.converter.OrderConverter;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.*;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.order.repository.OrderRepository;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public final class OrderUseCase {
|
||||
private final OrderRepository orderRepository;
|
||||
private final CustomerRepository customerRepository;
|
||||
private final BookRepository bookRepository;
|
||||
|
||||
public OrderUseCase(OrderRepository orderRepository, CustomerRepository customerRepository, BookRepository bookRepository) {
|
||||
this.orderRepository = orderRepository;
|
||||
this.customerRepository = customerRepository;
|
||||
this.bookRepository = bookRepository;
|
||||
}
|
||||
|
||||
public UUID createOrder(OrderInfo orderInfo) throws CustomerNotFoundException, IllegalCustomerPointException, NotValidOrderException {
|
||||
validateOrderInfo(orderInfo);
|
||||
|
||||
PaymentMethod paymentMethod = parsePaymentMethod(orderInfo.getPaymentMethod());
|
||||
UUID customerId = parseCustomerId(orderInfo.getCustomerId());
|
||||
|
||||
Customer customer = customerRepository.findById(customerId)
|
||||
.orElseThrow(() -> new CustomerNotFoundException(customerId));
|
||||
|
||||
Map<String, fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book> books = orderInfo.getOrderLines().stream()
|
||||
.map(line -> bookRepository.findByIsbn(line.getBookId())
|
||||
.orElseThrow(() -> new BookNotFoundException("Book with ISBN " + line.getBookId() + " was not found")))
|
||||
.collect(Collectors.toMap(fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book::getIsbn, b -> b));
|
||||
|
||||
validateOrderLines(orderInfo, books);
|
||||
|
||||
double totalPrice = calculateTotalPrice(orderInfo, books);
|
||||
double totalPriceToPay = calculateTotalPriceToPay(orderInfo, books);
|
||||
|
||||
Order domainOrder = OrderConverter.toDomain(orderInfo);
|
||||
|
||||
Order order = Order.builder()
|
||||
.customerId(customerId)
|
||||
.paymentMethod(paymentMethod)
|
||||
.orderLines(domainOrder.getOrderLines())
|
||||
.shippingAddress(domainOrder.getShippingAddress())
|
||||
.totalPrice(totalPrice)
|
||||
.totalPriceToPay(totalPriceToPay)
|
||||
.build();
|
||||
|
||||
if (paymentMethod == PaymentMethod.LOYALTY_POINTS) {
|
||||
handleLoyaltyPointsPayment(customer, totalPrice);
|
||||
}
|
||||
|
||||
return orderRepository.save(order).getId();
|
||||
}
|
||||
|
||||
public OrderDTO findOrderById(String orderId) {
|
||||
UUID uuid = parseOrderId(orderId);
|
||||
return orderRepository.findById(uuid)
|
||||
.map(OrderConverter::toDTO)
|
||||
.orElseThrow(() -> new OrderNotFoundException("Order not found"));
|
||||
}
|
||||
|
||||
public List<OrderDTO> findOrdersByCustomerId(String customerId) throws CustomerNotFoundException {
|
||||
UUID uuid = parseCustomerId(customerId);
|
||||
customerRepository.findById(uuid)
|
||||
.orElseThrow(() -> new CustomerNotFoundException(uuid));
|
||||
|
||||
return orderRepository.findByCustomerId(uuid)
|
||||
.stream()
|
||||
.map(OrderConverter::toDTO)
|
||||
.toList();
|
||||
}
|
||||
|
||||
private void validateOrderLines(OrderInfo orderInfo, Map<String, fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book> books) {
|
||||
orderInfo.getOrderLines().forEach(line -> {
|
||||
if (line.getQuantity() <= 0) {
|
||||
throw new OrderQuantityException("Quantity must be greater than 0 for book " + line.getBookId());
|
||||
}
|
||||
var book = books.get(line.getBookId());
|
||||
if (book.getStock() < line.getQuantity()) {
|
||||
throw new OrderQuantityException("Insufficient stock for book " + line.getBookId());
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private void validateOrderInfo(OrderInfo orderInfo) throws NotValidOrderException {
|
||||
if (orderInfo == null) {
|
||||
throw new NotValidOrderException("Order cannot be null");
|
||||
}
|
||||
if (orderInfo.getOrderLines() == null || orderInfo.getOrderLines().isEmpty()) {
|
||||
throw new OrderQuantityException("Order must contain at least one book");
|
||||
}
|
||||
if (orderInfo.getAddress() == null) {
|
||||
throw new NotValidOrderException("Shipping address is required");
|
||||
}
|
||||
}
|
||||
|
||||
private double calculateTotalPriceToPay(OrderInfo orderInfo, Map<String, fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book> books) {
|
||||
// Apply any potential discounts here
|
||||
return calculateTotalPrice(orderInfo, books);
|
||||
}
|
||||
|
||||
private UUID parseCustomerId(String customerId) throws CustomerNotFoundException {
|
||||
if (customerId == null || customerId.trim().isEmpty()) {
|
||||
throw new CustomerNotFoundException(null);
|
||||
}
|
||||
try {
|
||||
return UUID.fromString(customerId);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new CustomerNotFoundException(null);
|
||||
}
|
||||
}
|
||||
|
||||
private PaymentMethod parsePaymentMethod(String paymentMethod) {
|
||||
try {
|
||||
return PaymentMethod.valueOf(paymentMethod);
|
||||
} catch (IllegalArgumentException | NullPointerException e) {
|
||||
throw new InvalidPaymentMethodException("Payment method " + paymentMethod + " is not valid");
|
||||
}
|
||||
}
|
||||
|
||||
private UUID parseOrderId(String orderId) {
|
||||
try {
|
||||
return UUID.fromString(orderId);
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new OrderNotFoundException("Order with ID " + orderId + " was not found");
|
||||
}
|
||||
}
|
||||
|
||||
private double calculateTotalPrice(OrderInfo orderInfo, Map<String, fr.iut_fbleau.but3.dev62.mylibrary.book.entity.Book> books) {
|
||||
return orderInfo.getOrderLines().stream()
|
||||
.mapToDouble(line -> books.get(line.getBookId()).getPrice() * line.getQuantity())
|
||||
.sum();
|
||||
}
|
||||
|
||||
private void handleLoyaltyPointsPayment(Customer customer, double totalPrice) throws IllegalCustomerPointException {
|
||||
int requiredPoints = (int) (totalPrice * 100);
|
||||
if (customer.getLoyaltyPoints() < requiredPoints) {
|
||||
throw new IllegalOrderPointException("Not enough loyalty points to pay for the order");
|
||||
}
|
||||
customer.removeLoyaltyPoints(requiredPoints);
|
||||
customerRepository.save(customer);
|
||||
}
|
||||
}
|
@@ -0,0 +1,66 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.order.validator;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Address;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.order.entity.Order;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.order.exception.NotValidOrderException;
|
||||
|
||||
public class OrderValidator {
|
||||
|
||||
public static void validate(Order order) {
|
||||
if (order == null) {
|
||||
throw new NotValidOrderException("Order cannot be null");
|
||||
}
|
||||
validatePrices(order);
|
||||
validateAddress(order);
|
||||
validatePaymentMethod(order);
|
||||
validateOrderLines(order);
|
||||
}
|
||||
|
||||
private static void validatePrices(Order order) {
|
||||
if (order.getTotalPrice() <= 0) {
|
||||
throw new NotValidOrderException("Total price must be positive");
|
||||
}
|
||||
if (order.getTotalPriceToPay() <= 0) {
|
||||
throw new NotValidOrderException("Total price to pay must be positive");
|
||||
}
|
||||
if (order.getTotalPriceToPay() > order.getTotalPrice()) {
|
||||
throw new NotValidOrderException("Total price to pay cannot exceed total price");
|
||||
}
|
||||
}
|
||||
|
||||
private static void validateOrderLines(Order order) {
|
||||
if (order.getOrderLines() == null || order.getOrderLines().isEmpty()) {
|
||||
throw new NotValidOrderException("Order must contain at least one line");
|
||||
}
|
||||
|
||||
order.getOrderLines().forEach(line -> {
|
||||
if (line.getQuantity() <= 0) {
|
||||
throw new NotValidOrderException("Quantity must be positive");
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
private static void validateAddress(Order order) {
|
||||
Address address = order.getShippingAddress();
|
||||
if (address == null) {
|
||||
throw new NotValidOrderException("Shipping address is required");
|
||||
}
|
||||
|
||||
if (isNullOrBlank(address.getStreet()) ||
|
||||
isNullOrBlank(address.getCity()) ||
|
||||
isNullOrBlank(address.getPostalCode()) ||
|
||||
isNullOrBlank(address.getCountry())) {
|
||||
throw new NotValidOrderException("All address fields are required");
|
||||
}
|
||||
}
|
||||
|
||||
private static void validatePaymentMethod(Order order) {
|
||||
if (order.getPaymentMethod() == null) {
|
||||
throw new NotValidOrderException("Payment method cannot be null");
|
||||
}
|
||||
}
|
||||
|
||||
private static boolean isNullOrBlank(String value) {
|
||||
return value == null || value.trim().isEmpty();
|
||||
}
|
||||
}
|
@@ -0,0 +1,19 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.review;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class ReviewDto {
|
||||
private final UUID reviewId;
|
||||
private final long bookId;
|
||||
private final String customerName;
|
||||
private final String comment;
|
||||
private final int rating;
|
||||
}
|
||||
|
@@ -0,0 +1,16 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.review;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
public class ReviewInfo {
|
||||
private final String customerId;
|
||||
private final long isbn;
|
||||
private final int rating;
|
||||
private final String comment;
|
||||
}
|
||||
|
@@ -0,0 +1,29 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.review.converter;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.review.ReviewDto;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.review.ReviewInfo;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.review.entity.Review;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public class ReviewConverter {
|
||||
public static ReviewDto toDto(Review review, UUID reviewId, String customerName) {
|
||||
return ReviewDto.builder()
|
||||
.reviewId(reviewId)
|
||||
.bookId(review.getIsbn())
|
||||
.customerName(customerName)
|
||||
.comment(review.getComment())
|
||||
.rating(review.getRating())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static Review toDomain(ReviewInfo info) {
|
||||
return Review.builder()
|
||||
.customerId(UUID.fromString(info.getCustomerId()))
|
||||
.isbn(info.getIsbn())
|
||||
.rating(info.getRating())
|
||||
.comment(info.getComment())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,19 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.review.entity;
|
||||
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
@AllArgsConstructor
|
||||
@NoArgsConstructor
|
||||
public class Review {
|
||||
private UUID customerId;
|
||||
private long isbn;
|
||||
private int rating;
|
||||
private String comment;
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.review.exception;
|
||||
|
||||
public class InvalidReviewRatingException extends RuntimeException {
|
||||
public InvalidReviewRatingException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,8 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.review.exception;
|
||||
|
||||
public class ReviewAlreadyExistsException extends RuntimeException {
|
||||
public ReviewAlreadyExistsException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,8 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.review.exception;
|
||||
|
||||
public class ReviewNotFoundException extends RuntimeException {
|
||||
public ReviewNotFoundException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,44 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.review.repository;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.review.entity.Review;
|
||||
|
||||
import java.util.*;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
public class ReviewRepository {
|
||||
private final List<Review> reviews = new ArrayList<>();
|
||||
|
||||
public void save(Review review) {
|
||||
reviews.add(review);
|
||||
}
|
||||
|
||||
public List<Review> findAll() {
|
||||
return new ArrayList<>(reviews);
|
||||
}
|
||||
|
||||
public List<Review> findByIsbn(long isbn) {
|
||||
return reviews.stream().filter(r -> r.getIsbn() == isbn).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public List<Review> findByCustomerId(UUID customerId) {
|
||||
return reviews.stream().filter(r -> r.getCustomerId().equals(customerId)).collect(Collectors.toList());
|
||||
}
|
||||
|
||||
public boolean existsByCustomerIdAndIsbn(UUID customerId, long isbn) {
|
||||
return reviews.stream().anyMatch(r -> r.getCustomerId().equals(customerId) && r.getIsbn() == isbn);
|
||||
}
|
||||
|
||||
public void deleteByCustomerIdAndIsbn(UUID customerId, long isbn) {
|
||||
reviews.removeIf(r -> r.getCustomerId().equals(customerId) && r.getIsbn() == isbn);
|
||||
}
|
||||
|
||||
public void update(Review review) {
|
||||
deleteByCustomerIdAndIsbn(review.getCustomerId(), review.getIsbn());
|
||||
save(review);
|
||||
}
|
||||
|
||||
public void deleteAll() {
|
||||
reviews.clear();
|
||||
}
|
||||
}
|
||||
|
@@ -0,0 +1,71 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.review.usecase;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.repository.BookRepository;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.customer.repository.CustomerRepository;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.review.entity.Review;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.review.exception.InvalidReviewRatingException;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.review.exception.ReviewAlreadyExistsException;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.review.exception.ReviewNotFoundException;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.book.exception.BookNotFoundException;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.customer.exception.CustomerNotFoundException;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.review.repository.ReviewRepository;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.review.validator.ReviewValidator;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
public class ReviewUseCase {
|
||||
|
||||
private final ReviewRepository reviewRepository;
|
||||
private final BookRepository bookRepository;
|
||||
private final CustomerRepository customerRepository;
|
||||
|
||||
public ReviewUseCase(ReviewRepository reviewRepository, BookRepository bookRepository, CustomerRepository customerRepository) {
|
||||
this.reviewRepository = reviewRepository;
|
||||
this.bookRepository = bookRepository;
|
||||
this.customerRepository = customerRepository;
|
||||
}
|
||||
|
||||
public void deleteReview(long isbn, UUID customerId) {
|
||||
if (!reviewRepository.existsByCustomerIdAndIsbn(customerId, isbn)) {
|
||||
throw new ReviewNotFoundException("No review found for the given customer and book.");
|
||||
}
|
||||
reviewRepository.deleteByCustomerIdAndIsbn(customerId, isbn);
|
||||
}
|
||||
|
||||
public void submitReview(Review review) throws CustomerNotFoundException {
|
||||
ReviewValidator.validate(review);
|
||||
|
||||
if (!bookRepository.existsByIsbn(String.valueOf(review.getIsbn()))) {
|
||||
throw new BookNotFoundException("The book with ISBN " + review.getIsbn() + " was not found.");
|
||||
}
|
||||
|
||||
if (!customerRepository.existsById(review.getCustomerId())) {
|
||||
throw new CustomerNotFoundException(review.getCustomerId());
|
||||
}
|
||||
|
||||
if (reviewRepository.existsByCustomerIdAndIsbn(review.getCustomerId(), review.getIsbn())) {
|
||||
throw new ReviewAlreadyExistsException("A review already exists for this customer and book.");
|
||||
}
|
||||
|
||||
reviewRepository.save(review);
|
||||
}
|
||||
|
||||
public List<Review> getReviewsByCustomer(UUID customerId) {
|
||||
return reviewRepository.findByCustomerId(customerId);
|
||||
}
|
||||
|
||||
public void updateReview(Review review) {
|
||||
ReviewValidator.validate(review);
|
||||
|
||||
if (!reviewRepository.existsByCustomerIdAndIsbn(review.getCustomerId(), review.getIsbn())) {
|
||||
throw new ReviewNotFoundException("Cannot update: no existing review found for this customer and book.");
|
||||
}
|
||||
|
||||
reviewRepository.update(review);
|
||||
}
|
||||
|
||||
public List<Review> getReviewsByBook(long isbn) {
|
||||
return reviewRepository.findByIsbn(isbn);
|
||||
}
|
||||
}
|
@@ -0,0 +1,30 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.review.validator;
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.review.entity.Review;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.review.exception.InvalidReviewRatingException;
|
||||
|
||||
public class ReviewValidator {
|
||||
|
||||
public static void validate(Review review) {
|
||||
if (review == null) {
|
||||
throw new IllegalArgumentException("The review provided is null.");
|
||||
}
|
||||
|
||||
if (review.getCustomerId() == null) {
|
||||
throw new IllegalArgumentException("Customer ID is required.");
|
||||
}
|
||||
|
||||
if (review.getIsbn() <= 0) {
|
||||
throw new IllegalArgumentException("ISBN must be a positive number.");
|
||||
}
|
||||
|
||||
if (review.getComment() == null || review.getComment().trim().isEmpty()) {
|
||||
throw new IllegalArgumentException("A review must contain a comment.");
|
||||
}
|
||||
|
||||
int rating = review.getRating();
|
||||
if (rating < 1 || rating > 5) {
|
||||
throw new InvalidReviewRatingException("Rating must be within the range of 1 to 5.");
|
||||
}
|
||||
}
|
||||
}
|
@@ -0,0 +1,15 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.subscription;
|
||||
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import java.util.UUID;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class SubscriptionDTO {
|
||||
private final UUID id;
|
||||
private final UUID customerId;
|
||||
private final Integer duration;
|
||||
private final String paymentMethod;
|
||||
private final String debutDate;
|
||||
}
|
@@ -0,0 +1,7 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.subscription;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
public record SubscriptionInfo(UUID customerId, Integer duration, String paymentMethod) {
|
||||
|
||||
}
|
@@ -0,0 +1,31 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.subscription.converter;
|
||||
|
||||
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionDTO;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.SubscriptionInfo;
|
||||
import fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity.Subscription;
|
||||
|
||||
public final class SubscriptionConverter {
|
||||
private SubscriptionConverter() {
|
||||
|
||||
}
|
||||
|
||||
public static Subscription toDomain(SubscriptionInfo newSubscription) {
|
||||
return Subscription.builder()
|
||||
.customerId(newSubscription.customerId())
|
||||
.duration(newSubscription.duration())
|
||||
.paymentMethod(newSubscription.paymentMethod())
|
||||
.build();
|
||||
}
|
||||
|
||||
public static SubscriptionDTO toDTO(Subscription subscription) {
|
||||
return SubscriptionDTO.builder()
|
||||
.id(subscription.getId())
|
||||
.customerId(subscription.getCustomerId())
|
||||
.duration(subscription.getDuration())
|
||||
.paymentMethod(subscription.getPaymentMethod())
|
||||
.debutDate(subscription.getDebutDate())
|
||||
.build();
|
||||
}
|
||||
|
||||
}
|
@@ -0,0 +1,24 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.subscription.entity;
|
||||
|
||||
import java.util.UUID;
|
||||
import lombok.Builder;
|
||||
import lombok.Getter;
|
||||
import java.time.LocalDate;
|
||||
|
||||
@Builder
|
||||
@Getter
|
||||
public class Subscription {
|
||||
private UUID id;
|
||||
private UUID customerId;
|
||||
private Integer duration;
|
||||
private String paymentMethod;
|
||||
private String debutDate;
|
||||
|
||||
public void setRandomUUID() {
|
||||
this.id = UUID.randomUUID();
|
||||
}
|
||||
|
||||
public void setDebutDate(String debutDate) {
|
||||
this.debutDate = LocalDate.now().toString();
|
||||
}
|
||||
}
|
@@ -0,0 +1,8 @@
|
||||
package fr.iut_fbleau.but3.dev62.mylibrary.subscription.exception;
|
||||
|
||||
public class NotValidSubscriptionException extends Exception {
|
||||
|
||||
public NotValidSubscriptionException(String message) {
|
||||
super(message);
|
||||
}
|
||||
}
|
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user