From 14562461f16d80f1fddeb1d654cfc32ac6349073 Mon Sep 17 00:00:00 2001 From: Karat120 <140876935+Karat120@users.noreply.github.com> Date: Mon, 23 Feb 2026 12:08:12 +0700 Subject: [PATCH 1/3] Initial commit --- .github/workflows/api-tests.yml | 8 + .gitignore | 33 +++ README.md | 2 + checkstyle.xml | 257 ++++++++++++++++++ lombok.config | 4 + pom.xml | 247 +++++++++++++++++ .../java/ru/practicum/shareit/ShareItApp.java | 13 + .../ru/practicum/shareit/booking/Booking.java | 7 + .../shareit/booking/BookingController.java | 12 + .../shareit/booking/dto/BookingDto.java | 7 + .../shareit/item/ItemController.java | 12 + .../practicum/shareit/item/dto/ItemDto.java | 7 + .../ru/practicum/shareit/item/model/Item.java | 7 + .../shareit/request/ItemRequest.java | 7 + .../request/ItemRequestController.java | 12 + .../shareit/request/dto/ItemRequestDto.java | 7 + .../java/ru/practicum/shareit/user/User.java | 7 + .../shareit/user/UserController.java | 12 + .../resources/application-test.properties | 13 + src/main/resources/application.properties | 14 + .../ru/practicum/shareit/ShareItTests.java | 13 + suppressions.xml | 7 + 22 files changed, 708 insertions(+) create mode 100644 .github/workflows/api-tests.yml create mode 100644 .gitignore create mode 100644 README.md create mode 100644 checkstyle.xml create mode 100644 lombok.config create mode 100644 pom.xml create mode 100644 src/main/java/ru/practicum/shareit/ShareItApp.java create mode 100644 src/main/java/ru/practicum/shareit/booking/Booking.java create mode 100644 src/main/java/ru/practicum/shareit/booking/BookingController.java create mode 100644 src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java create mode 100644 src/main/java/ru/practicum/shareit/item/ItemController.java create mode 100644 src/main/java/ru/practicum/shareit/item/dto/ItemDto.java create mode 100644 src/main/java/ru/practicum/shareit/item/model/Item.java create mode 100644 src/main/java/ru/practicum/shareit/request/ItemRequest.java create mode 100644 src/main/java/ru/practicum/shareit/request/ItemRequestController.java create mode 100644 src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java create mode 100644 src/main/java/ru/practicum/shareit/user/User.java create mode 100644 src/main/java/ru/practicum/shareit/user/UserController.java create mode 100644 src/main/resources/application-test.properties create mode 100644 src/main/resources/application.properties create mode 100644 src/test/java/ru/practicum/shareit/ShareItTests.java create mode 100644 suppressions.xml diff --git a/.github/workflows/api-tests.yml b/.github/workflows/api-tests.yml new file mode 100644 index 00000000..51c3d8b3 --- /dev/null +++ b/.github/workflows/api-tests.yml @@ -0,0 +1,8 @@ +name: ShareIt API Tests + +on: + pull_request: + +jobs: + build: + uses: yandex-praktikum/java-shareit/.github/workflows/api-tests.yml@ci \ No newline at end of file diff --git a/.gitignore b/.gitignore new file mode 100644 index 00000000..549e00a2 --- /dev/null +++ b/.gitignore @@ -0,0 +1,33 @@ +HELP.md +target/ +!.mvn/wrapper/maven-wrapper.jar +!**/src/main/**/target/ +!**/src/test/**/target/ + +### STS ### +.apt_generated +.classpath +.factorypath +.project +.settings +.springBeans +.sts4-cache + +### IntelliJ IDEA ### +.idea +*.iws +*.iml +*.ipr + +### NetBeans ### +/nbproject/private/ +/nbbuild/ +/dist/ +/nbdist/ +/.nb-gradle/ +build/ +!**/src/main/**/build/ +!**/src/test/**/build/ + +### VS Code ### +.vscode/ diff --git a/README.md b/README.md new file mode 100644 index 00000000..47a75f04 --- /dev/null +++ b/README.md @@ -0,0 +1,2 @@ +# java-shareit +Template repository for Shareit project. diff --git a/checkstyle.xml b/checkstyle.xml new file mode 100644 index 00000000..c28a0d3c --- /dev/null +++ b/checkstyle.xml @@ -0,0 +1,257 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/lombok.config b/lombok.config new file mode 100644 index 00000000..b0056c11 --- /dev/null +++ b/lombok.config @@ -0,0 +1,4 @@ +config.stopBubbling = true +lombok.anyconstructor.addconstructorproperties = false +lombok.addLombokGeneratedAnnotation = true +lombok.addSuppressWarnings = false \ No newline at end of file diff --git a/pom.xml b/pom.xml new file mode 100644 index 00000000..2db888c5 --- /dev/null +++ b/pom.xml @@ -0,0 +1,247 @@ + + + 4.0.0 + + org.springframework.boot + spring-boot-starter-parent + 3.3.2 + + + + ru.practicum + shareit + 0.0.1-SNAPSHOT + + ShareIt + + + 21 + + + + + org.springframework.boot + spring-boot-starter-web + + + org.springframework.boot + spring-boot-starter-actuator + + + org.springframework.boot + spring-boot-configuration-processor + true + + + + org.postgresql + postgresql + runtime + + + + org.projectlombok + lombok + true + + + + com.h2database + h2 + test + + + org.springframework.boot + spring-boot-starter-test + test + + + org.springframework.boot + spring-boot-starter-validation + + + + + + + src/main/resources + true + + + + + org.springframework.boot + spring-boot-maven-plugin + + + + org.projectlombok + lombok + + + + + + + + + org.apache.maven.plugins + maven-surefire-plugin + + + test + + + + + org.apache.maven.plugins + maven-checkstyle-plugin + 3.1.2 + + checkstyle.xml + true + true + true + + + + + check + + compile + + + + + com.puppycrawl.tools + checkstyle + 10.3 + + + + + com.github.spotbugs + spotbugs-maven-plugin + 4.8.5.0 + + Max + High + + + + + check + + + + + + org.jacoco + jacoco-maven-plugin + 0.8.12 + + file + + + + jacoco-initialize + + prepare-agent + + + + jacoco-check + + check + + + + + BUNDLE + + + INSTRUCTION + COVEREDRATIO + 0.01 + + + LINE + COVEREDRATIO + 0.9 + + + BRANCH + COVEREDRATIO + 0.6 + + + COMPLEXITY + COVEREDRATIO + 0.6 + + + METHOD + COVEREDRATIO + 0.7 + + + CLASS + MISSEDCOUNT + 1 + + + + + + + + jacoco-report + test + + report + + + + + + + + + + check + + + + org.apache.maven.plugins + maven-checkstyle-plugin + + + com.github.spotbugs + spotbugs-maven-plugin + + + + + + + com.github.spotbugs + spotbugs-maven-plugin + + + + + + coverage + + + + org.jacoco + jacoco-maven-plugin + + + + + + + diff --git a/src/main/java/ru/practicum/shareit/ShareItApp.java b/src/main/java/ru/practicum/shareit/ShareItApp.java new file mode 100644 index 00000000..a00ad567 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/ShareItApp.java @@ -0,0 +1,13 @@ +package ru.practicum.shareit; + +import org.springframework.boot.SpringApplication; +import org.springframework.boot.autoconfigure.SpringBootApplication; + +@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/Booking.java b/src/main/java/ru/practicum/shareit/booking/Booking.java new file mode 100644 index 00000000..2d9c6668 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/Booking.java @@ -0,0 +1,7 @@ +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 new file mode 100644 index 00000000..b94493d4 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/BookingController.java @@ -0,0 +1,12 @@ +package ru.practicum.shareit.booking; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * TODO Sprint add-bookings. + */ +@RestController +@RequestMapping(path = "/bookings") +public class BookingController { +} diff --git a/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java b/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java new file mode 100644 index 00000000..861de9e0 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/booking/dto/BookingDto.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.booking.dto; + +/** + * TODO Sprint add-bookings. + */ +public class BookingDto { +} diff --git a/src/main/java/ru/practicum/shareit/item/ItemController.java b/src/main/java/ru/practicum/shareit/item/ItemController.java new file mode 100644 index 00000000..bb17668b --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -0,0 +1,12 @@ +package ru.practicum.shareit.item; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * TODO Sprint add-controllers. + */ +@RestController +@RequestMapping("/items") +public class ItemController { +} diff --git a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java b/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java new file mode 100644 index 00000000..9319d7d7 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.item.dto; + +/** + * TODO Sprint add-controllers. + */ +public class ItemDto { +} diff --git a/src/main/java/ru/practicum/shareit/item/model/Item.java b/src/main/java/ru/practicum/shareit/item/model/Item.java new file mode 100644 index 00000000..44eb73dd --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/model/Item.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.item.model; + +/** + * TODO Sprint add-controllers. + */ +public class Item { +} diff --git a/src/main/java/ru/practicum/shareit/request/ItemRequest.java b/src/main/java/ru/practicum/shareit/request/ItemRequest.java new file mode 100644 index 00000000..95d6f23c --- /dev/null +++ b/src/main/java/ru/practicum/shareit/request/ItemRequest.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.request; + +/** + * TODO Sprint add-item-requests. + */ +public class ItemRequest { +} diff --git a/src/main/java/ru/practicum/shareit/request/ItemRequestController.java b/src/main/java/ru/practicum/shareit/request/ItemRequestController.java new file mode 100644 index 00000000..064e2e9c --- /dev/null +++ b/src/main/java/ru/practicum/shareit/request/ItemRequestController.java @@ -0,0 +1,12 @@ +package ru.practicum.shareit.request; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * TODO Sprint add-item-requests. + */ +@RestController +@RequestMapping(path = "/requests") +public class ItemRequestController { +} diff --git a/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java b/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java new file mode 100644 index 00000000..7b3ed544 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/request/dto/ItemRequestDto.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.request.dto; + +/** + * TODO Sprint add-item-requests. + */ +public class ItemRequestDto { +} diff --git a/src/main/java/ru/practicum/shareit/user/User.java b/src/main/java/ru/practicum/shareit/user/User.java new file mode 100644 index 00000000..ae6e7f33 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/user/User.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.user; + +/** + * TODO Sprint add-controllers. + */ +public class User { +} diff --git a/src/main/java/ru/practicum/shareit/user/UserController.java b/src/main/java/ru/practicum/shareit/user/UserController.java new file mode 100644 index 00000000..03039b9d --- /dev/null +++ b/src/main/java/ru/practicum/shareit/user/UserController.java @@ -0,0 +1,12 @@ +package ru.practicum.shareit.user; + +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +/** + * TODO Sprint add-controllers. + */ +@RestController +@RequestMapping(path = "/users") +public class UserController { +} diff --git a/src/main/resources/application-test.properties b/src/main/resources/application-test.properties new file mode 100644 index 00000000..9e9bc4b6 --- /dev/null +++ b/src/main/resources/application-test.properties @@ -0,0 +1,13 @@ +spring.jpa.hibernate.ddl-auto=none +spring.jpa.properties.hibernate.format_sql=true +spring.sql.init.mode=always +logging.level.org.springframework.orm.jpa=INFO +logging.level.org.springframework.transaction=INFO +logging.level.org.springframework.transaction.interceptor=TRACE +logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG + +# TODO Append connection to H2 DB +#spring.datasource.driverClassName +#spring.datasource.url +#spring.datasource.username +#spring.datasource.password diff --git a/src/main/resources/application.properties b/src/main/resources/application.properties new file mode 100644 index 00000000..51c51801 --- /dev/null +++ b/src/main/resources/application.properties @@ -0,0 +1,14 @@ +spring.jpa.hibernate.ddl-auto=none +spring.jpa.properties.hibernate.format_sql=true +spring.sql.init.mode=always + +logging.level.org.springframework.orm.jpa=INFO +logging.level.org.springframework.transaction=INFO +logging.level.org.springframework.transaction.interceptor=TRACE +logging.level.org.springframework.orm.jpa.JpaTransactionManager=DEBUG + +# TODO Append connection to Postgres DB +#spring.datasource.driverClassName +#spring.datasource.url +#spring.datasource.username +#spring.datasource.password diff --git a/src/test/java/ru/practicum/shareit/ShareItTests.java b/src/test/java/ru/practicum/shareit/ShareItTests.java new file mode 100644 index 00000000..4d79052f --- /dev/null +++ b/src/test/java/ru/practicum/shareit/ShareItTests.java @@ -0,0 +1,13 @@ +package ru.practicum.shareit; + +import org.junit.jupiter.api.Test; +import org.springframework.boot.test.context.SpringBootTest; + +@SpringBootTest +class ShareItTests { + + @Test + void contextLoads() { + } + +} diff --git a/suppressions.xml b/suppressions.xml new file mode 100644 index 00000000..b2c6822f --- /dev/null +++ b/suppressions.xml @@ -0,0 +1,7 @@ + + + + + \ No newline at end of file From eb2df7480c7fad0b0ddb93f953cc7395f5447c77 Mon Sep 17 00:00:00 2001 From: Karat120 Date: Sun, 15 Mar 2026 15:12:13 +0500 Subject: [PATCH 2/3] add-controllers --- .../shareit/exception/ConflictException.java | 7 ++ .../shareit/exception/ErrorHandler.java | 41 ++++++++ .../shareit/exception/NotFoundException.java | 7 ++ .../exception/ValidationException.java | 7 ++ .../shareit/item/ItemController.java | 52 ++++++++-- .../ru/practicum/shareit/item/ItemMapper.java | 25 +++++ .../practicum/shareit/item/ItemService.java | 16 +++ .../shareit/item/ItemServiceImpl.java | 97 +++++++++++++++++++ .../practicum/shareit/item/dto/ItemDto.java | 20 +++- .../ru/practicum/shareit/item/model/Item.java | 21 +++- .../java/ru/practicum/shareit/user/User.java | 17 +++- .../shareit/user/UserController.java | 49 ++++++++-- .../ru/practicum/shareit/user/UserMapper.java | 21 ++++ .../practicum/shareit/user/UserService.java | 16 +++ .../shareit/user/UserServiceImpl.java | 80 +++++++++++++++ .../practicum/shareit/user/dto/UserDto.java | 17 ++++ 16 files changed, 468 insertions(+), 25 deletions(-) create mode 100644 src/main/java/ru/practicum/shareit/exception/ConflictException.java create mode 100644 src/main/java/ru/practicum/shareit/exception/ErrorHandler.java create mode 100644 src/main/java/ru/practicum/shareit/exception/NotFoundException.java create mode 100644 src/main/java/ru/practicum/shareit/exception/ValidationException.java create mode 100644 src/main/java/ru/practicum/shareit/item/ItemMapper.java create mode 100644 src/main/java/ru/practicum/shareit/item/ItemService.java create mode 100644 src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java create mode 100644 src/main/java/ru/practicum/shareit/user/UserMapper.java create mode 100644 src/main/java/ru/practicum/shareit/user/UserService.java create mode 100644 src/main/java/ru/practicum/shareit/user/UserServiceImpl.java create mode 100644 src/main/java/ru/practicum/shareit/user/dto/UserDto.java diff --git a/src/main/java/ru/practicum/shareit/exception/ConflictException.java b/src/main/java/ru/practicum/shareit/exception/ConflictException.java new file mode 100644 index 00000000..78851230 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/exception/ConflictException.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.exception; + +public class ConflictException extends RuntimeException { + public ConflictException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java b/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java new file mode 100644 index 00000000..7c5bb444 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/exception/ErrorHandler.java @@ -0,0 +1,41 @@ +package ru.practicum.shareit.exception; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.MethodArgumentNotValidException; +import org.springframework.web.bind.annotation.*; + +import java.util.Map; + +@Slf4j +@RestControllerAdvice +public class ErrorHandler { + + @ExceptionHandler(Exception.class) + @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR) + public Map catchAllExceptions(Exception ex) { + log.error("500: ", ex); + return Map.of("error", "Произошла ошибка"); + } + + @ExceptionHandler({ValidationException.class, MethodArgumentNotValidException.class}) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public Map catchValidationErrors(Exception ex) { + log.error("400: {}", ex.getMessage()); + return Map.of("error", "Ошибка валидации"); + } + + @ExceptionHandler(ConflictException.class) + @ResponseStatus(HttpStatus.CONFLICT) + public Map catchConflictStatus(ConflictException ex) { + log.error("409: {}", ex.getMessage()); + return Map.of("error", ex.getMessage()); + } + + @ExceptionHandler(NotFoundException.class) + @ResponseStatus(HttpStatus.NOT_FOUND) + public Map catchNotFoundStatus(NotFoundException ex) { + log.error("404: {}", ex.getMessage()); + return Map.of("error", ex.getMessage()); + } +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/exception/NotFoundException.java b/src/main/java/ru/practicum/shareit/exception/NotFoundException.java new file mode 100644 index 00000000..508b5456 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/exception/NotFoundException.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.exception; + +public class NotFoundException extends RuntimeException { + public NotFoundException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/practicum/shareit/exception/ValidationException.java b/src/main/java/ru/practicum/shareit/exception/ValidationException.java new file mode 100644 index 00000000..59043da1 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/exception/ValidationException.java @@ -0,0 +1,7 @@ +package ru.practicum.shareit.exception; + +public class ValidationException extends RuntimeException { + public ValidationException(String message) { + super(message); + } +} diff --git a/src/main/java/ru/practicum/shareit/item/ItemController.java b/src/main/java/ru/practicum/shareit/item/ItemController.java index bb17668b..853d3444 100644 --- a/src/main/java/ru/practicum/shareit/item/ItemController.java +++ b/src/main/java/ru/practicum/shareit/item/ItemController.java @@ -1,12 +1,52 @@ 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 lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.item.dto.ItemDto; -/** - * TODO Sprint add-controllers. - */ +import java.util.List; + +@Slf4j @RestController @RequestMapping("/items") +@RequiredArgsConstructor public class ItemController { -} + + private final ItemService itemService; + private static final String USER_ID_HEADER = "X-Sharer-User-Id"; + + @GetMapping + public List fetchAllUserItems(@RequestHeader(USER_ID_HEADER) Long ownerId) { + log.info("GET /items (владелец id={})", ownerId); + return itemService.getByOwner(ownerId); + } + + @GetMapping("/search") + public List findItems(@RequestParam(name = "text") String query) { + log.info("GET /items/search?text={}", query); + return itemService.search(query); + } + + @PatchMapping("/{itemId}") + public ItemDto modifyItem(@RequestHeader(USER_ID_HEADER) Long requesterId, + @PathVariable(name = "itemId") Long id, + @RequestBody ItemDto payload) { + log.info("PATCH /items/{} от пользователя id={}", id, requesterId); + return itemService.update(requesterId, id, payload); + } + + @PostMapping + public ItemDto addNewItem(@RequestHeader(USER_ID_HEADER) Long creatorId, + @Valid @RequestBody ItemDto payload) { + log.info("POST /items от пользователя id={}", creatorId); + return itemService.create(creatorId, payload); + } + + @GetMapping("/{itemId}") + public ItemDto fetchItemById(@PathVariable(name = "itemId") Long id) { + log.info("GET /items/{}", id); + return itemService.getById(id); + } +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/item/ItemMapper.java b/src/main/java/ru/practicum/shareit/item/ItemMapper.java new file mode 100644 index 00000000..11e611bf --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/ItemMapper.java @@ -0,0 +1,25 @@ +package ru.practicum.shareit.item; + +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.model.Item; + +public class ItemMapper { + public static Item toItem(ItemDto dto) { + return Item.builder() + .available(dto.getAvailable()) + .description(dto.getDescription()) + .name(dto.getName()) + .id(dto.getId()) + .build(); + } + + public static ItemDto toItemDto(Item entity) { + return ItemDto.builder() + .requestId(entity.getRequestId()) + .available(entity.getAvailable()) + .description(entity.getDescription()) + .name(entity.getName()) + .id(entity.getId()) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/item/ItemService.java b/src/main/java/ru/practicum/shareit/item/ItemService.java new file mode 100644 index 00000000..1b308318 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/ItemService.java @@ -0,0 +1,16 @@ +package ru.practicum.shareit.item; + +import ru.practicum.shareit.item.dto.ItemDto; +import java.util.List; + +public interface ItemService { + ItemDto update(Long userId, Long itemId, ItemDto itemDto); + + List search(String text); + + ItemDto getById(Long itemId); + + ItemDto create(Long userId, ItemDto itemDto); + + List getByOwner(Long userId); +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java b/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java new file mode 100644 index 00000000..cd6c86ff --- /dev/null +++ b/src/main/java/ru/practicum/shareit/item/ItemServiceImpl.java @@ -0,0 +1,97 @@ +package ru.practicum.shareit.item; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.item.dto.ItemDto; +import ru.practicum.shareit.item.model.Item; +import ru.practicum.shareit.user.User; +import ru.practicum.shareit.user.UserService; +import ru.practicum.shareit.user.UserMapper; + +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class ItemServiceImpl implements ItemService { + + private final Map repository = new HashMap<>(); + private final UserService userService; + private Long lastGeneratedId = 1L; + + @Override + public List search(String searchStr) { + if (searchStr == null || searchStr.isBlank()) { + return Collections.emptyList(); + } + + String lowerCaseQuery = searchStr.toLowerCase(); + return repository.values().stream() + .filter(Item::getAvailable) + .filter(item -> item.getName().toLowerCase().contains(lowerCaseQuery) + || item.getDescription().toLowerCase().contains(lowerCaseQuery)) + .map(ItemMapper::toItemDto) + .collect(Collectors.toList()); + } + + @Override + public ItemDto getById(Long id) { + Item found = repository.get(id); + if (found == null) { + throw new NotFoundException("Объект с идентификатором " + id + " не существует"); + } + return ItemMapper.toItemDto(found); + } + + @Override + public List getByOwner(Long ownerId) { + userService.getById(ownerId); + + return repository.values().stream() + .filter(obj -> obj.getOwner().getId().equals(ownerId)) + .map(ItemMapper::toItemDto) + .collect(Collectors.toList()); + } + + @Override + public ItemDto update(Long userId, Long itemId, ItemDto dto) { + Item target = repository.get(itemId); + + if (Objects.isNull(target)) { + throw new NotFoundException("Вещь с id " + itemId + " не найдена"); + } + + if (!Objects.equals(target.getOwner().getId(), userId)) { + throw new NotFoundException("Доступ запрещен: редактировать может только владелец"); + } + + if (dto.getName() != null && !dto.getName().trim().isEmpty()) { + target.setName(dto.getName()); + } + if (dto.getDescription() != null && !dto.getDescription().trim().isEmpty()) { + target.setDescription(dto.getDescription()); + } + if (dto.getAvailable() != null) { + target.setAvailable(dto.getAvailable()); + } + + return ItemMapper.toItemDto(target); + } + + @Override + public ItemDto create(Long userId, ItemDto itemDto) { + User creator = UserMapper.toUser(userService.getById(userId)); + Item newEntry = ItemMapper.toItem(itemDto); + + newEntry.setId(lastGeneratedId++); + newEntry.setOwner(creator); + + repository.put(newEntry.getId(), newEntry); + log.info("Сохранена новая вещь с ID: {}", newEntry.getId()); + + return ItemMapper.toItemDto(newEntry); + } +} \ No newline at end of file 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 9319d7d7..1098f215 100644 --- a/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java +++ b/src/main/java/ru/practicum/shareit/item/dto/ItemDto.java @@ -1,7 +1,19 @@ package ru.practicum.shareit.item.dto; -/** - * TODO Sprint add-controllers. - */ +import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder public class ItemDto { -} + private Long id; + @NotBlank + private String name; + @NotBlank + private String description; + @NotNull + private Boolean available; + private Long requestId; +} \ No newline at end of file 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 44eb73dd..dbb28048 100644 --- a/src/main/java/ru/practicum/shareit/item/model/Item.java +++ b/src/main/java/ru/practicum/shareit/item/model/Item.java @@ -1,7 +1,20 @@ package ru.practicum.shareit.item.model; -/** - * TODO Sprint add-controllers. - */ +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; +import ru.practicum.shareit.user.User; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor public class Item { -} + private Long id; + private String name; + private String description; + private Boolean available; + private User owner; + private Long requestId; +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/user/User.java b/src/main/java/ru/practicum/shareit/user/User.java index ae6e7f33..69127b15 100644 --- a/src/main/java/ru/practicum/shareit/user/User.java +++ b/src/main/java/ru/practicum/shareit/user/User.java @@ -1,7 +1,16 @@ package ru.practicum.shareit.user; -/** - * TODO Sprint add-controllers. - */ +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +@Data +@Builder +@AllArgsConstructor +@NoArgsConstructor public class User { -} + private String email; + private String name; + private Long id; +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/user/UserController.java b/src/main/java/ru/practicum/shareit/user/UserController.java index 03039b9d..1391acf0 100644 --- a/src/main/java/ru/practicum/shareit/user/UserController.java +++ b/src/main/java/ru/practicum/shareit/user/UserController.java @@ -1,12 +1,47 @@ package ru.practicum.shareit.user; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; +import ru.practicum.shareit.user.dto.UserDto; -/** - * TODO Sprint add-controllers. - */ +import java.util.List; + +@Slf4j @RestController -@RequestMapping(path = "/users") +@RequestMapping("/users") +@RequiredArgsConstructor public class UserController { -} + private final UserService userService; + + @DeleteMapping("/{id}") + public void removeUser(@PathVariable Long id) { + log.info("DELETE request for user ID: {}", id); + userService.delete(id); + } + + @GetMapping + public List getAllUsers() { + log.info("GET request for all users"); + return userService.findAll(); + } + + @PatchMapping("/{id}") + public UserDto patchUser(@PathVariable Long id, @RequestBody UserDto dto) { + log.info("PATCH request for user ID: {}", id); + return userService.update(id, dto); + } + + @GetMapping("/{id}") + public UserDto getUser(@PathVariable Long id) { + log.info("GET request for user ID: {}", id); + return userService.getById(id); + } + + @PostMapping + public UserDto saveUser(@Valid @RequestBody UserDto dto) { + log.info("POST request to create user: {}", dto.getEmail()); + return userService.create(dto); + } +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/user/UserMapper.java b/src/main/java/ru/practicum/shareit/user/UserMapper.java new file mode 100644 index 00000000..a4620b17 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/user/UserMapper.java @@ -0,0 +1,21 @@ +package ru.practicum.shareit.user; + +import ru.practicum.shareit.user.dto.UserDto; + +public class UserMapper { + public static User toUser(UserDto dto) { + return User.builder() + .email(dto.getEmail()) + .name(dto.getName()) + .id(dto.getId()) + .build(); + } + + public static UserDto toUserDto(User model) { + return UserDto.builder() + .name(model.getName()) + .email(model.getEmail()) + .id(model.getId()) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/user/UserService.java b/src/main/java/ru/practicum/shareit/user/UserService.java new file mode 100644 index 00000000..f92f2f6c --- /dev/null +++ b/src/main/java/ru/practicum/shareit/user/UserService.java @@ -0,0 +1,16 @@ +package ru.practicum.shareit.user; + +import ru.practicum.shareit.user.dto.UserDto; +import java.util.List; + +public interface UserService { + void delete(Long id); + + List findAll(); + + UserDto update(Long id, UserDto userDto); + + UserDto getById(Long id); + + UserDto create(UserDto userDto); +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java b/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java new file mode 100644 index 00000000..fb9aa035 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/user/UserServiceImpl.java @@ -0,0 +1,80 @@ +package ru.practicum.shareit.user; + +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Service; +import ru.practicum.shareit.exception.ConflictException; +import ru.practicum.shareit.exception.NotFoundException; +import ru.practicum.shareit.exception.ValidationException; +import ru.practicum.shareit.user.dto.UserDto; + +import java.util.*; +import java.util.stream.Collectors; + +@Slf4j +@Service +@RequiredArgsConstructor +public class UserServiceImpl implements UserService { + private final Map dataStorage = new HashMap<>(); + private Long sequenceId = 1L; + + @Override + public void delete(Long id) { + dataStorage.remove(id); + } + + @Override + public List findAll() { + return dataStorage.values().stream() + .map(UserMapper::toUserDto) + .collect(Collectors.toList()); + } + + @Override + public UserDto getById(Long id) { + User user = dataStorage.get(id); + if (user == null) { + throw new NotFoundException("Пользователь не найден: " + id); + } + return UserMapper.toUserDto(user); + } + + @Override + public UserDto update(Long id, UserDto dto) { + User existingUser = dataStorage.get(id); + if (existingUser == null) { + throw new NotFoundException("Невозможно обновить: пользователь " + id + " не существует"); + } + + if (dto.getEmail() != null && !dto.getEmail().equalsIgnoreCase(existingUser.getEmail())) { + if (!dto.getEmail().contains("@")) { + throw new ValidationException("Некорректный формат email"); + } + validateEmailUniqueness(dto.getEmail(), id); + existingUser.setEmail(dto.getEmail()); + } + + if (dto.getName() != null && !dto.getName().trim().isEmpty()) { + existingUser.setName(dto.getName()); + } + + return UserMapper.toUserDto(existingUser); + } + + @Override + public UserDto create(UserDto dto) { + validateEmailUniqueness(dto.getEmail(), null); + User newUser = UserMapper.toUser(dto); + newUser.setId(sequenceId++); + dataStorage.put(newUser.getId(), newUser); + return UserMapper.toUserDto(newUser); + } + + private void validateEmailUniqueness(String email, Long currentUserId) { + boolean isDuplicate = dataStorage.values().stream() + .anyMatch(u -> u.getEmail().equalsIgnoreCase(email) && !u.getId().equals(currentUserId)); + if (isDuplicate) { + throw new ConflictException("Электронная почта " + email + " уже используется"); + } + } +} \ No newline at end of file diff --git a/src/main/java/ru/practicum/shareit/user/dto/UserDto.java b/src/main/java/ru/practicum/shareit/user/dto/UserDto.java new file mode 100644 index 00000000..e2ec0803 --- /dev/null +++ b/src/main/java/ru/practicum/shareit/user/dto/UserDto.java @@ -0,0 +1,17 @@ +package ru.practicum.shareit.user.dto; + +import jakarta.validation.constraints.Email; +import jakarta.validation.constraints.NotBlank; +import lombok.Builder; +import lombok.Data; + +@Data +@Builder +public class UserDto { + @NotBlank + @Email + private String email; + @NotBlank + private String name; + private Long id; +} \ No newline at end of file From c2399d9a860412a2ba633a9c99809ef61fd9e06e Mon Sep 17 00:00:00 2001 From: Evgeniy Date: Mon, 16 Mar 2026 22:02:13 +0700 Subject: [PATCH 3/3] shareIT14 --- shareIt14 | 1 + 1 file changed, 1 insertion(+) create mode 160000 shareIt14 diff --git a/shareIt14 b/shareIt14 new file mode 160000 index 00000000..14562461 --- /dev/null +++ b/shareIt14 @@ -0,0 +1 @@ +Subproject commit 14562461f16d80f1fddeb1d654cfc32ac6349073