Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
16 changes: 15 additions & 1 deletion src/main/java/ru/practicum/shareit/booking/Booking.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,21 @@
package ru.practicum.shareit.booking;

import java.time.LocalDateTime;

import lombok.Data;
import ru.practicum.shareit.item.model.Item;
import ru.practicum.shareit.user.model.User;

/**
* TODO Sprint add-bookings.
* Модель бронирования вещи.
* Описывает период бронирования, вещь, арендатора и текущий статус.
*/
@Data
public class Booking {
private Long id;
private LocalDateTime start;
private LocalDateTime end;
private Item item;
private User booker;
private BookingStatus status;
}
11 changes: 11 additions & 0 deletions src/main/java/ru/practicum/shareit/booking/BookingStatus.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ru.practicum.shareit.booking;

/**
* Статусы бронирования.
*/
public enum BookingStatus {
WAITING,
APPROVED,
REJECTED,
CANCELED
}
51 changes: 51 additions & 0 deletions src/main/java/ru/practicum/shareit/exceptions/ErrorHandler.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,51 @@
package ru.practicum.shareit.exceptions;

import lombok.extern.slf4j.Slf4j;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.MethodArgumentNotValidException;
import org.springframework.web.bind.annotation.ExceptionHandler;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestControllerAdvice;

/**
* Глобальный обработчик исключений.
*/
@Slf4j
@RestControllerAdvice
public class ErrorHandler {

@ExceptionHandler({MethodArgumentNotValidException.class})
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleValidationException(final Exception e) {
log.warn("Ошибка валидации объекта");
return new ErrorResponse(e.getMessage());
}

@ExceptionHandler(NotFoundException.class)
@ResponseStatus(HttpStatus.NOT_FOUND)
public ErrorResponse handleNotFoundException(final NotFoundException e) {
log.warn("Объект не найден");
return new ErrorResponse(e.getMessage());
}

@ExceptionHandler(SecurityException.class)
@ResponseStatus(HttpStatus.FORBIDDEN)
public ErrorResponse handleSecurityException(final SecurityException e) {
log.warn("Доступ запрещён");
return new ErrorResponse(e.getMessage());
}

@ExceptionHandler(IllegalStateException.class)
@ResponseStatus(HttpStatus.CONFLICT)
public ErrorResponse handleIllegalStateException(final IllegalStateException e) {
log.warn("Конфликт данных");
return new ErrorResponse(e.getMessage());
}

@ExceptionHandler(IllegalArgumentException.class)
@ResponseStatus(HttpStatus.BAD_REQUEST)
public ErrorResponse handleIllegalArgumentException(final IllegalArgumentException e) {
log.warn("Некорректный запрос");
return new ErrorResponse(e.getMessage());
}
}
13 changes: 13 additions & 0 deletions src/main/java/ru/practicum/shareit/exceptions/ErrorResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package ru.practicum.shareit.exceptions;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

/**
* DTO ошибки для возврата клиенту.
*/
@Getter
@RequiredArgsConstructor
public class ErrorResponse {
private final String error;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package ru.practicum.shareit.exceptions;

public class NotFoundException extends RuntimeException {
public NotFoundException(String message) {
super(message);
}
}
39 changes: 36 additions & 3 deletions src/main/java/ru/practicum/shareit/item/ItemController.java
Original file line number Diff line number Diff line change
@@ -1,12 +1,45 @@
package ru.practicum.shareit.item;

import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.*;
import ru.practicum.shareit.item.dto.ItemDto;

import java.util.List;

/**
* TODO Sprint add-controllers.
* REST-контроллер для работы с вещами.
*/
@RestController
@RequestMapping("/items")
@RequiredArgsConstructor
public class ItemController {
private final ItemService itemService;

@PostMapping
public ItemDto create(@RequestHeader("X-Sharer-User-Id") Long userId, @Valid @RequestBody ItemDto itemDto) {
return itemService.create(userId, itemDto);
}

@PatchMapping("/{itemId}")
public ItemDto update(@RequestHeader("X-Sharer-User-Id") Long userId,
@PathVariable Long itemId,
@RequestBody ItemDto itemDto) {
return itemService.update(userId, itemId, itemDto);
}

@GetMapping("/{itemId}")
public ItemDto getById(@PathVariable Long itemId) {
return itemService.getById(itemId);
}

@GetMapping
public List<ItemDto> getAllByOwner(@RequestHeader("X-Sharer-User-Id") Long userId) {
return itemService.getAllByOwner(userId);
}

@GetMapping("/search")
public List<ItemDto> search(@RequestParam String text) {
return itemService.search(text);
}
}
38 changes: 38 additions & 0 deletions src/main/java/ru/practicum/shareit/item/ItemMapper.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package ru.practicum.shareit.item;

import jakarta.validation.constraints.NotNull;
import ru.practicum.shareit.item.dto.ItemDto;
import ru.practicum.shareit.item.model.Item;
import lombok.experimental.UtilityClass;
import ru.practicum.shareit.user.model.User;

@UtilityClass
public class ItemMapper {

/**
* Преобразует {@link Item} в {@link ItemDto}.
*/
public static ItemDto toItemDto(@NotNull Item item) {
ItemDto dto = new ItemDto();
dto.setId(item.getId());
dto.setName(item.getName());
dto.setDescription(item.getDescription());
dto.setAvailable(item.getAvailable());
dto.setOwnerId(item.getOwner() != null ? item.getOwner().getId() : null);
dto.setRequestId(item.getRequest() != null ? item.getRequest().getId() : null);
return dto;
}

/**
* Преобразует {@link ItemDto} в {@link Item}.
*/
public static Item toItem(@NotNull ItemDto dto, @NotNull User owner) {
Item item = new Item();
item.setName(dto.getName());
item.setDescription(dto.getDescription());
item.setAvailable(dto.getAvailable());
item.setOwner(owner);

return item;
}
}
26 changes: 26 additions & 0 deletions src/main/java/ru/practicum/shareit/item/ItemService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ru.practicum.shareit.item;

import java.util.List;

import ru.practicum.shareit.item.dto.ItemDto;

/**
* Сервис для операций с вещами.
*/
public interface ItemService {

/** Создаёт вещь. */
ItemDto create(Long userId, ItemDto itemDto);

/** Обновляет вещь. */
ItemDto update(Long userId, Long itemId, ItemDto itemDto);

/** Возвращает вещь по id. */
ItemDto getById(Long itemId);

/** Возвращает список вещей владельца. */
List<ItemDto> getAllByOwner(Long userId);

/** Ищет доступные вещи по тексту в названии или описании. */
List<ItemDto> search(String text);
}
95 changes: 95 additions & 0 deletions src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package ru.practicum.shareit.item;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import ru.practicum.shareit.exceptions.NotFoundException;
import ru.practicum.shareit.item.dto.ItemDto;
import ru.practicum.shareit.item.model.Item;
import ru.practicum.shareit.item.repository.ItemRepository;
import ru.practicum.shareit.user.model.User;
import ru.practicum.shareit.user.repository.UserRepository;

import java.util.List;

/**
* In-memory реализация {@link ItemService}.
*/
@Service
@RequiredArgsConstructor
public class ItemServiceImpl implements ItemService {
private final UserRepository userRepository;
private final ItemRepository itemRepository;

@Override
public ItemDto create(Long userId, ItemDto itemDto) {
User owner = userRepository.getById(userId);
if (owner == null) {
throw new NotFoundException("User with id=" + userId + " not found");
}
Item item = ItemMapper.toItem(itemDto, owner);
Item created = itemRepository.create(item);
return ItemMapper.toItemDto(created);
}

@Override
public ItemDto update(Long userId, Long itemId, ItemDto itemDto) {
Item existing = itemRepository.getById(itemId);
if (existing == null) {
throw new NotFoundException("Item with id=" + itemId + " not found");
}

Long ownerId = existing.getOwner() != null ? existing.getOwner().getId() : null;
if (ownerId == null || !ownerId.equals(userId)) {
throw new SecurityException("Only owner can update item with id=" + itemId);
}

if (itemDto.getName() != null) {
existing.setName(itemDto.getName());
}
if (itemDto.getDescription() != null) {
existing.setDescription(itemDto.getDescription());
}
if (itemDto.getAvailable() != null) {
existing.setAvailable(itemDto.getAvailable());
}
itemRepository.update(existing);
return ItemMapper.toItemDto(existing);
}

@Override
public ItemDto getById(Long itemId) {
Item item = itemRepository.getById(itemId);
if (item == null) {
throw new NotFoundException("Item with id=" + itemId + " not found");
}
return ItemMapper.toItemDto(item);
}

@Override
public List<ItemDto> getAllByOwner(Long userId) {
return itemRepository.getAll().stream()
.filter(item -> {
Long ownerId = item.getOwner() != null ? item.getOwner().getId() : null;
return userId.equals(ownerId);
})
.map(ItemMapper::toItemDto)
.toList();
}

@Override
public List<ItemDto> search(String text) {
if (text == null || text.isBlank()) {
return List.of();
}
String query = text.toLowerCase();
return itemRepository.getAll().stream()
.filter(item -> !Boolean.FALSE.equals(item.getAvailable()))
.filter(item -> {
String name = item.getName() != null ? item.getName().toLowerCase() : "";
String description = item.getDescription() != null ? item.getDescription().toLowerCase() : "";
return name.contains(query) || description.contains(query);
})
.map(ItemMapper::toItemDto)
.toList();
}
}
20 changes: 19 additions & 1 deletion src/main/java/ru/practicum/shareit/item/dto/ItemDto.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,25 @@
package ru.practicum.shareit.item.dto;

import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.NotBlank;
import lombok.Data;

/**
* TODO Sprint add-controllers.
* DTO вещи.
* Используется для обмена данными о вещи через REST.
*/
@Data
public class ItemDto {
private Long id;

@NotBlank
private String name;

@NotBlank
private String description;

@NotNull
private Boolean available;
private Long ownerId;
private Long requestId;
}
14 changes: 13 additions & 1 deletion src/main/java/ru/practicum/shareit/item/model/Item.java
Original file line number Diff line number Diff line change
@@ -1,7 +1,19 @@
package ru.practicum.shareit.item.model;

import lombok.Data;
import ru.practicum.shareit.request.ItemRequest;
import ru.practicum.shareit.user.model.User;

/**
* TODO Sprint add-controllers.
* Модель вещи.
* Описывает вещь, которую владелец может сдавать в аренду.
*/
@Data
public class Item {
private Long id;
private String name;
private String description;
private Boolean available;
private User owner;
private ItemRequest request;
}
Loading