From aa9e0bcc722bfaf71735736a6b2dc1be7668a219 Mon Sep 17 00:00:00 2001 From: Andersen Date: Tue, 14 Nov 2023 14:55:00 +0300 Subject: [PATCH] =?UTF-8?q?refactor:=20=D1=81=D0=BE=D0=B7=D0=B4=D0=B0?= =?UTF-8?q?=D1=82=D1=8C=20shema.sql,=20exception=20UnavailableStateExcepti?= =?UTF-8?q?on.java,=20UnavailableItemException.java,=20PostWithoutBookingE?= =?UTF-8?q?xception.java,=20BookingServiceImpl,=20BookingController?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 14 ++ .../shareit/aspects/LoggingAspect.java | 16 +- .../ru/practicum/shareit/booking/Booking.java | 19 +- .../shareit/booking/BookingController.java | 47 ++++- .../booking/dao/BookingRepository.java | 17 ++ .../booking/service/BookingService.java | 17 ++ .../booking/service/BookingServiceImpl.java | 186 ++++++++++++++++++ .../shareit/constants/MyConstants.java | 24 +-- .../shareit/exception/ErrorHandler.java | 17 ++ .../PostWithoutBookingException.java | 8 + .../exception/UnavailableItemException.java | 7 + .../exception/UnavailableStateException.java | 7 + .../ru/practicum/shareit/item/Comment.java | 31 +++ .../shareit/item/ItemController.java | 22 ++- .../shareit/item/dao/CommentRepository.java | 19 ++ .../practicum/shareit/item/dao/ItemDao.java | 21 -- .../shareit/item/dao/ItemDaoImpl.java | 97 --------- .../shareit/item/dao/ItemRepository.java | 15 ++ .../practicum/shareit/item/dto/ItemDto.java | 7 + .../ru/practicum/shareit/item/model/Item.java | 26 +++ .../shareit/item/service/ItemService.java | 13 +- .../shareit/item/service/ItemServiceImpl.java | 167 ++++++++++++++-- .../java/ru/practicum/shareit/user/User.java | 8 + .../shareit/user/UserController.java | 16 +- .../practicum/shareit/user/dao/UserDao.java | 23 --- .../shareit/user/dao/UserDaoImpl.java | 99 ---------- .../shareit/user/dao/UserRepository.java | 9 + .../shareit/user/service/UserService.java | 11 +- .../shareit/user/service/UserServiceImpl.java | 61 ++++-- src/main/resources/application.properties | 29 ++- src/main/resources/schema.sql | 40 ++++ .../ru/practicum/shareit/ShareItTests.java | 6 +- 32 files changed, 766 insertions(+), 333 deletions(-) create mode 100644 src/main/java/ru/practicum/shareit/booking/dao/BookingRepository.java create mode 100644 src/main/java/ru/practicum/shareit/booking/service/BookingService.java create mode 100644 src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java create mode 100644 src/main/java/ru/practicum/shareit/exception/PostWithoutBookingException.java create mode 100644 src/main/java/ru/practicum/shareit/exception/UnavailableItemException.java create mode 100644 src/main/java/ru/practicum/shareit/exception/UnavailableStateException.java create mode 100644 src/main/java/ru/practicum/shareit/item/Comment.java create mode 100644 src/main/java/ru/practicum/shareit/item/dao/CommentRepository.java delete mode 100644 src/main/java/ru/practicum/shareit/item/dao/ItemDao.java delete mode 100644 src/main/java/ru/practicum/shareit/item/dao/ItemDaoImpl.java create mode 100644 src/main/java/ru/practicum/shareit/item/dao/ItemRepository.java delete mode 100644 src/main/java/ru/practicum/shareit/user/dao/UserDao.java delete mode 100644 src/main/java/ru/practicum/shareit/user/dao/UserDaoImpl.java create mode 100644 src/main/java/ru/practicum/shareit/user/dao/UserRepository.java create mode 100644 src/main/resources/schema.sql diff --git a/pom.xml b/pom.xml index ed574e8..8b7cc74 100644 --- a/pom.xml +++ b/pom.xml @@ -64,6 +64,20 @@ org.springframework.boot spring-boot-starter-aop + + + + org.springframework.boot + spring-boot-starter-data-jpa + + + + + org.postgresql + postgresql + 42.6.0 + + diff --git a/src/main/java/ru/practicum/shareit/aspects/LoggingAspect.java b/src/main/java/ru/practicum/shareit/aspects/LoggingAspect.java index a056794..dad788a 100644 --- a/src/main/java/ru/practicum/shareit/aspects/LoggingAspect.java +++ b/src/main/java/ru/practicum/shareit/aspects/LoggingAspect.java @@ -9,6 +9,7 @@ import org.springframework.stereotype.Component; import ru.practicum.shareit.constants.MyConstants; import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.user.User; import ru.practicum.shareit.user.dto.UserDto; import java.util.List; @@ -17,10 +18,9 @@ @Aspect @Slf4j public class LoggingAspect { - - @AfterReturning(pointcut = MyConstants.ADD_USER_POINTCUT, returning = "userDto") - public void afterReturningAddUserAdvices(UserDto userDto) { - log.info("Пользователь " + userDto + "добавлен."); + @AfterReturning(pointcut = MyConstants.ADD_USER_POINTCUT, returning = "user") + public void afterReturningAddUserAdvices(User user) { + log.info("Пользователь " + user + "добавлен."); } @AfterThrowing(pointcut = MyConstants.ADD_USER_POINTCUT, throwing = "exception") @@ -29,9 +29,9 @@ public void afterThrowingAddUserAdvices(JoinPoint joinPoint, Throwable exception log.info("В методе " + methodSignature.getMethod() + " выброшено исключение: " + exception.getMessage()); } - @AfterReturning(pointcut = MyConstants.REMOVE_USER_POINTCUT, returning = "response") - public void afterReturningRemoveUserByIdAdvices(String response) { - log.info(response); + @AfterReturning(pointcut = MyConstants.REMOVE_USER_POINTCUT) + public void afterReturningRemoveUserByIdAdvices() { + log.info("Пользователь удалён."); } @AfterThrowing(pointcut = MyConstants.REMOVE_USER_POINTCUT, throwing = "exception") @@ -42,7 +42,7 @@ public void afterThrowingRemoveUserByIdAdvices(JoinPoint joinPoint, Throwable ex @AfterReturning(pointcut = MyConstants.GET_ALL_USERS_POINTCUT) public void afterReturningGetAllUserAdvice() { - log.info("Список всех пользователей получен."); + log.info("Получен список всех пользователей."); } @AfterReturning(pointcut = MyConstants.UPDATE_USER_POINTCUT, returning = "userDto") diff --git a/src/main/java/ru/practicum/shareit/booking/Booking.java b/src/main/java/ru/practicum/shareit/booking/Booking.java index 8d8aa11..150699d 100644 --- a/src/main/java/ru/practicum/shareit/booking/Booking.java +++ b/src/main/java/ru/practicum/shareit/booking/Booking.java @@ -6,17 +6,34 @@ import ru.practicum.shareit.item.model.Item; import ru.practicum.shareit.user.User; +import javax.persistence.*; import java.time.LocalDateTime; @Data @EqualsAndHashCode @ToString +@Entity +@Table(name = "bookings") public class Booking { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") private int id; + @Column(name = "start_time") private LocalDateTime start; + @Column(name = "end_time") private LocalDateTime end; + @Transient + private int itemId; + @Enumerated(value = EnumType.STRING) + private BookingStatus status; + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "item_id") private Item item; + @ManyToOne(cascade = CascadeType.ALL) + @JoinColumn(name = "user_id") private User booker; - private BookingStatus status; + @Transient + int bookerId; } diff --git a/src/main/java/ru/practicum/shareit/booking/BookingController.java b/src/main/java/ru/practicum/shareit/booking/BookingController.java index b94493d..6343e6d 100644 --- a/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ b/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -1,12 +1,49 @@ package ru.practicum.shareit.booking; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.booking.service.BookingService; +import ru.practicum.shareit.user.service.UserService; + +import java.util.List; -/** - * TODO Sprint add-bookings. - */ @RestController @RequestMapping(path = "/bookings") public class BookingController { + + @Autowired + private BookingService bookingService; + @Autowired + private UserService userService; + + @PostMapping + public Booking addBooking(@RequestBody Booking booking, @RequestHeader("X-Sharer-User-Id") int userId) { + userService.getUserById(userId); + return bookingService.addBooking(booking, userId); + } + + @PatchMapping("/{bookingId}") + public Booking confirmationOrRejectionBooking(@PathVariable int bookingId, + @RequestHeader("X-Sharer-User-Id") int userId, + @RequestParam(name = "approved") String bookingStatus) { + return bookingService.confirmationOrRejectionBooking(bookingId, userId, bookingStatus); + } + + @GetMapping("/{bookingId}") + public Booking getBookingById(@RequestHeader("X-Sharer-User-Id") int userId, @PathVariable int bookingId) { + return bookingService.getBookingById(bookingId, userId); + } + + @GetMapping() + public List getAllBookingCurrentUser(@RequestHeader("X-Sharer-User-Id") int userId, + @RequestParam(name = "state", defaultValue = "ALL") String state) { + return bookingService.getAllBookingCurrentUser(userId, state); + } + + @GetMapping("/owner") + public List getAllBookingCurrentOwner(@RequestHeader("X-Sharer-User-Id") int userId, + @RequestParam(name = "state", defaultValue = "ALL") String state) { + return bookingService.getAllBookingCurrentOwner(userId, state); + } + } diff --git a/src/main/java/ru/practicum/shareit/booking/dao/BookingRepository.java b/src/main/java/ru/practicum/shareit/booking/dao/BookingRepository.java new file mode 100644 index 0000000..93c9d87 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/dao/BookingRepository.java @@ -0,0 +1,17 @@ +package ru.practicum.shareit.booking.dao; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.user.User; + +import java.util.List; + +@Repository +public interface BookingRepository extends JpaRepository { + List findByBookerEquals(User user); + + List findByItemEquals(Item item); +} + diff --git a/src/main/java/ru/practicum/shareit/booking/service/BookingService.java b/src/main/java/ru/practicum/shareit/booking/service/BookingService.java new file mode 100644 index 0000000..89ae1f8 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/service/BookingService.java @@ -0,0 +1,17 @@ +package ru.practicum.shareit.booking.service; + +import ru.practicum.shareit.booking.Booking; + +import java.util.List; + +public interface BookingService { + Booking addBooking(Booking booking, int bookerId); + + Booking getBookingById(int bookingId, int userId); + + List getAllBookingCurrentUser(int userId, String state); + + List getAllBookingCurrentOwner(int userId, String state); + + Booking confirmationOrRejectionBooking(int bookingId, int userId, String bookingStatus); +} diff --git a/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java b/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java new file mode 100644 index 0000000..c57bbd8 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java @@ -0,0 +1,186 @@ +package ru.practicum.shareit.booking.service; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.booking.BookingStatus; +import ru.practicum.shareit.booking.dao.BookingRepository; +import ru.practicum.shareit.exception.NoDataFoundException; +import ru.practicum.shareit.exception.UnavailableItemException; +import ru.practicum.shareit.exception.UnavailableStateException; +import ru.practicum.shareit.item.dao.ItemRepository; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.dao.UserRepository; + +import java.time.LocalDateTime; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; +import java.util.stream.Collectors; + +@Service +public class BookingServiceImpl implements BookingService { + + @Autowired + private BookingRepository bookingRepository; + @Autowired + private UserRepository userRepository; + @Autowired + private ItemRepository itemRepository; + + @Override + public Booking addBooking(Booking booking, int bookerId) { + Optional optional = itemRepository.findById(booking.getItemId()); + Optional optionalUser = userRepository.findById(bookerId); + if (optional.isPresent() && optionalUser.isPresent()) { + Item item = optional.get(); + User user = optionalUser.get(); + booking.setBooker(user); + booking.setItem(item); + item.getBookingList().add(booking); + LocalDateTime start = booking.getStart(); + LocalDateTime end = booking.getEnd(); + if (item.getOwnerId() == bookerId) { + throw new NoDataFoundException("Владелец вещи не может разместить заказ на свою же вещь."); + } + if (item.getAvailable().toString().equals("true") && start != null && end != null && start.isBefore(end) + && end.isAfter(start) && !start.isBefore(LocalDateTime.now())) { + booking.setStatus(BookingStatus.WAITING); + } else { + throw new UnavailableItemException(item.getName() + " статус " + item.getAvailable()); + } + } else { + throw new NoDataFoundException("Item с id: " + booking.getItemId() + " не найдена."); + } + return bookingRepository.save(booking); + } + + @Override + public Booking getBookingById(int bookingId, int userId) { + Optional optional = bookingRepository.findById(bookingId); + if (optional.isPresent()) { + Booking booking = optional.get(); + if (booking.getBooker().getId() == userId || booking.getItem().getOwnerId() == userId) { + return optional.get(); + } else { + throw new NoDataFoundException("Booking с id: " + bookingId + " не найден."); + } + } else { + throw new NoDataFoundException("Booking с id: " + bookingId + " не найден."); + } + } + + @Override + public List getAllBookingCurrentUser(int userId, String state) { + Optional optionalUser = userRepository.findById(userId); + if (optionalUser.isPresent()) { + switch (state) { + case "ALL": + return bookingRepository.findByBookerEquals(optionalUser.get()).stream().sorted((e1, e2) -> + e2.getStart().compareTo(e1.getStart())).collect(Collectors.toList()); + case "REJECTED": + return bookingRepository.findByBookerEquals(optionalUser.get()).stream() + .filter(e -> e.getStatus().toString().equals("REJECTED")).sorted((e1, e2) -> + e2.getStart().compareTo(e1.getStart())).collect(Collectors.toList()); + case "WAITING": + return bookingRepository.findByBookerEquals(optionalUser.get()).stream() + .filter(e -> e.getStatus().toString().equals("WAITING")).sorted((e1, e2) -> + e2.getStart().compareTo(e1.getStart())).collect(Collectors.toList()); + case "FUTURE": + return bookingRepository.findByBookerEquals(optionalUser.get()).stream() + .filter(e -> e.getStart().isAfter(LocalDateTime.now())).sorted((e1, e2) -> + e2.getStart().compareTo(e1.getStart())).collect(Collectors.toList()); + case "PAST": + return bookingRepository.findByBookerEquals(optionalUser.get()).stream() + .filter(e -> e.getEnd().isBefore(LocalDateTime.now())).sorted((e1, e2) -> + e2.getStart().compareTo(e1.getStart())).collect(Collectors.toList()); + case "CURRENT": + return bookingRepository.findByBookerEquals(optionalUser.get()).stream() + .filter(e -> e.getStart().isBefore(LocalDateTime.now()) + && e.getEnd().isAfter(LocalDateTime.now())).sorted((e1, e2) -> + e2.getStart().compareTo(e1.getStart())).collect(Collectors.toList()); + default: + throw new UnavailableStateException("Unknown state: UNSUPPORTED_STATUS"); + } + } else { + throw new NoDataFoundException("Пользователь с id:" + userId + " не найден."); + } + } + + @Override + public List getAllBookingCurrentOwner(int userId, String state) { //TODO + Optional optionalUser = userRepository.findById(userId); + List ownerBookingList = new ArrayList<>(); + if (optionalUser.isPresent()) { + List optionalItem = itemRepository.findByOwnerIdEquals(userId); + for (Item item : optionalItem) { + ownerBookingList.addAll(bookingRepository.findByItemEquals(item)); + } + switch (state) { + case "ALL": + return ownerBookingList.stream().sorted((e1, e2) -> e2.getStart().compareTo(e1.getStart())) + .collect(Collectors.toList()); + case "REJECTED": + return ownerBookingList.stream().filter(e -> e.getStatus().toString().equals("REJECTED")) + .sorted((e1, e2) -> e2.getStart().compareTo(e1.getStart())) + .collect(Collectors.toList()); + case "WAITING": + return ownerBookingList.stream().filter(e -> e.getStatus().toString().equals("WAITING")) + .sorted((e1, e2) -> e2.getStart().compareTo(e1.getStart())) + .collect(Collectors.toList()); + case "FUTURE": + return ownerBookingList.stream().filter(e -> e.getStart().isAfter(LocalDateTime.now())) + .sorted((e1, e2) -> e2.getStart().compareTo(e1.getStart())) + .collect(Collectors.toList()); + case "PAST": + return ownerBookingList.stream().filter(e -> e.getEnd().isBefore(LocalDateTime.now())) + .sorted((e1, e2) -> e2.getStart().compareTo(e1.getStart())) + .collect(Collectors.toList()); + case "CURRENT": + return ownerBookingList.stream().filter(e -> e.getStart().isBefore(LocalDateTime.now()) + && e.getEnd().isAfter(LocalDateTime.now())) + .sorted((e1, e2) -> e2.getStart().compareTo(e1.getStart())) + .collect(Collectors.toList()); + default: + throw new UnavailableItemException("Unknown state: UNSUPPORTED_STATUS"); + } + } else { + throw new NoDataFoundException("Пользователь с id:" + userId + " не найден."); + } + } + + public Booking getBookingByOwner(int bookingId, int userId) { + Optional optional = bookingRepository.findById(bookingId); + Optional optionalUser = userRepository.findById(userId); + if (optional.isPresent() && optionalUser.isPresent()) { + if (optionalUser.get().getId() == optional.get().getItem().getOwnerId()) { + return optional.get(); + } else { + throw new NoDataFoundException("Подтверждение или отклонение бронирования может осуществлять" + + "только владелец вещи."); + } + } else { + throw new NoDataFoundException("Пользователя или владельца вещи не существует."); + } + } + + @Override + public Booking confirmationOrRejectionBooking(int bookingId, int userId, String bookingStatus) { + Booking booking = getBookingByOwner(bookingId, userId); + switch (bookingStatus) { + case "true": + if (booking.getStatus().toString().equals("APPROVED")) { + throw new UnavailableItemException("Статус уже был присвоен."); + } + booking.setStatus(BookingStatus.APPROVED); + bookingRepository.save(booking); + return booking; + case "false": + booking.setStatus(BookingStatus.REJECTED); + bookingRepository.save(booking); + return booking; + } + return booking; + } +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/constants/MyConstants.java b/src/main/java/ru/practicum/shareit/constants/MyConstants.java index 3ca2b2b..7f07223 100644 --- a/src/main/java/ru/practicum/shareit/constants/MyConstants.java +++ b/src/main/java/ru/practicum/shareit/constants/MyConstants.java @@ -1,29 +1,29 @@ package ru.practicum.shareit.constants; public class MyConstants { - public static final String ADD_USER_POINTCUT = "execution(public ru.practicum.shareit.user.dto.UserDto ru.practicum.shareit.user.dao.UserDaoImpl.addUser" + - "(ru.practicum.shareit.user.User))"; - public static final String REMOVE_USER_POINTCUT = "execution(public String ru.practicum.shareit.user.dao.UserDaoImpl.removeUserById" + - "(*))"; + public static final String ADD_USER_POINTCUT = "execution(public ru.practicum.shareit.user.User " + + "ru.practicum.shareit.user.service.UserServiceImpl.addUser(ru.practicum.shareit.user.User))"; + public static final String REMOVE_USER_POINTCUT = "execution(public void " + + "ru.practicum.shareit.user.service.UserServiceImpl.removeUserById(*))"; - public static final String GET_ALL_USERS_POINTCUT = "execution(public java.util.List " + - "ru.practicum.shareit.user.dao.UserDaoImpl.getAllUsers())"; + public static final String GET_ALL_USERS_POINTCUT = "execution(public java.util.List " + + "ru.practicum.shareit.user.service.UserServiceImpl.getAllUsers())"; public static final String UPDATE_USER_POINTCUT = "execution(public ru.practicum.shareit.user.dto.UserDto " + - "ru.practicum.shareit.user.dao.UserDaoImpl.UpdateUser(..))"; + "ru.practicum.shareit.user.service.UserServiceImpl.UpdateUser(..))"; public static final String ADD_ITEM_POINTCUT = "execution(public ru.practicum.shareit.item.dto.ItemDto " + - "ru.practicum.shareit.item.dao.ItemDaoImpl.addItem(..))"; + "ru.practicum.shareit.item.service.ItemServiceImpl.addItem(..))"; public static final String UPDATE_ITEM_POINTCUT = "execution(public ru.practicum.shareit.item.dto.ItemDto " + - "ru.practicum.shareit.item.dao.ItemDaoImpl.updateItem(..))"; + "ru.practicum.shareit.item.service.ItemServiceImpl.updateItem(..))"; public static final String GET_ITEM_BY_ID_POINTCUT = "execution(public ru.practicum.shareit.item.dto.ItemDto " + - "ru.practicum.shareit.item.dao.ItemDaoImpl.getItemById(..))"; + "ru.practicum.shareit.item.service.ItemServiceImpl.getItemDtoById(..))"; public static final String GET_ALL_ITEM_POINTCUT = "execution(public java.util.List " + - "ru.practicum.shareit.item.dao.ItemDaoImpl.getAllItemForOwner(..))"; + "ru.practicum.shareit.item.service.ItemServiceImpl.getAllItemForOwner(..))"; public static final String SEARCH_ITEM_POINTCUT = "execution(public java.util.List " + - "ru.practicum.shareit.item.dao.ItemDaoImpl.searchItem(..))"; + "ru.practicum.shareit.item.service.ItemServiceImpl.searchItem(..))"; } diff --git a/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java index 051c25f..452c568 100644 --- a/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java +++ b/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java @@ -20,4 +20,21 @@ public ErrorResponse emailDuplicate(EmailDuplicateException e) { return new ErrorResponse(e.getMessage(), "Данный email уже существует."); } + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse unavailableItem(UnavailableItemException e) { + return new ErrorResponse(e.getMessage(), "Данная вещь не доступна для бронирования."); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse unavailableState(UnavailableStateException e) { + return new ErrorResponse(e.getMessage(), "Не верный статус."); + } + + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse postWithoutBooking(PostWithoutBookingException e) { + return new ErrorResponse(e.getMessage(), "Пользователь не может оставить комментарий к данной вещи."); + } } diff --git a/src/main/java/ru/practicum/shareit/exception/PostWithoutBookingException.java b/src/main/java/ru/practicum/shareit/exception/PostWithoutBookingException.java new file mode 100644 index 0000000..93485bb --- /dev/null +++ b/src/main/java/ru/practicum/shareit/exception/PostWithoutBookingException.java @@ -0,0 +1,8 @@ +package ru.practicum.shareit.exception; + +public class PostWithoutBookingException extends RuntimeException { + + public PostWithoutBookingException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/practicum/shareit/exception/UnavailableItemException.java b/src/main/java/ru/practicum/shareit/exception/UnavailableItemException.java new file mode 100644 index 0000000..98a4d11 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/exception/UnavailableItemException.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.exception; + +public class UnavailableItemException extends RuntimeException { + public UnavailableItemException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/practicum/shareit/exception/UnavailableStateException.java b/src/main/java/ru/practicum/shareit/exception/UnavailableStateException.java new file mode 100644 index 0000000..84e0fbb --- /dev/null +++ b/src/main/java/ru/practicum/shareit/exception/UnavailableStateException.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.exception; + +public class UnavailableStateException extends RuntimeException { + public UnavailableStateException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/practicum/shareit/item/Comment.java b/src/main/java/ru/practicum/shareit/item/Comment.java new file mode 100644 index 0000000..f396ef7 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/Comment.java @@ -0,0 +1,31 @@ +package ru.practicum.shareit.item; + +import lombok.Data; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.user.User; + +import javax.persistence.*; +import java.time.LocalDateTime; + +@Entity +@Table(name = "comments") +@Data +public class Comment { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private int id; + @Column(name = "text") + private String text; + @ManyToOne + @JoinColumn(name = "item_id") + private Item item; + @ManyToOne + @JoinColumn(name = "author") + private User author; + @Column(name = "created") + private LocalDateTime created; + @Transient + String authorName; +} diff --git a/src/main/java/ru/practicum/shareit/item/ItemController.java b/src/main/java/ru/practicum/shareit/item/ItemController.java index 51997c7..f1e3014 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemController.java +++ b/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -17,14 +17,14 @@ public class ItemController { private ItemService itemService; @PostMapping - public ItemDto addItem(@RequestHeader("X-Sharer-User-Id") int userId, - @Valid @RequestBody Item item) { + public Item addItem(@RequestHeader("X-Sharer-User-Id") int userId, + @Valid @RequestBody Item item) { return itemService.addItem(userId, item); } @GetMapping("/{id}") - public ItemDto getItemById(@PathVariable(name = "id") int itemId) { - return itemService.getItemDtoById(itemId); + public ItemDto getItemById(@PathVariable(name = "id") int itemId, @RequestHeader("X-Sharer-User-Id") int userId) { + return itemService.getItemByIdDto(itemId, userId); } @GetMapping @@ -33,14 +33,20 @@ public List getAllItemForOwner(@RequestHeader("X-Sharer-User-Id") int u } @PatchMapping("/{id}") - public ItemDto updateItem(@RequestHeader("X-Sharer-User-Id") int userId, - @PathVariable(name = "id") int itemId, - @RequestBody Map fields) { + public Item updateItem(@RequestHeader("X-Sharer-User-Id") int userId, + @PathVariable(name = "id") int itemId, + @RequestBody Map fields) { return itemService.updateItem(userId, itemId, fields); } @GetMapping("/search") - public List searchItem(@RequestParam(name = "text") String request) { + public List searchItem(@RequestParam(name = "text") String request) { return itemService.searchItem(request); } + + @PostMapping("/{itemId}/comment") + public Comment addComment(@PathVariable int itemId, @RequestHeader("X-Sharer-User-Id") + int userId, @RequestBody Comment comment) { + return itemService.addComment(itemId, userId, comment); + } } diff --git a/src/main/java/ru/practicum/shareit/item/dao/CommentRepository.java b/src/main/java/ru/practicum/shareit/item/dao/CommentRepository.java new file mode 100644 index 0000000..27e1953 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/dao/CommentRepository.java @@ -0,0 +1,19 @@ +package ru.practicum.shareit.item.dao; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import ru.practicum.shareit.item.Comment; + +import java.util.List; + +public interface CommentRepository extends JpaRepository { + @Query(value = "select * " + + "from comments " + + "where author = ?1 AND item_id = ?2", nativeQuery = true) + Comment getUserCommentByItem(int userId, int itemId); + + @Query(value = "select * " + + "from comments " + + "where item_id = ?1", nativeQuery = true) + List getCommentByItem(int itemId); +} diff --git a/src/main/java/ru/practicum/shareit/item/dao/ItemDao.java b/src/main/java/ru/practicum/shareit/item/dao/ItemDao.java deleted file mode 100644 index cf00efc..0000000 --- a/src/main/java/ru/practicum/shareit/item/dao/ItemDao.java +++ /dev/null @@ -1,21 +0,0 @@ -package ru.practicum.shareit.item.dao; - -import ru.practicum.shareit.item.dto.ItemDto; -import ru.practicum.shareit.item.model.Item; - -import java.util.List; -import java.util.Map; - -public interface ItemDao { - ItemDto addItem(int userId, Item item); - - ItemDto updateItem(int userId, int itemId, Map fields); - - Item getItemById(int itemId); - - ItemDto getItemDtoById(int itemId); - - List getAllItemForOwner(int ownerId); - - List searchItem(String request); -} diff --git a/src/main/java/ru/practicum/shareit/item/dao/ItemDaoImpl.java b/src/main/java/ru/practicum/shareit/item/dao/ItemDaoImpl.java deleted file mode 100644 index 5033d25..0000000 --- a/src/main/java/ru/practicum/shareit/item/dao/ItemDaoImpl.java +++ /dev/null @@ -1,97 +0,0 @@ -package ru.practicum.shareit.item.dao; - -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Repository; -import org.springframework.util.ReflectionUtils; -import ru.practicum.shareit.exception.NoDataFoundException; -import ru.practicum.shareit.item.ItemMapper; -import ru.practicum.shareit.item.dto.ItemDto; -import ru.practicum.shareit.item.model.Item; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; -import java.util.regex.Pattern; -import java.util.stream.Collectors; - -@Repository -public class ItemDaoImpl implements ItemDao { - private final Map itemMap = new HashMap<>(); - @Autowired - private ItemMapper itemMapper; - private int count = 0; - - @Override - public ItemDto addItem(int userId, Item item) { - item.setOwnerId(userId); - item.setId(++count); - ItemDto itemDto = itemMapper.itemDto(item); - itemMap.put(item.getId(), item); - return itemDto; - } - - @Override - public ItemDto updateItem(int userId, int itemId, Map fields) { - Item item = getItemById(itemId); - if (item.getOwnerId() == userId) { - fields.forEach((key, value) -> { - Field field = ReflectionUtils.findField(Item.class, (String) key); - field.setAccessible(true); - if (!((String) key).equalsIgnoreCase("id") - && !((String) key).equalsIgnoreCase("ownerId")) { - ReflectionUtils.setField(field, item, value); - } - }); - return itemMapper.itemDto(item); - } else { - throw new NoDataFoundException("Пользователь с id:" + userId - + " не является владельцем данной вещи. Изменения не сохранены."); - } - } - - @Override - public Item getItemById(int itemId) { - if (itemMap.containsKey(itemId)) { - return itemMap.get(itemId); - } else { - throw new NoDataFoundException("Item с id: " + itemId + " не найдена."); - } - } - - @Override - public ItemDto getItemDtoById(int itemId) { - if (itemMap.containsKey(itemId)) { - return itemMapper.itemDto(itemMap.get(itemId)); - } else { - throw new NoDataFoundException("Item с id: " + itemId + " не найдена."); - } - } - - @Override - public List getAllItemForOwner(int ownerId) { - List itemDtoList = new ArrayList<>(); - for (Item item : itemMap.values()) { - if (item.getOwnerId() == ownerId) { - itemDtoList.add(itemMapper.itemDto(item)); - } - } - return itemDtoList; - } - - @Override - public List searchItem(String request) { - if (request.isBlank()) { - return new ArrayList<>(); - } - List listItem = new ArrayList<>(); - Pattern pattern = Pattern.compile(request.toLowerCase()); - for (Item item : itemMap.values().stream().filter(item -> item.getAvailable() == true - && pattern.matcher((item.getName() + " " + item.getDescription()).toLowerCase()).find()) - .collect(Collectors.toList())) { - listItem.add(itemMapper.itemDto(item)); - } - return listItem; - } -} diff --git a/src/main/java/ru/practicum/shareit/item/dao/ItemRepository.java b/src/main/java/ru/practicum/shareit/item/dao/ItemRepository.java new file mode 100644 index 0000000..aca2c88 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/dao/ItemRepository.java @@ -0,0 +1,15 @@ +package ru.practicum.shareit.item.dao; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.practicum.shareit.item.model.Item; + +import java.util.List; + +public interface ItemRepository extends JpaRepository { + + List findByOwnerIdEquals(int ownerId); + + List findByNameIgnoreCaseContaining(String request); + + List findByDescriptionIgnoreCaseContaining(String request); +} diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java b/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java index 0ac7296..e193e16 100644 --- a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java +++ b/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java @@ -4,9 +4,13 @@ import lombok.EqualsAndHashCode; import lombok.ToString; import lombok.experimental.Accessors; +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.item.Comment; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; @Data @Accessors(chain = true) @@ -24,4 +28,7 @@ public class ItemDto { private Boolean available; private int ownerId; private String request; + private Booking lastBooking; + private Booking nextBooking; + private List comments = new ArrayList<>(); } diff --git a/src/main/java/ru/practicum/shareit/item/model/Item.java b/src/main/java/ru/practicum/shareit/item/model/Item.java index 292918d..ca8bf0c 100644 --- a/src/main/java/ru/practicum/shareit/item/model/Item.java +++ b/src/main/java/ru/practicum/shareit/item/model/Item.java @@ -1,25 +1,51 @@ package ru.practicum.shareit.item.model; +import com.fasterxml.jackson.annotation.JsonIgnore; import lombok.Data; import lombok.EqualsAndHashCode; import lombok.ToString; +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.item.Comment; +import javax.persistence.*; import javax.validation.constraints.NotBlank; import javax.validation.constraints.NotNull; +import java.util.ArrayList; +import java.util.List; +@Entity @Data @EqualsAndHashCode @ToString +@Table(name = "items") public class Item { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") private int id; @NotBlank(message = "Поле \'name\' не может быть пустым.") @NotNull(message = "Поле \'name\' не может быть пустым.") + @Column(name = "name") private String name; @NotBlank(message = "Поле \'description\' не может быть пустым.") + @Column(name = "description") private String description; @NotNull(message = "Поле \'available\' не может быть пустым.") + @Column(name = "available") private Boolean available; + @Column(name = "owner_id") private int ownerId; + @Column(name = "request") private String request; + @JsonIgnore + @OneToMany(mappedBy = "item") + List bookingList = new ArrayList<>(); + @JsonIgnore + @OneToMany(mappedBy = "item") + private List comments = new ArrayList<>(); + @Transient + private Booking lastBooking; + @Transient + private Booking nextBooking; } diff --git a/src/main/java/ru/practicum/shareit/item/service/ItemService.java b/src/main/java/ru/practicum/shareit/item/service/ItemService.java index 3c29d85..3b8a8fc 100644 --- a/src/main/java/ru/practicum/shareit/item/service/ItemService.java +++ b/src/main/java/ru/practicum/shareit/item/service/ItemService.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.item.service; +import ru.practicum.shareit.item.Comment; import ru.practicum.shareit.item.dto.ItemDto; import ru.practicum.shareit.item.model.Item; @@ -7,13 +8,17 @@ import java.util.Map; public interface ItemService { - ItemDto addItem(int userId, Item item); + Item addItem(int userId, Item item); - ItemDto updateItem(int userId, int itemId, Map fields); + Item updateItem(int userId, int itemId, Map fields); - ItemDto getItemDtoById(int itemId); + ItemDto getItemByIdDto(int itemId, int userId); + + Item getItemById(int itemId, int userId); List getAllItemForOwner(int ownerId); - List searchItem(String request); + List searchItem(String request); + + Comment addComment(int itemId, int userId, Comment comment); } diff --git a/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java b/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java index 8956af7..200be13 100644 --- a/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java +++ b/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java @@ -2,44 +2,179 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import ru.practicum.shareit.item.dao.ItemDao; +import org.springframework.util.ReflectionUtils; +import ru.practicum.shareit.booking.Booking; +import ru.practicum.shareit.booking.dao.BookingRepository; +import ru.practicum.shareit.exception.NoDataFoundException; +import ru.practicum.shareit.exception.PostWithoutBookingException; +import ru.practicum.shareit.exception.UnavailableItemException; +import ru.practicum.shareit.item.Comment; +import ru.practicum.shareit.item.ItemMapper; +import ru.practicum.shareit.item.dao.CommentRepository; +import ru.practicum.shareit.item.dao.ItemRepository; import ru.practicum.shareit.item.dto.ItemDto; import ru.practicum.shareit.item.model.Item; -import ru.practicum.shareit.user.dao.UserDao; +import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.dao.UserRepository; -import java.util.List; -import java.util.Map; +import java.lang.reflect.Field; +import java.time.LocalDateTime; +import java.util.*; +import java.util.stream.Collectors; @Service public class ItemServiceImpl implements ItemService { + + @Autowired + private ItemRepository itemRepository; + @Autowired + private UserRepository userRepository; @Autowired - private ItemDao itemDao; + private BookingRepository bookingRepository; @Autowired - private UserDao userDao; + private CommentRepository commentRepository; + @Autowired + private ItemMapper itemMapper; + + @Override + public Item addItem(int userId, Item item) { + if (userRepository.findById(userId).isPresent()) { + item.setOwnerId(userId); + return itemRepository.save(item); + } else { + throw new NoDataFoundException("Пользователь с id:" + userId + " не найден."); + } + } @Override - public ItemDto addItem(int userId, Item item) { - userDao.getUserById(userId); - return itemDao.addItem(userId, item); + public Item updateItem(int userId, int itemId, Map fields) { + Item item = getItemById(itemId, userId); + if (item.getOwnerId() == userId) { + fields.forEach((key, value) -> { + Field field = ReflectionUtils.findField(Item.class, (String) key); + field.setAccessible(true); + if (!((String) key).equalsIgnoreCase("id") + && !((String) key).equalsIgnoreCase("ownerId")) { + ReflectionUtils.setField(field, item, value); + } + }); + return itemRepository.save(item); + } else { + throw new NoDataFoundException("Пользователь с id:" + userId + + " не является владельцем данной вещи. Изменения не сохранены."); + } } @Override - public ItemDto updateItem(int userId, int itemId, Map fields) { - return itemDao.updateItem(userId, itemId, fields); + public ItemDto getItemByIdDto(int itemId, int userId) { //TODO + Optional optional = itemRepository.findById(itemId); + List commentsList = commentRepository.getCommentByItem(itemId); + for (Comment comment : commentsList) { + comment.setAuthorName(comment.getAuthor().getName()); + } + if (optional.isPresent()) { + Item item = optional.get(); + ItemDto itemDto = itemMapper.itemDto(optional.get()); + if (itemDto.getOwnerId() == userId) { + itemDto = addBookingToItem(item, itemDto); + } + itemDto.setComments(commentsList); + return itemDto; + } else { + throw new NoDataFoundException("Item с id: " + itemId + " не найдена."); + } + } + + private ItemDto addBookingToItem(Item item, ItemDto itemDto) { + if (!item.getBookingList().isEmpty()) { + List bookingListNext = item.getBookingList().stream() + .filter(e -> e.getStart().isAfter(LocalDateTime.now())) + .sorted(Comparator.comparing(Booking::getStart)).collect(Collectors.toList()); + List bookingListLast = item.getBookingList().stream() + .filter(e -> e.getEnd().isBefore(LocalDateTime.now())) + .sorted((e1, e2) -> e2.getEnd().compareTo(e1.getEnd())).collect(Collectors.toList()); + List bookingListLastAfterNow = item.getBookingList().stream() + .filter(e -> e.getEnd().isAfter(LocalDateTime.now()) && !e.getStatus().toString().equals("REJECTED")).collect(Collectors.toList()); + if (bookingListLast.size() >= 1 && bookingListNext.size() >= 1) { + itemDto.setLastBooking(bookingListLast.get(0)); + itemDto.getLastBooking().setBookerId(bookingListLast.get(0).getBooker().getId()); + itemDto.setNextBooking(bookingListNext.get(0)); + itemDto.getNextBooking().setBookerId(bookingListNext.get(0).getBooker().getId()); + } else if (bookingListLast.size() == 0 && bookingListLastAfterNow.size() >= 1) { + itemDto.setLastBooking(bookingListLastAfterNow.get(0)); + itemDto.getLastBooking().setBookerId(bookingListLastAfterNow.get(0).getBooker().getId()); + } + } + return itemDto; } @Override - public ItemDto getItemDtoById(int itemId) { - return itemDao.getItemDtoById(itemId); + public Item getItemById(int itemId, int userId) { + Optional optional = itemRepository.findById(itemId); + if (optional.isPresent()) { + return optional.get(); + } else { + throw new NoDataFoundException("Item с id: " + itemId + " не найдена."); + } } @Override public List getAllItemForOwner(int ownerId) { - return itemDao.getAllItemForOwner(ownerId); + List itemList = itemRepository.findByOwnerIdEquals(ownerId).stream() + .sorted(Comparator.comparingInt(Item::getId)).collect(Collectors.toList()); + List itemDtoList = new ArrayList<>(); + for (Item item : itemList) { + ItemDto itemDto = itemMapper.itemDto(item); + itemDtoList.add(addBookingToItem(item, itemDto)); + } + return itemDtoList; + } + + @Override + public List searchItem(String request) { + Set itemsList = new HashSet<>(); + if (request.isBlank()) { + return new ArrayList<>(); + } + itemsList.addAll(itemRepository.findByNameIgnoreCaseContaining(request)); + itemsList.addAll(itemRepository.findByDescriptionIgnoreCaseContaining(request)); + List itemsListSorted = itemsList.stream().filter(e -> e.getAvailable() == true) + .sorted(Comparator.comparing(Item::getName)).collect(Collectors.toList()); + return itemsListSorted; } @Override - public List searchItem(String request) { - return itemDao.searchItem(request); + public Comment addComment(int itemId, int userId, Comment comment) { + ItemDto itemDto = getItemByIdDto(itemId, userId); + Item item = getItemById(itemId, userId); + Optional optionalUser = userRepository.findById(userId); + if (optionalUser.isPresent()) { + User user = optionalUser.get(); + if (comment.getText().isBlank()) { + throw new UnavailableItemException("Комментарий не может быть пустым."); + } else { + comment.setItem(item); + comment.setAuthor(user); + comment.setAuthorName(user.getName()); + comment.setCreated(LocalDateTime.now()); + } + List itemListBooking = bookingRepository.findByBookerEquals(user).stream() + .filter(e -> e.getItem().getId() == itemId && !e.getStatus().toString() + .equals("REJECTED") && !e.getStart().isAfter(LocalDateTime.now())).collect(Collectors.toList()); + if (!itemListBooking.isEmpty()) { + itemDto.getComments().add(comment); + } else { + throw new PostWithoutBookingException("Пользователь с id:" + userId + + " не может оставить комментарий, " + + "т.к. не брал " + itemDto.getName() + " вещь в аренду."); + } + } else { + throw new NoDataFoundException("Пользователь с id:" + userId + " не найден."); + } + Comment userItemComment = commentRepository.getUserCommentByItem(userId, itemId); + if (userItemComment != null) { + comment.setId(userItemComment.getId()); + } + return commentRepository.save(comment); } } diff --git a/src/main/java/ru/practicum/shareit/user/User.java b/src/main/java/ru/practicum/shareit/user/User.java index b4e435d..de1e0a0 100644 --- a/src/main/java/ru/practicum/shareit/user/User.java +++ b/src/main/java/ru/practicum/shareit/user/User.java @@ -6,6 +6,7 @@ import lombok.experimental.Accessors; import org.springframework.stereotype.Component; +import javax.persistence.*; import javax.validation.constraints.Email; import javax.validation.constraints.NotBlank; @@ -14,12 +15,19 @@ @EqualsAndHashCode @ToString @Component +@Entity +@Table(name = "users") public class User { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") private int id; @NotBlank(message = "Name - не может быть пустым.") + @Column(name = "name") private String name; @NotBlank(message = "Email - не может быть пустым.") @Email(message = "Не правильный формат email") + @Column(name = "email") private String email; } diff --git a/src/main/java/ru/practicum/shareit/user/UserController.java b/src/main/java/ru/practicum/shareit/user/UserController.java index 841da17..4e2ec24 100644 --- a/src/main/java/ru/practicum/shareit/user/UserController.java +++ b/src/main/java/ru/practicum/shareit/user/UserController.java @@ -2,7 +2,6 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.web.bind.annotation.*; -import ru.practicum.shareit.user.dto.UserDto; import ru.practicum.shareit.user.service.UserService; import javax.validation.Valid; @@ -17,28 +16,29 @@ public class UserController { @Autowired private UserService userService; + @PostMapping - public UserDto addUser(@Valid @RequestBody User user) { + public User addUser(@Valid @RequestBody User user) { return userService.addUser(user); } @GetMapping - public List getAllUsers() { + public List getAllUsers() { return userService.getAllUsers(); } @GetMapping("/{id}") - public UserDto getUserById(@PathVariable(name = "id") int userId) { - return userService.getUserDtoById(userId); + public User getUserById(@PathVariable(name = "id") int userId) { + return userService.getUserById(userId); } @DeleteMapping("/{id}") - public UserDto removeUserById(@PathVariable(name = "id") int userId) { - return userService.removeUserById(userId); + public void removeUserById(@PathVariable(name = "id") int userId) { + userService.removeUserById(userId); } @PatchMapping("/{id}") - public UserDto updateUser(@PathVariable int id, @RequestBody Map fields) { + public User updateUser(@PathVariable int id, @RequestBody Map fields) { return userService.updateUser(id, fields); } } diff --git a/src/main/java/ru/practicum/shareit/user/dao/UserDao.java b/src/main/java/ru/practicum/shareit/user/dao/UserDao.java deleted file mode 100644 index 3c97d88..0000000 --- a/src/main/java/ru/practicum/shareit/user/dao/UserDao.java +++ /dev/null @@ -1,23 +0,0 @@ -package ru.practicum.shareit.user.dao; - - -import ru.practicum.shareit.user.User; -import ru.practicum.shareit.user.dto.UserDto; - -import java.util.List; -import java.util.Map; - -public interface UserDao { - - UserDto addUser(User user); - - List getAllUsers(); - - UserDto getUserDtoById(int userId); - - User getUserById(int userId); - - UserDto removeUserById(int userId); - - UserDto updateUser(int userId, Map fields); -} diff --git a/src/main/java/ru/practicum/shareit/user/dao/UserDaoImpl.java b/src/main/java/ru/practicum/shareit/user/dao/UserDaoImpl.java deleted file mode 100644 index 4714c5a..0000000 --- a/src/main/java/ru/practicum/shareit/user/dao/UserDaoImpl.java +++ /dev/null @@ -1,99 +0,0 @@ -package ru.practicum.shareit.user.dao; - -import lombok.extern.slf4j.Slf4j; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.stereotype.Repository; -import org.springframework.util.ReflectionUtils; -import ru.practicum.shareit.exception.EmailDuplicateException; -import ru.practicum.shareit.exception.NoDataFoundException; -import ru.practicum.shareit.user.User; -import ru.practicum.shareit.user.UserMapper; -import ru.practicum.shareit.user.dto.UserDto; - -import java.lang.reflect.Field; -import java.util.ArrayList; -import java.util.HashMap; -import java.util.List; -import java.util.Map; - -@Repository -@Slf4j -public class UserDaoImpl implements UserDao { - private final Map userMap = new HashMap<>(); - private int count = 0; - @Autowired - private UserMapper userMapper; - - @Override - public UserDto addUser(User user) { - checkEmailForDuplicate(user.getEmail()); - user.setId(++count); - UserDto userDto = userMapper.toUserDto(user); - userMap.putIfAbsent(user.getId(), user); - return userDto; - } - - @Override - public List getAllUsers() { - List userDtoList = new ArrayList<>(); - for (User user : userMap.values()) { - userDtoList.add(userMapper.toUserDto(user)); - } - return userDtoList; - } - - @Override - public User getUserById(int userId) { - if (userMap.containsKey(userId)) { - return userMap.get(userId); - } else { - throw new NoDataFoundException("Пользователь с id:" + userId + " не найден."); - } - } - - @Override - public UserDto getUserDtoById(int userId) { - if (userMap.containsKey(userId)) { - return userMapper.toUserDto(userMap.get(userId)); - } else { - throw new NoDataFoundException("Пользователь с id:" + userId + " не найден."); - } - } - - @Override - public UserDto removeUserById(int userId) { - if (userMap.containsKey(userId)) { - return userMapper.toUserDto(userMap.remove(userId)); - } else { - throw new NoDataFoundException("Пользователь с id:" + userId + " не найден."); - } - } - - @Override - public UserDto updateUser(int userId, Map fields) { - if (userMap.containsKey(userId)) { - User user = getUserById(userId); - fields.forEach((key, value) -> { - Field field = ReflectionUtils.findField(User.class, (String) key); - if (((String) key).equalsIgnoreCase("email") && !user.getEmail() - .equalsIgnoreCase((String) value)) { - checkEmailForDuplicate((String) value); - } - field.setAccessible(true); - ReflectionUtils.setField(field, user, value); - }); - return userMapper.toUserDto(user); - } else { - throw new NoDataFoundException("Пользователь с id:" + userId + " не найден."); - } - } - - private void checkEmailForDuplicate(String email) { - List userDtoList = getAllUsers(); - for (UserDto userDto : userDtoList) { - if (userDto.getEmail().equalsIgnoreCase(email)) { - throw new EmailDuplicateException("Email: " + email + " уже существует"); - } - } - } -} diff --git a/src/main/java/ru/practicum/shareit/user/dao/UserRepository.java b/src/main/java/ru/practicum/shareit/user/dao/UserRepository.java new file mode 100644 index 0000000..f2cb954 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/user/dao/UserRepository.java @@ -0,0 +1,9 @@ +package ru.practicum.shareit.user.dao; + +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.stereotype.Repository; +import ru.practicum.shareit.user.User; + +@Repository +public interface UserRepository extends JpaRepository { +} diff --git a/src/main/java/ru/practicum/shareit/user/service/UserService.java b/src/main/java/ru/practicum/shareit/user/service/UserService.java index 6bc91f4..4e940d4 100644 --- a/src/main/java/ru/practicum/shareit/user/service/UserService.java +++ b/src/main/java/ru/practicum/shareit/user/service/UserService.java @@ -1,20 +1,19 @@ package ru.practicum.shareit.user.service; import ru.practicum.shareit.user.User; -import ru.practicum.shareit.user.dto.UserDto; import java.util.List; import java.util.Map; public interface UserService { - public UserDto addUser(User user); + public User addUser(User user); - public List getAllUsers(); + public List getAllUsers(); - public UserDto getUserDtoById(int userId); + public User getUserById(int userId); - public UserDto removeUserById(int userId); + public void removeUserById(int userId); - public UserDto updateUser(int userId, Map fields); + public User updateUser(int userId, Map fields); } diff --git a/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java b/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java index 8aea666..893d434 100644 --- a/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java +++ b/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java @@ -2,42 +2,75 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; +import org.springframework.util.ReflectionUtils; +import ru.practicum.shareit.exception.EmailDuplicateException; +import ru.practicum.shareit.exception.NoDataFoundException; import ru.practicum.shareit.user.User; -import ru.practicum.shareit.user.dao.UserDao; -import ru.practicum.shareit.user.dto.UserDto; +import ru.practicum.shareit.user.dao.UserRepository; +import java.lang.reflect.Field; import java.util.List; import java.util.Map; +import java.util.Optional; @Service public class UserServiceImpl implements UserService { @Autowired - private UserDao userDao; + private UserRepository userRepository; @Override - - public UserDto addUser(User user) { - return userDao.addUser(user); + public User addUser(User user) { + return userRepository.save(user); } @Override - public List getAllUsers() { - return userDao.getAllUsers(); + public List getAllUsers() { + return userRepository.findAll(); } @Override - public UserDto getUserDtoById(int userId) { - return userDao.getUserDtoById(userId); + public User getUserById(int userId) { + Optional optional = userRepository.findById(userId); + if (optional.isPresent()) { + return optional.get(); + } else { + throw new NoDataFoundException("Пользователь с id:" + userId + " не найден."); + } } @Override - public UserDto removeUserById(int userId) { - return userDao.removeUserById(userId); + public void removeUserById(int userId) { + getUserById(userId); + userRepository.deleteById(userId); } @Override - public UserDto updateUser(int userId, Map fields) { - return userDao.updateUser(userId, fields); + public User updateUser(int userId, Map fields) { + if (getUserById(userId) != null) { + User user = getUserById(userId); + fields.forEach((key, value) -> { + Field field = ReflectionUtils.findField(User.class, (String) key); + if (((String) key).equalsIgnoreCase("email") && !user.getEmail() + .equalsIgnoreCase((String) value)) { + checkEmailForDuplicate((String) value); + } + field.setAccessible(true); + ReflectionUtils.setField(field, user, value); + }); + return userRepository.save(user); + } else { + throw new NoDataFoundException("Пользователь с id:" + userId + " не найден."); + } + } + + private void checkEmailForDuplicate(String email) { + List userList = getAllUsers(); + for (User user : userList) { + if (user.getEmail().equalsIgnoreCase(email)) { + throw new EmailDuplicateException("Email: " + email + " уже существует"); + } + } } } + diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 318785c..4573a5c 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -1,18 +1,31 @@ spring.jpa.hibernate.ddl-auto=none +spring.jpa.defer-datasource-initialization=true spring.jpa.properties.hibernate.dialect=org.hibernate.dialect.PostgreSQL10Dialect spring.jpa.properties.hibernate.format_sql=true spring.sql.init.mode=always + + logging.level.org.springframework.orm.jpa=INFO logging.level.org.springframework.transaction=INFO logging.level.org.springframework.transaction.interceptor=TRACE logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG -#--- -# TODO Append connection to DB -#--- -spring.config.activate.on-profile=ci,test -spring.datasource.driverClassName=org.h2.Driver -spring.datasource.url=jdbc:h2:mem:shareit -spring.datasource.username=test -spring.datasource.password=test +spring.datasource.driverClassName=org.postgresql.Driver +spring.datasource.url=jdbc:postgresql://localhost:5432/${db.name} +spring.datasource.username=andersen +spring.datasource.password=andersen + +db.name=shareit + +hibernate.dialect=org.hibernate.dialect.PostgreSQLDialect +hibernate.show_sql=true +javax.persistence.schema-generation.database.action=create +javax.persistence.schema-generation.create-script-source=schema.sql + +logging.level.org.hibernate.SQL=INFO +logging.level.org.hibernate.type.descriptor.sql.BasicBinder = TRACE + + + + diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 0000000..df06c56 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,40 @@ +DROP TABLE IF EXISTS comments; +DROP TABLE IF EXISTS bookings; +DROP TABLE IF EXISTS items; +DROP TABLE IF EXISTS users; + + +CREATE TABLE IF NOT EXISTS users( +id INTEGER GENERATED ALWAYS AS IDENTITY, +name varchar(250) NOT NULL, +email varchar(250)NOT NULL, +CONSTRAINT pk_user PRIMARY KEY (id), +CONSTRAINT UQ_USER_EMAIL UNIQUE (email)); + +CREATE TABLE IF NOT EXISTS items( +id INTEGER GENERATED ALWAYS AS IDENTITY, +name VARCHAR(250) NOT NULL, +description VARCHAR(500) NOT NULL, +available BOOLEAN NOT NULL, +owner_id INTEGER REFERENCES users(id), +request VARCHAR(100), +CONSTRAINT item_id PRIMARY KEY(id)); + +CREATE TABLE IF NOT EXISTS bookings( +id INTEGER GENERATED ALWAYS AS IDENTITY, +start_time TIMESTAMP WITHOUT TIME ZONE, +end_time TIMESTAMP WITHOUT TIME ZONE, +item_id INTEGER REFERENCES items(id), +user_id INTEGER REFERENCES users(id), +status VARCHAR(15), +CONSTRAINT booking_id PRIMARY KEY(id)); + +CREATE TABLE IF NOT EXISTS comments ( +id INTEGER GENERATED ALWAYS AS IDENTITY, +text VARCHAR(1000), +item_id INTEGER REFERENCES items(id) ON UPDATE CASCADE, +author INTEGER REFERENCES users(id) ON UPDATE CASCADE, +created TIMESTAMP WITHOUT TIME ZONE, +CONSTRAINT comments_id PRIMARY KEY(id)); + + diff --git a/src/test/java/ru/practicum/shareit/ShareItTests.java b/src/test/java/ru/practicum/shareit/ShareItTests.java index 4d79052..8502acd 100644 --- a/src/test/java/ru/practicum/shareit/ShareItTests.java +++ b/src/test/java/ru/practicum/shareit/ShareItTests.java @@ -6,8 +6,8 @@ @SpringBootTest class ShareItTests { - @Test - void contextLoads() { - } + @Test + void contextLoads() { + } }