From b75963fc74845b62608315efe1bd1217f74412d8 Mon Sep 17 00:00:00 2001 From: "vladimir.vasiliev" Date: Tue, 19 Aug 2025 11:27:49 +0300 Subject: [PATCH 1/3] =?UTF-8?q?add-bookings.=20=D0=98=D0=B7=D0=BC=D0=B5?= =?UTF-8?q?=D0=BD=D0=B8=D1=82=D1=8C=20=D1=81=D0=BE=D1=85=D1=80=D0=B0=D0=BD?= =?UTF-8?q?=D0=B5=D0=BD=D0=B8=D0=B5=20=D0=B4=D0=B0=D0=BD=D0=BD=D1=8B=D1=85?= =?UTF-8?q?=20=D0=B2=20=D0=91=D0=94.=20=D0=94=D0=BE=D0=B1=D0=B0=D0=B2?= =?UTF-8?q?=D0=B8=D1=82=D1=8C=20=D1=84=D1=83=D0=BD=D0=BA=D1=86=D0=B8=D0=BE?= =?UTF-8?q?=D0=BD=D0=B0=D0=BB=20"=D0=91=D1=80=D0=BE=D0=BD=D0=B8=D1=80?= =?UTF-8?q?=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8=D0=B5"=20=D0=B8=20"=D0=9A=D0=BE?= =?UTF-8?q?=D0=BC=D0=BC=D0=B5=D0=BD=D1=82=D0=B0=D1=80=D0=B8=D0=B8"?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- pom.xml | 7 +- .../ru/practicum/shareit/booking/Booking.java | 28 --- .../shareit/booking/BookingController.java | 55 ++++- .../shareit/booking/dto/BookingDto.java | 7 - .../booking/dto/BookingDtoRequest.java | 21 ++ .../booking/dto/BookingDtoResponse.java | 21 ++ .../shareit/booking/enumerated/State.java | 5 + .../shareit/booking/enumerated/Status.java | 5 + .../shareit/booking/mapper/BookingMapper.java | 33 +++ .../shareit/booking/model/Booking.java | 44 ++++ .../booking/repository/BookingRepository.java | 38 ++++ .../booking/service/BookingService.java | 20 ++ .../booking/service/BookingServiceImpl.java | 212 ++++++++++++++++++ .../shareit/exception/ErrorHandler.java | 7 + .../exception/ValidationException.java | 7 + .../shareit/item/ItemController.java | 21 +- .../shareit/item/dto/CommentDto.java | 15 ++ .../shareit/item/dto/ItemByIdDto.java | 21 ++ .../practicum/shareit/item/dto/ItemDto.java | 5 +- .../shareit/item/mapper/ItemMapper.java | 50 ++++- .../practicum/shareit/item/model/Comment.java | 31 +++ .../ru/practicum/shareit/item/model/Item.java | 28 ++- .../item/repository/CommentRepository.java | 7 + .../item/repository/ItemRepository.java | 22 +- .../item/repository/ItemRepositoryImpl.java | 55 ----- .../shareit/item/service/ItemService.java | 9 +- .../shareit/item/service/ItemServiceImpl.java | 80 ++++++- .../ru/practicum/shareit/user/model/User.java | 14 +- .../user/repository/UserRepository.java | 18 +- .../user/repository/UserRepositoryImpl.java | 56 ----- .../shareit/user/service/UserServiceImpl.java | 19 +- .../resources/application-local.properties | 7 + .../resources/application-test.properties | 9 +- src/main/resources/application.properties | 10 +- src/main/resources/schema.sql | 37 +++ 35 files changed, 805 insertions(+), 219 deletions(-) delete mode 100644 src/main/java/ru/practicum/shareit/booking/Booking.java delete mode 100644 src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java create mode 100644 src/main/java/ru/practicum/shareit/booking/dto/BookingDtoRequest.java create mode 100644 src/main/java/ru/practicum/shareit/booking/dto/BookingDtoResponse.java create mode 100644 src/main/java/ru/practicum/shareit/booking/enumerated/State.java create mode 100644 src/main/java/ru/practicum/shareit/booking/enumerated/Status.java create mode 100644 src/main/java/ru/practicum/shareit/booking/mapper/BookingMapper.java create mode 100644 src/main/java/ru/practicum/shareit/booking/model/Booking.java create mode 100644 src/main/java/ru/practicum/shareit/booking/repository/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/ValidationException.java create mode 100644 src/main/java/ru/practicum/shareit/item/dto/CommentDto.java create mode 100644 src/main/java/ru/practicum/shareit/item/dto/ItemByIdDto.java create mode 100644 src/main/java/ru/practicum/shareit/item/model/Comment.java create mode 100644 src/main/java/ru/practicum/shareit/item/repository/CommentRepository.java delete mode 100644 src/main/java/ru/practicum/shareit/item/repository/ItemRepositoryImpl.java delete mode 100644 src/main/java/ru/practicum/shareit/user/repository/UserRepositoryImpl.java create mode 100644 src/main/resources/application-local.properties create mode 100644 src/main/resources/schema.sql diff --git a/pom.xml b/pom.xml index 2db888c..853ff4e 100644 --- a/pom.xml +++ b/pom.xml @@ -33,6 +33,10 @@ spring-boot-configuration-processor true + + org.springframework.boot + spring-boot-starter-data-jpa + org.postgresql @@ -49,7 +53,8 @@ com.h2database h2 - test + + runtime org.springframework.boot diff --git a/src/main/java/ru/practicum/shareit/booking/Booking.java b/src/main/java/ru/practicum/shareit/booking/Booking.java deleted file mode 100644 index d76494a..0000000 --- a/src/main/java/ru/practicum/shareit/booking/Booking.java +++ /dev/null @@ -1,28 +0,0 @@ -package ru.practicum.shareit.booking; - -import lombok.Data; -import ru.practicum.shareit.item.model.Item; -import ru.practicum.shareit.user.model.User; - -import java.time.LocalDateTime; - -/** - * TODO Sprint add-bookings. - */ -@Data -public class Booking { - private Long id; - private LocalDateTime start; - private LocalDateTime end; - private Item item; - private User booker; - private Status status; - - enum Status { - WAITING, - APPROVED, - REJECTED, - CANCELED - } - -} diff --git a/src/main/java/ru/practicum/shareit/booking/BookingController.java b/src/main/java/ru/practicum/shareit/booking/BookingController.java index b94493d..09b7983 100644 --- a/src/main/java/ru/practicum/shareit/booking/BookingController.java +++ b/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -1,12 +1,57 @@ package ru.practicum.shareit.booking; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import jakarta.validation.Valid; +import lombok.AllArgsConstructor; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.booking.dto.BookingDtoRequest; +import ru.practicum.shareit.booking.dto.BookingDtoResponse; +import ru.practicum.shareit.booking.enumerated.State; +import ru.practicum.shareit.booking.service.BookingService; + +import java.util.List; -/** - * TODO Sprint add-bookings. - */ @RestController +@AllArgsConstructor @RequestMapping(path = "/bookings") public class BookingController { + + private final BookingService bookingService; + private final String userIdHeader = "X-Sharer-User-Id"; + + @PostMapping + public BookingDtoResponse createBooking( + @RequestHeader(userIdHeader) Long bookerId, + @Valid @RequestBody BookingDtoRequest bookingDtoRequest) { + return bookingService.createBooking(bookerId, bookingDtoRequest); + } + + @PatchMapping("/{bookingId}") + public BookingDtoResponse updateBooking( + @RequestHeader(userIdHeader) Long bookerId, + @RequestParam Boolean approved, + @PathVariable Long bookingId) { + return bookingService.updateBooking(bookerId, bookingId, approved); + } + + @GetMapping("/{bookingId}") + public BookingDtoResponse getBookingById( + @RequestHeader(userIdHeader) Long bookerId, + @PathVariable Long bookingId) { + return bookingService.getBookingById(bookerId, bookingId); + } + + @GetMapping + public List getBookingByUser( + @RequestHeader(userIdHeader) Long bookerId, + @RequestParam(defaultValue = "ALL") State state) { + return bookingService.getBookingByUser(bookerId, state); + } + + @GetMapping("/owner") + public List getBookingByItemsUser( + @RequestHeader(userIdHeader) Long userOwnerItemId, + @RequestParam(defaultValue = "ALL") State state) { + return bookingService.getBookingByItemsUser(userOwnerItemId, state); + } + } diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java b/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java deleted file mode 100644 index 861de9e..0000000 --- a/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java +++ /dev/null @@ -1,7 +0,0 @@ -package ru.practicum.shareit.booking.dto; - -/** - * TODO Sprint add-bookings. - */ -public class BookingDto { -} diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingDtoRequest.java b/src/main/java/ru/practicum/shareit/booking/dto/BookingDtoRequest.java new file mode 100644 index 0000000..7f84ca0 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/dto/BookingDtoRequest.java @@ -0,0 +1,21 @@ +package ru.practicum.shareit.booking.dto; + +import jakarta.validation.constraints.NotNull; +import lombok.AllArgsConstructor; +import lombok.Data; +import ru.practicum.shareit.booking.enumerated.Status; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +public class BookingDtoRequest { + + @NotNull(message = "Дата начала бронирования не может быть пустой") + private LocalDateTime start; + @NotNull(message = "Дата окончания бронирования не может быть пустой") + private LocalDateTime end; + @NotNull(message = "ID вещи не может быть пустым") + private Long itemId; + private Status status; +} diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingDtoResponse.java b/src/main/java/ru/practicum/shareit/booking/dto/BookingDtoResponse.java new file mode 100644 index 0000000..8c25e45 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/dto/BookingDtoResponse.java @@ -0,0 +1,21 @@ +package ru.practicum.shareit.booking.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import ru.practicum.shareit.booking.enumerated.Status; +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.user.dto.UserDto; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +public class BookingDtoResponse { + + private Long id; + private LocalDateTime start; + private LocalDateTime end; + private ItemDto item; + private UserDto booker; + private Status status; +} diff --git a/src/main/java/ru/practicum/shareit/booking/enumerated/State.java b/src/main/java/ru/practicum/shareit/booking/enumerated/State.java new file mode 100644 index 0000000..54b2467 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/enumerated/State.java @@ -0,0 +1,5 @@ +package ru.practicum.shareit.booking.enumerated; + +public enum State { + ALL, CURRENT, PAST, FUTURE, WAITING, REJECTED, UNSUPPORTED_STATUS +} diff --git a/src/main/java/ru/practicum/shareit/booking/enumerated/Status.java b/src/main/java/ru/practicum/shareit/booking/enumerated/Status.java new file mode 100644 index 0000000..3a1adab --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/enumerated/Status.java @@ -0,0 +1,5 @@ +package ru.practicum.shareit.booking.enumerated; + +public enum Status { + WAITING, APPROVED, REJECTED, CANCELED +} diff --git a/src/main/java/ru/practicum/shareit/booking/mapper/BookingMapper.java b/src/main/java/ru/practicum/shareit/booking/mapper/BookingMapper.java new file mode 100644 index 0000000..1884159 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/mapper/BookingMapper.java @@ -0,0 +1,33 @@ +package ru.practicum.shareit.booking.mapper; + +import ru.practicum.shareit.booking.dto.BookingDtoRequest; +import ru.practicum.shareit.booking.dto.BookingDtoResponse; +import ru.practicum.shareit.booking.model.Booking; +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.user.dto.UserDto; +import ru.practicum.shareit.user.model.User; + +public class BookingMapper { + + public static BookingDtoResponse toBookingDtoResponse(Booking booking, UserDto userDto, ItemDto itemDto) { + return new BookingDtoResponse( + booking.getId(), + booking.getStart(), + booking.getEnd(), + itemDto, + userDto, + booking.getStatus() + ); + } + + public static Booking toBooking(BookingDtoRequest bookingDtoRequest, User booker, Item item) { + return new Booking( + bookingDtoRequest.getStart(), + bookingDtoRequest.getEnd(), + item, + booker, + bookingDtoRequest.getStatus() + ); + } +} diff --git a/src/main/java/ru/practicum/shareit/booking/model/Booking.java b/src/main/java/ru/practicum/shareit/booking/model/Booking.java new file mode 100644 index 0000000..92a97c0 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/model/Booking.java @@ -0,0 +1,44 @@ +package ru.practicum.shareit.booking.model; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.booking.enumerated.Status; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.user.model.User; + +import java.time.LocalDateTime; + +@Data +@Entity +@NoArgsConstructor +@AllArgsConstructor +@Table(name = "bookings") +public class Booking { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + @Column(name = "start_date", nullable = false) + private LocalDateTime start; + @Column(name = "end_date", nullable = false) + private LocalDateTime end; + @ManyToOne + @JoinColumn(name = "item_id") + private Item item; + @ManyToOne + @JoinColumn(name = "booker_id") + private User booker; + @Enumerated(EnumType.STRING) + private Status status; + + public Booking(LocalDateTime start, LocalDateTime end, Item item, User booker, Status status) { + this.start = start; + this.end = end; + this.item = item; + this.booker = booker; + this.status = status; + } +} diff --git a/src/main/java/ru/practicum/shareit/booking/repository/BookingRepository.java b/src/main/java/ru/practicum/shareit/booking/repository/BookingRepository.java new file mode 100644 index 0000000..f6bb1c5 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/repository/BookingRepository.java @@ -0,0 +1,38 @@ +package ru.practicum.shareit.booking.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.practicum.shareit.booking.enumerated.Status; +import ru.practicum.shareit.booking.model.Booking; + +import java.time.LocalDateTime; +import java.util.Collection; +import java.util.List; +import java.util.Optional; + +public interface BookingRepository extends JpaRepository { + List findAllByBookerIdOrderByStartDesc(Long bookerId); + + Collection findAllByBookerIdAndStartIsBeforeAndEndIsAfterOrderByStartDesc(long bookerId, LocalDateTime startBefore, LocalDateTime endAfter); + + Collection findAllByBookerIdAndEndIsBeforeOrderByStartDesc(long bookerId, LocalDateTime endAfter); + + Collection findAllByBookerIdAndStartIsAfterOrderByStartDesc(long bookerId, LocalDateTime endAfter); + + Collection findAllByBookerIdAndStatusOrderByStartDesc(long bookerId, Status status); + + List findAllByItemIdInOrderByStartDesc(Collection itemId); + + Collection findAllByItemIdInAndStartIsBeforeAndEndIsAfterOrderByStartDesc(Collection itemId, LocalDateTime startBefore, LocalDateTime endAfter); + + Collection findAllByItemIdInAndEndIsBeforeOrderByStartDesc(Collection itemId, LocalDateTime endAfter); + + Collection findAllByItemIdInAndStartIsAfterOrderByStartDesc(Collection itemId, LocalDateTime endAfter); + + Collection findAllByItemIdInAndStatusOrderByStartDesc(Collection itemId, Status status); + + boolean existsBookingByItemIdAndBookerIdAndStatusAndEndIsBefore(Long itemId, long bookerId, Status status, LocalDateTime endBefore); + + Optional findFirstByItemIdAndStartBeforeAndStatusOrderByEndDesc(Long itemId, LocalDateTime startBefore, Status status); + + Optional findFirstByItemIdAndStartAfterAndStatusOrderByStartAsc(Long itemId, LocalDateTime startAfter, Status status); +} 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..dfe3ef9 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/service/BookingService.java @@ -0,0 +1,20 @@ +package ru.practicum.shareit.booking.service; + +import ru.practicum.shareit.booking.dto.BookingDtoRequest; +import ru.practicum.shareit.booking.dto.BookingDtoResponse; +import ru.practicum.shareit.booking.enumerated.State; + +import java.util.List; + +public interface BookingService { + + BookingDtoResponse getBookingById(Long userId, Long bookingId); + + List getBookingByUser(Long bookerId, State state); + + List getBookingByItemsUser(Long userOwnerItemId, State state); + + BookingDtoResponse createBooking(Long userId, BookingDtoRequest bookingDtoRequest); + + BookingDtoResponse updateBooking(Long userId, Long bookingId, Boolean approved); +} 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..0118d28 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java @@ -0,0 +1,212 @@ +package ru.practicum.shareit.booking.service; + +import lombok.AllArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import ru.practicum.shareit.booking.dto.BookingDtoRequest; +import ru.practicum.shareit.booking.dto.BookingDtoResponse; +import ru.practicum.shareit.booking.enumerated.State; +import ru.practicum.shareit.booking.enumerated.Status; +import ru.practicum.shareit.booking.mapper.BookingMapper; +import ru.practicum.shareit.booking.model.Booking; +import ru.practicum.shareit.booking.repository.BookingRepository; +import ru.practicum.shareit.exception.NotAccessException; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.exception.ValidationException; +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.mapper.ItemMapper; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.item.service.ItemService; +import ru.practicum.shareit.user.mapper.UserMapper; +import ru.practicum.shareit.user.model.User; +import ru.practicum.shareit.user.service.UserService; + +import java.time.LocalDateTime; +import java.util.List; +import java.util.Objects; + +@Slf4j +@Service +@AllArgsConstructor +public class BookingServiceImpl implements BookingService { + private final BookingRepository bookingRepository; + private final UserService userService; + private final ItemService itemService; + + + @Override + public BookingDtoResponse getBookingById(Long userId, Long bookingId) { + log.info("Запрос бронирования с ID = " + bookingId); + Booking booking = getBookingWithCheck(bookingId); + checkBookingUserForGet(userId, booking.getBooker().getId(), booking.getItem().getOwner().getId()); + return BookingMapper.toBookingDtoResponse( + getBookingWithCheck(bookingId), + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem())); + } + + @Override + public List getBookingByUser(Long bookerId, State state) { + log.info("Запрос всех бронирования пользователя с ID = " + bookerId); + userService.getUserWithCheck(bookerId); + return switch (state) { + case ALL -> bookingRepository.findAllByBookerIdOrderByStartDesc(bookerId).stream() + .map(booking -> BookingMapper.toBookingDtoResponse( + booking, + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem()))) + .toList(); + case CURRENT -> + bookingRepository.findAllByBookerIdAndStartIsBeforeAndEndIsAfterOrderByStartDesc(bookerId, LocalDateTime.now(), LocalDateTime.now()).stream() + .map(booking -> BookingMapper.toBookingDtoResponse( + booking, + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem()))) + .toList(); + case PAST -> + bookingRepository.findAllByBookerIdAndEndIsBeforeOrderByStartDesc(bookerId, LocalDateTime.now()).stream() + .map(booking -> BookingMapper.toBookingDtoResponse( + booking, + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem()))) + .toList(); + case FUTURE -> + bookingRepository.findAllByBookerIdAndStartIsAfterOrderByStartDesc(bookerId, LocalDateTime.now()).stream() + .map(booking -> BookingMapper.toBookingDtoResponse( + booking, + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem()))) + .toList(); + case WAITING -> + bookingRepository.findAllByBookerIdAndStatusOrderByStartDesc(bookerId, Status.WAITING).stream() + .map(booking -> BookingMapper.toBookingDtoResponse( + booking, + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem()))) + .toList(); + case REJECTED -> + bookingRepository.findAllByBookerIdAndStatusOrderByStartDesc(bookerId, Status.REJECTED).stream() + .map(booking -> BookingMapper.toBookingDtoResponse( + booking, + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem()))) + .toList(); + default -> throw new ValidationException("Недоступное состояние: " + state); + }; + + } + + @Override + public List getBookingByItemsUser(Long userOwnerItemId, State state) { + log.info("Запрос всех забронированных вещей пользователя с ID = " + userOwnerItemId); + userService.getUserWithCheck(userOwnerItemId); + List itemIds = itemService.getItemsByUserId(userOwnerItemId).stream().map(ItemDto::getId).toList(); + return switch (state) { + case ALL -> bookingRepository.findAllByItemIdInOrderByStartDesc(itemIds).stream() + .map(booking -> BookingMapper.toBookingDtoResponse( + booking, + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem()))) + .toList(); + case CURRENT -> + bookingRepository.findAllByItemIdInAndStartIsBeforeAndEndIsAfterOrderByStartDesc(itemIds, LocalDateTime.now(), LocalDateTime.now()).stream() + .map(booking -> BookingMapper.toBookingDtoResponse( + booking, + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem()))) + .toList(); + case PAST -> + bookingRepository.findAllByItemIdInAndEndIsBeforeOrderByStartDesc(itemIds, LocalDateTime.now()).stream() + .map(booking -> BookingMapper.toBookingDtoResponse( + booking, + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem()))) + .toList(); + case FUTURE -> + bookingRepository.findAllByItemIdInAndStartIsAfterOrderByStartDesc(itemIds, LocalDateTime.now()).stream() + .map(booking -> BookingMapper.toBookingDtoResponse( + booking, + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem()))) + .toList(); + case WAITING -> + bookingRepository.findAllByItemIdInAndStatusOrderByStartDesc(itemIds, Status.WAITING).stream() + .map(booking -> BookingMapper.toBookingDtoResponse( + booking, + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem()))) + .toList(); + case REJECTED -> + bookingRepository.findAllByItemIdInAndStatusOrderByStartDesc(itemIds, Status.REJECTED).stream() + .map(booking -> BookingMapper.toBookingDtoResponse( + booking, + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem()))) + .toList(); + default -> throw new ValidationException("Недоступное состояние: " + state); + }; + } + + @Override + public BookingDtoResponse createBooking(Long userId, BookingDtoRequest bookingDtoRequest) { + log.info("Создание бронирования"); + checkDates(bookingDtoRequest.getStart(), bookingDtoRequest.getEnd()); + User user = userService.getUserWithCheck(userId); + Item item = itemService.getItemWithCheck(bookingDtoRequest.getItemId()); + if (!item.getAvailable()) { + throw new ValidationException("Вещь с ID = " + bookingDtoRequest.getItemId() + " забронирована"); + } + bookingDtoRequest.setStatus(Status.WAITING); + return BookingMapper.toBookingDtoResponse( + bookingRepository.save( + BookingMapper.toBooking(bookingDtoRequest, user, item)), + UserMapper.toUserDto(user), + ItemMapper.toItemDto(item)); + } + + @Override + public BookingDtoResponse updateBooking(Long userId, Long bookingId, Boolean approved) { + log.info("Подтверждение бронирования"); + Booking booking = getBookingWithCheck(bookingId); + checkBookingUser(userId, booking.getItem().getOwner().getId()); + userService.getUserWithCheck(userId); + if (approved) { + booking.setStatus(Status.APPROVED); + } else { + booking.setStatus(Status.APPROVED); + } + return BookingMapper.toBookingDtoResponse( + bookingRepository.save(booking), + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem())); + } + + public Booking getBookingWithCheck(Long bookingId) { + return bookingRepository.findById(bookingId) + .orElseThrow(() -> new NotFoundException("Бронирование с ID = " + bookingId + " не найдено")); + } + + private void checkDates(LocalDateTime start, LocalDateTime end) { + if (start.isBefore(LocalDateTime.now())) { + throw new ValidationException("Дата начала бронирования не может быть раньше текущего времени"); + } + if (end.isBefore(LocalDateTime.now())) { + throw new ValidationException("Дата окончания бронирования не может быть раньше текущего времени"); + } + if (start.isEqual(end)) { + throw new ValidationException("Дата начала и дата окончания бронирования не могут быть равны"); + } + } + + private void checkBookingUser(Long userId, Long ownerItemId) { + if (!Objects.equals(userId, ownerItemId)) { + throw new NotAccessException("У данного пользователя нет прав на данное действие"); + } + } + + private void checkBookingUserForGet(Long userId, Long bookerId, Long ownerItemId) { + if (!Objects.equals(userId, bookerId) && !Objects.equals(userId, ownerItemId)) { + throw new NotAccessException("У данного пользователя нет прав на данное действие"); + } + } +} diff --git a/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java index 000fbff..00356ce 100644 --- a/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java +++ b/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java @@ -38,4 +38,11 @@ public ErrorResponse handleValidationException(NotAccessException e) { log.debug(e.getMessage(), e); return new ErrorResponse(HttpStatus.FORBIDDEN.value(), e.getMessage()); } + + @ExceptionHandler + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleValidationException(ValidationException e) { + log.debug(e.getMessage(), e); + return new ErrorResponse(HttpStatus.BAD_REQUEST.value(), e.getMessage()); + } } diff --git a/src/main/java/ru/practicum/shareit/exception/ValidationException.java b/src/main/java/ru/practicum/shareit/exception/ValidationException.java new file mode 100644 index 0000000..59043da --- /dev/null +++ b/src/main/java/ru/practicum/shareit/exception/ValidationException.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.exception; + +public class ValidationException extends RuntimeException { + public ValidationException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/practicum/shareit/item/ItemController.java b/src/main/java/ru/practicum/shareit/item/ItemController.java index af398c8..1e14f81 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemController.java +++ b/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -3,6 +3,8 @@ import jakarta.validation.Valid; import lombok.AllArgsConstructor; import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.item.dto.CommentDto; +import ru.practicum.shareit.item.dto.ItemByIdDto; import ru.practicum.shareit.item.dto.ItemDto; import ru.practicum.shareit.item.service.ItemService; @@ -14,21 +16,22 @@ public class ItemController { private final ItemService itemService; + private final String userIdHeader = "X-Sharer-User-Id"; @GetMapping - public List getItemsByUserId(@RequestHeader("X-Sharer-User-Id") Long userId) { + public List getItemsByUserId(@RequestHeader(userIdHeader) Long userId) { return itemService.getItemsByUserId(userId); } @GetMapping("/{itemId}") - public ItemDto getItemById(@PathVariable Long itemId) { - return itemService.getItemById(itemId); + public ItemByIdDto getItemById(@PathVariable Long itemId, @RequestHeader(userIdHeader) Long userId) { + return itemService.getItemById(itemId, userId); } @PostMapping public ItemDto createItem( @Valid @RequestBody ItemDto itemDto, - @RequestHeader("X-Sharer-User-Id") Long userId) { + @RequestHeader(userIdHeader) Long userId) { return itemService.createItem(itemDto, userId); } @@ -36,7 +39,7 @@ public ItemDto createItem( public ItemDto updateItem( @RequestBody ItemDto itemDto, @PathVariable Long itemId, - @RequestHeader("X-Sharer-User-Id") Long userId) { + @RequestHeader(userIdHeader) Long userId) { return itemService.updateItem(itemDto, itemId, userId); } @@ -44,4 +47,12 @@ public ItemDto updateItem( public List getItemsSearchByText(@RequestParam String text) { return itemService.searchItemsByText(text); } + + @PostMapping("/{itemId}/comment") + public CommentDto createCommet( + @RequestBody CommentDto commentDto, + @PathVariable Long itemId, + @RequestHeader(userIdHeader) Long userId) { + return itemService.createComment(itemId, userId, commentDto); + } } diff --git a/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java b/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java new file mode 100644 index 0000000..2a0f054 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java @@ -0,0 +1,15 @@ +package ru.practicum.shareit.item.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; + +import java.time.LocalDateTime; + +@Data +@AllArgsConstructor +public class CommentDto { + private Long id; + private String text; + private String authorName; + private LocalDateTime created; +} diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemByIdDto.java b/src/main/java/ru/practicum/shareit/item/dto/ItemByIdDto.java new file mode 100644 index 0000000..1f075c3 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/dto/ItemByIdDto.java @@ -0,0 +1,21 @@ +package ru.practicum.shareit.item.dto; + +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.booking.dto.BookingDtoResponse; + +import java.util.List; + +@Data +@AllArgsConstructor +@NoArgsConstructor +public class ItemByIdDto { + private Long id; + private String name; + private String description; + private Boolean available; + private BookingDtoResponse lastBooking; + private BookingDtoResponse nextBooking; + private List comments; +} 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 1b82a99..f55a5c2 100644 --- a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java +++ b/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java @@ -5,6 +5,8 @@ import lombok.AllArgsConstructor; import lombok.Data; +import java.util.List; + @Data @AllArgsConstructor public class ItemDto { @@ -15,5 +17,6 @@ public class ItemDto { private String description; @NotNull(message = "Статус о доступности не может быть пустым") private Boolean available; - private Long requestId; + + private List comments; } diff --git a/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java b/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java index 76264bf..4d51f03 100644 --- a/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java +++ b/src/main/java/ru/practicum/shareit/item/mapper/ItemMapper.java @@ -1,10 +1,16 @@ package ru.practicum.shareit.item.mapper; +import ru.practicum.shareit.booking.dto.BookingDtoResponse; +import ru.practicum.shareit.item.dto.CommentDto; +import ru.practicum.shareit.item.dto.ItemByIdDto; import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.model.Comment; import ru.practicum.shareit.item.model.Item; -import ru.practicum.shareit.request.ItemRequest; import ru.practicum.shareit.user.model.User; +import java.time.LocalDateTime; +import java.util.ArrayList; + public class ItemMapper { public static ItemDto toItemDto(Item item) { @@ -13,17 +19,51 @@ public static ItemDto toItemDto(Item item) { item.getName(), item.getDescription(), item.getAvailable(), - item.getRequest() != null ? item.getRequest().getId() : null + item.getComments() == null ? new ArrayList<>() : item.getComments().stream() + .map(ItemMapper::toCommentDto) + .toList() ); } - public static Item toItem(ItemDto itemDto, User owner, ItemRequest request) { + public static Item toItem(ItemDto itemDto, User owner) { return new Item( itemDto.getName(), itemDto.getDescription(), itemDto.getAvailable(), - owner, - request + owner + ); + } + + public static ItemByIdDto toItemByIdDto(Item item, BookingDtoResponse nextBooking, BookingDtoResponse lastBooking) { + return new ItemByIdDto( + item.getId(), + item.getName(), + item.getDescription(), + item.getAvailable(), + lastBooking, + nextBooking, + item.getComments() == null ? new ArrayList<>() : item.getComments().stream() + .map(ItemMapper::toCommentDto) + .toList() + ); + } + + public static CommentDto toCommentDto(Comment comment) { + return new CommentDto( + comment.getId(), + comment.getText(), + comment.getAuthor().getName(), + comment.getCreated() + ); + } + + public static Comment toComment(CommentDto commentDto, User author, Item item) { + return new Comment( + commentDto.getId(), + commentDto.getText(), + item, + author, + LocalDateTime.now() ); } diff --git a/src/main/java/ru/practicum/shareit/item/model/Comment.java b/src/main/java/ru/practicum/shareit/item/model/Comment.java new file mode 100644 index 0000000..c83a675 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/model/Comment.java @@ -0,0 +1,31 @@ +package ru.practicum.shareit.item.model; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.user.model.User; + +import java.time.LocalDateTime; + +@Data +@Entity +@AllArgsConstructor +@NoArgsConstructor +@Table(name = "comments") +public class Comment { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column(name = "id") + private Long id; + @Column(nullable = false, length = 500) + private String text; + @ManyToOne + @JoinColumn(name = "item_id") + private Item item; + @ManyToOne + @JoinColumn(name = "author_id") + private User author; + @Column + private LocalDateTime created; +} 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 69e581b..9a99d15 100644 --- a/src/main/java/ru/practicum/shareit/item/model/Item.java +++ b/src/main/java/ru/practicum/shareit/item/model/Item.java @@ -1,26 +1,46 @@ package ru.practicum.shareit.item.model; +import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Data; -import ru.practicum.shareit.request.ItemRequest; +import lombok.NoArgsConstructor; import ru.practicum.shareit.user.model.User; +import java.util.List; + @Data +@Entity +@NoArgsConstructor @AllArgsConstructor +@Table(name = "items") public class Item { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + @Column private Long id; + + @Column(nullable = false) private String name; + + @Column(nullable = false, length = 500) private String description; + + @Column private Boolean available; + + @ManyToOne + @JoinColumn(name = "owner_id") private User owner; - private ItemRequest request; - public Item(String name, String description, Boolean available, User owner, ItemRequest request) { + @OneToMany(mappedBy = "item") + private List comments; + + public Item(String name, String description, Boolean available, User owner) { this.name = name; this.description = description; this.available = available; this.owner = owner; - this.request = request; } } diff --git a/src/main/java/ru/practicum/shareit/item/repository/CommentRepository.java b/src/main/java/ru/practicum/shareit/item/repository/CommentRepository.java new file mode 100644 index 0000000..63277ce --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/repository/CommentRepository.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.item.repository; + +import org.springframework.data.jpa.repository.JpaRepository; +import ru.practicum.shareit.item.model.Comment; + +public interface CommentRepository extends JpaRepository { +} diff --git a/src/main/java/ru/practicum/shareit/item/repository/ItemRepository.java b/src/main/java/ru/practicum/shareit/item/repository/ItemRepository.java index 73c780d..b725926 100644 --- a/src/main/java/ru/practicum/shareit/item/repository/ItemRepository.java +++ b/src/main/java/ru/practicum/shareit/item/repository/ItemRepository.java @@ -1,20 +1,22 @@ package ru.practicum.shareit.item.repository; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.user.model.User; import java.util.List; -import java.util.Optional; -public interface ItemRepository { - List getItemsByUserId(Long userId); +public interface ItemRepository extends JpaRepository { - Item createItem(Item item); - - Item updateItem(Item item); - - Optional getItemById(Long itemId); - - List searchItemsByText(String text); + List findAllByOwner(User owner); + @Query( + "SELECT item " + + "FROM Item item " + + "WHERE (item.name ilike :text " + + "OR item.description ilike :text) " + + "AND item.available = true") + List findAllByTextIgnoreCaseAndAvailableIsTrue(String text); } diff --git a/src/main/java/ru/practicum/shareit/item/repository/ItemRepositoryImpl.java b/src/main/java/ru/practicum/shareit/item/repository/ItemRepositoryImpl.java deleted file mode 100644 index 297c005..0000000 --- a/src/main/java/ru/practicum/shareit/item/repository/ItemRepositoryImpl.java +++ /dev/null @@ -1,55 +0,0 @@ -package ru.practicum.shareit.item.repository; - -import org.springframework.stereotype.Repository; -import ru.practicum.shareit.item.model.Item; - -import java.util.*; - -@Repository -public class ItemRepositoryImpl implements ItemRepository { - - private final Map items = new HashMap<>(); - - @Override - public List getItemsByUserId(Long userId) { - return items.values().stream() - .filter(item -> Objects.equals(item.getOwner().getId(), userId)) - .toList(); - } - - @Override - public Item createItem(Item item) { - item.setId(getNewItemId()); - items.put(item.getId(), item); - return items.get(item.getId()); - } - - @Override - public Item updateItem(Item item) { - items.put(item.getId(), item); - return items.get(item.getId()); - } - - @Override - public Optional getItemById(Long itemId) { - return Optional.ofNullable(items.get(itemId)); - } - - @Override - public List searchItemsByText(String text) { - return items.values().stream() - .filter(item -> (containsIgnoreCase(item.getName(), text) || - containsIgnoreCase(item.getDescription(), text)) && item.getAvailable()) - .toList(); - } - - private Boolean containsIgnoreCase(String text, String containsText) { - return text.toLowerCase().contains(containsText.toLowerCase()); - } - - private Long getNewItemId() { - return items.keySet().stream() - .max(Long::compareTo) - .orElse(0L) + 1; - } -} 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 f76170f..05c04ce 100644 --- a/src/main/java/ru/practicum/shareit/item/service/ItemService.java +++ b/src/main/java/ru/practicum/shareit/item/service/ItemService.java @@ -1,17 +1,24 @@ package ru.practicum.shareit.item.service; +import ru.practicum.shareit.item.dto.CommentDto; +import ru.practicum.shareit.item.dto.ItemByIdDto; import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.model.Item; import java.util.List; public interface ItemService { List getItemsByUserId(Long userId); + Item getItemWithCheck(Long itemId); + ItemDto createItem(ItemDto itemDto, Long userId); ItemDto updateItem(ItemDto itemDto, Long itemId, Long userId); - ItemDto getItemById(Long itemId); + ItemByIdDto getItemById(Long itemId, Long userId); List searchItemsByText(String text); + + CommentDto createComment(Long itemId, Long userId, CommentDto commentDto); } 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 95ddca5..8c3e661 100644 --- a/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java +++ b/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java @@ -3,18 +3,31 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import ru.practicum.shareit.booking.dto.BookingDtoResponse; +import ru.practicum.shareit.booking.enumerated.Status; +import ru.practicum.shareit.booking.mapper.BookingMapper; +import ru.practicum.shareit.booking.model.Booking; +import ru.practicum.shareit.booking.repository.BookingRepository; import ru.practicum.shareit.exception.NotAccessException; import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.exception.ValidationException; +import ru.practicum.shareit.item.dto.CommentDto; +import ru.practicum.shareit.item.dto.ItemByIdDto; import ru.practicum.shareit.item.dto.ItemDto; import ru.practicum.shareit.item.mapper.ItemMapper; +import ru.practicum.shareit.item.model.Comment; import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.item.repository.CommentRepository; import ru.practicum.shareit.item.repository.ItemRepository; +import ru.practicum.shareit.user.mapper.UserMapper; import ru.practicum.shareit.user.model.User; import ru.practicum.shareit.user.service.UserService; +import java.time.LocalDateTime; import java.util.ArrayList; import java.util.List; import java.util.Objects; +import java.util.Optional; @Slf4j @Service @@ -23,20 +36,23 @@ public class ItemServiceImpl implements ItemService { private final ItemRepository itemRepository; private final UserService userService; + private final BookingRepository bookingRepository; + private final CommentRepository commentRepository; @Override public List getItemsByUserId(Long userId) { log.info("Поиск вещей пользователя с ID = {}", userId); - userService.getUserWithCheck(userId); - List itemsByUserId = itemRepository.getItemsByUserId(userId); + User user = userService.getUserWithCheck(userId); + List itemsByUserId = itemRepository.findAllByOwner(user); return itemsByUserId.stream() .map(ItemMapper::toItemDto) .toList(); } + @Override public Item getItemWithCheck(Long itemId) { - return itemRepository.getItemById(itemId) + return itemRepository.findById(itemId) .orElseThrow(() -> new NotFoundException("Вещь с ID = " + itemId + " не найдена")); } @@ -45,8 +61,8 @@ public ItemDto createItem(ItemDto itemDto, Long userId) { log.info("Создание вещи у пользователя с ID = {}", userId); User user = userService.getUserWithCheck(userId); return ItemMapper.toItemDto( - itemRepository.createItem( - ItemMapper.toItem(itemDto, user, null))); + itemRepository.save( + ItemMapper.toItem(itemDto, user))); } @Override @@ -63,14 +79,45 @@ public ItemDto updateItem(ItemDto itemDto, Long itemId, Long userId) { findItem.getDescription() : itemDto.getDescription()); findItem.setAvailable(itemDto.getAvailable() != null ? itemDto.getAvailable() : findItem.getAvailable()); - return ItemMapper.toItemDto(itemRepository.updateItem(findItem)); + return ItemMapper.toItemDto(itemRepository.save(findItem)); } @Override - public ItemDto getItemById(Long itemId) { + public ItemByIdDto getItemById(Long itemId, Long userId) { log.info("Запрос вещи с ID = {}", itemId); Item item = getItemWithCheck(itemId); - return ItemMapper.toItemDto(item); + User user = userService.getUserWithCheck(userId); + BookingDtoResponse lastBooking = null; + BookingDtoResponse nextBooking = null; + if (item.getOwner().getId().equals(userId)) { + lastBooking = getLastBookingDtoResponse(itemId, lastBooking, user, item); + nextBooking = getNextBookingDtoResponse(itemId, nextBooking, user, item); + } + return ItemMapper.toItemByIdDto(item, nextBooking, lastBooking); + } + + private BookingDtoResponse getNextBookingDtoResponse(Long itemId, BookingDtoResponse nextBooking, User user, Item item) { + Optional nextBookingOptional = bookingRepository.findFirstByItemIdAndStartAfterAndStatusOrderByStartAsc( + itemId, LocalDateTime.now(), Status.APPROVED); + if (nextBookingOptional.isPresent()) { + nextBooking = BookingMapper.toBookingDtoResponse( + nextBookingOptional.get(), + UserMapper.toUserDto(user), + ItemMapper.toItemDto(item)); + } + return nextBooking; + } + + private BookingDtoResponse getLastBookingDtoResponse(Long itemId, BookingDtoResponse lastBooking, User user, Item item) { + Optional lastBookingOptional = bookingRepository.findFirstByItemIdAndStartBeforeAndStatusOrderByEndDesc( + itemId, LocalDateTime.now(), Status.APPROVED); + if (lastBookingOptional.isPresent()) { + lastBooking = BookingMapper.toBookingDtoResponse( + lastBookingOptional.get(), + UserMapper.toUserDto(user), + ItemMapper.toItemDto(item)); + } + return lastBooking; } @Override @@ -79,11 +126,26 @@ public List searchItemsByText(String text) { if (text.isBlank()) { return new ArrayList<>(); } - return itemRepository.searchItemsByText(text).stream() + return itemRepository.findAllByTextIgnoreCaseAndAvailableIsTrue(text).stream() .map(ItemMapper::toItemDto) .toList(); } + @Override + public CommentDto createComment(Long itemId, Long userId, CommentDto commentDto) { + if (!bookingRepository.existsBookingByItemIdAndBookerIdAndStatusAndEndIsBefore(itemId, userId, + Status.APPROVED, LocalDateTime.now())) { + throw new ValidationException("У пользователя с ID = " + userId + " не было ни одной брони на вещь с ID = " + itemId); + } + User author = userService.getUserWithCheck(userId); + Item item = getItemWithCheck(itemId); + Comment comment = ItemMapper.toComment(commentDto, author, item); + comment.setItem(item); + comment.setAuthor(author); + comment.setCreated(LocalDateTime.now()); + return ItemMapper.toCommentDto(commentRepository.save(comment)); + } + private void checkUser(Item item, Long userIdFromRequest) { if (!Objects.equals(item.getOwner().getId(), userIdFromRequest)) { throw new NotAccessException("Вещь с ID = " + item.getId() + " не принадлежит пользователю с ID = " + userIdFromRequest); diff --git a/src/main/java/ru/practicum/shareit/user/model/User.java b/src/main/java/ru/practicum/shareit/user/model/User.java index c3779e3..794b025 100644 --- a/src/main/java/ru/practicum/shareit/user/model/User.java +++ b/src/main/java/ru/practicum/shareit/user/model/User.java @@ -1,15 +1,27 @@ package ru.practicum.shareit.user.model; +import jakarta.persistence.*; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; @Data +@Entity +@NoArgsConstructor @AllArgsConstructor +@Table(name = "users") public class User { + + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - private String name; + + @Column private String email; + @Column + private String name; + public User(String name, String email) { this.name = name; this.email = email; diff --git a/src/main/java/ru/practicum/shareit/user/repository/UserRepository.java b/src/main/java/ru/practicum/shareit/user/repository/UserRepository.java index 820e66f..9218c89 100644 --- a/src/main/java/ru/practicum/shareit/user/repository/UserRepository.java +++ b/src/main/java/ru/practicum/shareit/user/repository/UserRepository.java @@ -1,21 +1,9 @@ package ru.practicum.shareit.user.repository; +import org.springframework.data.jpa.repository.JpaRepository; import ru.practicum.shareit.user.model.User; -import java.util.List; -import java.util.Optional; +public interface UserRepository extends JpaRepository { -public interface UserRepository { - - List getUsers(); - - Optional getUserById(Long userId); - - User createUser(User user); - - User updateUser(User user); - - void deleteUserById(Long userId); - - void checkEmail(String email); + boolean existsUserByEmail(String email); } diff --git a/src/main/java/ru/practicum/shareit/user/repository/UserRepositoryImpl.java b/src/main/java/ru/practicum/shareit/user/repository/UserRepositoryImpl.java deleted file mode 100644 index d52efba..0000000 --- a/src/main/java/ru/practicum/shareit/user/repository/UserRepositoryImpl.java +++ /dev/null @@ -1,56 +0,0 @@ -package ru.practicum.shareit.user.repository; - -import org.springframework.stereotype.Repository; -import ru.practicum.shareit.exception.AlreadyExistException; -import ru.practicum.shareit.user.model.User; - -import java.util.*; - -@Repository -public class UserRepositoryImpl implements UserRepository { - - private final Map users = new HashMap<>(); - - @Override - public List getUsers() { - return new ArrayList<>(users.values()); - } - - @Override - public Optional getUserById(Long userId) { - return Optional.ofNullable(users.get(userId)); - } - - @Override - public User createUser(User user) { - user.setId(getNewUserId()); - users.put(user.getId(), user); - return users.get(user.getId()); - } - - @Override - public User updateUser(User user) { - users.put(user.getId(), user); - return users.get(user.getId()); - } - - @Override - public void deleteUserById(Long userId) { - users.remove(userId); - } - - @Override - public void checkEmail(String email) { - if (getUsers().stream() - .map(User::getEmail) - .anyMatch(email::equals)) { - throw new AlreadyExistException("Пользователь с почтой " + email + " уже существует"); - } - } - - private Long getNewUserId() { - return users.keySet().stream() - .max(Long::compareTo) - .orElse(0L) + 1; - } -} 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 8cc1711..fe2dced 100644 --- a/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java +++ b/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java @@ -3,6 +3,7 @@ import lombok.AllArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.stereotype.Service; +import ru.practicum.shareit.exception.AlreadyExistException; import ru.practicum.shareit.exception.NotFoundException; import ru.practicum.shareit.user.dto.UserDto; import ru.practicum.shareit.user.mapper.UserMapper; @@ -25,16 +26,16 @@ public UserDto getUserById(Long userId) { @Override public User getUserWithCheck(Long userId) { - return userRepository.getUserById(userId) + return userRepository.findById(userId) .orElseThrow(() -> new NotFoundException("Пользователь с ID = " + userId + " не найден")); } @Override public UserDto createUser(UserDto userDto) { log.info("Создание пользователя"); - userRepository.checkEmail(userDto.getEmail()); + checkEmail(userDto.getEmail()); return UserMapper.toUserDto( - userRepository.createUser( + userRepository.save( UserMapper.toUser(userDto))); } @@ -43,19 +44,25 @@ public UserDto updateUser(UserDto userDto, Long userId) { log.info("Обновление пользователя с ID = {}", userId); User findUser = getUserWithCheck(userId); if (userDto.getEmail() != null && !userDto.getEmail().equals(findUser.getEmail())) { - userRepository.checkEmail(userDto.getEmail()); + checkEmail(userDto.getEmail()); } findUser.setEmail(userDto.getEmail() == null || userDto.getEmail().isBlank() ? findUser.getEmail() : userDto.getEmail()); findUser.setName(userDto.getName() == null || userDto.getName().isBlank() ? findUser.getName() : userDto.getName()); - return UserMapper.toUserDto(userRepository.updateUser(findUser)); + return UserMapper.toUserDto(userRepository.save(findUser)); } @Override public void deleteUserById(Long userId) { log.info("Удаление пользователя с ID = {}", userId); getUserWithCheck(userId); - userRepository.deleteUserById(userId); + userRepository.deleteById(userId); + } + + private void checkEmail(String email) { + if (userRepository.existsUserByEmail(email)) { + throw new AlreadyExistException("Пользователь с почтой " + email + " уже существует"); + } } } diff --git a/src/main/resources/application-local.properties b/src/main/resources/application-local.properties new file mode 100644 index 0000000..61f0636 --- /dev/null +++ b/src/main/resources/application-local.properties @@ -0,0 +1,7 @@ +spring.jpa.show-sql=true + +spring.datasource.url=jdbc:h2:file:./db/shareIt +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password +logging.level.org.zalando.logbook: TRACE \ No newline at end of file diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties index 9e9bc4b..2bc5896 100644 --- a/src/main/resources/application-test.properties +++ b/src/main/resources/application-test.properties @@ -6,8 +6,7 @@ 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 H2 DB -#spring.datasource.driverClassName -#spring.datasource.url -#spring.datasource.username -#spring.datasource.password +spring.datasource.url=jdbc:h2:file:./db/shareIt +spring.datasource.driverClassName=org.h2.Driver +spring.datasource.username=sa +spring.datasource.password=password diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties index 51c5180..90b8b74 100644 --- a/src/main/resources/application.properties +++ b/src/main/resources/application.properties @@ -7,8 +7,8 @@ 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 Postgres DB -#spring.datasource.driverClassName -#spring.datasource.url -#spring.datasource.username -#spring.datasource.password +spring.datasource.driverClassName=org.postgresql.Driver +spring.datasource.url=jdbc:postgresql://localhost:5432/shareIt +spring.datasource.username=root +spring.datasource.password=root + diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql new file mode 100644 index 0000000..b2d46a3 --- /dev/null +++ b/src/main/resources/schema.sql @@ -0,0 +1,37 @@ +DROP TABLE IF EXISTS comments, bookings, items, users CASCADE ; + +CREATE TABLE IF NOT EXISTS users +( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(255) NOT NULL, + email VARCHAR(255) NOT NULL, + CONSTRAINT UQ_USER_EMAIL UNIQUE (email) + ); + +CREATE TABLE IF NOT EXISTS items +( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + name VARCHAR(255) NOT NULL, + description VARCHAR(500) NOT NULL, + available boolean default true, + owner_id BIGINT REFERENCES users (id) ON DELETE RESTRICT + ); + +CREATE TABLE IF NOT EXISTS bookings +( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + start_date TIMESTAMP WITHOUT TIME ZONE NOT NULL, + end_date TIMESTAMP WITHOUT TIME ZONE NOT NULL, + item_id BIGINT REFERENCES items (id) ON DELETE RESTRICT, + booker_id BIGINT REFERENCES users (id)ON DELETE RESTRICT, + status VARCHAR + ); + +CREATE TABLE IF NOT EXISTS comments +( + id BIGINT GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY, + text VARCHAR (500) NOT NULL, + item_id BIGINT REFERENCES items (id) ON DELETE RESTRICT, + author_id BIGINT REFERENCES users (id) ON DELETE RESTRICT, + created TIMESTAMP WITHOUT TIME ZONE NOT NULL + ); \ No newline at end of file From 3bed9a20996d3bc3866b3f8e3983e786e5a686b9 Mon Sep 17 00:00:00 2001 From: "vladimir.vasiliev" Date: Wed, 20 Aug 2025 08:56:52 +0300 Subject: [PATCH 2/3] =?UTF-8?q?add-bookings.=20=D0=A0=D0=B5=D0=B2=D1=8C?= =?UTF-8?q?=D1=8E.=20=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B8=D1=82=D1=8C=20?= =?UTF-8?q?=D0=BF=D1=80=D0=BE=D0=B2=D0=B5=D1=80=D0=BA=D0=B8=20=D0=B2=D1=80?= =?UTF-8?q?=D0=B5=D0=BC=D0=B5=D0=BD=D0=B8=20=D0=BD=D0=B0=20=D0=B0=D0=BD?= =?UTF-8?q?=D0=BD=D0=BE=D1=82=D0=B0=D1=86=D0=B8=D0=B8.=20=D0=94=D0=BE?= =?UTF-8?q?=D0=B1=D0=B0=D0=B2=D0=B8=D1=82=D1=8C=20=D0=BF=D1=80=D0=BE=D0=B2?= =?UTF-8?q?=D0=B5=D1=80=D0=BA=D1=83=20=D1=81=D1=82=D0=B0=D1=82=D1=83=D1=81?= =?UTF-8?q?=D0=B0=20=D1=83=20=D0=B0=D0=BF=D0=B8=20=D0=BF=D0=BE=D0=B4=D1=82?= =?UTF-8?q?=D0=B2=D0=B5=D1=80=D0=B6=D0=B4=D0=B5=D0=BD=D0=B8=D1=8F=20=D0=B1?= =?UTF-8?q?=D1=80=D0=BE=D0=BD=D0=B8.=20=D0=9E=D0=B1=D0=BE=D0=B3=D0=B0?= =?UTF-8?q?=D1=82=D0=B8=D1=82=D1=8C=20=D0=BE=D1=82=D0=B2=D0=B5=D1=82=20?= =?UTF-8?q?=D0=B4=D0=BB=D1=8F=20=D0=B0=D0=BF=D0=B8=20=D0=BF=D0=BE=20=D0=BF?= =?UTF-8?q?=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D1=8E=20=D1=81=D0=BF?= =?UTF-8?q?=D0=B8=D1=81=D0=BA=D0=B0=20=D0=B2=D0=B5=D1=89=D0=B5=D0=B9=20?= =?UTF-8?q?=D1=83=20=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82?= =?UTF-8?q?=D0=B5=D0=BB=D1=8F.=20=D0=9D=D0=B5=D0=B1=D0=BE=D0=BB=D1=8C?= =?UTF-8?q?=D1=88=D0=B8=D0=B5=20=D0=BA=D0=BE=D1=80=D1=80=D0=B5=D0=BA=D1=82?= =?UTF-8?q?=D0=B8=D1=80=D0=BE=D0=B2=D0=BA=D0=B8=20=D0=BA=D0=BE=D0=B4=D0=B0?= =?UTF-8?q?.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../booking/dto/BookingDtoRequest.java | 4 +++ .../booking/service/BookingServiceImpl.java | 18 +++++------ .../shareit/item/ItemController.java | 2 +- .../shareit/item/dto/CommentDto.java | 2 ++ .../practicum/shareit/item/dto/ItemDto.java | 13 ++++++++ .../shareit/item/service/ItemServiceImpl.java | 31 ++++++++++++++----- 6 files changed, 53 insertions(+), 17 deletions(-) diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingDtoRequest.java b/src/main/java/ru/practicum/shareit/booking/dto/BookingDtoRequest.java index 7f84ca0..de14e85 100644 --- a/src/main/java/ru/practicum/shareit/booking/dto/BookingDtoRequest.java +++ b/src/main/java/ru/practicum/shareit/booking/dto/BookingDtoRequest.java @@ -1,5 +1,7 @@ package ru.practicum.shareit.booking.dto; +import jakarta.validation.constraints.Future; +import jakarta.validation.constraints.FutureOrPresent; import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Data; @@ -11,8 +13,10 @@ @AllArgsConstructor public class BookingDtoRequest { + @FutureOrPresent(message = "Дата начала бронирования не может быть раньше текущего времени") @NotNull(message = "Дата начала бронирования не может быть пустой") private LocalDateTime start; + @Future(message = "Дата окончания бронирования не может быть раньше текущего времени") @NotNull(message = "Дата окончания бронирования не может быть пустой") private LocalDateTime end; @NotNull(message = "ID вещи не может быть пустым") diff --git a/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java b/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java index 0118d28..4e8205f 100644 --- a/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java +++ b/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java @@ -40,7 +40,7 @@ public BookingDtoResponse getBookingById(Long userId, Long bookingId) { Booking booking = getBookingWithCheck(bookingId); checkBookingUserForGet(userId, booking.getBooker().getId(), booking.getItem().getOwner().getId()); return BookingMapper.toBookingDtoResponse( - getBookingWithCheck(bookingId), + booking, UserMapper.toUserDto(booking.getBooker()), ItemMapper.toItemDto(booking.getItem())); } @@ -169,11 +169,14 @@ public BookingDtoResponse updateBooking(Long userId, Long bookingId, Boolean app log.info("Подтверждение бронирования"); Booking booking = getBookingWithCheck(bookingId); checkBookingUser(userId, booking.getItem().getOwner().getId()); + if (!booking.getStatus().equals(Status.WAITING)) { + throw new ValidationException("Статус у бронирования должен быть \"В ожидании подтверждения\""); + } userService.getUserWithCheck(userId); if (approved) { booking.setStatus(Status.APPROVED); } else { - booking.setStatus(Status.APPROVED); + booking.setStatus(Status.REJECTED); } return BookingMapper.toBookingDtoResponse( bookingRepository.save(booking), @@ -181,21 +184,18 @@ public BookingDtoResponse updateBooking(Long userId, Long bookingId, Boolean app ItemMapper.toItemDto(booking.getItem())); } - public Booking getBookingWithCheck(Long bookingId) { + private Booking getBookingWithCheck(Long bookingId) { return bookingRepository.findById(bookingId) .orElseThrow(() -> new NotFoundException("Бронирование с ID = " + bookingId + " не найдено")); } private void checkDates(LocalDateTime start, LocalDateTime end) { - if (start.isBefore(LocalDateTime.now())) { - throw new ValidationException("Дата начала бронирования не может быть раньше текущего времени"); - } - if (end.isBefore(LocalDateTime.now())) { - throw new ValidationException("Дата окончания бронирования не может быть раньше текущего времени"); - } if (start.isEqual(end)) { throw new ValidationException("Дата начала и дата окончания бронирования не могут быть равны"); } + if (start.isAfter(end)) { + throw new ValidationException("Дата начала бронирования не может быть позже даты окончания бронирования"); + } } private void checkBookingUser(Long userId, Long ownerItemId) { diff --git a/src/main/java/ru/practicum/shareit/item/ItemController.java b/src/main/java/ru/practicum/shareit/item/ItemController.java index 1e14f81..3f1858f 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemController.java +++ b/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -50,7 +50,7 @@ public List getItemsSearchByText(@RequestParam String text) { @PostMapping("/{itemId}/comment") public CommentDto createCommet( - @RequestBody CommentDto commentDto, + @Valid @RequestBody CommentDto commentDto, @PathVariable Long itemId, @RequestHeader(userIdHeader) Long userId) { return itemService.createComment(itemId, userId, commentDto); diff --git a/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java b/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java index 2a0f054..77adf45 100644 --- a/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java +++ b/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java @@ -1,5 +1,6 @@ package ru.practicum.shareit.item.dto; +import jakarta.validation.constraints.NotBlank; import lombok.AllArgsConstructor; import lombok.Data; @@ -9,6 +10,7 @@ @AllArgsConstructor public class CommentDto { private Long id; + @NotBlank(message = "Текст отзыва не может быть пустым") private String text; private String authorName; private LocalDateTime created; 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 f55a5c2..ea81900 100644 --- a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java +++ b/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java @@ -4,11 +4,14 @@ import jakarta.validation.constraints.NotNull; import lombok.AllArgsConstructor; import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.booking.dto.BookingDtoResponse; import java.util.List; @Data @AllArgsConstructor +@NoArgsConstructor public class ItemDto { private Long id; @NotBlank(message = "Название не может быть пустым") @@ -19,4 +22,14 @@ public class ItemDto { private Boolean available; private List comments; + + private List bookings; + + public ItemDto(Long id, String name, String description, Boolean available, List comments) { + this.id = id; + this.name = name; + this.description = description; + this.available = available; + this.comments = comments; + } } 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 8c3e661..8696c70 100644 --- a/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java +++ b/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java @@ -24,10 +24,7 @@ import ru.practicum.shareit.user.service.UserService; import java.time.LocalDateTime; -import java.util.ArrayList; -import java.util.List; -import java.util.Objects; -import java.util.Optional; +import java.util.*; @Slf4j @Service @@ -45,8 +42,13 @@ public List getItemsByUserId(Long userId) { log.info("Поиск вещей пользователя с ID = {}", userId); User user = userService.getUserWithCheck(userId); List itemsByUserId = itemRepository.findAllByOwner(user); + Map> bookingsByIdItem = getMapItemIdAndListBooking( + itemsByUserId.stream() + .map(Item::getId) + .toList()); return itemsByUserId.stream() .map(ItemMapper::toItemDto) + .peek(item -> item.setBookings(bookingsByIdItem.get(item.getId()))) .toList(); } @@ -140,9 +142,6 @@ public CommentDto createComment(Long itemId, Long userId, CommentDto commentDto) User author = userService.getUserWithCheck(userId); Item item = getItemWithCheck(itemId); Comment comment = ItemMapper.toComment(commentDto, author, item); - comment.setItem(item); - comment.setAuthor(author); - comment.setCreated(LocalDateTime.now()); return ItemMapper.toCommentDto(commentRepository.save(comment)); } @@ -152,4 +151,22 @@ private void checkUser(Item item, Long userIdFromRequest) { } } + private Map> getMapItemIdAndListBooking(List itemIds) { + Map> map = new HashMap<>(); + List bookings = bookingRepository.findAllByItemIdInOrderByStartDesc(itemIds).stream() + .map(booking -> BookingMapper.toBookingDtoResponse( + booking, + UserMapper.toUserDto(booking.getBooker()), + ItemMapper.toItemDto(booking.getItem()))) + .toList(); + bookings.forEach(booking -> { + if (map.containsKey(booking.getItem().getId())) { + map.get(booking.getItem().getId()).add(booking); + } else { + map.put(booking.getItem().getId(), new ArrayList<>(Arrays.asList(booking))); + } + }); + return map; + } + } From ac52d287616ecb7e593aede2f6eecdb380640fd5 Mon Sep 17 00:00:00 2001 From: "vladimir.vasiliev" Date: Wed, 20 Aug 2025 09:58:59 +0300 Subject: [PATCH 3/3] =?UTF-8?q?add-bookings.=20=D0=A0=D0=B5=D0=B2=D1=8C?= =?UTF-8?q?=D1=8E.=20=D0=98=D0=B7=D0=BC=D0=B5=D0=BD=D0=B8=D1=82=D1=8C=20?= =?UTF-8?q?=D0=B2=20=D0=BF=D0=BE=D0=BB=D1=83=D1=87=D0=B5=D0=BD=D0=B8=D0=B8?= =?UTF-8?q?=20=D0=B2=D1=81=D0=B5=D1=85=20=D0=B2=D0=B5=D1=89=D0=B5=D0=B9=20?= =?UTF-8?q?=D0=BF=D0=BE=D0=BB=D1=8C=D0=B7=D0=BE=D0=B2=D0=B0=D1=82=D0=B5?= =?UTF-8?q?=D0=BB=D1=8F=20=D0=BC=D0=B0=D1=81=D1=81=D0=B8=D0=B2=20=D0=B1?= =?UTF-8?q?=D1=80=D0=BE=D0=BD=D0=B8=D1=80=D0=BE=D0=B2=D0=B0=D0=BD=D0=B8?= =?UTF-8?q?=D0=B9=20=D0=BD=D0=B0=20=D0=BF=D0=BE=D1=81=D0=BB=D0=B5=D0=B4?= =?UTF-8?q?=D0=BD=D0=B5=D0=B5=20=D0=B7=D0=B0=D0=B2=D0=B5=D1=80=D1=88=D0=B5?= =?UTF-8?q?=D0=BD=D0=BD=D0=BE=D0=B5=20=D0=B8=20=D0=BF=D0=B5=D1=80=D0=B2?= =?UTF-8?q?=D0=BE=D0=B5=20=D0=B1=D0=BB=D0=B8=D0=B6=D0=B0=D0=B9=D1=88=D0=B5?= =?UTF-8?q?=D0=B5=20=D0=B1=D1=80=D0=BE=D0=BD=D0=B8=D1=80=D0=BE=D0=B2=D0=B0?= =?UTF-8?q?=D0=BD=D0=B8=D0=B5.?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../booking/service/BookingServiceImpl.java | 4 ++-- .../practicum/shareit/item/ItemController.java | 2 +- .../ru/practicum/shareit/item/dto/ItemDto.java | 10 ---------- .../shareit/item/service/ItemService.java | 2 +- .../shareit/item/service/ItemServiceImpl.java | 16 +++++++++++++--- 5 files changed, 17 insertions(+), 17 deletions(-) diff --git a/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java b/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java index 4e8205f..bb42749 100644 --- a/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java +++ b/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java @@ -13,7 +13,7 @@ import ru.practicum.shareit.exception.NotAccessException; import ru.practicum.shareit.exception.NotFoundException; import ru.practicum.shareit.exception.ValidationException; -import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.dto.ItemByIdDto; import ru.practicum.shareit.item.mapper.ItemMapper; import ru.practicum.shareit.item.model.Item; import ru.practicum.shareit.item.service.ItemService; @@ -100,7 +100,7 @@ public List getBookingByUser(Long bookerId, State state) { public List getBookingByItemsUser(Long userOwnerItemId, State state) { log.info("Запрос всех забронированных вещей пользователя с ID = " + userOwnerItemId); userService.getUserWithCheck(userOwnerItemId); - List itemIds = itemService.getItemsByUserId(userOwnerItemId).stream().map(ItemDto::getId).toList(); + List itemIds = itemService.getItemsByUserId(userOwnerItemId).stream().map(ItemByIdDto::getId).toList(); return switch (state) { case ALL -> bookingRepository.findAllByItemIdInOrderByStartDesc(itemIds).stream() .map(booking -> BookingMapper.toBookingDtoResponse( diff --git a/src/main/java/ru/practicum/shareit/item/ItemController.java b/src/main/java/ru/practicum/shareit/item/ItemController.java index 3f1858f..1176090 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemController.java +++ b/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -19,7 +19,7 @@ public class ItemController { private final String userIdHeader = "X-Sharer-User-Id"; @GetMapping - public List getItemsByUserId(@RequestHeader(userIdHeader) Long userId) { + public List getItemsByUserId(@RequestHeader(userIdHeader) Long userId) { return itemService.getItemsByUserId(userId); } 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 ea81900..442e215 100644 --- a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java +++ b/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java @@ -5,7 +5,6 @@ import lombok.AllArgsConstructor; import lombok.Data; import lombok.NoArgsConstructor; -import ru.practicum.shareit.booking.dto.BookingDtoResponse; import java.util.List; @@ -23,13 +22,4 @@ public class ItemDto { private List comments; - private List bookings; - - public ItemDto(Long id, String name, String description, Boolean available, List comments) { - this.id = id; - this.name = name; - this.description = description; - this.available = available; - this.comments = comments; - } } 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 05c04ce..f775db4 100644 --- a/src/main/java/ru/practicum/shareit/item/service/ItemService.java +++ b/src/main/java/ru/practicum/shareit/item/service/ItemService.java @@ -8,7 +8,7 @@ import java.util.List; public interface ItemService { - List getItemsByUserId(Long userId); + List getItemsByUserId(Long userId); Item getItemWithCheck(Long itemId); 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 8696c70..bbdd0c6 100644 --- a/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java +++ b/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java @@ -38,7 +38,7 @@ public class ItemServiceImpl implements ItemService { @Override - public List getItemsByUserId(Long userId) { + public List getItemsByUserId(Long userId) { log.info("Поиск вещей пользователя с ID = {}", userId); User user = userService.getUserWithCheck(userId); List itemsByUserId = itemRepository.findAllByOwner(user); @@ -47,8 +47,18 @@ public List getItemsByUserId(Long userId) { .map(Item::getId) .toList()); return itemsByUserId.stream() - .map(ItemMapper::toItemDto) - .peek(item -> item.setBookings(bookingsByIdItem.get(item.getId()))) + .map(item -> { + List bookings = bookingsByIdItem.getOrDefault(item.getId(), new ArrayList<>()); + BookingDtoResponse lastBooking = bookings.stream() + .filter(booking -> booking.getStatus().equals(Status.APPROVED)) + .max(Comparator.comparing(BookingDtoResponse::getEnd)) + .orElse(null); + BookingDtoResponse nextBooking = bookings.stream() + .filter(booking -> booking.getStatus().equals(Status.APPROVED)) + .min(Comparator.comparing(BookingDtoResponse::getStart)) + .orElse(null); + return ItemMapper.toItemByIdDto(item, nextBooking, lastBooking); + }) .toList(); }