diff --git a/pom.xml b/pom.xml
index c890aeb..2b0e3ee 100644
--- a/pom.xml
+++ b/pom.xml
@@ -41,6 +41,11 @@
runtime
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
org.projectlombok
lombok
diff --git a/src/main/java/ru/practicum/shareit/Constants.java b/src/main/java/ru/practicum/shareit/Constants.java
new file mode 100644
index 0000000..efa7be6
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/Constants.java
@@ -0,0 +1,6 @@
+package ru.practicum.shareit;
+
+public class Constants {
+ public static final String USER_ID_HEADER = "X-Sharer-User-Id";
+ public static final String ALL = "ALL";
+}
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 2d9c666..0000000
--- a/src/main/java/ru/practicum/shareit/booking/Booking.java
+++ /dev/null
@@ -1,7 +0,0 @@
-package ru.practicum.shareit.booking;
-
-/**
- * TODO Sprint add-bookings.
- */
-public class Booking {
-}
diff --git a/src/main/java/ru/practicum/shareit/booking/BookingController.java b/src/main/java/ru/practicum/shareit/booking/BookingController.java
index b94493d..501fc78 100644
--- a/src/main/java/ru/practicum/shareit/booking/BookingController.java
+++ b/src/main/java/ru/practicum/shareit/booking/BookingController.java
@@ -1,12 +1,66 @@
package ru.practicum.shareit.booking;
-import org.springframework.web.bind.annotation.RequestMapping;
-import org.springframework.web.bind.annotation.RestController;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.Positive;
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+import ru.practicum.shareit.booking.dto.BookingDto;
+import ru.practicum.shareit.booking.enums.BookingState;
+import ru.practicum.shareit.booking.model.Booking;
+import ru.practicum.shareit.booking.service.BookingService;
+
+import java.util.Collection;
+
+import static ru.practicum.shareit.Constants.ALL;
+import static ru.practicum.shareit.Constants.USER_ID_HEADER;
/**
* TODO Sprint add-bookings.
*/
@RestController
@RequestMapping(path = "/bookings")
+@RequiredArgsConstructor
public class BookingController {
+
+ private final BookingService bookingService;
+
+ @PostMapping
+ public Booking createBooking(@RequestHeader(USER_ID_HEADER) @Positive Long userId,
+ @Valid @RequestBody BookingDto bookingDto) {
+
+ return bookingService.createBooking(userId, bookingDto);
+ }
+
+ @PatchMapping("/{bookingId}")
+ public Booking updateBookingStatus(
+ @RequestHeader(USER_ID_HEADER) @Positive Long userId,
+ @PathVariable @Positive Long bookingId,
+ @RequestParam Boolean approved) {
+
+ return bookingService.updateBookingStatus(userId, bookingId, approved);
+ }
+
+ @GetMapping("/{bookingId}")
+ public Booking getBooking(
+ @RequestHeader(USER_ID_HEADER) @Positive Long userId,
+ @PathVariable @Positive Long bookingId) {
+
+ return bookingService.getBooking(userId, bookingId);
+ }
+
+ @GetMapping
+ public Collection findByBookerAndState(
+ @RequestHeader(USER_ID_HEADER) @Positive Long bookerId,
+ @RequestParam(defaultValue = ALL) BookingState state) {
+
+ return bookingService.findByBookerAndState(bookerId, state);
+ }
+
+ @GetMapping("/owner")
+ public Collection findByOwnerAndState(
+ @RequestHeader(USER_ID_HEADER) @Positive Long ownerId,
+ @RequestParam(defaultValue = ALL) BookingState state) {
+
+ return bookingService.findByOwnerAndState(ownerId, state);
+ }
}
diff --git a/src/main/java/ru/practicum/shareit/booking/BookingMapper.java b/src/main/java/ru/practicum/shareit/booking/BookingMapper.java
new file mode 100644
index 0000000..2b63de2
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/booking/BookingMapper.java
@@ -0,0 +1,16 @@
+package ru.practicum.shareit.booking;
+
+import ru.practicum.shareit.booking.dto.BookingDto;
+import ru.practicum.shareit.booking.model.Booking;
+
+public class BookingMapper {
+
+ public static Booking toBooking(BookingDto bookingDto) {
+ Booking booking = new Booking();
+
+ booking.setStart(bookingDto.getStart());
+ booking.setEnd(bookingDto.getEnd());
+
+ return booking;
+ }
+}
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..07fa13c
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/booking/dao/BookingRepository.java
@@ -0,0 +1,55 @@
+package ru.practicum.shareit.booking.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+import ru.practicum.shareit.booking.model.Booking;
+import ru.practicum.shareit.booking.dto.LastAndNextDate;
+import ru.practicum.shareit.booking.enums.BookingStatus;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Repository
+public interface BookingRepository extends JpaRepository {
+
+ @Query("""
+ SELECT
+ b.item.id as itemId,
+ MAX(CASE WHEN b.start < CURRENT_TIMESTAMP THEN b.start ELSE NULL END) as lastBooking,
+ MIN(CASE WHEN b.start > CURRENT_TIMESTAMP THEN b.start ELSE NULL END) as nextBooking
+ FROM Booking b
+ WHERE b.item.ownerId = ?1
+ GROUP BY b.item.id
+ """)
+ List findLastAndNextDatesByOwnerId(Long ownerId);
+
+ Boolean existsByItemIdAndBookerIdAndStatusIsAndEndBefore(Long itemId, Long bookerId,
+ BookingStatus status, LocalDateTime now);
+
+ // by booker
+ List findByBookerIdOrderByEndDesc(Long bookerId);
+
+ List findByBookerIdAndStatusIsOrderByEndDesc(Long bookerId, BookingStatus status);
+
+ List findByBookerIdAndStartIsBeforeAndEndIsBeforeOrderByEndDesc(Long bookerId,
+ LocalDateTime start,
+ LocalDateTime end);
+
+ List findByBookerIdAndStartIsAfterOrderByEndDesc(Long bookerId, LocalDateTime date);
+
+ List findByBookerIdAndEndIsBeforeOrderByEndDesc(Long bookerId, LocalDateTime date);
+
+ // by owner
+ List findByItemOwnerIdOrderByEndDesc(Long ownerId);
+
+ List findByItemOwnerIdAndStatusIsOrderByEndDesc(Long ownerId, BookingStatus status);
+
+ List findByItemOwnerIdAndStartIsBeforeAndEndIsBeforeOrderByEndDesc(Long ownerId,
+ LocalDateTime start,
+ LocalDateTime end);
+
+ List findByItemOwnerIdAndStartIsAfterOrderByEndDesc(Long ownerId, LocalDateTime date);
+
+ List findByItemOwnerIdAndEndIsBeforeOrderByEndDesc(Long ownerId, LocalDateTime date);
+}
diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java b/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java
index 861de9e..14e48fa 100644
--- a/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java
+++ b/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java
@@ -1,7 +1,21 @@
package ru.practicum.shareit.booking.dto;
-/**
- * TODO Sprint add-bookings.
- */
+import jakarta.validation.constraints.FutureOrPresent;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Positive;
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
public class BookingDto {
+ @NotNull
+ @Positive
+ private Long itemId;
+ @NotNull
+ @FutureOrPresent
+ private LocalDateTime start;
+ @NotNull
+ @FutureOrPresent
+ private LocalDateTime end;
}
diff --git a/src/main/java/ru/practicum/shareit/booking/dto/LastAndNextDate.java b/src/main/java/ru/practicum/shareit/booking/dto/LastAndNextDate.java
new file mode 100644
index 0000000..fb6d2af
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/booking/dto/LastAndNextDate.java
@@ -0,0 +1,11 @@
+package ru.practicum.shareit.booking.dto;
+
+import java.time.LocalDateTime;
+
+public interface LastAndNextDate {
+ Long getItemId();
+
+ LocalDateTime getLastBooking();
+
+ LocalDateTime getNextBooking();
+}
diff --git a/src/main/java/ru/practicum/shareit/booking/enums/BookingState.java b/src/main/java/ru/practicum/shareit/booking/enums/BookingState.java
new file mode 100644
index 0000000..8222368
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/booking/enums/BookingState.java
@@ -0,0 +1,5 @@
+package ru.practicum.shareit.booking.enums;
+
+public enum BookingState {
+ ALL, CURRENT, PAST, FUTURE, WAITING, REJECTED
+}
diff --git a/src/main/java/ru/practicum/shareit/booking/enums/BookingStatus.java b/src/main/java/ru/practicum/shareit/booking/enums/BookingStatus.java
new file mode 100644
index 0000000..d7314c8
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/booking/enums/BookingStatus.java
@@ -0,0 +1,5 @@
+package ru.practicum.shareit.booking.enums;
+
+public enum BookingStatus {
+ WAITING, REJECTED, APPROVED
+}
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..7668627
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/booking/model/Booking.java
@@ -0,0 +1,35 @@
+package ru.practicum.shareit.booking.model;
+
+import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import ru.practicum.shareit.booking.enums.BookingStatus;
+import ru.practicum.shareit.item.model.Item;
+import ru.practicum.shareit.user.model.User;
+
+import java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@Table(name = "bookings", schema = "public")
+public class Booking {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+ @Column(name = "start_date")
+ private LocalDateTime start;
+ @Column(name = "end_date")
+ private LocalDateTime end;
+ @ManyToOne
+ @JoinColumn(name = "item_id")
+ private Item item;
+ @ManyToOne
+ @JoinColumn(name = "booker_id")
+ private User booker;
+ @Column(name = "status")
+ @Enumerated(EnumType.STRING)
+ private BookingStatus 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..3743d05
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/booking/service/BookingService.java
@@ -0,0 +1,19 @@
+package ru.practicum.shareit.booking.service;
+
+import ru.practicum.shareit.booking.model.Booking;
+import ru.practicum.shareit.booking.dto.BookingDto;
+import ru.practicum.shareit.booking.enums.BookingState;
+
+import java.util.Collection;
+
+public interface BookingService {
+ Booking createBooking(Long bookerId, BookingDto bookingDto);
+
+ Booking updateBookingStatus(Long userId, Long bookingId, Boolean approved);
+
+ Booking getBooking(Long userId, Long bookingId);
+
+ Collection findByBookerAndState(Long bookerId, BookingState state);
+
+ Collection findByOwnerAndState(Long ownerId, BookingState state);
+}
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..b90fb4d
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java
@@ -0,0 +1,140 @@
+package ru.practicum.shareit.booking.service;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import ru.practicum.shareit.booking.BookingMapper;
+import ru.practicum.shareit.booking.dao.BookingRepository;
+import ru.practicum.shareit.booking.dto.BookingDto;
+import ru.practicum.shareit.booking.enums.BookingState;
+import ru.practicum.shareit.booking.enums.BookingStatus;
+import ru.practicum.shareit.booking.model.Booking;
+import ru.practicum.shareit.item.dao.ItemRepository;
+import ru.practicum.shareit.item.model.Item;
+import ru.practicum.shareit.user.dao.UserRepository;
+import ru.practicum.shareit.user.model.User;
+import ru.practicum.shareit.validation.exceptions.BadRequestException;
+import ru.practicum.shareit.validation.exceptions.ForbiddenException;
+import ru.practicum.shareit.validation.exceptions.NotFoundException;
+
+import java.time.LocalDateTime;
+import java.util.Collection;
+
+@Service
+@RequiredArgsConstructor
+public class BookingServiceImpl implements BookingService {
+
+ private final BookingRepository bookingRepository;
+ private final UserRepository userRepository;
+ private final ItemRepository itemRepository;
+
+ @Override
+ @Transactional
+ public Booking createBooking(Long bookerId, BookingDto bookingDto) {
+ throwIfDatesInvalid(bookingDto);
+
+ User user = userRepository.findById(bookerId)
+ .orElseThrow(() -> new NotFoundException("Пользователь с id " + bookerId + " не найден"));
+ Item item = itemRepository.findById(bookingDto.getItemId())
+ .orElseThrow(() -> new NotFoundException("Предмет с id " + bookingDto.getItemId() + " не найден"));
+
+ if (!item.getIsAvailable()) {
+ throw new BadRequestException("Предмет не доступен");
+ }
+
+ Booking booking = BookingMapper.toBooking(bookingDto);
+ booking.setItem(item);
+ booking.setBooker(user);
+ booking.setStatus(BookingStatus.WAITING);
+
+ return bookingRepository.save(booking);
+ }
+
+ @Override
+ @Transactional
+ public Booking updateBookingStatus(Long userId, Long bookingId, Boolean approved) {
+ Booking booking = bookingRepository.findById(bookingId)
+ .orElseThrow(() -> new NotFoundException("Бронирование с id " + bookingId + " не найдено"));
+
+ if (!isOwner(booking, userId)) {
+ throw new ForbiddenException("Только владелец предмета может поменять статус");
+ }
+
+ if (approved) {
+ booking.setStatus(BookingStatus.APPROVED);
+ } else {
+ booking.setStatus(BookingStatus.REJECTED);
+ }
+
+ return bookingRepository.save(booking);
+ }
+
+ @Override
+ public Booking getBooking(Long userId, Long bookingId) {
+ Booking booking = bookingRepository.findById(bookingId)
+ .orElseThrow(() -> new NotFoundException("Бронирование с id " + bookingId + " не найдено"));
+
+ if (!isBooker(booking, userId) && !isOwner(booking, userId)) {
+ throw new ForbiddenException("Только владелец или арендатор могут посмотреть бронирование");
+ }
+
+ return booking;
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Collection findByBookerAndState(Long bookerId, BookingState state) {
+ if (!userRepository.existsById(bookerId)) {
+ throw new NotFoundException("Пользователь с id " + bookerId + " не найден");
+ }
+
+ LocalDateTime now = LocalDateTime.now();
+
+ return switch (state) {
+ case REJECTED ->
+ bookingRepository.findByBookerIdAndStatusIsOrderByEndDesc(bookerId, BookingStatus.REJECTED);
+ case WAITING -> bookingRepository.findByBookerIdAndStatusIsOrderByEndDesc(bookerId, BookingStatus.WAITING);
+ case CURRENT ->
+ bookingRepository.findByBookerIdAndStartIsBeforeAndEndIsBeforeOrderByEndDesc(bookerId, now, now);
+ case PAST -> bookingRepository.findByBookerIdAndEndIsBeforeOrderByEndDesc(bookerId, now);
+ case FUTURE -> bookingRepository.findByBookerIdAndStartIsAfterOrderByEndDesc(bookerId, now);
+ default -> bookingRepository.findByBookerIdOrderByEndDesc(bookerId);
+ };
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Collection findByOwnerAndState(Long ownerId, BookingState state) {
+ if (!userRepository.existsById(ownerId)) {
+ throw new NotFoundException("Пользователь с id " + ownerId + " не найден");
+ }
+
+ LocalDateTime now = LocalDateTime.now();
+
+ return switch (state) {
+ case REJECTED ->
+ bookingRepository.findByItemOwnerIdAndStatusIsOrderByEndDesc(ownerId, BookingStatus.REJECTED);
+ case WAITING ->
+ bookingRepository.findByItemOwnerIdAndStatusIsOrderByEndDesc(ownerId, BookingStatus.WAITING);
+ case CURRENT ->
+ bookingRepository.findByItemOwnerIdAndStartIsBeforeAndEndIsBeforeOrderByEndDesc(ownerId, now, now);
+ case PAST -> bookingRepository.findByItemOwnerIdAndEndIsBeforeOrderByEndDesc(ownerId, now);
+ case FUTURE -> bookingRepository.findByItemOwnerIdAndStartIsAfterOrderByEndDesc(ownerId, now);
+ default -> bookingRepository.findByItemOwnerIdOrderByEndDesc(ownerId);
+ };
+ }
+
+ private void throwIfDatesInvalid(BookingDto bookingDto) {
+ if (!bookingDto.getStart().isBefore(bookingDto.getEnd())) {
+ throw new BadRequestException("Начало не может быть после конца");
+ }
+ }
+
+ private boolean isBooker(Booking booking, Long userId) {
+ return booking.getBooker().getId().equals(userId);
+ }
+
+ private boolean isOwner(Booking booking, Long userId) {
+ return booking.getItem().getOwnerId().equals(userId);
+ }
+}
diff --git a/src/main/java/ru/practicum/shareit/item/ItemController.java b/src/main/java/ru/practicum/shareit/item/ItemController.java
index 4cdcdc7..ba963fe 100644
--- a/src/main/java/ru/practicum/shareit/item/ItemController.java
+++ b/src/main/java/ru/practicum/shareit/item/ItemController.java
@@ -4,6 +4,8 @@
import lombok.RequiredArgsConstructor;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
+import ru.practicum.shareit.item.dto.CommentCreate;
+import ru.practicum.shareit.item.dto.CommentDto;
import ru.practicum.shareit.item.dto.ItemDto;
import ru.practicum.shareit.item.service.ItemService;
import ru.practicum.shareit.validation.OnCreate;
@@ -11,15 +13,13 @@
import java.util.Collection;
-/**
- * TODO Sprint add-controllers.
- */
+import static ru.practicum.shareit.Constants.USER_ID_HEADER;
+
@RestController
@RequestMapping("/items")
@RequiredArgsConstructor
public class ItemController {
- private static final String USER_ID_HEADER = "X-Sharer-User-Id";
private final ItemService itemService;
@GetMapping("/{itemId}")
@@ -52,4 +52,11 @@ public ItemDto updateItem(
return itemService.updateItem(userId, itemId, itemDto);
}
+ @PostMapping("/{itemId}/comment")
+ public CommentDto addComment(@RequestHeader(USER_ID_HEADER) @Positive Long userId,
+ @PathVariable @Positive Long itemId,
+ @RequestBody CommentCreate commentText) {
+
+ return itemService.addComment(userId, itemId, commentText);
+ }
}
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..0e65b07
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/item/dao/CommentRepository.java
@@ -0,0 +1,15 @@
+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.model.Comment;
+
+import java.util.Collection;
+
+public interface CommentRepository extends JpaRepository {
+
+ Collection findByItemId(Long itemId);
+
+ @Query("SELECT c FROM Comment c JOIN FETCH c.item WHERE c.item.ownerId = ?1")
+ Collection findByItemOwnerId(Long ownerId);
+}
diff --git a/src/main/java/ru/practicum/shareit/item/dao/ItemRepository.java b/src/main/java/ru/practicum/shareit/item/dao/ItemRepository.java
index d785762..5592d22 100644
--- a/src/main/java/ru/practicum/shareit/item/dao/ItemRepository.java
+++ b/src/main/java/ru/practicum/shareit/item/dao/ItemRepository.java
@@ -1,18 +1,21 @@
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.model.Item;
import java.util.Collection;
-import java.util.Optional;
-public interface ItemRepository {
- Optional- getItem(Long itemId);
+public interface ItemRepository extends JpaRepository
- {
- Collection
- searchItems(String text);
+ Collection
- findByOwnerId(Long userId);
- Collection
- getItems(Long userId);
-
- Item addItem(Item item);
-
- Optional
- updateItem(Item item);
+ @Query("""
+ SELECT i
+ FROM Item i
+ WHERE i.isAvailable = true
+ AND (LOWER(i.name) LIKE LOWER(%:text%)
+ OR LOWER(i.description) LIKE LOWER(%:text%))
+ """)
+ Collection
- searchByNameOrDescription(String text);
}
diff --git a/src/main/java/ru/practicum/shareit/item/dao/ItemRepositoryInMemory.java b/src/main/java/ru/practicum/shareit/item/dao/ItemRepositoryInMemory.java
deleted file mode 100644
index e899f9b..0000000
--- a/src/main/java/ru/practicum/shareit/item/dao/ItemRepositoryInMemory.java
+++ /dev/null
@@ -1,78 +0,0 @@
-package ru.practicum.shareit.item.dao;
-
-import org.springframework.stereotype.Repository;
-import ru.practicum.shareit.item.model.Item;
-
-import java.util.*;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicLong;
-
-@Repository
-public class ItemRepositoryInMemory implements ItemRepository {
-
- private final Map storage = new ConcurrentHashMap<>();
- private final AtomicLong idCounter = new AtomicLong(1L);
-
- @Override
- public Optional
- getItem(Long itemId) {
- return Optional.ofNullable(storage.get(itemId));
- }
-
- @Override
- public Collection
- searchItems(String text) {
- return storage.values()
- .stream()
- .filter(Item::getIsAvailable)
- .filter(item ->
- item.getName().toLowerCase(Locale.ROOT).contains(text) ||
- item.getDescription().toLowerCase(Locale.ROOT).contains(text))
- .toList();
- }
-
- @Override
- public Collection
- getItems(Long userId) {
- return storage.values()
- .stream()
- .filter(item -> Objects.equals(item.getOwnerId(), userId))
- .toList();
- }
-
- @Override
- public Item addItem(Item item) {
- Long id = idCounter.getAndIncrement();
- item.setId(id);
- storage.put(id, item);
-
- return item;
- }
-
- @Override
- public Optional
- updateItem(Item newItem) {
- if (!storage.containsKey(newItem.getId())) {
- return Optional.empty();
- }
-
- Item oldItem = storage.get(newItem.getId());
- patchItem(oldItem, newItem);
-
- return Optional.of(oldItem);
- }
-
- private void patchItem(Item oldItem, Item newItem) {
- String name = newItem.getName();
- String description = newItem.getDescription();
- Boolean isAvailable = newItem.getIsAvailable();
-
- if (name != null) {
- oldItem.setName(name);
- }
-
- if (description != null) {
- oldItem.setDescription(description);
- }
-
- if (isAvailable != null) {
- oldItem.setIsAvailable(isAvailable);
- }
- }
-}
diff --git a/src/main/java/ru/practicum/shareit/item/dto/CommentCreate.java b/src/main/java/ru/practicum/shareit/item/dto/CommentCreate.java
new file mode 100644
index 0000000..3d52cde
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/item/dto/CommentCreate.java
@@ -0,0 +1,4 @@
+package ru.practicum.shareit.item.dto;
+
+public record CommentCreate(String text) {
+}
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..1a228f2
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/item/dto/CommentDto.java
@@ -0,0 +1,14 @@
+package ru.practicum.shareit.item.dto;
+
+import lombok.Data;
+
+import java.time.LocalDateTime;
+
+@Data
+public class CommentDto {
+ private Long id;
+ private Long itemId;
+ private String authorName;
+ private String text;
+ 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 c26a3ca..f5de282 100644
--- a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java
+++ b/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java
@@ -9,9 +9,10 @@
import ru.practicum.shareit.validation.OnCreate;
import ru.practicum.shareit.validation.OnUpdate;
-/**
- * TODO Sprint add-controllers.
- */
+import java.time.LocalDateTime;
+import java.util.Collection;
+
+
@Data
@AllArgsConstructor
@NoArgsConstructor
@@ -25,4 +26,7 @@ public class ItemDto {
private String description;
@NotNull(groups = OnCreate.class, message = "Статус должен быть заполнен")
private Boolean available;
+ private LocalDateTime lastBooking;
+ private LocalDateTime nextBooking;
+ private Collection comments;
}
diff --git a/src/main/java/ru/practicum/shareit/item/mappers/CommentMapper.java b/src/main/java/ru/practicum/shareit/item/mappers/CommentMapper.java
new file mode 100644
index 0000000..1d69853
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/item/mappers/CommentMapper.java
@@ -0,0 +1,17 @@
+package ru.practicum.shareit.item.mappers;
+
+import ru.practicum.shareit.item.dto.CommentDto;
+import ru.practicum.shareit.item.model.Comment;
+
+public class CommentMapper {
+ public static CommentDto toCommentDto(Comment comment) {
+ CommentDto commentDto = new CommentDto();
+ commentDto.setId(comment.getId());
+ commentDto.setItemId(comment.getItem().getId());
+ commentDto.setAuthorName(comment.getAuthor().getName());
+ commentDto.setText(comment.getText());
+ commentDto.setCreated(comment.getCreated());
+
+ return commentDto;
+ }
+}
diff --git a/src/main/java/ru/practicum/shareit/item/ItemMapper.java b/src/main/java/ru/practicum/shareit/item/mappers/ItemMapper.java
similarity index 61%
rename from src/main/java/ru/practicum/shareit/item/ItemMapper.java
rename to src/main/java/ru/practicum/shareit/item/mappers/ItemMapper.java
index 05c0b4d..e886ca2 100644
--- a/src/main/java/ru/practicum/shareit/item/ItemMapper.java
+++ b/src/main/java/ru/practicum/shareit/item/mappers/ItemMapper.java
@@ -1,4 +1,4 @@
-package ru.practicum.shareit.item;
+package ru.practicum.shareit.item.mappers;
import ru.practicum.shareit.item.dto.ItemDto;
import ru.practicum.shareit.item.model.Item;
@@ -21,4 +21,18 @@ public static ItemDto toItemDto(Item item) {
itemDto.setAvailable(item.getIsAvailable());
return itemDto;
}
+
+ public static Item merge(Item item, ItemDto itemDto) {
+ if (itemDto.getName() != null) {
+ item.setName(itemDto.getName());
+ }
+ if (itemDto.getDescription() != null) {
+ item.setDescription(itemDto.getDescription());
+ }
+ if (itemDto.getAvailable() != null) {
+ item.setIsAvailable(itemDto.getAvailable());
+ }
+
+ return item;
+ }
}
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..8fe06e4
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/item/model/Comment.java
@@ -0,0 +1,32 @@
+package ru.practicum.shareit.item.model;
+
+import jakarta.persistence.*;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import ru.practicum.shareit.user.model.User;
+
+import java.time.LocalDateTime;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@Entity
+@Table(name = "comments", schema = "public")
+public class Comment {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private Long id;
+ @ManyToOne
+ @JoinColumn(name = "item_id")
+ private Item item;
+ @ManyToOne
+ @JoinColumn(name = "author_id")
+ private User author;
+ @Column(name = "content")
+ private String text;
+ @Column(name = "created_at")
+ 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 93f135d..5b85018 100644
--- a/src/main/java/ru/practicum/shareit/item/model/Item.java
+++ b/src/main/java/ru/practicum/shareit/item/model/Item.java
@@ -1,19 +1,25 @@
package ru.practicum.shareit.item.model;
+import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
-/**
- * TODO Sprint add-controllers.
- */
@Data
@AllArgsConstructor
@NoArgsConstructor
+@Entity
+@Table(name = "items", schema = "public")
public class Item {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
+ @Column(name = "name", nullable = false)
private String name;
+ @Column(name = "description", nullable = false)
private String description;
+ @Column(name = "available", nullable = false)
private Boolean isAvailable;
+ @Column(name = "owner_id", nullable = false)
private Long ownerId;
}
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 d5ac715..d2add2d 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,7 @@
package ru.practicum.shareit.item.service;
+import ru.practicum.shareit.item.dto.CommentCreate;
+import ru.practicum.shareit.item.dto.CommentDto;
import ru.practicum.shareit.item.dto.ItemDto;
import java.util.Collection;
@@ -14,4 +16,6 @@ public interface ItemService {
ItemDto addItem(Long userId, ItemDto itemDto);
ItemDto updateItem(Long userId, Long itemId, ItemDto itemDto);
+
+ CommentDto addComment(Long userId, Long itemId, CommentCreate commentText);
}
\ No newline at end of file
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 4b633b8..d8cd5eb 100644
--- a/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java
+++ b/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java
@@ -2,34 +2,52 @@
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
-import ru.practicum.shareit.item.ItemMapper;
+import org.springframework.transaction.annotation.Transactional;
+import ru.practicum.shareit.booking.dao.BookingRepository;
+import ru.practicum.shareit.booking.dto.LastAndNextDate;
+import ru.practicum.shareit.booking.enums.BookingStatus;
+import ru.practicum.shareit.item.dao.CommentRepository;
import ru.practicum.shareit.item.dao.ItemRepository;
+import ru.practicum.shareit.item.dto.CommentCreate;
+import ru.practicum.shareit.item.dto.CommentDto;
import ru.practicum.shareit.item.dto.ItemDto;
+import ru.practicum.shareit.item.mappers.CommentMapper;
+import ru.practicum.shareit.item.mappers.ItemMapper;
+import ru.practicum.shareit.item.model.Comment;
import ru.practicum.shareit.item.model.Item;
-import ru.practicum.shareit.user.dao.UserRepository;
-import ru.practicum.shareit.validation.NotFoundException;
+import ru.practicum.shareit.user.model.User;
+import ru.practicum.shareit.user.service.UserService;
+import ru.practicum.shareit.validation.exceptions.BadRequestException;
+import ru.practicum.shareit.validation.exceptions.NotFoundException;
+import java.time.LocalDateTime;
import java.util.Collection;
import java.util.Collections;
-import java.util.Locale;
-import java.util.Optional;
+import java.util.List;
+import java.util.Map;
+import java.util.function.Function;
+import java.util.stream.Collectors;
@Service
@RequiredArgsConstructor
public class ItemServiceImpl implements ItemService {
private final ItemRepository itemRepository;
- private final UserRepository userRepository;
+ private final CommentRepository commentRepository;
+ private final BookingRepository bookingRepository;
+ private final UserService userService;
@Override
+ @Transactional(readOnly = true)
public ItemDto getItem(Long itemId) {
- Optional
- maybeItem = itemRepository.getItem(itemId);
- if (maybeItem.isPresent()) {
- return ItemMapper.toItemDto(maybeItem.get());
- }
+ Item item = itemRepository.findById(itemId)
+ .orElseThrow(() -> new NotFoundException("Предмет с id " + itemId + " не найден"));
+
+ ItemDto itemDto = ItemMapper.toItemDto(item);
+ Collection comments = commentRepository.findByItemId(itemId);
- throw new NotFoundException("Предмет с id " + itemId + " не найден");
+ return enrichWithComments(itemDto, comments);
}
@Override
@@ -38,55 +56,149 @@ public Collection searchItems(String text) {
return Collections.emptyList();
}
- text = text.toLowerCase(Locale.ROOT);
-
- return itemRepository.searchItems(text)
+ return itemRepository.searchByNameOrDescription(text)
.stream()
.map(ItemMapper::toItemDto)
.toList();
}
@Override
- public Collection getItems(Long userId) {
- throwIfUserNotFound(userId);
+ @Transactional(readOnly = true)
+ public Collection getItems(Long ownerId) {
+ userService.throwIfUserNotFound(ownerId);
- Collection
- items = itemRepository.getItems(userId);
+ Collection
- items = itemRepository.findByOwnerId(ownerId);
- return items.stream()
- .map(ItemMapper::toItemDto)
- .toList();
+ Map datesMap = getDatesMap(ownerId);
+ Map> commentsMap = getCommentsMap(ownerId);
+
+ return enrichWithDatesAndComments(items, datesMap, commentsMap);
}
@Override
+ @Transactional
public ItemDto addItem(Long userId, ItemDto itemDto) {
- throwIfUserNotFound(userId);
+ userService.throwIfUserNotFound(userId);
Item item = ItemMapper.toItem(itemDto);
item.setOwnerId(userId);
- return ItemMapper.toItemDto(itemRepository.addItem(item));
+ return ItemMapper.toItemDto(itemRepository.save(item));
}
@Override
+ @Transactional
public ItemDto updateItem(Long userId, Long itemId, ItemDto itemDto) {
- throwIfUserNotFound(userId);
+ userService.throwIfUserNotFound(userId);
- Item item = ItemMapper.toItem(itemDto);
- item.setOwnerId(userId);
- item.setId(itemId);
+ Item currentItem = itemRepository.findById(itemId)
+ .orElseThrow(() -> new NotFoundException("Предмет с id " + itemId + " не найден"));
+
+ currentItem.setOwnerId(userId);
+ ItemMapper.merge(currentItem, itemDto);
+
+ return ItemMapper.toItemDto(itemRepository.save(currentItem));
+ }
- Optional
- maybeItem = itemRepository.updateItem(item);
+ @Override
+ @Transactional
+ public CommentDto addComment(Long userId, Long itemId, CommentCreate commentText) {
+
+ User user = userService.getUser(userId);
+ Item item = itemRepository.findById(itemId)
+ .orElseThrow(() -> new NotFoundException("Предмет с id " + itemId + " не найден"));
- if (maybeItem.isPresent()) {
- return ItemMapper.toItemDto(maybeItem.get());
+ if (!bookingRepository.existsByItemIdAndBookerIdAndStatusIsAndEndBefore(
+ itemId, userId, BookingStatus.APPROVED, LocalDateTime.now())) {
+ throw new BadRequestException("Пользователь никогда не брал предмет в аренду");
}
- throw new NotFoundException("Предмет с id " + itemId + " не найден");
+ Comment comment = Comment.builder()
+ .text(commentText.text())
+ .item(item)
+ .author(user)
+ .created(LocalDateTime.now())
+ .build();
+
+ return CommentMapper.toCommentDto(commentRepository.save(comment));
}
- private void throwIfUserNotFound(Long userId) {
- if (!userRepository.contains(userId)) {
- throw new NotFoundException("Пользователь с id " + userId + " не найден");
- }
+ /**
+ * Обогащает коллекцию ItemDto датами бронирований и комментариями.
+ *
+ * @param items исходная коллекция предметов
+ * @param datesMap таблица последних и следующих бронирований по Id предмета
+ * @param commentsMap таблица комментариев по Id предмета
+ * @return обогащенная коллекция ItemDto
+ */
+ private Collection enrichWithDatesAndComments(Collection
- items,
+ Map datesMap,
+ Map> commentsMap) {
+
+ return items.stream()
+ .map(item -> {
+ ItemDto dto = ItemMapper.toItemDto(item);
+
+ LastAndNextDate dates = datesMap.get(dto.getId());
+ if (dates != null) {
+ dto.setLastBooking(dates.getLastBooking());
+ dto.setNextBooking(dates.getNextBooking());
+ }
+
+ dto.setComments(commentsMap.getOrDefault(dto.getId(), Collections.emptyList()));
+
+ return dto;
+ })
+ .toList();
+ }
+
+ /**
+ * Обогащает ItemDto комментариями к предмету.
+ *
+ * @param dto DTO предмета для обогащения
+ * @return обогащенный ItemDto с комментариями
+ */
+ private ItemDto enrichWithComments(ItemDto dto, Collection comments) {
+ Collection commentsDto = comments.stream()
+ .map(CommentMapper::toCommentDto)
+ .toList();
+
+ dto.setComments(commentsDto);
+
+ return dto;
+ }
+
+ /**
+ * Создает таблицу последних и следующих бронирований для предметов владельца.
+ *
+ * @param ownerId ID владельца предметов
+ * @return {@code Map} где ключ - Id предмета
+ */
+ private Map getDatesMap(Long ownerId) {
+ List dates = bookingRepository.findLastAndNextDatesByOwnerId(ownerId);
+
+ return dates.stream()
+ .collect(Collectors.toMap(
+ LastAndNextDate::getItemId,
+ Function.identity()
+ ));
+ }
+
+ /**
+ * Создает таблицу комментариев для всех предметов владельца.
+ *
+ * @param ownerId ID владельца предметов
+ * @return {@code Map} где ключ - Id предмета
+ */
+ private Map> getCommentsMap(Long ownerId) {
+ Collection comments = commentRepository.findByItemOwnerId(ownerId);
+ return comments.stream()
+ .collect(Collectors.groupingBy(
+ comment -> comment.getItem().getId(),
+ Collectors.mapping(
+ CommentMapper::toCommentDto,
+ Collectors.toList()
+ )
+ ));
}
}
diff --git a/src/main/java/ru/practicum/shareit/user/UserMapper.java b/src/main/java/ru/practicum/shareit/user/UserMapper.java
index 7adf898..0bdf44f 100644
--- a/src/main/java/ru/practicum/shareit/user/UserMapper.java
+++ b/src/main/java/ru/practicum/shareit/user/UserMapper.java
@@ -10,4 +10,14 @@ public static User toUser(UserDto userDto) {
user.setEmail(userDto.getEmail());
return user;
}
+
+ public static User merge(User user, UserDto userDto) {
+ if (userDto.getName() != null) {
+ user.setName(userDto.getName());
+ }
+ if (userDto.getEmail() != null) {
+ user.setEmail(userDto.getEmail());
+ }
+ return user;
+ }
}
diff --git a/src/main/java/ru/practicum/shareit/user/dao/UserRepository.java b/src/main/java/ru/practicum/shareit/user/dao/UserRepository.java
index 2f185ef..d90d213 100644
--- a/src/main/java/ru/practicum/shareit/user/dao/UserRepository.java
+++ b/src/main/java/ru/practicum/shareit/user/dao/UserRepository.java
@@ -1,17 +1,7 @@
package ru.practicum.shareit.user.dao;
+import org.springframework.data.jpa.repository.JpaRepository;
import ru.practicum.shareit.user.model.User;
-import java.util.Optional;
-
-public interface UserRepository {
- Optional getUser(Long userId);
-
- User addUser(User user);
-
- Optional updateUser(User user);
-
- boolean deleteUser(Long userId);
-
- boolean contains(Long userId);
+public interface UserRepository extends JpaRepository {
}
diff --git a/src/main/java/ru/practicum/shareit/user/dao/UserRepositoryInMemory.java b/src/main/java/ru/practicum/shareit/user/dao/UserRepositoryInMemory.java
deleted file mode 100644
index aadb2c1..0000000
--- a/src/main/java/ru/practicum/shareit/user/dao/UserRepositoryInMemory.java
+++ /dev/null
@@ -1,82 +0,0 @@
-package ru.practicum.shareit.user.dao;
-
-import org.springframework.stereotype.Repository;
-import ru.practicum.shareit.user.model.User;
-import ru.practicum.shareit.validation.NotUniqueException;
-
-import java.util.Map;
-import java.util.Optional;
-import java.util.concurrent.ConcurrentHashMap;
-import java.util.concurrent.atomic.AtomicLong;
-
-@Repository
-public class UserRepositoryInMemory implements UserRepository {
-
- private final Map storage = new ConcurrentHashMap<>();
- private final AtomicLong idCounter = new AtomicLong(1L);
-
- @Override
- public Optional getUser(Long userId) {
- return Optional.ofNullable(storage.get(userId));
- }
-
- @Override
- public User addUser(User user) {
- if (isEmailTaken(user.getEmail())) {
- throw new NotUniqueException("Email " + user.getEmail() + " уже занят");
- }
-
- Long id = idCounter.getAndIncrement();
- user.setId(id);
- storage.put(id, user);
-
- return user;
- }
-
- @Override
- public Optional updateUser(User newUser) {
- if (!storage.containsKey(newUser.getId())) {
- return Optional.empty();
- }
-
- User oldUser = storage.get(newUser.getId());
- patchUser(oldUser, newUser);
-
- return Optional.of(oldUser);
-
- }
-
- @Override
- public boolean deleteUser(Long userId) {
- User user = storage.remove(userId);
-
- return user != null;
- }
-
- @Override
- public boolean contains(Long userId) {
- return storage.containsKey(userId);
- }
-
- private boolean isEmailTaken(String email) {
- return storage.values()
- .stream()
- .anyMatch(user -> user.getEmail().equals(email));
- }
-
- private void patchUser(User oldUser, User newUser) {
- String name = newUser.getName();
- String email = newUser.getEmail();
-
- if (name != null) {
- oldUser.setName(name);
- }
-
- if (email != null) {
- if (isEmailTaken(email)) {
- throw new NotUniqueException("Email " + email + " уже занят");
- }
- oldUser.setEmail(email);
- }
- }
-}
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 0a07207..122e90c 100644
--- a/src/main/java/ru/practicum/shareit/user/model/User.java
+++ b/src/main/java/ru/practicum/shareit/user/model/User.java
@@ -1,17 +1,21 @@
package ru.practicum.shareit.user.model;
+import jakarta.persistence.*;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
-/**
- * TODO Sprint add-controllers.
- */
@Data
@AllArgsConstructor
@NoArgsConstructor
+@Entity
+@Table(name = "users", schema = "public")
public class User {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
+ @Column(name = "name")
private String name;
+ @Column(name = "email", unique = true)
private String email;
}
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 ba3e769..bcc94cf 100644
--- a/src/main/java/ru/practicum/shareit/user/service/UserService.java
+++ b/src/main/java/ru/practicum/shareit/user/service/UserService.java
@@ -11,4 +11,6 @@ public interface UserService {
User updateUser(Long userId, UserDto userDto);
void deleteUser(Long userId);
+
+ void throwIfUserNotFound(Long userId);
}
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 82250d9..2e0f389 100644
--- a/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java
+++ b/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java
@@ -6,9 +6,7 @@
import ru.practicum.shareit.user.dao.UserRepository;
import ru.practicum.shareit.user.dto.UserDto;
import ru.practicum.shareit.user.model.User;
-import ru.practicum.shareit.validation.NotFoundException;
-
-import java.util.Optional;
+import ru.practicum.shareit.validation.exceptions.NotFoundException;
@Service
@RequiredArgsConstructor
@@ -18,31 +16,32 @@ public class UserServiceImpl implements UserService {
@Override
public User getUser(Long userId) {
- Optional maybeUser = userRepository.getUser(userId);
-
- return maybeUser.orElseThrow(() -> new NotFoundException("Пользователь с id " + userId + " не найден"));
+ return userRepository.findById(userId)
+ .orElseThrow(() -> new NotFoundException("Пользователь с id " + userId + " не найден"));
}
@Override
public User addUser(UserDto userDto) {
- return userRepository.addUser(UserMapper.toUser(userDto));
+ return userRepository.save(UserMapper.toUser(userDto));
}
@Override
public User updateUser(Long userId, UserDto userDto) {
- User user = UserMapper.toUser(userDto);
- user.setId(userId);
+ User currentUser = getUser(userId);
- Optional maybeUser = userRepository.updateUser(user);
+ UserMapper.merge(currentUser, userDto);
- return maybeUser.orElseThrow(() -> new NotFoundException("Пользователь с id " + userId + " не найден"));
+ return userRepository.save(currentUser);
}
@Override
public void deleteUser(Long userId) {
- boolean isDeleted = userRepository.deleteUser(userId);
+ throwIfUserNotFound(userId);
+ userRepository.deleteById(userId);
+ }
- if (!isDeleted) {
+ public void throwIfUserNotFound(Long userId) {
+ if (!userRepository.existsById(userId)) {
throw new NotFoundException("Пользователь с id " + userId + " не найден");
}
}
diff --git a/src/main/java/ru/practicum/shareit/validation/GlobalExceptionHandler.java b/src/main/java/ru/practicum/shareit/validation/GlobalExceptionHandler.java
index 64b34f4..7d246e7 100644
--- a/src/main/java/ru/practicum/shareit/validation/GlobalExceptionHandler.java
+++ b/src/main/java/ru/practicum/shareit/validation/GlobalExceptionHandler.java
@@ -1,11 +1,16 @@
package ru.practicum.shareit.validation;
+import org.hibernate.exception.ConstraintViolationException;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.validation.FieldError;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ControllerAdvice;
import org.springframework.web.bind.annotation.ExceptionHandler;
+import ru.practicum.shareit.validation.exceptions.BadRequestException;
+import ru.practicum.shareit.validation.exceptions.ForbiddenException;
+import ru.practicum.shareit.validation.exceptions.NotFoundException;
+import ru.practicum.shareit.validation.exceptions.NotUniqueException;
import java.util.Collections;
import java.util.HashMap;
@@ -37,4 +42,25 @@ public ResponseEntity