diff --git a/pom.xml b/pom.xml
index 2db888c..1b18148 100644
--- a/pom.xml
+++ b/pom.xml
@@ -51,15 +51,28 @@
h2
test
+
org.springframework.boot
spring-boot-starter-test
test
+
org.springframework.boot
spring-boot-starter-validation
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+ org.postgresql
+ postgresql
+ runtime
+
diff --git a/src/main/java/ru/practicum/shareit/ShareItApp.java b/src/main/java/ru/practicum/shareit/ShareItApp.java
index a00ad56..83b60e0 100644
--- a/src/main/java/ru/practicum/shareit/ShareItApp.java
+++ b/src/main/java/ru/practicum/shareit/ShareItApp.java
@@ -5,9 +5,7 @@
@SpringBootApplication
public class ShareItApp {
-
public static void main(String[] args) {
SpringApplication.run(ShareItApp.class, args);
}
-
}
diff --git a/src/main/java/ru/practicum/shareit/booking/controller/BookingController.java b/src/main/java/ru/practicum/shareit/booking/controller/BookingController.java
index 427a4b6..3498754 100644
--- a/src/main/java/ru/practicum/shareit/booking/controller/BookingController.java
+++ b/src/main/java/ru/practicum/shareit/booking/controller/BookingController.java
@@ -1,12 +1,63 @@
package ru.practicum.shareit.booking.controller;
-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.model.Booking;
+import ru.practicum.shareit.booking.model.BookingState;
+import ru.practicum.shareit.booking.service.BookingService;
+
+import java.util.Collection;
/**
* TODO Sprint add-bookings.
*/
@RestController
@RequestMapping(path = "/bookings")
+@RequiredArgsConstructor
public class BookingController {
+
+ private final BookingService bookingService;
+
+ @PostMapping
+ public Booking createBooking(@RequestHeader("X-Sharer-User-Id") @Positive Long userId,
+ @Valid @RequestBody BookingDto bookingDto) {
+
+ return bookingService.createBooking(userId, bookingDto);
+ }
+
+ @PatchMapping("/{bookingId}")
+ public Booking updateBookingStatus(
+ @RequestHeader("X-Sharer-User-Id") @Positive Long userId,
+ @PathVariable @Positive Long bookingId,
+ @RequestParam Boolean approved) {
+
+ return bookingService.updateBookingStatus(userId, bookingId, approved);
+ }
+
+ @GetMapping("/{bookingId}")
+ public Booking getBooking(
+ @RequestHeader("X-Sharer-User-Id") @Positive Long userId,
+ @PathVariable @Positive Long bookingId) {
+
+ return bookingService.getBooking(userId, bookingId);
+ }
+
+ @GetMapping
+ public Collection findByBookerAndState(
+ @RequestHeader("X-Sharer-User-Id") @Positive Long bookerId,
+ @RequestParam (defaultValue = "ALL") BookingState state) {
+
+ return bookingService.findByBookerAndState(bookerId, state);
+ }
+
+ @GetMapping("/owner")
+ public Collection findByOwnerAndState(
+ @RequestHeader("X-Sharer-User-Id") @Positive Long ownerId,
+ @RequestParam (defaultValue = "ALL") BookingState state) {
+
+ return bookingService.findByOwnerAndState(ownerId, state);
+ }
}
diff --git a/src/main/java/ru/practicum/shareit/booking/dao/BookingMapper.java b/src/main/java/ru/practicum/shareit/booking/dao/BookingMapper.java
index 80e814a..b328a53 100644
--- a/src/main/java/ru/practicum/shareit/booking/dao/BookingMapper.java
+++ b/src/main/java/ru/practicum/shareit/booking/dao/BookingMapper.java
@@ -6,13 +6,10 @@
@UtilityClass
public class BookingMapper {
- public static BookingDto mapToBooking(Booking booking) {
- return new BookingDto(
- booking.getStart(),
- booking.getEnd(),
- booking.getItem(),
- booking.getBooker(),
- booking.getStatus()
- );
+ public static Booking mapToBooking(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..1e08da0
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/booking/dao/BookingRepository.java
@@ -0,0 +1,52 @@
+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.model.BookingStatus;
+import ru.practicum.shareit.booking.model.LastAndNextDate;
+
+import java.time.LocalDateTime;
+import java.util.Collection;
+import java.util.List;
+
+@Repository
+public interface BookingRepository extends JpaRepository {
+
+ Collection findByBookerIdOrderByEndDesc(Long bookerId);
+
+ Collection findByItemOwnerIdOrderByEndDesc(Long ownerId);
+
+ Collection findByBookerIdAndStartIsBeforeAndEndIsBeforeOrderByEndDesc(Long bookerId, LocalDateTime now,
+ LocalDateTime now1);
+
+ Collection findByBookerIdAndStatusIsOrderByEndDesc(Long bookerId, BookingStatus bookingStatus);
+
+ Collection findByBookerIdAndEndIsBeforeOrderByEndDesc(Long bookerId, LocalDateTime now);
+
+ Collection findByBookerIdAndStartIsAfterOrderByEndDesc(Long bookerId, LocalDateTime now);
+
+ Collection findByItemOwnerIdAndStartIsBeforeAndEndIsBeforeOrderByEndDesc(Long ownerId, LocalDateTime now,
+ LocalDateTime now1);
+
+ Collection findByItemOwnerIdAndStatusIsOrderByEndDesc(Long ownerId, BookingStatus bookingStatus);
+
+ Collection findByItemOwnerIdAndEndIsBeforeOrderByEndDesc(Long ownerId, LocalDateTime now);
+
+ Collection findByItemOwnerIdAndStartIsAfterOrderByEndDesc(Long ownerId, LocalDateTime now);
+
+ boolean existsByItemIdAndBookerIdAndStatusIsAndEndBefore(Long itemId, Long userId, BookingStatus bookingStatus,
+ LocalDateTime now);
+
+ @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);
+}
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 2f99f25..040c09d 100644
--- a/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java
+++ b/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java
@@ -1,20 +1,29 @@
package ru.practicum.shareit.booking.dto;
+import jakarta.validation.constraints.FutureOrPresent;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Positive;
import lombok.AllArgsConstructor;
-import ru.practicum.shareit.item.model.Item;
-import ru.practicum.shareit.booking.model.BookingStatus;
-import ru.practicum.shareit.user.model.User;
+import lombok.Getter;
+import lombok.Setter;
-import java.time.LocalDate;
+import java.time.LocalDateTime;
/**
* TODO Sprint add-bookings.
*/
+@Setter
+@Getter
@AllArgsConstructor
public class BookingDto {
- private LocalDate start;
- private LocalDate end;
- private Item item;
- private User booker;
- private BookingStatus status;
+ @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/model/Booking.java b/src/main/java/ru/practicum/shareit/booking/model/Booking.java
index 090f6c9..33ea486 100644
--- a/src/main/java/ru/practicum/shareit/booking/model/Booking.java
+++ b/src/main/java/ru/practicum/shareit/booking/model/Booking.java
@@ -1,22 +1,45 @@
package ru.practicum.shareit.booking.model;
+import jakarta.persistence.*;
import lombok.AllArgsConstructor;
-import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
import ru.practicum.shareit.item.model.Item;
import ru.practicum.shareit.user.model.User;
-import java.time.LocalDate;
+import java.time.LocalDateTime;
/**
* TODO Sprint add-bookings.
*/
-@Data
+@Entity
@AllArgsConstructor
+@NoArgsConstructor
+@Setter
+@Getter
+@Table(name = "bookings")
public class Booking {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
- private LocalDate start;
- private LocalDate end;
+
+ @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/model/BookingState.java b/src/main/java/ru/practicum/shareit/booking/model/BookingState.java
new file mode 100644
index 0000000..a574273
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/booking/model/BookingState.java
@@ -0,0 +1,10 @@
+package ru.practicum.shareit.booking.model;
+
+public enum BookingState {
+ ALL,
+ CURRENT,
+ PAST,
+ FUTURE,
+ WAITING,
+ REJECTED
+}
diff --git a/src/main/java/ru/practicum/shareit/booking/model/LastAndNextDate.java b/src/main/java/ru/practicum/shareit/booking/model/LastAndNextDate.java
new file mode 100644
index 0000000..c793bb1
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/booking/model/LastAndNextDate.java
@@ -0,0 +1,11 @@
+package ru.practicum.shareit.booking.model;
+
+import java.time.LocalDateTime;
+
+public interface LastAndNextDate {
+ Long getItemId();
+
+ LocalDateTime getLastBooking();
+
+ LocalDateTime getNextBooking();
+}
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..4e08db2
--- /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.dto.BookingDto;
+import ru.practicum.shareit.booking.model.Booking;
+import ru.practicum.shareit.booking.model.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..87396bf
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/booking/service/BookingServiceImpl.java
@@ -0,0 +1,125 @@
+package ru.practicum.shareit.booking.service;
+
+import jakarta.validation.ValidationException;
+import lombok.AllArgsConstructor;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import ru.practicum.shareit.booking.dao.BookingRepository;
+import ru.practicum.shareit.booking.dto.BookingDto;
+import ru.practicum.shareit.booking.model.Booking;
+import ru.practicum.shareit.booking.model.BookingState;
+import ru.practicum.shareit.booking.model.BookingStatus;
+import ru.practicum.shareit.exceptions.BadRequestException;
+import ru.practicum.shareit.exceptions.NotFoundException;
+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 java.time.LocalDateTime;
+import java.util.Collection;
+
+import static ru.practicum.shareit.booking.dao.BookingMapper.mapToBooking;
+
+@Service
+@AllArgsConstructor
+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) {
+ if (!bookingDto.getStart().isBefore(bookingDto.getEnd())) {
+ throw new BadRequestException("Начало не может быть после конца бронирования");
+ }
+ 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 = mapToBooking(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("Бронирование не найдено"));
+ if (!booking.getItem().getOwnerId().equals(userId)) {
+ throw new BadRequestException("Менять статус может только владелец");
+ }
+ 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("Бронирование не найдено"));
+ if (!booking.getBooker().getId().equals(userId) && !booking.getItem().getOwnerId().equals(userId)) {
+ throw new ValidationException("Только владелец или арендатор могут посмотреть бронирование");
+ }
+ return booking;
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Collection findByBookerAndState(Long bookerId, BookingState state) {
+ if (userRepository.findById(bookerId).isEmpty()) {
+ throw new NotFoundException("Пользователь с id " + bookerId + " не найден");
+ }
+ LocalDateTime now = LocalDateTime.now();
+
+ switch (state) {
+ case CURRENT:
+ return bookingRepository.findByBookerIdAndStartIsBeforeAndEndIsBeforeOrderByEndDesc(bookerId, now, now);
+ case REJECTED:
+ return bookingRepository.findByBookerIdAndStatusIsOrderByEndDesc(bookerId, BookingStatus.REJECTED);
+ case WAITING:
+ return bookingRepository.findByBookerIdAndStatusIsOrderByEndDesc(bookerId, BookingStatus.WAITING);
+ case PAST:
+ return bookingRepository.findByBookerIdAndEndIsBeforeOrderByEndDesc(bookerId, now);
+ case FUTURE:
+ return bookingRepository.findByBookerIdAndStartIsAfterOrderByEndDesc(bookerId, now);
+ }
+ return bookingRepository.findByBookerIdOrderByEndDesc(bookerId);
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public Collection findByOwnerAndState(Long ownerId, BookingState state) {
+ if (userRepository.findById(ownerId).isEmpty()) {
+ throw new NotFoundException("Пользователь с id " + ownerId + " не найден");
+ }
+ LocalDateTime now = LocalDateTime.now();
+
+ switch (state) {
+ case CURRENT:
+ return bookingRepository.findByItemOwnerIdAndStartIsBeforeAndEndIsBeforeOrderByEndDesc(ownerId, now,
+ now);
+ case REJECTED:
+ return bookingRepository.findByItemOwnerIdAndStatusIsOrderByEndDesc(ownerId, BookingStatus.REJECTED);
+ case WAITING:
+ return bookingRepository.findByItemOwnerIdAndStatusIsOrderByEndDesc(ownerId, BookingStatus.WAITING);
+ case PAST:
+ return bookingRepository.findByItemOwnerIdAndEndIsBeforeOrderByEndDesc(ownerId, now);
+ case FUTURE:
+ return bookingRepository.findByItemOwnerIdAndStartIsAfterOrderByEndDesc(ownerId, now);
+ }
+ return bookingRepository.findByItemOwnerIdOrderByEndDesc(ownerId);
+ }
+}
diff --git a/src/main/java/ru/practicum/shareit/exceptions/ErrorHandler.java b/src/main/java/ru/practicum/shareit/exceptions/ErrorHandler.java
index ef558d3..871c064 100644
--- a/src/main/java/ru/practicum/shareit/exceptions/ErrorHandler.java
+++ b/src/main/java/ru/practicum/shareit/exceptions/ErrorHandler.java
@@ -26,9 +26,16 @@ public ErrorResponse handleNotFoundException(final NotFoundException e) {
}
@ExceptionHandler
- @ResponseStatus(HttpStatus.NOT_FOUND)
+ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
public ErrorResponse handleInternalServerException(final InternalServerException e) {
log.warn("Ошибка сервера");
return new ErrorResponse(e.getMessage());
}
-}
+
+ @ExceptionHandler
+ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+ public ErrorResponse handleThrowable(final Throwable e) {
+ log.error("Непредвиденная ошибка");
+ return new ErrorResponse(e.getMessage());
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/practicum/shareit/item/controller/ItemController.java b/src/main/java/ru/practicum/shareit/item/controller/ItemController.java
index b6fdcf2..5ddfb15 100644
--- a/src/main/java/ru/practicum/shareit/item/controller/ItemController.java
+++ b/src/main/java/ru/practicum/shareit/item/controller/ItemController.java
@@ -4,6 +4,8 @@
import lombok.extern.slf4j.Slf4j;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
+import ru.practicum.shareit.item.dto.CommentDto;
+import ru.practicum.shareit.item.model.Comment;
import ru.practicum.shareit.validation.OnCreate;
import ru.practicum.shareit.validation.OnUpdate;
import ru.practicum.shareit.item.dto.ItemDto;
@@ -33,9 +35,8 @@ public ItemDto addItem(@Validated(OnCreate.class) @RequestBody final ItemDto ite
public ItemDto updateItem(@PathVariable("itemId") final long itemId,
@RequestHeader("X-Sharer-User-Id") long ownerId,
@Validated(OnUpdate.class) @RequestBody final ItemDto itemDto) {
- itemDto.setId(itemId);
log.info("Предмет обновлен");
- return itemService.updateItem(itemDto, ownerId);
+ return itemService.updateItem(itemDto, itemId, ownerId);
}
@GetMapping("/{itemId}")
@@ -55,4 +56,12 @@ public Collection getNecessaryItem(@RequestParam String text) {
log.info("Список предметов содержащих " + text + " выведен");
return itemService.getNecessaryItem(text);
}
+
+ @PostMapping("/{itemId}/comment")
+ public CommentDto addComment(@PathVariable("itemId") final long itemId,
+ @RequestHeader("X-Sharer-User-Id") long ownerId,
+ @RequestBody Comment comment) {
+ log.info("Комментарий добавлен");
+ return itemService.addComment(ownerId, itemId, comment);
+ }
}
diff --git a/src/main/java/ru/practicum/shareit/item/dao/CommentMapper.java b/src/main/java/ru/practicum/shareit/item/dao/CommentMapper.java
new file mode 100644
index 0000000..f12ff09
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/item/dao/CommentMapper.java
@@ -0,0 +1,17 @@
+package ru.practicum.shareit.item.dao;
+
+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/dao/CommentRepository.java b/src/main/java/ru/practicum/shareit/item/dao/CommentRepository.java
new file mode 100644
index 0000000..3a6202c
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/item/dao/CommentRepository.java
@@ -0,0 +1,17 @@
+package ru.practicum.shareit.item.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+import ru.practicum.shareit.item.model.Comment;
+
+import java.util.Collection;
+import java.util.List;
+
+@Repository
+public interface CommentRepository extends JpaRepository {
+ Collection findByItemId(Long itemId);
+
+ @Query("SELECT c FROM Comment c JOIN FETCH c.item WHERE c.item.ownerId = ?1")
+ List findByItemOwnerId(Long ownerId);
+}
diff --git a/src/main/java/ru/practicum/shareit/item/dao/InMemoryItemStorage.java b/src/main/java/ru/practicum/shareit/item/dao/InMemoryItemStorage.java
deleted file mode 100644
index 4cfb451..0000000
--- a/src/main/java/ru/practicum/shareit/item/dao/InMemoryItemStorage.java
+++ /dev/null
@@ -1,79 +0,0 @@
-package ru.practicum.shareit.item.dao;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Repository;
-import ru.practicum.shareit.exceptions.NotFoundException;
-import ru.practicum.shareit.item.model.Item;
-
-import java.util.*;
-
-
-@Slf4j
-@Repository
-@RequiredArgsConstructor
-public class InMemoryItemStorage {
- private final Map items = new HashMap<>();
-
- private Long getNextId() {
- long currentMaxId = items.keySet()
- .stream()
- .mapToLong(id -> id)
- .max()
- .orElse(0);
- return ++currentMaxId;
- }
-
- public Item addItem(Item item) {
- item.setId(getNextId());
- items.put(item.getId(), item);
- return item;
- }
-
- public Item updateItem(Item newItem) {
- Item item = items.get(newItem.getId());
- if (newItem.getName() != null) {
- item.setName(newItem.getName());
- }
- if (newItem.getDescription() != null) {
- item.setDescription(newItem.getDescription());
- }
- if (newItem.getAvailable() != null) {
- item.setAvailable(newItem.getAvailable());
- }
- return item;
- }
-
- public Item getItem(long itemId) {
- if (!items.containsKey(itemId)) {
- log.warn("Предмет с указанным id " + itemId + " не найден");
- throw new NotFoundException("Предмет с id = " + itemId + " не найден");
- }
- return items.get(itemId);
- }
-
- public List- getAllOwnerItems(long ownerId) {
- List
- ownerItems = new ArrayList<>();
- for (Item item : items.values()) {
- if (item.getOwnerId() == ownerId) {
- ownerItems.add(item);
- }
- }
- return ownerItems;
- }
-
- public List
- getNecessaryItem(String text) {
- List
- necessaryItems = new ArrayList<>();
- if (text == null || text.isBlank()) {
- return Collections.emptyList();
- }
- text = text.toLowerCase(Locale.ROOT);
- for (Item item : items.values()) {
- if (item.getAvailable() && item.getName().toLowerCase(Locale.ROOT).contains(text)
- || item.getDescription().toLowerCase(Locale.ROOT).contains(text)) {
- necessaryItems.add(item);
- }
- }
- return necessaryItems;
- }
-}
diff --git a/src/main/java/ru/practicum/shareit/item/dao/ItemMapper.java b/src/main/java/ru/practicum/shareit/item/dao/ItemMapper.java
index 689a5aa..5829d97 100644
--- a/src/main/java/ru/practicum/shareit/item/dao/ItemMapper.java
+++ b/src/main/java/ru/practicum/shareit/item/dao/ItemMapper.java
@@ -7,12 +7,12 @@
@UtilityClass
public class ItemMapper {
public static ItemDto toItemDto(Item item) {
- return new ItemDto(
- item.getId(),
- item.getName(),
- item.getDescription(),
- item.getAvailable()
- );
+ ItemDto itemDto = new ItemDto();
+ itemDto.setId(item.getId());
+ itemDto.setName(item.getName());
+ itemDto.setDescription(item.getDescription());
+ itemDto.setAvailable(item.getIsAvailable());
+ return itemDto;
}
public static Item toItem(ItemDto itemDto) {
@@ -20,7 +20,7 @@ public static Item toItem(ItemDto itemDto) {
item.setId(itemDto.getId());
item.setName(itemDto.getName());
item.setDescription(itemDto.getDescription());
- item.setAvailable(itemDto.getAvailable());
+ item.setIsAvailable(itemDto.getAvailable());
return item;
}
}
\ No newline at end of file
diff --git a/src/main/java/ru/practicum/shareit/item/dao/ItemRepository.java b/src/main/java/ru/practicum/shareit/item/dao/ItemRepository.java
new file mode 100644
index 0000000..f2bc1cf
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/item/dao/ItemRepository.java
@@ -0,0 +1,24 @@
+package ru.practicum.shareit.item.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.stereotype.Repository;
+import ru.practicum.shareit.item.model.Item;
+
+import java.util.Collection;
+import java.util.List;
+
+@Repository
+public interface ItemRepository extends JpaRepository
- {
+
+ @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);
+
+ List
- findByOwnerId(Long userId);
+}
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 4c141ef..f8e2967 100644
--- a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java
+++ b/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java
@@ -4,13 +4,18 @@
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
+import lombok.NoArgsConstructor;
import ru.practicum.shareit.validation.OnCreate;
+import java.time.LocalDateTime;
+import java.util.Collection;
+
/**
* TODO Sprint add-controllers.
*/
@Data
@AllArgsConstructor
+@NoArgsConstructor
public class ItemDto {
private Long id;
@NotBlank(groups = OnCreate.class, message = "Название предмета должно быть заполнено")
@@ -19,4 +24,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/model/Comment.java b/src/main/java/ru/practicum/shareit/item/model/Comment.java
new file mode 100644
index 0000000..8463d6a
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/item/model/Comment.java
@@ -0,0 +1,35 @@
+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")
+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 68193d0..3dab078 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,34 @@
package ru.practicum.shareit.item.model;
+import jakarta.persistence.*;
import lombok.AllArgsConstructor;
-import lombok.Data;
+import lombok.Getter;
import lombok.NoArgsConstructor;
+import lombok.Setter;
/**
* TODO Sprint add-controllers.
*/
-@Data
+@Entity
@AllArgsConstructor
@NoArgsConstructor
+@Getter
+@Setter
+@Table(name = "items")
public class Item {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
+
+ @Column(name = "name")
private String name;
+
+ @Column(name = "description")
private String description;
- private Boolean available;
+
+ @Column(name = "available")
+ private Boolean isAvailable;
+
+ @Column(name = "owner_id")
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 5119e06..277a7e9 100644
--- a/src/main/java/ru/practicum/shareit/item/service/ItemService.java
+++ b/src/main/java/ru/practicum/shareit/item/service/ItemService.java
@@ -1,6 +1,8 @@
package ru.practicum.shareit.item.service;
+import ru.practicum.shareit.item.dto.CommentDto;
import ru.practicum.shareit.item.dto.ItemDto;
+import ru.practicum.shareit.item.model.Comment;
import java.util.Collection;
import java.util.List;
@@ -9,11 +11,13 @@ public interface ItemService {
ItemDto addItem(ItemDto itemDto, Long userId);
- ItemDto updateItem(ItemDto itemDto, long userId);
+ ItemDto updateItem(ItemDto itemDto, Long itemId, long userId);
ItemDto getItem(long itemId);
List getAllOwnerItems(long ownerId);
Collection getNecessaryItem(String text);
+
+ CommentDto addComment(Long userId, Long itemId, Comment comment);
}
diff --git a/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java b/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java
index 762aaee..e25ce6e 100644
--- a/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java
+++ b/src/main/java/ru/practicum/shareit/item/service/ItemServiceImpl.java
@@ -3,15 +3,27 @@
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import ru.practicum.shareit.booking.dao.BookingRepository;
+import ru.practicum.shareit.booking.model.BookingStatus;
+import ru.practicum.shareit.booking.model.LastAndNextDate;
+import ru.practicum.shareit.exceptions.BadRequestException;
import ru.practicum.shareit.exceptions.NotFoundException;
-import ru.practicum.shareit.item.dao.InMemoryItemStorage;
+import ru.practicum.shareit.item.dao.CommentMapper;
+import ru.practicum.shareit.item.dao.CommentRepository;
+import ru.practicum.shareit.item.dao.ItemRepository;
+import ru.practicum.shareit.item.dto.CommentDto;
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.user.dao.InMemoryUserStorage;
+import ru.practicum.shareit.user.dao.UserRepository;
+import ru.practicum.shareit.user.model.User;
-import java.util.ArrayList;
-import java.util.List;
+import java.time.LocalDateTime;
+import java.util.*;
+import java.util.stream.Collectors;
+import static ru.practicum.shareit.item.dao.CommentMapper.toCommentDto;
import static ru.practicum.shareit.item.dao.ItemMapper.toItem;
import static ru.practicum.shareit.item.dao.ItemMapper.toItemDto;
@@ -19,52 +31,137 @@
@Service
@RequiredArgsConstructor
public class ItemServiceImpl implements ItemService {
- private final InMemoryItemStorage inMemoryItemStorage;
- private final InMemoryUserStorage inMemoryUserStorage;
+ private final ItemRepository itemRepository;
+ private final UserRepository userRepository;
+ private final CommentRepository commentRepository;
+ private final BookingRepository bookingRepository;
@Override
+ @Transactional
public ItemDto addItem(ItemDto itemDto, Long ownerId) {
- if (!inMemoryUserStorage.getUsers().containsKey(ownerId)) {
+ if (!userRepository.existsById(ownerId)) {
log.warn("Владелец с указанным id " + ownerId + " не найден");
throw new NotFoundException("Владелец с id " + ownerId + " не найден");
}
Item item = toItem(itemDto);
item.setOwnerId(ownerId);
- return toItemDto(inMemoryItemStorage.addItem(item));
+ return toItemDto(itemRepository.save(item));
}
@Override
- public ItemDto updateItem(ItemDto itemDto, long userId) {
- if (!inMemoryUserStorage.getUsers().containsKey(userId)) {
+ @Transactional
+ public ItemDto updateItem(ItemDto itemDto,Long itemId, long userId) {
+ if (!userRepository.existsById(userId)) {
log.warn("Пользователь с указанным id " + userId + " не найден");
throw new NotFoundException("Пользователь с id " + userId + " не найден");
}
- Item newItem = toItem(itemDto);
+ Item newItem = itemRepository.findById(itemId)
+ .orElseThrow(() -> new NotFoundException("Предмет с id " + itemId + " не найден"));
newItem.setOwnerId(userId);
- return toItemDto(inMemoryItemStorage.updateItem(newItem));
+
+ if (itemDto.getName() != null) {
+ newItem.setName(itemDto.getName());
+ }
+ if (itemDto.getDescription() != null) {
+ newItem.setDescription(itemDto.getDescription());
+ }
+ if (itemDto.getAvailable() != null) {
+ newItem.setIsAvailable(itemDto.getAvailable());
+ }
+ return toItemDto(itemRepository.save(newItem));
}
@Override
+ @Transactional(readOnly = true)
public ItemDto getItem(long itemId) {
- return toItemDto(inMemoryItemStorage.getItem(itemId));
+ Item item = itemRepository.findById(itemId)
+ .orElseThrow(() -> new NotFoundException("Предмет с id " + itemId + " не найден"));
+
+ ItemDto itemDto = toItemDto(item);
+ Collection comments = commentRepository.findByItemId(itemId);
+
+ return addCommentsToItem(itemDto, comments);
}
@Override
+ @Transactional(readOnly = true)
public List getAllOwnerItems(long ownerId) {
- List itemDtoList = new ArrayList<>();
- for (Item item : inMemoryItemStorage.getAllOwnerItems(ownerId)) {
- itemDtoList.add(toItemDto(item));
+ List
- items = itemRepository.findByOwnerId(ownerId);
+
+ if (items.isEmpty()) {
+ return Collections.emptyList();
}
- return itemDtoList;
+
+ List itemIds = items.stream()
+ .map(Item::getId)
+ .toList();
+
+ List comments = commentRepository.findByItemOwnerId(ownerId);
+ Map> commentsMap = comments.stream()
+ .collect(Collectors.groupingBy(
+ comment -> comment.getItem().getId(),
+ Collectors.mapping(CommentMapper::toCommentDto, Collectors.toList())
+ ));
+
+ List dates = bookingRepository.findLastAndNextDatesByOwnerId(ownerId);
+ Map datesMap = new HashMap<>();
+ for (LastAndNextDate date : dates) {
+ datesMap.put(date.getItemId(), date);
+ }
+
+ return items.stream()
+ .map(item -> {
+ ItemDto dto = toItemDto(item);
+
+ LastAndNextDate bookingDates = datesMap.get(item.getId());
+ if (bookingDates != null) {
+ dto.setLastBooking(bookingDates.getLastBooking());
+ dto.setNextBooking(bookingDates.getNextBooking());
+ }
+
+ dto.setComments(commentsMap.getOrDefault(item.getId(), Collections.emptyList()));
+
+ return dto;
+ })
+ .collect(Collectors.toList());
}
@Override
public List getNecessaryItem(String text) {
+ if (text == null || text.isBlank()) {
+ return Collections.emptyList();
+ }
List itemDtoList = new ArrayList<>();
- for (Item item : inMemoryItemStorage.getNecessaryItem(text)) {
+ for (Item item : itemRepository.searchByNameOrDescription(text)) {
itemDtoList.add(toItemDto(item));
}
return itemDtoList;
}
-}
+
+ @Override
+ public CommentDto addComment(Long userId, Long itemId, Comment comment) {
+ User user = userRepository.findById(userId)
+ .orElseThrow(() -> new NotFoundException("Пользователь с id " + userId + " не найден"));
+
+ Item item = itemRepository.findById(itemId)
+ .orElseThrow(() -> new NotFoundException("Предмет с id " + itemId + " не найден"));
+
+ if (!bookingRepository.existsByItemIdAndBookerIdAndStatusIsAndEndBefore(
+ itemId, userId, BookingStatus.APPROVED, LocalDateTime.now())) {
+ throw new BadRequestException("Пользователь никогда не брал предмет в аренду");
+ }
+ comment.setItem(item);
+ comment.setAuthor(user);
+ comment.setCreated(LocalDateTime.now());
+ return toCommentDto(commentRepository.save(comment));
+ }
+
+ private ItemDto addCommentsToItem(ItemDto itemDto, Collection comments) {
+ Collection commentsDto = comments.stream()
+ .map(CommentMapper::toCommentDto)
+ .toList();
+ itemDto.setComments(commentsDto);
+ return itemDto;
+ }
+}
\ No newline at end of file
diff --git a/src/main/java/ru/practicum/shareit/user/controller/UserController.java b/src/main/java/ru/practicum/shareit/user/controller/UserController.java
index 1c1e928..d198bf1 100644
--- a/src/main/java/ru/practicum/shareit/user/controller/UserController.java
+++ b/src/main/java/ru/practicum/shareit/user/controller/UserController.java
@@ -1,11 +1,14 @@
package ru.practicum.shareit.user.controller;
-import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
+import ru.practicum.shareit.user.model.User;
import ru.practicum.shareit.user.service.UserServiceImpl;
import ru.practicum.shareit.user.dto.UserDto;
+import ru.practicum.shareit.validation.OnCreate;
+import ru.practicum.shareit.validation.OnUpdate;
import java.util.Collection;
@@ -22,13 +25,14 @@ public class UserController {
private final UserServiceImpl userServiceImpl;
@PostMapping
- public UserDto addUser(@Valid @RequestBody final UserDto user) {
+ public UserDto addUser(@Validated(OnCreate.class) @RequestBody final UserDto user) {
log.info("Пользователь добавлен");
return userServiceImpl.addUser(user);
}
@PatchMapping("/{userId}")
- public UserDto updateUser(@PathVariable("userId") final Long userId, @Valid @RequestBody final UserDto userDto) {
+ public UserDto updateUser(@PathVariable("userId") final Long userId,
+ @Validated(OnUpdate.class) @RequestBody final UserDto userDto) {
userDto.setId(userId);
log.info("Пользователь обновлен");
return userServiceImpl.updateUser(userDto);
@@ -41,7 +45,7 @@ public Collection getAllUsers() {
}
@GetMapping("/{userId}")
- public UserDto getUser(@PathVariable("userId") final Long userId) {
+ public User getUser(@PathVariable("userId") final Long userId) {
log.info("Польователь с id " + userId + " выведен");
return userServiceImpl.getUser(userId);
}
@@ -51,4 +55,4 @@ public void deleteUser(@PathVariable("userId") final Long userId) {
log.info("Пользователь с id " + userId + " удален");
userServiceImpl.deleteUser(userId);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/java/ru/practicum/shareit/user/dao/InMemoryUserStorage.java b/src/main/java/ru/practicum/shareit/user/dao/InMemoryUserStorage.java
deleted file mode 100644
index 18c3226..0000000
--- a/src/main/java/ru/practicum/shareit/user/dao/InMemoryUserStorage.java
+++ /dev/null
@@ -1,60 +0,0 @@
-package ru.practicum.shareit.user.dao;
-
-import lombok.RequiredArgsConstructor;
-import lombok.extern.slf4j.Slf4j;
-import org.springframework.stereotype.Repository;
-import ru.practicum.shareit.user.model.User;
-
-import java.util.ArrayList;
-import java.util.HashMap;
-import java.util.List;
-import java.util.Map;
-
-@Slf4j
-@Repository
-@RequiredArgsConstructor
-public class InMemoryUserStorage {
- private final Map users = new HashMap<>();
-
- private Long getNextId() {
- long currentMaxId = users.keySet()
- .stream()
- .mapToLong(id -> id)
- .max()
- .orElse(0);
- return ++currentMaxId;
- }
-
- public User addUser(User user) {
- user.setId(getNextId());
- users.put(user.getId(), user);
- return user;
- }
-
- public User updateUser(User newUser) {
- User user = users.get(newUser.getId());
- if (newUser.getName() != null) {
- user.setName(newUser.getName());
- }
- if (newUser.getEmail() != null) {
- user.setEmail(newUser.getEmail());
- }
- return user;
- }
-
- public List getAllUsers() {
- return new ArrayList<>(users.values());
- }
-
- public User getUser(Long id) {
- return users.get(id);
- }
-
- public void deleteUser(Long id) {
- users.remove(id);
- }
-
- public Map getUsers() {
- return users;
- }
-}
diff --git a/src/main/java/ru/practicum/shareit/user/dao/UserRepository.java b/src/main/java/ru/practicum/shareit/user/dao/UserRepository.java
new file mode 100644
index 0000000..6ee781a
--- /dev/null
+++ b/src/main/java/ru/practicum/shareit/user/dao/UserRepository.java
@@ -0,0 +1,9 @@
+package ru.practicum.shareit.user.dao;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.stereotype.Repository;
+import ru.practicum.shareit.user.model.User;
+
+@Repository
+public interface UserRepository extends JpaRepository {
+}
diff --git a/src/main/java/ru/practicum/shareit/user/dto/UserDto.java b/src/main/java/ru/practicum/shareit/user/dto/UserDto.java
index b4707c7..6bc7abe 100644
--- a/src/main/java/ru/practicum/shareit/user/dto/UserDto.java
+++ b/src/main/java/ru/practicum/shareit/user/dto/UserDto.java
@@ -2,15 +2,23 @@
import jakarta.validation.Valid;
import jakarta.validation.constraints.Email;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Data;
+import ru.practicum.shareit.validation.OnCreate;
+import ru.practicum.shareit.validation.OnUpdate;
@Data
@Valid
@AllArgsConstructor
public class UserDto {
private Long id;
+
+ @NotBlank(groups = OnCreate.class, message = "Имя пользователя не может быть пустым")
private String name;
- @Email(message = "Email should be valid")
+
+ @NotNull(groups = OnCreate.class, message = "Почта не должна быть пустой")
+ @Email(groups = {OnCreate.class, OnUpdate.class}, message = "Почта должна быть в нужном формате")
private String 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 2db2df6..8905e5a 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,28 @@
package ru.practicum.shareit.user.model;
-import jakarta.validation.constraints.Email;
+import jakarta.persistence.*;
import lombok.AllArgsConstructor;
-import lombok.Data;
+import lombok.Getter;
+import lombok.NoArgsConstructor;
+import lombok.Setter;
/**
* TODO Sprint add-controllers.
*/
-@Data
+@Entity
@AllArgsConstructor
+@NoArgsConstructor
+@Getter
+@Setter
+@Table(name = "users")
public class User {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
+
+ @Column(name = "name")
private String name;
- @Email(message = "Email should be valid")
+
+ @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 ccb07e8..5f500ad 100644
--- a/src/main/java/ru/practicum/shareit/user/service/UserService.java
+++ b/src/main/java/ru/practicum/shareit/user/service/UserService.java
@@ -1,10 +1,10 @@
package ru.practicum.shareit.user.service;
import ru.practicum.shareit.user.dto.UserDto;
+import ru.practicum.shareit.user.model.User;
import java.util.List;
-
public interface UserService {
UserDto addUser(UserDto userDto);
@@ -13,7 +13,7 @@ public interface UserService {
List getAllUsers();
- UserDto getUser(Long id);
+ User getUser(Long id);
void deleteUser(Long id);
-}
+}
\ No newline at end of file
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 2a83d52..2876ea8 100644
--- a/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java
+++ b/src/main/java/ru/practicum/shareit/user/service/UserServiceImpl.java
@@ -1,12 +1,11 @@
package ru.practicum.shareit.user.service;
-import jakarta.validation.ValidationException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
-import ru.practicum.shareit.exceptions.BadRequestException;
+import org.springframework.transaction.annotation.Transactional;
import ru.practicum.shareit.exceptions.NotFoundException;
-import ru.practicum.shareit.user.dao.InMemoryUserStorage;
+import ru.practicum.shareit.user.dao.UserRepository;
import ru.practicum.shareit.user.dto.UserDto;
import ru.practicum.shareit.user.model.User;
@@ -20,56 +19,49 @@
@Service
@RequiredArgsConstructor
public class UserServiceImpl implements UserService {
- private final InMemoryUserStorage inMemoryUserStorage;
+ private final UserRepository userRepository;
@Override
+ @Transactional
public UserDto addUser(UserDto userDto) {
User user = toUser(userDto);
- if (user.getEmail() == null) {
- log.warn("Некорректная почта");
- throw new BadRequestException("некорректный тип почты");
- }
- for (User usr : inMemoryUserStorage.getUsers().values()) {
- if (usr.getEmail().equals(user.getEmail())) {
- log.warn("такая почта уже используется");
- throw new ValidationException("Нельзя добавить существующую почту");
- }
- }
- return toUserDto(inMemoryUserStorage.addUser((user)));
+ return toUserDto(userRepository.save(user));
}
@Override
+ @Transactional
public UserDto updateUser(UserDto userDto) {
- for (User usr : inMemoryUserStorage.getUsers().values()) {
- if (usr.getEmail().equals(userDto.getEmail())) {
- log.warn("такая почта уже используется");
- throw new ValidationException("Нельзя добавить существующую почту");
- }
- }
- if (!inMemoryUserStorage.getUsers().containsKey(userDto.getId())) {
+ if (!userRepository.existsById(userDto.getId())) {
log.warn("Пользователь с указанным id не найден");
throw new NotFoundException("Пользователь с id = " + userDto.getId() + " не найден");
}
- User newUser = toUser(userDto);
- return toUserDto(inMemoryUserStorage.updateUser(newUser));
+ User newUser = getUser(userDto.getId());
+ if (userDto.getName() != null) {
+ newUser.setName(userDto.getName());
+ }
+ if (userDto.getEmail() != null) {
+ newUser.setEmail(userDto.getEmail());
+ }
+ return toUserDto(userRepository.save(newUser));
}
@Override
public List getAllUsers() {
List dtoList = new ArrayList<>();
- for (User user : inMemoryUserStorage.getAllUsers()) {
+ for (User user : userRepository.findAll()) {
dtoList.add(toUserDto(user));
}
return dtoList;
}
@Override
- public UserDto getUser(Long id) {
- return toUserDto(inMemoryUserStorage.getUser(id));
+ public User getUser(Long id) {
+ return userRepository.findById(id)
+ .orElseThrow(() -> new NotFoundException("Пользователь с id = " + id + " не найден"));
}
@Override
public void deleteUser(Long id) {
- inMemoryUserStorage.deleteUser(id);
+ userRepository.deleteById(id);
}
-}
+}
\ No newline at end of file
diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties
index 9e9bc4b..713b80d 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.driverClassName=org.h2.Driver
+spring.datasource.url=jdbc:h2:mem:testdb
+spring.datasource.username=dbuser
+spring.datasource.password=12345
diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties
index 51c5180..d95862f 100644
--- a/src/main/resources/application.properties
+++ b/src/main/resources/application.properties
@@ -7,8 +7,12 @@ 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://0.0.0.0:5432/shareit
+spring.datasource.username=dbuser
+spring.datasource.password=12345
+
+#spring.datasource.driverClassName=org.h2.Driver
+#spring.datasource.url=jdbc:h2:mem:testdb
+#spring.datasource.username=dbuser
+#spring.datasource.password=12345
diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql
new file mode 100644
index 0000000..3d56497
--- /dev/null
+++ b/src/main/resources/schema.sql
@@ -0,0 +1,39 @@
+CREATE TABLE IF NOT EXISTS users (
+ id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ email VARCHAR(512) NOT NULL,
+ CONSTRAINT pk_user PRIMARY KEY (id),
+ CONSTRAINT UQ_USER_EMAIL UNIQUE (email)
+);
+
+CREATE TABLE IF NOT EXISTS items (
+ id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
+ name VARCHAR(255) NOT NULL,
+ description VARCHAR(255) NOT NULL,
+ available BOOLEAN NOT NULL,
+ owner_id BIGINT NOT NULL,
+ CONSTRAINT pk_item PRIMARY KEY (id),
+ FOREIGN KEY (owner_id) REFERENCES users(id) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS bookings (
+ id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
+ start_date TIMESTAMP WITHOUT TIME ZONE NOT NULL,
+ end_date TIMESTAMP WITHOUT TIME ZONE NOT NULL,
+ item_id BIGINT NOT NULL,
+ booker_id BIGINT NOT NULL,
+ status VARCHAR(255) NOT NULL,
+ CONSTRAINT pk_booking PRIMARY KEY (id),
+ FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE,
+ FOREIGN KEY (booker_id) REFERENCES users(id) ON DELETE CASCADE
+);
+
+CREATE TABLE IF NOT EXISTS comments (
+ id BIGINT GENERATED BY DEFAULT AS IDENTITY NOT NULL,
+ item_id BIGINT NOT NULL,
+ author_id BIGINT NOT NULL,
+ content TEXT,
+ created_at TIMESTAMP DEFAULT CURRENT_TIMESTAMP,
+ FOREIGN KEY (item_id) REFERENCES items(id) ON DELETE CASCADE,
+ FOREIGN KEY (author_id) REFERENCES users(id) ON DELETE CASCADE
+);
\ No newline at end of file