diff --git a/.github/copilot-instructions.md b/.github/copilot-instructions.md index 7ebab26..45dada4 100644 --- a/.github/copilot-instructions.md +++ b/.github/copilot-instructions.md @@ -1 +1 @@ -When performing a code review, respond in Russian. +Комментарии в Pull Request обязательно должны быть на русском языке! diff --git a/compose.yaml b/compose.yaml index 3eb1dfa..07cc69d 100644 --- a/compose.yaml +++ b/compose.yaml @@ -15,13 +15,13 @@ services: interval: 5s retries: 10 - main-db: + ewm-db: image: postgres:16.1 - container_name: main_db + container_name: ewm_db ports: - "5433:5432" environment: - - POSTGRES_DB=mainDB + - POSTGRES_DB=ewmDB - POSTGRES_USER=dbuser - POSTGRES_PASSWORD=12345 healthcheck: @@ -42,14 +42,16 @@ services: - SPRING_DATASOURCE_USERNAME=dbuser - SPRING_DATASOURCE_PASSWORD=12345 - main-service: + ewm-service: build: ./main-service - container_name: main_service + container_name: ewm_service ports: - "8080:8080" depends_on: - - main-db + - stats-server + - ewm-db environment: - - SPRING_DATASOURCE_URL=jdbc:postgresql://main-db:5432/mainDB + - SPRING_DATASOURCE_URL=jdbc:postgresql://ewm-db:5432/ewmDB - SPRING_DATASOURCE_USERNAME=dbuser - - SPRING_DATASOURCE_PASSWORD=12345 \ No newline at end of file + - SPRING_DATASOURCE_PASSWORD=12345 + - STATS_SERVER_URL=http://stats-server:9090 \ No newline at end of file diff --git a/main-service/src/main/java/ru/practicum/category/controller/AdminCategoryController.java b/main-service/src/main/java/ru/practicum/category/controller/AdminCategoryController.java index 7212051..859fbf6 100644 --- a/main-service/src/main/java/ru/practicum/category/controller/AdminCategoryController.java +++ b/main-service/src/main/java/ru/practicum/category/controller/AdminCategoryController.java @@ -32,9 +32,8 @@ public void deleteCategory(@PathVariable @Positive Long categoryId) { } @PatchMapping("/{categoryId}") - public CategoryDto updateCategory( - @PathVariable @Positive Long categoryId, - @RequestBody @Valid NewCategoryDto newCategoryDto) { + public CategoryDto updateCategory(@PathVariable @Positive Long categoryId, + @RequestBody @Valid NewCategoryDto newCategoryDto) { log.debug("Controller: updateCategory id={}, data={}", categoryId, newCategoryDto); return categoryService.updateCategory(categoryId, newCategoryDto); } diff --git a/main-service/src/main/java/ru/practicum/compilation/controller/AdminCompilationController.java b/main-service/src/main/java/ru/practicum/compilation/controller/AdminCompilationController.java index 1897cf1..f994c1e 100644 --- a/main-service/src/main/java/ru/practicum/compilation/controller/AdminCompilationController.java +++ b/main-service/src/main/java/ru/practicum/compilation/controller/AdminCompilationController.java @@ -1,4 +1,41 @@ package ru.practicum.compilation.controller; +import jakarta.validation.Valid; +import jakarta.validation.constraints.Positive; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.*; +import ru.practicum.compilation.dto.CompilationDto; +import ru.practicum.compilation.dto.NewCompilationDto; +import ru.practicum.compilation.dto.UpdateCompilationRequest; +import ru.practicum.compilation.service.CompilationService; + +@Slf4j +@RestController +@RequestMapping("/admin/compilations") +@RequiredArgsConstructor public class AdminCompilationController { + private final CompilationService compilationService; + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public CompilationDto createCompilation(@RequestBody @Valid NewCompilationDto request) { + log.debug("Controller: createCompilation data {}", request); + return compilationService.createCompilation(request); + } + + @DeleteMapping("/{compId}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteCompilation(@PathVariable @Positive Long compId) { + log.debug("Controller: deleteCompilation compId={}", compId); + compilationService.deleteCompilation(compId); + } + + @PatchMapping("/{compId}") + public CompilationDto updateCompilation(@PathVariable @Positive Long compId, + @RequestBody @Valid UpdateCompilationRequest request) { + log.debug("Controller: updateCompilation compId={} and data {}", compId, request); + return compilationService.updateCompilation(compId, request); + } } diff --git a/main-service/src/main/java/ru/practicum/compilation/controller/PublicCompilationController.java b/main-service/src/main/java/ru/practicum/compilation/controller/PublicCompilationController.java index afe13ce..5ee2a3c 100644 --- a/main-service/src/main/java/ru/practicum/compilation/controller/PublicCompilationController.java +++ b/main-service/src/main/java/ru/practicum/compilation/controller/PublicCompilationController.java @@ -1,4 +1,36 @@ package ru.practicum.compilation.controller; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.web.bind.annotation.*; +import ru.practicum.compilation.dto.CompilationDto; +import ru.practicum.compilation.service.CompilationService; + +import java.util.List; + +@Slf4j +@RestController +@RequestMapping("/compilations") +@RequiredArgsConstructor public class PublicCompilationController { + private final CompilationService compilationService; + + @GetMapping + public List getCompilations(@RequestParam(required = false) Boolean pinned, + @RequestParam(defaultValue = "0") @PositiveOrZero Integer from, + @RequestParam(defaultValue = "10") @Positive Integer size) { + Pageable pageable = PageRequest.of(from / size, size); + log.debug("Controller: getCompilations pinned={}, from={}, size={}", pinned, from, size); + return compilationService.getCompilations(pinned, pageable); + } + + @GetMapping("/{compId}") + public CompilationDto getCompilation(@PathVariable @Positive Long compId) { + log.debug("Controller: getCompilation compId={}", compId); + return compilationService.getCompilationById(compId); + } } diff --git a/main-service/src/main/java/ru/practicum/compilation/dto/CompilationDto.java b/main-service/src/main/java/ru/practicum/compilation/dto/CompilationDto.java index 342835b..078f753 100644 --- a/main-service/src/main/java/ru/practicum/compilation/dto/CompilationDto.java +++ b/main-service/src/main/java/ru/practicum/compilation/dto/CompilationDto.java @@ -2,11 +2,11 @@ import ru.practicum.event.dto.EventShortDto; -import java.util.List; +import java.util.Set; public record CompilationDto( Long id, - List events, + Set events, boolean pinned, String title ) { diff --git a/main-service/src/main/java/ru/practicum/compilation/dto/UpdateCompilationRequest.java b/main-service/src/main/java/ru/practicum/compilation/dto/UpdateCompilationRequest.java index 7d5063c..137f266 100644 --- a/main-service/src/main/java/ru/practicum/compilation/dto/UpdateCompilationRequest.java +++ b/main-service/src/main/java/ru/practicum/compilation/dto/UpdateCompilationRequest.java @@ -1,6 +1,5 @@ package ru.practicum.compilation.dto; -import jakarta.validation.constraints.NotBlank; import jakarta.validation.constraints.Size; import java.util.Set; @@ -10,7 +9,6 @@ public record UpdateCompilationRequest( boolean pinned, - @NotBlank(message = "Заголовок подборки не может быть пустым") @Size(min = 1, max = 50, message = "Заголовок должен содержать от {min} до {max} символов") String title ) { diff --git a/main-service/src/main/java/ru/practicum/compilation/mapper/CompilationMapper.java b/main-service/src/main/java/ru/practicum/compilation/mapper/CompilationMapper.java index 0f0c19f..fb761c5 100644 --- a/main-service/src/main/java/ru/practicum/compilation/mapper/CompilationMapper.java +++ b/main-service/src/main/java/ru/practicum/compilation/mapper/CompilationMapper.java @@ -1,4 +1,19 @@ package ru.practicum.compilation.mapper; -public class CompilationMapper { +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import ru.practicum.compilation.dto.CompilationDto; +import ru.practicum.compilation.dto.NewCompilationDto; +import ru.practicum.compilation.model.Compilation; +import ru.practicum.event.mapper.EventMapper; + +@Mapper(componentModel = "spring", uses = EventMapper.class) +public interface CompilationMapper { + + @Mapping(target = "id", ignore = true) + @Mapping(target = "events", ignore = true) + Compilation toEntity(NewCompilationDto request); + + @Mapping(target = "events", qualifiedByName = "toEventShortWithoutStats") + CompilationDto toDto(Compilation compilation); } diff --git a/main-service/src/main/java/ru/practicum/compilation/model/Compilation.java b/main-service/src/main/java/ru/practicum/compilation/model/Compilation.java index 0b851d0..d142911 100644 --- a/main-service/src/main/java/ru/practicum/compilation/model/Compilation.java +++ b/main-service/src/main/java/ru/practicum/compilation/model/Compilation.java @@ -23,7 +23,7 @@ public class Compilation { private String title; @Column(nullable = false) - private boolean pinned = false; + private boolean pinned; @ManyToMany(fetch = FetchType.LAZY) @JoinTable( diff --git a/main-service/src/main/java/ru/practicum/compilation/repository/CompilationRepository.java b/main-service/src/main/java/ru/practicum/compilation/repository/CompilationRepository.java index fde531f..349ca99 100644 --- a/main-service/src/main/java/ru/practicum/compilation/repository/CompilationRepository.java +++ b/main-service/src/main/java/ru/practicum/compilation/repository/CompilationRepository.java @@ -1,7 +1,24 @@ package ru.practicum.compilation.repository; +import org.springframework.data.domain.Page; +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.compilation.model.Compilation; +import java.util.Optional; + public interface CompilationRepository extends JpaRepository { + Page findByPinned(Boolean pinned, Pageable pageable); + + @Query(""" + SELECT c FROM Compilation c + LEFT JOIN FETCH c.events e + LEFT JOIN FETCH e.category + LEFT JOIN FETCH e.initiator + WHERE c.id = :id""") + Optional findByIdWithEvents(@Param("id") Long id); + + boolean existsByTitle(String title); } diff --git a/main-service/src/main/java/ru/practicum/compilation/service/CompilationService.java b/main-service/src/main/java/ru/practicum/compilation/service/CompilationService.java index 8d22d2a..7acb92c 100644 --- a/main-service/src/main/java/ru/practicum/compilation/service/CompilationService.java +++ b/main-service/src/main/java/ru/practicum/compilation/service/CompilationService.java @@ -1,4 +1,20 @@ package ru.practicum.compilation.service; +import org.springframework.data.domain.Pageable; +import ru.practicum.compilation.dto.CompilationDto; +import ru.practicum.compilation.dto.NewCompilationDto; +import ru.practicum.compilation.dto.UpdateCompilationRequest; + +import java.util.List; + public interface CompilationService { + CompilationDto createCompilation(NewCompilationDto request); + + void deleteCompilation(Long compId); + + CompilationDto updateCompilation(Long compId, UpdateCompilationRequest request); + + List getCompilations(Boolean pinned, Pageable pageable); + + CompilationDto getCompilationById(Long compId); } diff --git a/main-service/src/main/java/ru/practicum/compilation/service/CompilationServiceImpl.java b/main-service/src/main/java/ru/practicum/compilation/service/CompilationServiceImpl.java index 663fca0..c8d3d46 100644 --- a/main-service/src/main/java/ru/practicum/compilation/service/CompilationServiceImpl.java +++ b/main-service/src/main/java/ru/practicum/compilation/service/CompilationServiceImpl.java @@ -1,4 +1,131 @@ package ru.practicum.compilation.service; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Sort; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; +import ru.practicum.compilation.dto.CompilationDto; +import ru.practicum.compilation.dto.NewCompilationDto; +import ru.practicum.compilation.dto.UpdateCompilationRequest; +import ru.practicum.compilation.mapper.CompilationMapper; +import ru.practicum.compilation.model.Compilation; +import ru.practicum.compilation.repository.CompilationRepository; +import ru.practicum.event.model.Event; +import ru.practicum.event.repository.EventRepository; +import ru.practicum.exception.ConflictException; +import ru.practicum.exception.NotFoundException; + +import java.util.ArrayList; +import java.util.HashSet; +import java.util.List; + +@Slf4j +@Service +@RequiredArgsConstructor +@Transactional(readOnly = true) public class CompilationServiceImpl implements CompilationService { + private final CompilationRepository compilationRepository; + private final EventRepository eventRepository; + private final CompilationMapper compilationMapper; + + @Override + @Transactional + public CompilationDto createCompilation(NewCompilationDto request) { + if (compilationRepository.existsByTitle(request.title())) + throw new ConflictException("Сборник с таким названием (" + request.title() + ") уже существует"); + + Compilation compilation = compilationMapper.toEntity(request); + + if (request.events() != null && !request.events().isEmpty()) { + List events = eventRepository.findAllByEventIds(new ArrayList<>(request.events())); + + if (events.size() != request.events().size()) throw new NotFoundException("Не все события найдены"); + compilation.setEvents(new HashSet<>(events)); + + } else compilation.setEvents(new HashSet<>()); + Compilation savedCompilation = compilationRepository.save(compilation); + + log.info("Создан сборник: {}", request); + return compilationMapper.toDto(savedCompilation); + } + + @Override + @Transactional + public void deleteCompilation(Long compId) { + if (!compilationRepository.existsById(compId)) + throw new NotFoundException("Сборник с идентификатором " + compId + " не найден"); + + log.info("Удален сборник compId={}", compId); + compilationRepository.deleteById(compId); + } + + @Override + @Transactional + public CompilationDto updateCompilation(Long compId, UpdateCompilationRequest request) { + Compilation compilation = getCompilationOrThrow(compId); + + if (request.title() != null && !request.title().equals(compilation.getTitle()) + && compilationRepository.existsByTitle(request.title())) + throw new ConflictException("Сборник с таким названием (" + request.title() + ") уже существует"); + + if (request.title() != null) compilation.setTitle(request.title()); + compilation.setPinned(request.pinned()); + + if (request.events() != null) { + if (request.events().isEmpty()) { + compilation.setEvents(new HashSet<>()); + } else { + List events = eventRepository.findAllByEventIds(new ArrayList<>(request.events())); + if (events.size() != request.events().size()) + throw new NotFoundException("Некоторые события не найдены"); + compilation.setEvents(new HashSet<>(events)); + } + } + + log.info("Обновлен сборник request={}", request); + return compilationMapper.toDto(compilationRepository.save(compilation)); + } + + @Override + public List getCompilations(Boolean pinned, Pageable pageable) { + Pageable sortedPageable = PageRequest.of( + pageable.getPageNumber(), + pageable.getPageSize(), + Sort.by("id").ascending() + ); + + Page compilationsPage; + if (pinned != null) + compilationsPage = compilationRepository.findByPinned(pinned, sortedPageable); + else + compilationsPage = compilationRepository.findAll(sortedPageable); + + List compilations = compilationsPage.getContent(); + + if (!compilations.isEmpty()) { + log.info("Получен список сборников pinned={}, pageable={}", pinned, pageable); + return compilations.stream() + .map(compilationMapper::toDto) + .toList(); + } + log.info("Список сборников пуст"); + return List.of(); + } + + @Override + public CompilationDto getCompilationById(Long compId) { + Compilation compilation = getCompilationOrThrow(compId); + + log.info("Получен сборник compId={}", compId); + return compilationMapper.toDto(compilation); + } + + private Compilation getCompilationOrThrow(Long compId) { + return compilationRepository.findByIdWithEvents(compId) + .orElseThrow(() -> new NotFoundException("Сборник с идентификатором " + compId + " не найден")); + } } diff --git a/main-service/src/main/java/ru/practicum/event/controller/AdminEventController.java b/main-service/src/main/java/ru/practicum/event/controller/AdminEventController.java index a5ccbea..4a3d982 100644 --- a/main-service/src/main/java/ru/practicum/event/controller/AdminEventController.java +++ b/main-service/src/main/java/ru/practicum/event/controller/AdminEventController.java @@ -30,9 +30,8 @@ public List getEventsAdmin(@ModelAttribute @Valid SearchEventAdmin } @PatchMapping("/{eventId}") - public EventFullDto updateEventAdmin( - @PathVariable @Positive Long eventId, - @RequestBody @Valid UpdateEventAdminRequest request + public EventFullDto updateEventAdmin(@PathVariable @Positive Long eventId, + @RequestBody @Valid UpdateEventAdminRequest request ) { log.debug("Controller: updateEventAdmin id={}, data {}", eventId, request); return eventService.updateEventAdmin(eventId, request); diff --git a/main-service/src/main/java/ru/practicum/event/controller/PrivateEventController.java b/main-service/src/main/java/ru/practicum/event/controller/PrivateEventController.java index a8ea325..7c3d58e 100644 --- a/main-service/src/main/java/ru/practicum/event/controller/PrivateEventController.java +++ b/main-service/src/main/java/ru/practicum/event/controller/PrivateEventController.java @@ -31,10 +31,9 @@ public class PrivateEventController { private final RequestService requestService; @GetMapping - public List getEvents( - @PathVariable("userId") @Positive Long userId, - @RequestParam(defaultValue = "0") @PositiveOrZero Integer from, - @RequestParam(defaultValue = "10") @Positive Integer size + public List getEvents(@PathVariable("userId") @Positive Long userId, + @RequestParam(defaultValue = "0") @PositiveOrZero Integer from, + @RequestParam(defaultValue = "10") @Positive Integer size ) { Pageable pageable = PageRequest.of(from / size, size); log.debug("Controller: getEvents with id={} with pageable {}", userId, pageable); @@ -43,48 +42,43 @@ public List getEvents( @PostMapping @ResponseStatus(HttpStatus.CREATED) - public EventFullDto createEvent( - @PathVariable("userId") @Positive Long userId, - @RequestBody @Valid NewEventDto newEventDto + public EventFullDto createEvent(@PathVariable("userId") @Positive Long userId, + @RequestBody @Valid NewEventDto newEventDto ) { log.debug("Controller: createEvent with id={} with data {}", userId, newEventDto); return eventService.createEvent(userId, newEventDto); } @GetMapping("/{eventId}") - public EventFullDto getEvent( - @PathVariable("userId") @Positive Long userId, - @PathVariable("eventId") @Positive Long eventId, - HttpServletRequest request + public EventFullDto getEvent(@PathVariable("userId") @Positive Long userId, + @PathVariable("eventId") @Positive Long eventId, + HttpServletRequest request ) { log.debug("Controller: getEvent with id={} and eventId={}", userId, eventId); return eventService.getEvent(userId, eventId, request.getRemoteAddr()); } @PatchMapping("/{eventId}") - public EventFullDto updateEvent( - @PathVariable("userId") @Positive Long userId, - @PathVariable("eventId") @Positive Long eventId, - @RequestBody @Valid UpdateEventUserRequest request + public EventFullDto updateEvent(@PathVariable("userId") @Positive Long userId, + @PathVariable("eventId") @Positive Long eventId, + @RequestBody @Valid UpdateEventUserRequest request ) { log.debug("Controller: updateEvent with id={} and eventId={} with data {}", userId, eventId, request); return eventService.updateEvent(userId, eventId, request); } @GetMapping("/{eventId}/requests") - public List getRequestsByEvent( - @PathVariable("userId") @Positive Long userId, - @PathVariable("eventId") @Positive Long eventId + public List getRequestsByEvent(@PathVariable("userId") @Positive Long userId, + @PathVariable("eventId") @Positive Long eventId ) { log.debug("Controller: getRequestsByEvent with id={} and eventId={}", userId, eventId); return requestService.getRequestsByEvent(userId, eventId); } @PatchMapping("/{eventId}/requests") - public EventRequestStatusUpdateResult updateRequestStatus( - @PathVariable("userId") @Positive Long userId, - @PathVariable("eventId") @Positive Long eventId, - @RequestBody EventRequestStatusUpdateRequest request + public EventRequestStatusUpdateResult updateRequestStatus(@PathVariable("userId") @Positive Long userId, + @PathVariable("eventId") @Positive Long eventId, + @RequestBody EventRequestStatusUpdateRequest request ) { log.debug("Controller: updateRequestStatus with id={} and eventId={} with data {}", userId, eventId, request); return requestService.updateRequestStatus(userId, eventId, request); diff --git a/main-service/src/main/java/ru/practicum/event/mapper/EventMapper.java b/main-service/src/main/java/ru/practicum/event/mapper/EventMapper.java index 9e3b980..6ae6349 100644 --- a/main-service/src/main/java/ru/practicum/event/mapper/EventMapper.java +++ b/main-service/src/main/java/ru/practicum/event/mapper/EventMapper.java @@ -18,6 +18,11 @@ public interface EventMapper { EventShortDto toEventShortDto(Event event, Integer confirmedRequests, Long views); + @Named("toEventShortWithoutStats") + @Mapping(target = "confirmedRequests", ignore = true) + @Mapping(target = "views", ignore = true) + EventShortDto toEventShortWithoutStats(Event event); + EventFullDto toEventFullDto(Event event, Integer confirmedRequests, Long views); @Mapping(target = "id", ignore = true) diff --git a/main-service/src/main/java/ru/practicum/user/controller/AdminUserController.java b/main-service/src/main/java/ru/practicum/user/controller/AdminUserController.java index 5338fd2..b55727b 100644 --- a/main-service/src/main/java/ru/practicum/user/controller/AdminUserController.java +++ b/main-service/src/main/java/ru/practicum/user/controller/AdminUserController.java @@ -30,10 +30,9 @@ public UserDto createUser(@RequestBody @Valid NewUserRequest userRequest) { } @GetMapping - public List getUsers( - @RequestParam(required = false) List ids, - @RequestParam(defaultValue = "0") @PositiveOrZero int from, - @RequestParam(defaultValue = "10") @Positive int size + public List getUsers(@RequestParam(required = false) List ids, + @RequestParam(defaultValue = "0") @PositiveOrZero int from, + @RequestParam(defaultValue = "10") @Positive int size ) { Pageable pageable = PageRequest.of(from / size, size); log.debug("Controller: getUsers ids={}", ids); diff --git a/main-service/src/main/resources/application.properties b/main-service/src/main/resources/application.properties index 7f201cb..d531835 100644 --- a/main-service/src/main/resources/application.properties +++ b/main-service/src/main/resources/application.properties @@ -1,6 +1,6 @@ # Server Configuration server.port=8080 -stats.server.url=http://localhost:9090 +stats.server.url=${STATS_SERVER_URL} # JPA / Hibernate spring.jpa.hibernate.ddl-auto=validate spring.jpa.properties.hibernate.format_sql=true diff --git a/stats/stats-client/src/main/resources/application.properties b/stats/stats-client/src/main/resources/application.properties deleted file mode 100644 index 6d6b68c..0000000 --- a/stats/stats-client/src/main/resources/application.properties +++ /dev/null @@ -1 +0,0 @@ -stats.server.url=http://localhost:9090 \ No newline at end of file diff --git a/stats/stats-server/src/main/java/ru/practicum/controller/StatsController.java b/stats/stats-server/src/main/java/ru/practicum/controller/StatsController.java index f5bc0a6..e4f6d1f 100644 --- a/stats/stats-server/src/main/java/ru/practicum/controller/StatsController.java +++ b/stats/stats-server/src/main/java/ru/practicum/controller/StatsController.java @@ -8,6 +8,7 @@ import ru.practicum.CreateEndpointHitDto; import ru.practicum.StatsRequest; import ru.practicum.ViewStatsDto; +import ru.practicum.exception.BadRequestException; import ru.practicum.service.StatsService; import java.util.List; @@ -30,6 +31,11 @@ public List getStats(@RequestParam("start") String start, @Request @RequestParam(value = "uris", required = false) List uris, @RequestParam(value = "unique", defaultValue = "false") boolean unique) { StatsRequest request = StatsRequest.of(start, end, uris, unique); + + if (request.start().isAfter(request.end())) { + throw new BadRequestException("Дата начала должна быть раньше даты окончания"); + } + log.debug("Controller: getStats request={}", request); return statsService.getStats(request); } diff --git a/stats/stats-server/src/main/java/ru/practicum/exception/BadRequestException.java b/stats/stats-server/src/main/java/ru/practicum/exception/BadRequestException.java new file mode 100644 index 0000000..7aee4bc --- /dev/null +++ b/stats/stats-server/src/main/java/ru/practicum/exception/BadRequestException.java @@ -0,0 +1,7 @@ +package ru.practicum.exception; + +public class BadRequestException extends RuntimeException { + public BadRequestException(String message) { + super(message); + } +} diff --git a/stats/stats-server/src/main/java/ru/practicum/exception/ErrorHandler.java b/stats/stats-server/src/main/java/ru/practicum/exception/ErrorHandler.java new file mode 100644 index 0000000..8fb199b --- /dev/null +++ b/stats/stats-server/src/main/java/ru/practicum/exception/ErrorHandler.java @@ -0,0 +1,29 @@ +package ru.practicum.exception; + +import jakarta.servlet.http.HttpServletRequest; +import org.springframework.http.HttpStatus; +import org.springframework.web.bind.annotation.ExceptionHandler; +import org.springframework.web.bind.annotation.ResponseStatus; +import org.springframework.web.bind.annotation.RestControllerAdvice; + +@RestControllerAdvice +public class ErrorHandler { + @ExceptionHandler(BadRequestException.class) + @ResponseStatus(HttpStatus.BAD_REQUEST) + public ErrorResponse handleBadRequestException(BadRequestException exception, HttpServletRequest request) { + return buildResponse(HttpStatus.BAD_REQUEST, exception, request); + } + + private ErrorResponse buildResponse(HttpStatus httpStatus, Exception exception, HttpServletRequest request) { + return buildResponse(httpStatus, exception.getMessage(), exception, request); + } + + private ErrorResponse buildResponse(HttpStatus httpStatus, String message, Exception exception, HttpServletRequest request) { + String path = request.getRequestURI(); + String httpMethod = request.getMethod(); + int statusCode = httpStatus.value(); + String error = httpStatus.getReasonPhrase(); + + return new ErrorResponse(path, httpMethod, statusCode, error, message); + } +} diff --git a/stats/stats-server/src/main/java/ru/practicum/exception/ErrorResponse.java b/stats/stats-server/src/main/java/ru/practicum/exception/ErrorResponse.java new file mode 100644 index 0000000..31994d6 --- /dev/null +++ b/stats/stats-server/src/main/java/ru/practicum/exception/ErrorResponse.java @@ -0,0 +1,10 @@ +package ru.practicum.exception; + +public record ErrorResponse( + String path, + String httpMethod, + int statusCode, + String error, + String message +) { +} \ No newline at end of file