diff --git a/ewm-main-service/pom.xml b/ewm-main-service/pom.xml
index dba2a53..233e90f 100644
--- a/ewm-main-service/pom.xml
+++ b/ewm-main-service/pom.xml
@@ -44,7 +44,6 @@
org.springframework.boot
spring-boot-starter-validation
- 3.5.14
org.springframework.boot
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/EwmApplication.java b/ewm-main-service/src/main/java/ru/practicum/ewm/EwmApplication.java
index f1dfbc9..9c35c42 100644
--- a/ewm-main-service/src/main/java/ru/practicum/ewm/EwmApplication.java
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/EwmApplication.java
@@ -2,8 +2,10 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
@SpringBootApplication
+@ComponentScan(basePackages = {"ru.practicum.ewm", "ru.practicum.client"}) // Добавь это!
public class EwmApplication {
public static void main(String[] args) {
SpringApplication.run(EwmApplication.class, args);
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/controller/ErrorHandler.java b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/ErrorHandler.java
new file mode 100644
index 0000000..4233366
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/ErrorHandler.java
@@ -0,0 +1,69 @@
+package ru.practicum.ewm.controller;
+
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.dao.DataIntegrityViolationException;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.MethodArgumentNotValidException;
+import org.springframework.web.bind.MissingServletRequestParameterException;
+import org.springframework.web.bind.annotation.ExceptionHandler;
+import org.springframework.web.bind.annotation.ResponseStatus;
+import org.springframework.web.bind.annotation.RestControllerAdvice;
+import ru.practicum.ewm.dto.response.ApiError;
+import ru.practicum.ewm.exception.BadRequestException;
+import ru.practicum.ewm.exception.ConflictException;
+import ru.practicum.ewm.exception.NotFoundException;
+
+import java.time.LocalDateTime;
+import java.time.format.DateTimeFormatter;
+import java.util.stream.Collectors;
+
+@Slf4j
+@RestControllerAdvice
+public class ErrorHandler {
+
+ private static final DateTimeFormatter FORMATTER = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+ @ExceptionHandler(NotFoundException.class)
+ @ResponseStatus(HttpStatus.NOT_FOUND)
+ public ApiError handleNotFound(final NotFoundException e) {
+ return createApiError(HttpStatus.NOT_FOUND, "The required object was not found.", e.getMessage());
+ }
+
+ @ExceptionHandler({ConflictException.class, DataIntegrityViolationException.class})
+ @ResponseStatus(HttpStatus.CONFLICT)
+ public ApiError handleConflict(final Exception e) {
+ return createApiError(HttpStatus.CONFLICT, "For the requested operation the conditions are not met.", e.getMessage());
+ }
+
+ @ExceptionHandler(MethodArgumentNotValidException.class)
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public ApiError handleMethodArgumentNotValid(final MethodArgumentNotValidException e) {
+ String message = e.getBindingResult().getFieldErrors().stream()
+ .map(error -> "Field: " + error.getField() + ". Error: " + error.getDefaultMessage() + ". Value: " + error.getRejectedValue())
+ .collect(Collectors.joining(", "));
+ return createApiError(HttpStatus.BAD_REQUEST, "Incorrectly made request.", message);
+ }
+
+ @ExceptionHandler({BadRequestException.class, MissingServletRequestParameterException.class})
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public ApiError handleBadRequest(final Exception e) {
+ return createApiError(HttpStatus.BAD_REQUEST, "Incorrectly made request.", e.getMessage());
+ }
+
+ @ExceptionHandler(Throwable.class)
+ @ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
+ public ApiError handleThrowable(final Throwable e) {
+ log.error("500 Error: ", e);
+ return createApiError(HttpStatus.INTERNAL_SERVER_ERROR, "Error occurred", e.getMessage());
+ }
+
+ private ApiError createApiError(HttpStatus status, String reason, String message) {
+ return ApiError.builder()
+ .status(status.name())
+ .reason(reason)
+ .message(message != null ? message : "No message available")
+ .timestamp(LocalDateTime.now().format(FORMATTER))
+ .errors(java.util.List.of())
+ .build();
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminCategoryController.java b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminCategoryController.java
new file mode 100644
index 0000000..524ec14
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminCategoryController.java
@@ -0,0 +1,36 @@
+package ru.practicum.ewm.controller.admin;
+
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+import ru.practicum.ewm.dto.request.NewCategoryDto;
+import ru.practicum.ewm.dto.response.CategoryDto;
+import ru.practicum.ewm.service.CategoryService;
+
+@RestController
+@RequestMapping("/admin/categories")
+@RequiredArgsConstructor
+public class AdminCategoryController {
+
+ private final CategoryService categoryService;
+ private static final String CAT_ID_PATH = "/{catId}";
+
+ @PostMapping
+ @ResponseStatus(HttpStatus.CREATED)
+ public CategoryDto createCategory(@Valid @RequestBody NewCategoryDto newCategoryDto) {
+ return categoryService.createCategory(newCategoryDto);
+ }
+
+ @PatchMapping(CAT_ID_PATH)
+ public CategoryDto updateCategory(@PathVariable Long catId,
+ @Valid @RequestBody CategoryDto categoryDto) {
+ return categoryService.updateCategory(catId, categoryDto);
+ }
+
+ @DeleteMapping(CAT_ID_PATH)
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ public void deleteCategory(@PathVariable Long catId) {
+ categoryService.deleteCategory(catId);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminCompilationController.java b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminCompilationController.java
new file mode 100644
index 0000000..f0b9069
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminCompilationController.java
@@ -0,0 +1,38 @@
+package ru.practicum.ewm.controller.admin;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+import ru.practicum.ewm.dto.request.NewCompilationDto;
+import ru.practicum.ewm.dto.request.UpdateCompilationRequest;
+import ru.practicum.ewm.dto.response.CompilationDto;
+import ru.practicum.ewm.service.CompilationService;
+
+import jakarta.validation.Valid;
+
+@RestController
+@RequestMapping("/admin/compilations")
+@RequiredArgsConstructor
+public class AdminCompilationController {
+
+ private final CompilationService compilationService;
+ private static final String COMP_ID_PATH = "/{compId}";
+
+ @PostMapping
+ @ResponseStatus(HttpStatus.CREATED)
+ public CompilationDto addCompilation(@Valid @RequestBody NewCompilationDto newCompilationDto) {
+ return compilationService.addCompilation(newCompilationDto);
+ }
+
+ @DeleteMapping(COMP_ID_PATH)
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ public void deleteCompilation(@PathVariable Long compId) {
+ compilationService.deleteCompilation(compId);
+ }
+
+ @PatchMapping(COMP_ID_PATH)
+ public CompilationDto updateCompilation(@PathVariable Long compId,
+ @Valid @RequestBody UpdateCompilationRequest updateRequest) {
+ return compilationService.updateCompilation(compId, updateRequest);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminEventController.java b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminEventController.java
new file mode 100644
index 0000000..1502ee3
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminEventController.java
@@ -0,0 +1,39 @@
+package ru.practicum.ewm.controller.admin;
+
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.web.bind.annotation.*;
+import ru.practicum.ewm.dto.request.UpdateEventAdmin;
+import ru.practicum.ewm.dto.response.EventFullDto;
+import ru.practicum.ewm.model.EventState;
+import ru.practicum.ewm.service.EventService;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/events")
+@RequiredArgsConstructor
+public class AdminEventController {
+
+ private final EventService eventService;
+
+ @GetMapping
+ public List getEvents(
+ @RequestParam(required = false) List users,
+ @RequestParam(required = false) List states,
+ @RequestParam(required = false) List categories,
+ @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeStart,
+ @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeEnd,
+ @RequestParam(defaultValue = "0") int from,
+ @RequestParam(defaultValue = "10") int size) {
+ return eventService.getEventsByAdmin(users, states, categories, rangeStart, rangeEnd, from, size);
+ }
+
+ @PatchMapping("/{eventId}")
+ public EventFullDto updateEvent(@PathVariable Long eventId,
+ @Valid @RequestBody UpdateEventAdmin updateRequest) {
+ return eventService.updateEventByAdmin(eventId, updateRequest);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminLocationController.java b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminLocationController.java
new file mode 100644
index 0000000..1c77e55
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminLocationController.java
@@ -0,0 +1,41 @@
+package ru.practicum.ewm.controller.admin;
+
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+import ru.practicum.ewm.dto.request.NewLocationDto;
+import ru.practicum.ewm.dto.response.LocationDto;
+import ru.practicum.ewm.service.LocationService;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/locations")
+@RequiredArgsConstructor
+public class AdminLocationController {
+
+ private final LocationService locationService;
+
+ @PostMapping
+ @ResponseStatus(HttpStatus.CREATED)
+ public LocationDto createLocation(@Valid @RequestBody NewLocationDto locationDto) {
+ return locationService.createLocation(locationDto);
+ }
+
+ @GetMapping
+ public List getAllLocations() {
+ return locationService.getAllLocations();
+ }
+
+ @GetMapping("/{locationId}")
+ public LocationDto getLocation(@PathVariable Long locationId) {
+ return locationService.getLocation(locationId);
+ }
+
+ @DeleteMapping("/{locationId}")
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ public void deleteLocation(@PathVariable Long locationId) {
+ locationService.deleteLocation(locationId);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminUserController.java b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminUserController.java
new file mode 100644
index 0000000..0ac13b1
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/admin/AdminUserController.java
@@ -0,0 +1,42 @@
+package ru.practicum.ewm.controller.admin;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import ru.practicum.ewm.dto.request.NewUserRequest;
+import ru.practicum.ewm.dto.response.UserDto;
+import ru.practicum.ewm.service.UserService;
+
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.PositiveOrZero;
+import java.util.List;
+
+@RestController
+@RequestMapping("/admin/users")
+@RequiredArgsConstructor
+@Validated
+public class AdminUserController {
+
+ private final UserService userService;
+
+ @GetMapping
+ public List getUsers(@RequestParam(required = false) List ids,
+ @RequestParam(defaultValue = "0") @PositiveOrZero int from,
+ @RequestParam(defaultValue = "10") @Positive int size) {
+ return userService.getUsers(ids, from, size);
+ }
+
+ @PostMapping
+ @ResponseStatus(HttpStatus.CREATED)
+ public UserDto createUser(@Valid @RequestBody NewUserRequest newUserRequest) {
+ return userService.createUser(newUserRequest);
+ }
+
+ @DeleteMapping("/{userId}")
+ @ResponseStatus(HttpStatus.NO_CONTENT)
+ public void deleteUser(@PathVariable Long userId) {
+ userService.deleteUser(userId);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/controller/priv/PrivateEventController.java b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/priv/PrivateEventController.java
new file mode 100644
index 0000000..9750beb
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/priv/PrivateEventController.java
@@ -0,0 +1,65 @@
+package ru.practicum.ewm.controller.priv;
+
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+import ru.practicum.ewm.dto.request.NewEventDto;
+import ru.practicum.ewm.dto.request.UpdateEventUser;
+import ru.practicum.ewm.dto.request.RequestStatusUpdate;
+import ru.practicum.ewm.dto.response.EventFullDto;
+import ru.practicum.ewm.dto.response.EventShortDto;
+import ru.practicum.ewm.dto.response.RequestDto;
+import ru.practicum.ewm.dto.response.RequestStatusUpdateResult;
+import ru.practicum.ewm.service.EventService;
+import ru.practicum.ewm.service.RequestService;
+import java.util.List;
+
+@RestController
+@RequestMapping("/users/{userId}/events")
+@RequiredArgsConstructor
+public class PrivateEventController {
+
+ private final EventService eventService;
+ private final RequestService requestService; // Добавлено поле для внедрения
+
+ @GetMapping
+ public List getEvents(@PathVariable Long userId,
+ @RequestParam(defaultValue = "0") int from,
+ @RequestParam(defaultValue = "10") int size) {
+ return eventService.getEventsByUserId(userId, from, size);
+ }
+
+ @PostMapping
+ @ResponseStatus(HttpStatus.CREATED)
+ public EventFullDto createEvent(@PathVariable Long userId, @Valid @RequestBody NewEventDto newEventDto) {
+ return eventService.createEvent(userId, newEventDto);
+ }
+
+ @GetMapping("/{eventId}")
+ public EventFullDto getEvent(@PathVariable Long userId, @PathVariable Long eventId) {
+ return eventService.getEventByIdAndUserId(userId, eventId);
+ }
+
+ @PatchMapping("/{eventId}")
+ public EventFullDto updateEvent(@PathVariable Long userId, @PathVariable Long eventId,
+ @Valid @RequestBody UpdateEventUser updateRequest) {
+ return eventService.updateEventByUserId(userId, eventId, updateRequest);
+ }
+
+ // --- Методы для работы с запросами на участие в событии (организатор) ---
+
+ @GetMapping("/{eventId}/requests")
+ public List getEventRequests(@PathVariable Long userId, @PathVariable Long eventId) {
+
+ return requestService.getEventRequests(userId, eventId);
+ }
+
+ @PatchMapping("/{eventId}/requests")
+ public RequestStatusUpdateResult updateRequestStatus(@PathVariable Long userId,
+ @PathVariable Long eventId,
+ @Valid @RequestBody RequestStatusUpdate updateRequest) {
+
+ return requestService.updateRequestStatus(userId, eventId, updateRequest);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/controller/priv/PrivateRequestController.java b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/priv/PrivateRequestController.java
new file mode 100644
index 0000000..6ab26b1
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/priv/PrivateRequestController.java
@@ -0,0 +1,33 @@
+package ru.practicum.ewm.controller.priv;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+import ru.practicum.ewm.dto.response.RequestDto;
+import ru.practicum.ewm.service.RequestService;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/users/{userId}/requests")
+@RequiredArgsConstructor
+public class PrivateRequestController {
+
+ private final RequestService requestService;
+
+ @GetMapping
+ public List getRequests(@PathVariable Long userId) {
+ return requestService.getRequestsByUserId(userId);
+ }
+
+ @PostMapping
+ @ResponseStatus(HttpStatus.CREATED)
+ public RequestDto createRequest(@PathVariable Long userId, @RequestParam Long eventId) {
+ return requestService.createRequest(userId, eventId);
+ }
+
+ @PatchMapping("/{requestId}/cancel")
+ public RequestDto cancelRequest(@PathVariable Long userId, @PathVariable Long requestId) {
+ return requestService.cancelRequest(userId, requestId);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/controller/pub/PublicCategoryController.java b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/pub/PublicCategoryController.java
new file mode 100644
index 0000000..3ee31ae
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/pub/PublicCategoryController.java
@@ -0,0 +1,27 @@
+package ru.practicum.ewm.controller.pub;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+import ru.practicum.ewm.dto.response.CategoryDto;
+import ru.practicum.ewm.service.CategoryService;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/categories")
+@RequiredArgsConstructor
+public class PublicCategoryController {
+
+ private final CategoryService categoryService;
+
+ @GetMapping
+ public List getCategories(@RequestParam(defaultValue = "0") int from,
+ @RequestParam(defaultValue = "10") int size) {
+ return categoryService.getCategories(from, size);
+ }
+
+ @GetMapping("/{catId}")
+ public CategoryDto getCategory(@PathVariable Long catId) {
+ return categoryService.getCategory(catId);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/controller/pub/PublicCompilationController.java b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/pub/PublicCompilationController.java
new file mode 100644
index 0000000..576e8ed
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/pub/PublicCompilationController.java
@@ -0,0 +1,28 @@
+package ru.practicum.ewm.controller.pub;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.*;
+import ru.practicum.ewm.dto.response.CompilationDto;
+import ru.practicum.ewm.service.CompilationService;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/compilations")
+@RequiredArgsConstructor
+public class PublicCompilationController {
+
+ private final CompilationService compilationService;
+
+ @GetMapping
+ public List getCompilations(@RequestParam(required = false) Boolean pinned,
+ @RequestParam(defaultValue = "0") int from,
+ @RequestParam(defaultValue = "10") int size) {
+ return compilationService.getCompilations(pinned, from, size);
+ }
+
+ @GetMapping("/{compId}")
+ public CompilationDto getCompilationById(@PathVariable Long compId) {
+ return compilationService.getCompilationById(compId);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/controller/pub/PublicEventController.java b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/pub/PublicEventController.java
new file mode 100644
index 0000000..4eed182
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/pub/PublicEventController.java
@@ -0,0 +1,44 @@
+package ru.practicum.ewm.controller.pub;
+
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.PositiveOrZero;
+import lombok.RequiredArgsConstructor;
+import org.springframework.format.annotation.DateTimeFormat;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+import ru.practicum.ewm.dto.response.EventFullDto;
+import ru.practicum.ewm.dto.response.EventShortDto;
+import ru.practicum.ewm.service.EventService;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@RestController
+@RequestMapping("/events")
+@RequiredArgsConstructor
+@Validated
+public class PublicEventController {
+
+ private final EventService eventService;
+
+ @GetMapping
+ public List getEvents(
+ @RequestParam(required = false) String text,
+ @RequestParam(required = false) List categories,
+ @RequestParam(required = false) Boolean paid,
+ @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeStart,
+ @RequestParam(required = false) @DateTimeFormat(pattern = "yyyy-MM-dd HH:mm:ss") LocalDateTime rangeEnd,
+ @RequestParam(defaultValue = "false") Boolean onlyAvailable,
+ @RequestParam(required = false) String sort,
+ @RequestParam(defaultValue = "0") @PositiveOrZero int from,
+ @RequestParam(defaultValue = "10") @Positive int size,
+ HttpServletRequest request) {
+ return eventService.getEventsPublic(text, categories, paid, rangeStart, rangeEnd, onlyAvailable, sort, from, size, request);
+ }
+
+ @GetMapping("/{id}")
+ public EventFullDto getEvent(@PathVariable Long id, HttpServletRequest request) {
+ return eventService.getEventByIdPublic(id, request);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/controller/pub/PublicLocationController.java b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/pub/PublicLocationController.java
new file mode 100644
index 0000000..cca0008
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/controller/pub/PublicLocationController.java
@@ -0,0 +1,29 @@
+package ru.practicum.ewm.controller.pub;
+
+import lombok.RequiredArgsConstructor;
+import org.springframework.web.bind.annotation.GetMapping;
+import org.springframework.web.bind.annotation.PathVariable;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+import ru.practicum.ewm.dto.response.LocationDto;
+import ru.practicum.ewm.service.LocationService;
+
+import java.util.List;
+
+@RestController
+@RequestMapping("/locations")
+@RequiredArgsConstructor
+public class PublicLocationController {
+
+ private final LocationService locationService;
+
+ @GetMapping
+ public List getAllLocations() {
+ return locationService.getAllLocations();
+ }
+
+ @GetMapping("/{locationId}")
+ public LocationDto getLocation(@PathVariable Long locationId) {
+ return locationService.getLocation(locationId);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/dto/request/NewEventDto.java b/ewm-main-service/src/main/java/ru/practicum/ewm/dto/request/NewEventDto.java
index cfb19ff..9f7a7f3 100644
--- a/ewm-main-service/src/main/java/ru/practicum/ewm/dto/request/NewEventDto.java
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/dto/request/NewEventDto.java
@@ -6,6 +6,7 @@
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
+import ru.practicum.ewm.dto.response.LocationDto;
import java.time.LocalDateTime;
@@ -30,7 +31,7 @@ public class NewEventDto {
Long category;
@NotNull
- Long location;
+ LocationDto location;
@NotNull
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/dto/request/UpdateEventAdmin.java b/ewm-main-service/src/main/java/ru/practicum/ewm/dto/request/UpdateEventAdmin.java
index b707545..2673a71 100644
--- a/ewm-main-service/src/main/java/ru/practicum/ewm/dto/request/UpdateEventAdmin.java
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/dto/request/UpdateEventAdmin.java
@@ -6,6 +6,7 @@
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
+import ru.practicum.ewm.dto.response.LocationDto;
import java.time.LocalDateTime;
@@ -24,7 +25,7 @@ public class UpdateEventAdmin {
String description;
Long category;
- Long location;
+ LocationDto location;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
LocalDateTime eventDate;
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/dto/request/UpdateEventUser.java b/ewm-main-service/src/main/java/ru/practicum/ewm/dto/request/UpdateEventUser.java
index cdcc376..12385b5 100644
--- a/ewm-main-service/src/main/java/ru/practicum/ewm/dto/request/UpdateEventUser.java
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/dto/request/UpdateEventUser.java
@@ -6,6 +6,7 @@
import lombok.Getter;
import lombok.Setter;
import lombok.experimental.FieldDefaults;
+import ru.practicum.ewm.dto.response.LocationDto;
import java.time.LocalDateTime;
@@ -24,7 +25,7 @@ public class UpdateEventUser {
String description;
Long category;
- Long location;
+ LocationDto location;
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
LocalDateTime eventDate;
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/dto/response/ApiError.java b/ewm-main-service/src/main/java/ru/practicum/ewm/dto/response/ApiError.java
new file mode 100644
index 0000000..ce9b904
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/dto/response/ApiError.java
@@ -0,0 +1,19 @@
+package ru.practicum.ewm.dto.response;
+
+import lombok.*;
+import lombok.experimental.FieldDefaults;
+
+import java.util.List;
+
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
+@FieldDefaults(level = AccessLevel.PRIVATE)
+public class ApiError {
+ List errors;
+ String message;
+ String reason;
+ String status;
+ String timestamp;
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/dto/response/CategoryDto.java b/ewm-main-service/src/main/java/ru/practicum/ewm/dto/response/CategoryDto.java
index 5cd4ece..fa2945c 100644
--- a/ewm-main-service/src/main/java/ru/practicum/ewm/dto/response/CategoryDto.java
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/dto/response/CategoryDto.java
@@ -1,14 +1,19 @@
package ru.practicum.ewm.dto.response;
-import lombok.AccessLevel;
-import lombok.Getter;
-import lombok.Setter;
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import lombok.*;
import lombok.experimental.FieldDefaults;
-@Getter
-@Setter
+@Data
+@Builder
+@AllArgsConstructor
+@NoArgsConstructor
@FieldDefaults(level = AccessLevel.PRIVATE)
public class CategoryDto {
Long id;
- String name;
+
+ @NotBlank
+ @Size(min = 1, max = 50)
+ String name;
}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/exception/BadRequestException.java b/ewm-main-service/src/main/java/ru/practicum/ewm/exception/BadRequestException.java
new file mode 100644
index 0000000..7dc9124
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/exception/BadRequestException.java
@@ -0,0 +1,7 @@
+package ru.practicum.ewm.exception;
+
+public class BadRequestException extends RuntimeException {
+ public BadRequestException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/exception/ConflictException.java b/ewm-main-service/src/main/java/ru/practicum/ewm/exception/ConflictException.java
new file mode 100644
index 0000000..2603c54
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/exception/ConflictException.java
@@ -0,0 +1,7 @@
+package ru.practicum.ewm.exception;
+
+public class ConflictException extends RuntimeException {
+ public ConflictException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/exception/NotFoundException.java b/ewm-main-service/src/main/java/ru/practicum/ewm/exception/NotFoundException.java
new file mode 100644
index 0000000..ef340c6
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/exception/NotFoundException.java
@@ -0,0 +1,7 @@
+package ru.practicum.ewm.exception;
+
+public class NotFoundException extends RuntimeException {
+ public NotFoundException(String message) {
+ super(message);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/mapper/CompilationMapper.java b/ewm-main-service/src/main/java/ru/practicum/ewm/mapper/CompilationMapper.java
new file mode 100644
index 0000000..cdce13d
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/mapper/CompilationMapper.java
@@ -0,0 +1,35 @@
+package ru.practicum.ewm.mapper;
+
+import ru.practicum.ewm.dto.request.NewCompilationDto;
+import ru.practicum.ewm.dto.response.CompilationDto;
+import ru.practicum.ewm.model.Compilation;
+import ru.practicum.ewm.model.Event;
+
+import java.util.Set;
+import java.util.stream.Collectors;
+
+public class CompilationMapper {
+
+ public static Compilation toCompilation(NewCompilationDto dto, Set events) {
+ Compilation compilation = new Compilation();
+ compilation.setTitle(dto.getTitle());
+ compilation.setEvents(events);
+ compilation.setPinned(dto.getPinned() != null ? dto.getPinned() : false);
+ return compilation;
+ }
+
+ public static CompilationDto toDto(Compilation compilation) {
+ CompilationDto dto = new CompilationDto();
+ dto.setId(compilation.getId());
+ dto.setTitle(compilation.getTitle());
+ dto.setPinned(compilation.getPinned());
+
+ if (compilation.getEvents() != null) {
+
+ dto.setEvents(compilation.getEvents().stream()
+ .map(EventMapper::toShortDto)
+ .collect(Collectors.toSet()));
+ }
+ return dto;
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/mapper/EventMapper.java b/ewm-main-service/src/main/java/ru/practicum/ewm/mapper/EventMapper.java
new file mode 100644
index 0000000..6a8bee9
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/mapper/EventMapper.java
@@ -0,0 +1,60 @@
+package ru.practicum.ewm.mapper;
+
+import ru.practicum.ewm.dto.response.EventFullDto;
+import ru.practicum.ewm.dto.response.EventShortDto;
+import ru.practicum.ewm.dto.request.NewEventDto;
+import ru.practicum.ewm.model.Event;
+
+public class EventMapper {
+
+ public static EventShortDto toShortDto(Event event) {
+ EventShortDto eventShortDto = new EventShortDto();
+ eventShortDto.setId(event.getId());
+ eventShortDto.setTitle(event.getTitle());
+ eventShortDto.setAnnotation(event.getAnnotation());
+ eventShortDto.setCategory(CategoryMapper.toDto(event.getCategory()));
+ eventShortDto.setEventDate(event.getEventDate());
+ eventShortDto.setInitiator(UserMapper.toDto(event.getInitiator()));
+ eventShortDto.setPaid(event.getPaid());
+ eventShortDto.setConfirmedRequests(event.getConfirmedRequests() != null ? event.getConfirmedRequests() : 0L);
+ eventShortDto.setViews(event.getViews() != null ? event.getViews() : 0L);
+ return eventShortDto;
+ }
+
+ public static EventFullDto toFullDto(Event event) {
+ EventFullDto eventFullDto = new EventFullDto();
+ eventFullDto.setId(event.getId());
+ eventFullDto.setTitle(event.getTitle());
+ eventFullDto.setAnnotation(event.getAnnotation());
+ eventFullDto.setDescription(event.getDescription());
+ eventFullDto.setCategory(CategoryMapper.toDto(event.getCategory()));
+ eventFullDto.setEventDate(event.getEventDate());
+ eventFullDto.setInitiator(UserMapper.toDto(event.getInitiator()));
+
+ if (event.getLocation() != null) {
+ eventFullDto.setLocation(LocationMapper.toDto(event.getLocation()));
+ }
+
+ eventFullDto.setCreatedOn(event.getCreatedOn());
+ eventFullDto.setPublishedOn(event.getPublishedOn());
+ eventFullDto.setState(event.getState());
+ eventFullDto.setPaid(event.getPaid());
+ eventFullDto.setParticipantLimit(event.getParticipantLimit());
+ eventFullDto.setRequestModeration(event.getRequestModeration());
+ eventFullDto.setConfirmedRequests(event.getConfirmedRequests() != null ? event.getConfirmedRequests() : 0L);
+ eventFullDto.setViews(event.getViews() != null ? event.getViews() : 0L);
+ return eventFullDto;
+ }
+
+ public static Event toEvent(NewEventDto newEventDto) {
+ Event event = new Event();
+ event.setTitle(newEventDto.getTitle());
+ event.setAnnotation(newEventDto.getAnnotation());
+ event.setDescription(newEventDto.getDescription());
+ event.setEventDate(newEventDto.getEventDate());
+ event.setPaid(newEventDto.getPaid());
+ event.setParticipantLimit(newEventDto.getParticipantLimit());
+ event.setRequestModeration(newEventDto.getRequestModeration());
+ return event;
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/mapper/LocationMapper.java b/ewm-main-service/src/main/java/ru/practicum/ewm/mapper/LocationMapper.java
index 22fab6f..be4ad71 100644
--- a/ewm-main-service/src/main/java/ru/practicum/ewm/mapper/LocationMapper.java
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/mapper/LocationMapper.java
@@ -7,6 +7,7 @@
public class LocationMapper {
public static LocationDto toDto(Location location) {
+ if (location == null) return null;
LocationDto locationDto = new LocationDto();
locationDto.setId(location.getId());
locationDto.setName(location.getName());
@@ -17,6 +18,7 @@ public static LocationDto toDto(Location location) {
}
public static Location toLocation(NewLocationDto request) {
+ if (request == null) return null;
Location location = new Location();
location.setName(request.getName());
location.setLat(request.getLat());
@@ -24,4 +26,12 @@ public static Location toLocation(NewLocationDto request) {
location.setRadius(request.getRadius());
return location;
}
+
+ public static Location toLocation(LocationDto dto) {
+ if (dto == null) return null;
+ Location location = new Location();
+ location.setLat(dto.getLat());
+ location.setLon(dto.getLon());
+ return location;
+ }
}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/mapper/RequestMapper.java b/ewm-main-service/src/main/java/ru/practicum/ewm/mapper/RequestMapper.java
new file mode 100644
index 0000000..f9932b4
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/mapper/RequestMapper.java
@@ -0,0 +1,17 @@
+package ru.practicum.ewm.mapper;
+
+import ru.practicum.ewm.dto.response.RequestDto;
+import ru.practicum.ewm.model.ParticipationRequest;
+
+public class RequestMapper {
+
+ public static RequestDto toDto(ParticipationRequest request) {
+ RequestDto requestDto = new RequestDto();
+ requestDto.setId(request.getId());
+ requestDto.setEvent(request.getEvent().getId());
+ requestDto.setRequester(request.getRequester().getId());
+ requestDto.setStatus(request.getStatus());
+ requestDto.setCreated(request.getCreated());
+ return requestDto;
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/model/Event.java b/ewm-main-service/src/main/java/ru/practicum/ewm/model/Event.java
index 84d8aea..a536bdd 100644
--- a/ewm-main-service/src/main/java/ru/practicum/ewm/model/Event.java
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/model/Event.java
@@ -22,10 +22,10 @@ public class Event {
@Column(nullable = false)
String title;
- @Column(nullable = false)
+ @Column(nullable = false, length = 2000)
String annotation;
- @Column(nullable = false, columnDefinition = "TEXT")
+ @Column(nullable = false, length = 7000)
String description;
@ManyToOne
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/model/Location.java b/ewm-main-service/src/main/java/ru/practicum/ewm/model/Location.java
index 981754a..9d03cb1 100644
--- a/ewm-main-service/src/main/java/ru/practicum/ewm/model/Location.java
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/model/Location.java
@@ -17,7 +17,7 @@ public class Location {
@GeneratedValue(strategy = GenerationType.IDENTITY)
Long id;
- @Column(nullable = false)
+ @Column
String name;
@Column(nullable = false)
@@ -26,6 +26,6 @@ public class Location {
@Column(nullable = false)
Float lon;
- @Column(nullable = false)
+ @Column
Float radius;
}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/repository/CategoryRepository.java b/ewm-main-service/src/main/java/ru/practicum/ewm/repository/CategoryRepository.java
new file mode 100644
index 0000000..6902215
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/repository/CategoryRepository.java
@@ -0,0 +1,7 @@
+package ru.practicum.ewm.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import ru.practicum.ewm.model.Category;
+
+public interface CategoryRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/repository/CompilationRepository.java b/ewm-main-service/src/main/java/ru/practicum/ewm/repository/CompilationRepository.java
new file mode 100644
index 0000000..e1e88b1
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/repository/CompilationRepository.java
@@ -0,0 +1,11 @@
+package ru.practicum.ewm.repository;
+
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import ru.practicum.ewm.model.Compilation;
+
+import java.util.List;
+
+public interface CompilationRepository extends JpaRepository {
+ List findAllByPinned(Boolean pinned, Pageable pageable);
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/repository/EventRepository.java b/ewm-main-service/src/main/java/ru/practicum/ewm/repository/EventRepository.java
new file mode 100644
index 0000000..464f914
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/repository/EventRepository.java
@@ -0,0 +1,56 @@
+package ru.practicum.ewm.repository;
+
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+import ru.practicum.ewm.model.Event;
+import ru.practicum.ewm.model.EventState;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.Optional;
+
+public interface EventRepository extends JpaRepository {
+
+ @Query("SELECT COUNT(e) > 0 FROM Event e WHERE e.category.id = :catId")
+ boolean existsByCategoryId(@Param("catId") Long catId);
+
+ List findAllByInitiatorId(Long initiatorId, Pageable pageable);
+
+ Optional findByIdAndInitiatorId(Long eventId, Long initiatorId);
+
+ @Query("SELECT e FROM Event e " +
+ "WHERE (:users IS NULL OR e.initiator.id IN :users) " +
+ "AND (:states IS NULL OR e.state IN :states) " +
+ "AND (:categories IS NULL OR e.category.id IN :categories) " +
+ "AND (CAST(:rangeStart AS timestamp) IS NULL OR e.eventDate >= :rangeStart) " +
+ "AND (CAST(:rangeEnd AS timestamp) IS NULL OR e.eventDate <= :rangeEnd)")
+ List findEventsByAdmin(
+ @Param("users") List users,
+ @Param("states") List states,
+ @Param("categories") List categories,
+ @Param("rangeStart") LocalDateTime rangeStart,
+ @Param("rangeEnd") LocalDateTime rangeEnd,
+ Pageable pageable);
+
+ @Query(value = "SELECT * FROM events e " +
+ "WHERE e.state = :state " +
+ "AND (:text IS NULL OR (e.annotation ILIKE CONCAT('%', :text, '%') OR e.description ILIKE CONCAT('%', :text, '%'))) " +
+ "AND (:categories IS NULL OR e.category_id IN :categories) " +
+ "AND (:paid IS NULL OR e.paid = :paid) " +
+ "AND (e.event_date >= :rangeStart) " +
+ "AND (e.event_date <= :rangeEnd) " +
+ "AND (:onlyAvailable = false OR e.participant_limit = 0 OR e.confirmed_requests < e.participant_limit)",
+ nativeQuery = true)
+ List findPublishedEvents(
+ @Param("state") String state, // <-- теперь String, не EventState
+ @Param("text") String text,
+ @Param("categories") List categories,
+ @Param("paid") Boolean paid,
+ @Param("rangeStart") LocalDateTime rangeStart,
+ @Param("rangeEnd") LocalDateTime rangeEnd,
+ @Param("onlyAvailable") Boolean onlyAvailable,
+ Pageable pageable);
+ Optional findByIdAndState(Long id, EventState state);
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/repository/LocationRepository.java b/ewm-main-service/src/main/java/ru/practicum/ewm/repository/LocationRepository.java
new file mode 100644
index 0000000..d45fc8a
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/repository/LocationRepository.java
@@ -0,0 +1,7 @@
+package ru.practicum.ewm.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import ru.practicum.ewm.model.Location;
+
+public interface LocationRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/repository/ParticipationRequestRepository.java b/ewm-main-service/src/main/java/ru/practicum/ewm/repository/ParticipationRequestRepository.java
new file mode 100644
index 0000000..161190e
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/repository/ParticipationRequestRepository.java
@@ -0,0 +1,18 @@
+package ru.practicum.ewm.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import ru.practicum.ewm.model.ParticipationRequest;
+import ru.practicum.ewm.model.RequestStatus;
+
+import java.util.List;
+
+public interface ParticipationRequestRepository extends JpaRepository {
+
+ List findAllByRequesterId(Long requesterId);
+
+ List findAllByEventId(Long eventId);
+
+ Long countByEventIdAndStatus(Long eventId, RequestStatus status);
+
+ boolean existsByRequesterIdAndEventId(Long requesterId, Long eventId);
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/repository/UserRepository.java b/ewm-main-service/src/main/java/ru/practicum/ewm/repository/UserRepository.java
new file mode 100644
index 0000000..f2ac76c
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/repository/UserRepository.java
@@ -0,0 +1,7 @@
+package ru.practicum.ewm.repository;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import ru.practicum.ewm.model.User;
+
+public interface UserRepository extends JpaRepository {
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/service/CategoryService.java b/ewm-main-service/src/main/java/ru/practicum/ewm/service/CategoryService.java
new file mode 100644
index 0000000..fbc0808
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/service/CategoryService.java
@@ -0,0 +1,19 @@
+package ru.practicum.ewm.service;
+
+import ru.practicum.ewm.dto.request.NewCategoryDto;
+import ru.practicum.ewm.dto.response.CategoryDto;
+
+import java.util.List;
+
+public interface CategoryService {
+
+ CategoryDto createCategory(NewCategoryDto newCategoryDto);
+
+ CategoryDto updateCategory(Long catId, CategoryDto categoryDto);
+
+ void deleteCategory(Long catId);
+
+ List getCategories(int from, int size);
+
+ CategoryDto getCategory(Long catId);
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/service/CategoryServiceImpl.java b/ewm-main-service/src/main/java/ru/practicum/ewm/service/CategoryServiceImpl.java
new file mode 100644
index 0000000..1087e7c
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/service/CategoryServiceImpl.java
@@ -0,0 +1,77 @@
+package ru.practicum.ewm.service;
+
+import lombok.AccessLevel;
+import lombok.RequiredArgsConstructor;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import ru.practicum.ewm.dto.request.NewCategoryDto;
+import ru.practicum.ewm.dto.response.CategoryDto;
+import ru.practicum.ewm.exception.ConflictException;
+import ru.practicum.ewm.exception.NotFoundException;
+import ru.practicum.ewm.mapper.CategoryMapper;
+import ru.practicum.ewm.model.Category;
+import ru.practicum.ewm.repository.CategoryRepository;
+import ru.practicum.ewm.repository.EventRepository;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@RequiredArgsConstructor
+@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
+@Transactional(readOnly = true)
+public class CategoryServiceImpl implements CategoryService {
+
+ CategoryRepository categoryRepository;
+
+ @Override
+ @Transactional
+ public CategoryDto createCategory(NewCategoryDto newCategoryDto) {
+ Category category = CategoryMapper.toCategory(newCategoryDto);
+ Category savedCategory = categoryRepository.save(category);
+ return CategoryMapper.toDto(savedCategory);
+ }
+
+ @Override
+ @Transactional
+ public CategoryDto updateCategory(Long catId, CategoryDto categoryDto) {
+ Category category = categoryRepository.findById(catId)
+ .orElseThrow(() -> new NotFoundException("Category not found"));
+ category.setName(categoryDto.getName());
+ Category updatedCategory = categoryRepository.save(category);
+ return CategoryMapper.toDto(updatedCategory);
+ }
+
+ @Override
+ public List getCategories(int from, int size) {
+ PageRequest pageRequest = PageRequest.of(from / size, size);
+ return categoryRepository.findAll(pageRequest).stream()
+ .map(CategoryMapper::toDto)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public CategoryDto getCategory(Long catId) {
+ return categoryRepository.findById(catId)
+ .map(CategoryMapper::toDto)
+ .orElseThrow(() -> new NotFoundException("Category with id=" + catId + " was not found"));
+ }
+
+
+ private final EventRepository eventRepository;
+
+ @Override
+ @Transactional
+ public void deleteCategory(Long catId) {
+ if (!categoryRepository.existsById(catId)) {
+ throw new NotFoundException("Category not found");
+ }
+
+ if (eventRepository.existsByCategoryId(catId)) {
+ throw new ConflictException("The category is not empty");
+ }
+ categoryRepository.deleteById(catId);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/service/CompilationService.java b/ewm-main-service/src/main/java/ru/practicum/ewm/service/CompilationService.java
new file mode 100644
index 0000000..7dc1424
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/service/CompilationService.java
@@ -0,0 +1,18 @@
+package ru.practicum.ewm.service;
+
+import ru.practicum.ewm.dto.request.NewCompilationDto;
+import ru.practicum.ewm.dto.request.UpdateCompilationRequest;
+import ru.practicum.ewm.dto.response.CompilationDto;
+
+import java.util.List;
+
+public interface CompilationService {
+ // Admin API
+ CompilationDto addCompilation(NewCompilationDto newCompilationDto);
+ void deleteCompilation(Long compId);
+ CompilationDto updateCompilation(Long compId, UpdateCompilationRequest updateRequest);
+
+ // Public API
+ List getCompilations(Boolean pinned, int from, int size);
+ CompilationDto getCompilationById(Long compId);
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/service/CompilationServiceImpl.java b/ewm-main-service/src/main/java/ru/practicum/ewm/service/CompilationServiceImpl.java
new file mode 100644
index 0000000..e0bf398
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/service/CompilationServiceImpl.java
@@ -0,0 +1,107 @@
+package ru.practicum.ewm.service;
+
+import lombok.AccessLevel;
+import lombok.RequiredArgsConstructor;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import ru.practicum.ewm.dto.request.NewCompilationDto;
+import ru.practicum.ewm.dto.request.UpdateCompilationRequest;
+import ru.practicum.ewm.dto.response.CompilationDto;
+import ru.practicum.ewm.exception.NotFoundException;
+import ru.practicum.ewm.mapper.CompilationMapper;
+import ru.practicum.ewm.model.Compilation;
+import ru.practicum.ewm.model.Event;
+import ru.practicum.ewm.repository.CompilationRepository;
+import ru.practicum.ewm.repository.EventRepository;
+
+import java.util.HashSet;
+import java.util.List;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+@Service
+@RequiredArgsConstructor
+@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
+@Transactional(readOnly = true)
+public class CompilationServiceImpl implements CompilationService {
+
+ CompilationRepository compilationRepository;
+ EventRepository eventRepository;
+
+ @Override
+ @Transactional
+ public CompilationDto addCompilation(NewCompilationDto newCompilationDto) {
+ Set events = new HashSet<>();
+
+ if (newCompilationDto.getEvents() != null && !newCompilationDto.getEvents().isEmpty()) {
+ events = new HashSet<>(eventRepository.findAllById(newCompilationDto.getEvents()));
+ }
+
+ Compilation compilation = CompilationMapper.toCompilation(newCompilationDto, events);
+
+ if (compilation.getPinned() == null) {
+ compilation.setPinned(false);
+ }
+
+ return CompilationMapper.toDto(compilationRepository.save(compilation));
+ }
+ @Override
+ @Transactional
+ public void deleteCompilation(Long compId) {
+ if (!compilationRepository.existsById(compId)) {
+ throw new RuntimeException("Compilation not found");
+ }
+ compilationRepository.deleteById(compId);
+ }
+
+ @Override
+ @Transactional
+ public CompilationDto updateCompilation(Long compId, UpdateCompilationRequest updateRequest) {
+ Compilation compilation = compilationRepository.findById(compId)
+ .orElseThrow(() -> new NotFoundException("Compilation with id=" + compId + " was not found"));
+
+
+ if (updateRequest.getEvents() != null) {
+ Set events = new HashSet<>(eventRepository.findAllById(updateRequest.getEvents()));
+ compilation.setEvents(events);
+ }
+
+
+ if (updateRequest.getPinned() != null) {
+ compilation.setPinned(updateRequest.getPinned());
+ }
+
+
+ if (updateRequest.getTitle() != null && !updateRequest.getTitle().isBlank()) {
+ compilation.setTitle(updateRequest.getTitle());
+ }
+
+ Compilation updatedCompilation = compilationRepository.save(compilation);
+ return CompilationMapper.toDto(updatedCompilation);
+ }
+
+ @Override
+ public List getCompilations(Boolean pinned, int from, int size) {
+ PageRequest pageRequest = PageRequest.of(from / size, size);
+ List compilations;
+
+ if (pinned != null) {
+ compilations = compilationRepository.findAllByPinned(pinned, pageRequest);
+ } else {
+ compilations = compilationRepository.findAll(pageRequest).getContent();
+ }
+
+ return compilations.stream()
+ .map(CompilationMapper::toDto)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ public CompilationDto getCompilationById(Long compId) {
+ Compilation compilation = compilationRepository.findById(compId)
+ .orElseThrow(() -> new RuntimeException("Compilation not found"));
+ return CompilationMapper.toDto(compilation);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/service/EventService.java b/ewm-main-service/src/main/java/ru/practicum/ewm/service/EventService.java
new file mode 100644
index 0000000..c74227b
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/service/EventService.java
@@ -0,0 +1,32 @@
+package ru.practicum.ewm.service;
+
+import jakarta.servlet.http.HttpServletRequest;
+import ru.practicum.ewm.dto.request.NewEventDto;
+import ru.practicum.ewm.dto.request.UpdateEventAdmin;
+import ru.practicum.ewm.dto.request.UpdateEventUser;
+import ru.practicum.ewm.dto.response.EventFullDto;
+import ru.practicum.ewm.dto.response.EventShortDto;
+import ru.practicum.ewm.model.EventState;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+public interface EventService {
+ // Private
+ List getEventsByUserId(Long userId, int from, int size);
+ EventFullDto createEvent(Long userId, NewEventDto newEventDto);
+ EventFullDto getEventByIdAndUserId(Long userId, Long eventId);
+ EventFullDto updateEventByUserId(Long userId, Long eventId, UpdateEventUser updateRequest);
+
+ // Admin
+ List getEventsByAdmin(List users, List states, List categories,
+ LocalDateTime rangeStart, LocalDateTime rangeEnd, int from, int size);
+ EventFullDto updateEventByAdmin(Long eventId, UpdateEventAdmin updateRequest);
+
+ // Public
+ List getEventsPublic(String text, List categories, Boolean paid,
+ LocalDateTime rangeStart, LocalDateTime rangeEnd,
+ Boolean onlyAvailable, String sort, int from, int size,
+ HttpServletRequest request);
+ EventFullDto getEventByIdPublic(Long id, HttpServletRequest request);
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/service/EventServiceImpl.java b/ewm-main-service/src/main/java/ru/practicum/ewm/service/EventServiceImpl.java
new file mode 100644
index 0000000..b0d46ab
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/service/EventServiceImpl.java
@@ -0,0 +1,259 @@
+package ru.practicum.ewm.service;
+
+import jakarta.servlet.http.HttpServletRequest;
+import lombok.AccessLevel;
+import lombok.RequiredArgsConstructor;
+import lombok.experimental.FieldDefaults;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import ru.practicum.client.StatsClient;
+import ru.practicum.ewm.dto.request.NewEventDto;
+import ru.practicum.ewm.dto.request.UpdateEventAdmin;
+import ru.practicum.ewm.dto.request.UpdateEventUser;
+import ru.practicum.ewm.dto.response.EventFullDto;
+import ru.practicum.ewm.dto.response.EventShortDto;
+import ru.practicum.ewm.dto.response.LocationDto;
+import ru.practicum.ewm.exception.BadRequestException;
+import ru.practicum.ewm.exception.ConflictException;
+import ru.practicum.ewm.exception.NotFoundException;
+import ru.practicum.ewm.mapper.EventMapper;
+import ru.practicum.ewm.mapper.LocationMapper;
+import ru.practicum.ewm.model.*;
+import ru.practicum.ewm.repository.*;
+
+import java.time.LocalDateTime;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@Slf4j
+@RequiredArgsConstructor
+@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
+@Transactional(readOnly = true)
+public class EventServiceImpl implements EventService {
+
+ EventRepository eventRepository;
+ UserRepository userRepository;
+ CategoryRepository categoryRepository;
+ LocationRepository locationRepository;
+ StatsClient statsClient;
+
+ static String APP_NAME = "ewm-main-service";
+
+ // --- PRIVATE API ---
+
+ @Override
+ @Transactional
+ public EventFullDto createEvent(Long userId, NewEventDto dto) {
+
+ if (dto.getEventDate().isBefore(LocalDateTime.now().plusHours(2))) {
+ throw new BadRequestException("Event date must be at least 2 hours in the future.");
+ }
+
+ User initiator = userRepository.findById(userId)
+ .orElseThrow(() -> new NotFoundException("User with id=" + userId + " was not found"));
+ Category category = categoryRepository.findById(dto.getCategory())
+ .orElseThrow(() -> new NotFoundException("Category with id=" + dto.getCategory() + " was not found"));
+
+
+ Location location = locationRepository.save(LocationMapper.toLocation(dto.getLocation()));
+
+ Event event = EventMapper.toEvent(dto);
+ event.setInitiator(initiator);
+ event.setCategory(category);
+ event.setLocation(location);
+ event.setCreatedOn(LocalDateTime.now());
+ event.setState(EventState.PENDING);
+ event.setConfirmedRequests(0L);
+ event.setViews(0L);
+
+
+ if (event.getPaid() == null) event.setPaid(false);
+ if (event.getParticipantLimit() == null) event.setParticipantLimit(0);
+ if (event.getRequestModeration() == null) event.setRequestModeration(true);
+
+ return EventMapper.toFullDto(eventRepository.save(event));
+ }
+
+ @Override
+ public List getEventsByUserId(Long userId, int from, int size) {
+ return eventRepository.findAllByInitiatorId(userId, PageRequest.of(from / size, size))
+ .stream().map(EventMapper::toShortDto).collect(Collectors.toList());
+ }
+
+ @Override
+ public EventFullDto getEventByIdAndUserId(Long userId, Long eventId) {
+ Event event = eventRepository.findByIdAndInitiatorId(eventId, userId)
+ .orElseThrow(() -> new NotFoundException("Event with id=" + eventId + " not found"));
+ return EventMapper.toFullDto(event);
+ }
+
+ @Override
+ @Transactional
+ public EventFullDto updateEventByUserId(Long userId, Long eventId, UpdateEventUser request) {
+ Event event = eventRepository.findByIdAndInitiatorId(eventId, userId)
+ .orElseThrow(() -> new NotFoundException("Event with id=" + eventId + " was not found"));
+
+ if (event.getState() == EventState.PUBLISHED) {
+ throw new ConflictException("Only pending or canceled events can be changed");
+ }
+
+ if (request.getEventDate() != null) {
+
+ if (request.getEventDate().isBefore(LocalDateTime.now().plusHours(2))) {
+ throw new BadRequestException("Event date must be at least 2 hours in the future.");
+ }
+ event.setEventDate(request.getEventDate());
+ }
+
+ if (request.getStateAction() != null) {
+ if (request.getStateAction().equals("SEND_TO_REVIEW")) {
+ event.setState(EventState.PENDING);
+ } else if (request.getStateAction().equals("CANCEL_REVIEW")) {
+ event.setState(EventState.CANCELED);
+ }
+ }
+
+
+ updateEventCommonFields(event, request.getAnnotation(), request.getCategory(), request.getDescription(),
+ request.getLocation(), request.getPaid(), request.getParticipantLimit(), request.getRequestModeration(), request.getTitle());
+
+ return EventMapper.toFullDto(eventRepository.save(event));
+ }
+
+ // --- ADMIN API ---
+
+ @Override
+ public List getEventsByAdmin(List users, List states, List categories,
+ LocalDateTime start, LocalDateTime end, int from, int size) {
+ return eventRepository.findEventsByAdmin(users, states, categories, start, end, PageRequest.of(from / size, size))
+ .stream().map(EventMapper::toFullDto).collect(Collectors.toList());
+ }
+
+ @Override
+ @Transactional
+ public EventFullDto updateEventByAdmin(Long eventId, UpdateEventAdmin request) {
+ Event event = eventRepository.findById(eventId)
+ .orElseThrow(() -> new NotFoundException("Event with id=" + eventId + " was not found"));
+
+ if (request.getEventDate() != null) {
+ if (request.getEventDate().isBefore(LocalDateTime.now().plusHours(1))) {
+ throw new BadRequestException("Event date must be at least 1 hour before publication.");
+ }
+ event.setEventDate(request.getEventDate());
+ }
+
+ if (request.getStateAction() != null) {
+ if (request.getStateAction().equals("PUBLISH_EVENT")) {
+ if (event.getState() != EventState.PENDING) {
+ throw new ConflictException("Cannot publish the event because it's not in the PENDING state.");
+ }
+ event.setState(EventState.PUBLISHED);
+ event.setPublishedOn(LocalDateTime.now());
+ } else if (request.getStateAction().equals("REJECT_EVENT")) {
+ if (event.getState() == EventState.PUBLISHED) {
+ throw new ConflictException("Cannot reject the event because it's already published.");
+ }
+ event.setState(EventState.CANCELED);
+ }
+ }
+
+ updateEventCommonFields(event, request.getAnnotation(), request.getCategory(), request.getDescription(),
+ request.getLocation(), request.getPaid(), request.getParticipantLimit(), request.getRequestModeration(), request.getTitle());
+
+ return EventMapper.toFullDto(eventRepository.save(event));
+ }
+
+ // --- PUBLIC API ---
+
+ @Override
+ public List getEventsPublic(String text, List categories, Boolean paid,
+ LocalDateTime rangeStart, LocalDateTime rangeEnd,
+ Boolean onlyAvailable, String sort, int from, int size,
+ HttpServletRequest request) {
+
+ if (rangeStart != null && rangeEnd != null && rangeStart.isAfter(rangeEnd)) {
+ throw new BadRequestException("Start date must be before end date");
+ }
+
+ List categoryIds = (categories != null && categories.isEmpty()) ? null : categories;
+
+ LocalDateTime start = (rangeStart != null) ? rangeStart : LocalDateTime.now().minusSeconds(1);
+ LocalDateTime end = (rangeEnd != null) ? rangeEnd : LocalDateTime.now().plusYears(100);
+
+ sendStats(request);
+
+ int pageSize = (size > 0) ? size : 10;
+ int pageNum = from / pageSize;
+ PageRequest pageable = PageRequest.of(pageNum, pageSize);
+
+ List events = eventRepository.findPublishedEvents(
+ EventState.PUBLISHED.name(), text, categoryIds, paid, start, end,
+ (onlyAvailable != null && onlyAvailable), pageable);
+ if (sort != null) {
+ if (sort.equalsIgnoreCase("VIEWS")) {
+ events = events.stream()
+ .sorted((e1, e2) -> Long.compare(
+ e2.getViews() != null ? e2.getViews() : 0,
+ e1.getViews() != null ? e1.getViews() : 0))
+ .collect(Collectors.toList());
+ } else if (sort.equalsIgnoreCase("EVENT_DATE")) {
+ events = events.stream()
+ .sorted((e1, e2) -> e1.getEventDate().compareTo(e2.getEventDate()))
+ .collect(Collectors.toList());
+ }
+ }
+
+ return events.stream().map(EventMapper::toShortDto).collect(Collectors.toList());
+ }
+ @Override
+ public EventFullDto getEventByIdPublic(Long id, HttpServletRequest request) {
+ Event event = eventRepository.findByIdAndState(id, EventState.PUBLISHED)
+ .orElseThrow(() -> new NotFoundException("Published event not found"));
+
+ sendStats(request);
+ event.setViews(event.getViews() + 1);
+ eventRepository.save(event);
+
+ return EventMapper.toFullDto(event);
+ }
+
+
+
+ private void updateEventCommonFields(Event event, String annotation, Long catId, String description,
+ LocationDto locationDto, Boolean paid, Integer partLimit, Boolean reqMod, String title) {
+ if (annotation != null && !annotation.isBlank()) event.setAnnotation(annotation);
+ if (catId != null) {
+ event.setCategory(categoryRepository.findById(catId)
+ .orElseThrow(() -> new NotFoundException("Category not found")));
+ }
+ if (description != null && !description.isBlank()) event.setDescription(description);
+
+
+ if (locationDto != null) {
+ Location location = locationRepository.save(LocationMapper.toLocation(locationDto));
+ event.setLocation(location);
+ }
+
+ if (paid != null) event.setPaid(paid);
+ if (partLimit != null) {
+ if (partLimit < 0) {
+ throw new BadRequestException("Participant limit cannot be negative");
+ }
+ event.setParticipantLimit(partLimit);
+ }
+ if (reqMod != null) event.setRequestModeration(reqMod);
+ if (title != null && !title.isBlank()) event.setTitle(title);
+ }
+
+
+ private void sendStats(HttpServletRequest request) {
+ try {
+ statsClient.hit(APP_NAME, request.getRequestURI(), request.getRemoteAddr(), LocalDateTime.now());
+ } catch (Exception e) {
+ log.error("Ошибка статистики: {}", e.getMessage());
+ }
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/service/LocationService.java b/ewm-main-service/src/main/java/ru/practicum/ewm/service/LocationService.java
new file mode 100644
index 0000000..05f12f7
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/service/LocationService.java
@@ -0,0 +1,17 @@
+package ru.practicum.ewm.service;
+
+import ru.practicum.ewm.dto.request.NewLocationDto;
+import ru.practicum.ewm.dto.response.LocationDto;
+
+import java.util.List;
+
+public interface LocationService {
+
+ LocationDto createLocation(NewLocationDto newLocationDto);
+
+ LocationDto getLocation(Long locationId);
+
+ List getAllLocations();
+
+ void deleteLocation(Long locationId);
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/service/LocationServiceImpl.java b/ewm-main-service/src/main/java/ru/practicum/ewm/service/LocationServiceImpl.java
new file mode 100644
index 0000000..e1464f4
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/service/LocationServiceImpl.java
@@ -0,0 +1,51 @@
+package ru.practicum.ewm.service;
+
+import lombok.AccessLevel;
+import lombok.RequiredArgsConstructor;
+import lombok.experimental.FieldDefaults;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import ru.practicum.ewm.dto.request.NewLocationDto;
+import ru.practicum.ewm.dto.response.LocationDto;
+import ru.practicum.ewm.mapper.LocationMapper;
+import ru.practicum.ewm.model.Location;
+import ru.practicum.ewm.repository.LocationRepository;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@RequiredArgsConstructor
+@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
+@Transactional(readOnly = true)
+public class LocationServiceImpl implements LocationService {
+
+ LocationRepository locationRepository;
+
+ @Override
+ @Transactional
+ public LocationDto createLocation(NewLocationDto newLocationDto) {
+ Location location = LocationMapper.toLocation(newLocationDto);
+ Location savedLocation = locationRepository.save(location);
+ return LocationMapper.toDto(savedLocation);
+ }
+ @Override
+ public LocationDto getLocation(Long locationId) {
+ Location location = locationRepository.findById(locationId)
+ .orElseThrow(() -> new RuntimeException("Location not found"));
+ return LocationMapper.toDto(location);
+ }
+
+ @Override
+ public List getAllLocations() {
+ return locationRepository.findAll().stream()
+ .map(LocationMapper::toDto)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ @Transactional
+ public void deleteLocation(Long locationId) {
+ locationRepository.deleteById(locationId);
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/service/RequestService.java b/ewm-main-service/src/main/java/ru/practicum/ewm/service/RequestService.java
new file mode 100644
index 0000000..93679df
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/service/RequestService.java
@@ -0,0 +1,18 @@
+package ru.practicum.ewm.service;
+
+import ru.practicum.ewm.dto.request.RequestStatusUpdate;
+import ru.practicum.ewm.dto.response.RequestDto;
+import ru.practicum.ewm.dto.response.RequestStatusUpdateResult;
+
+import java.util.List;
+
+public interface RequestService {
+ // Для тех, кто подает заявку (Requester)
+ List getRequestsByUserId(Long userId);
+ RequestDto createRequest(Long userId, Long eventId);
+ RequestDto cancelRequest(Long userId, Long requestId);
+
+ // Для организатора события (Initiator)
+ List getEventRequests(Long userId, Long eventId);
+ RequestStatusUpdateResult updateRequestStatus(Long userId, Long eventId, RequestStatusUpdate updateRequest);
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/service/RequestServiceImpl.java b/ewm-main-service/src/main/java/ru/practicum/ewm/service/RequestServiceImpl.java
new file mode 100644
index 0000000..8ec0140
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/service/RequestServiceImpl.java
@@ -0,0 +1,144 @@
+package ru.practicum.ewm.service;
+
+import lombok.AccessLevel;
+import lombok.RequiredArgsConstructor;
+import lombok.experimental.FieldDefaults;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import ru.practicum.ewm.dto.request.RequestStatusUpdate;
+import ru.practicum.ewm.dto.response.RequestDto;
+import ru.practicum.ewm.dto.response.RequestStatusUpdateResult;
+import ru.practicum.ewm.exception.ConflictException;
+import ru.practicum.ewm.exception.NotFoundException;
+import ru.practicum.ewm.mapper.RequestMapper;
+import ru.practicum.ewm.model.*;
+import ru.practicum.ewm.repository.EventRepository;
+import ru.practicum.ewm.repository.ParticipationRequestRepository;
+import ru.practicum.ewm.repository.UserRepository;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@RequiredArgsConstructor
+@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
+@Transactional(readOnly = true)
+public class RequestServiceImpl implements RequestService {
+
+ ParticipationRequestRepository requestRepository;
+ EventRepository eventRepository;
+ UserRepository userRepository;
+
+ @Override
+ public List getRequestsByUserId(Long userId) {
+ if (!userRepository.existsById(userId)) throw new NotFoundException("User not found");
+ return requestRepository.findAllByRequesterId(userId).stream()
+ .map(RequestMapper::toDto).collect(Collectors.toList());
+ }
+
+ @Override
+ @Transactional
+ public RequestDto createRequest(Long userId, Long eventId) {
+ User requester = userRepository.findById(userId).orElseThrow(() -> new NotFoundException("User not found"));
+ Event event = eventRepository.findById(eventId).orElseThrow(() -> new NotFoundException("Event not found"));
+
+ if (requestRepository.existsByRequesterIdAndEventId(userId, eventId)) {
+ throw new ConflictException("Request already exists");
+ }
+ if (event.getInitiator().getId().equals(userId)) {
+ throw new ConflictException("Initiator cannot request participation in their own event");
+ }
+ if (event.getState() != EventState.PUBLISHED) {
+ throw new ConflictException("Cannot participate in unpublished event");
+ }
+ if (event.getParticipantLimit() > 0 && event.getConfirmedRequests() >= event.getParticipantLimit()) {
+ throw new ConflictException("Participant limit reached");
+ }
+
+ ParticipationRequest request = new ParticipationRequest();
+ request.setCreated(LocalDateTime.now());
+ request.setEvent(event);
+ request.setRequester(requester);
+
+ if (!event.getRequestModeration() || event.getParticipantLimit() == 0) {
+ request.setStatus(RequestStatus.CONFIRMED);
+ event.setConfirmedRequests(event.getConfirmedRequests() + 1);
+ eventRepository.save(event);
+ } else {
+ request.setStatus(RequestStatus.PENDING);
+ }
+
+ return RequestMapper.toDto(requestRepository.save(request));
+ }
+
+ @Override
+ @Transactional
+ public RequestDto cancelRequest(Long userId, Long requestId) {
+ ParticipationRequest request = requestRepository.findById(requestId)
+ .orElseThrow(() -> new NotFoundException("Request with id=" + requestId + " was not found"));
+
+ if (!request.getRequester().getId().equals(userId)) {
+ throw new ConflictException("You can only cancel your own requests");
+ }
+
+ if (request.getStatus() != RequestStatus.PENDING) {
+ throw new ConflictException("Cannot cancel request with status: " + request.getStatus());
+ }
+
+ request.setStatus(RequestStatus.CANCELED);
+
+ if (request.getStatus() == RequestStatus.CONFIRMED) {
+ throw new ConflictException("Cannot cancel a confirmed request");
+ }
+
+ return RequestMapper.toDto(requestRepository.save(request));
+
+ }
+
+ @Override
+ public List getEventRequests(Long userId, Long eventId) {
+ Event event = eventRepository.findById(eventId).orElseThrow(() -> new NotFoundException("Event not found"));
+ if (!event.getInitiator().getId().equals(userId)) throw new ConflictException("Not your event");
+
+ return requestRepository.findAllByEventId(eventId).stream()
+ .map(RequestMapper::toDto).collect(Collectors.toList());
+ }
+
+ @Override
+ @Transactional
+ public RequestStatusUpdateResult updateRequestStatus(Long userId, Long eventId, RequestStatusUpdate update) {
+ Event event = eventRepository.findById(eventId).orElseThrow(() -> new NotFoundException("Event not found"));
+ if (!event.getInitiator().getId().equals(userId)) throw new ConflictException("Not your event");
+
+ List requests = requestRepository.findAllById(update.getRequestIds());
+
+ RequestStatusUpdateResult result = new RequestStatusUpdateResult();
+ result.setConfirmedRequests(new ArrayList<>());
+ result.setRejectedRequests(new ArrayList<>());
+
+ for (ParticipationRequest req : requests) {
+ if (req.getStatus() != RequestStatus.PENDING) {
+ throw new ConflictException("Request must be in status PENDING");
+ }
+
+ if (update.getStatus() == RequestStatus.REJECTED) {
+ req.setStatus(RequestStatus.REJECTED);
+ result.getRejectedRequests().add(RequestMapper.toDto(requestRepository.save(req)));
+ } else {
+ if (event.getParticipantLimit() == 0 || event.getConfirmedRequests() < event.getParticipantLimit()) {
+ req.setStatus(RequestStatus.CONFIRMED);
+ event.setConfirmedRequests(event.getConfirmedRequests() + 1);
+ result.getConfirmedRequests().add(RequestMapper.toDto(requestRepository.save(req)));
+ } else {
+ req.setStatus(RequestStatus.REJECTED);
+ result.getRejectedRequests().add(RequestMapper.toDto(requestRepository.save(req)));
+ throw new ConflictException("Participant limit reached");
+ }
+ }
+ }
+ eventRepository.save(event);
+ return result;
+ }
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/service/UserService.java b/ewm-main-service/src/main/java/ru/practicum/ewm/service/UserService.java
new file mode 100644
index 0000000..534a248
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/service/UserService.java
@@ -0,0 +1,15 @@
+package ru.practicum.ewm.service;
+
+import ru.practicum.ewm.dto.request.NewUserRequest;
+import ru.practicum.ewm.dto.response.UserDto;
+
+import java.util.List;
+
+public interface UserService {
+
+ UserDto createUser(NewUserRequest newUserRequest);
+
+ List getUsers(List ids, int from, int size);
+
+ void deleteUser(Long userId);
+}
\ No newline at end of file
diff --git a/ewm-main-service/src/main/java/ru/practicum/ewm/service/UserServiceImpl.java b/ewm-main-service/src/main/java/ru/practicum/ewm/service/UserServiceImpl.java
new file mode 100644
index 0000000..81a4b1f
--- /dev/null
+++ b/ewm-main-service/src/main/java/ru/practicum/ewm/service/UserServiceImpl.java
@@ -0,0 +1,54 @@
+package ru.practicum.ewm.service;
+
+import lombok.AccessLevel;
+import lombok.RequiredArgsConstructor;
+import lombok.experimental.FieldDefaults;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+import ru.practicum.ewm.dto.request.NewUserRequest;
+import ru.practicum.ewm.dto.response.UserDto;
+import ru.practicum.ewm.mapper.UserMapper;
+import ru.practicum.ewm.model.User;
+import ru.practicum.ewm.repository.UserRepository;
+
+import java.util.List;
+import java.util.stream.Collectors;
+
+@Service
+@RequiredArgsConstructor
+@FieldDefaults(level = AccessLevel.PRIVATE, makeFinal = true)
+@Transactional(readOnly = true)
+public class UserServiceImpl implements UserService {
+
+ UserRepository userRepository;
+
+ @Override
+ @Transactional
+ public UserDto createUser(NewUserRequest newUserRequest) {
+ User user = UserMapper.toUser(newUserRequest);
+ User savedUser = userRepository.save(user);
+ return UserMapper.toDto(savedUser);
+ }
+
+ @Override
+ public List getUsers(List ids, int from, int size) {
+ PageRequest pageRequest = PageRequest.of(from / size, size);
+
+ if (ids == null || ids.isEmpty()) {
+ return userRepository.findAll(pageRequest).stream()
+ .map(UserMapper::toDto)
+ .collect(Collectors.toList());
+ }
+
+ return userRepository.findAllById(ids).stream()
+ .map(UserMapper::toDto)
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ @Transactional
+ public void deleteUser(Long userId) {
+ userRepository.deleteById(userId);
+ }
+}
\ No newline at end of file