From 432589b4da4f57b76c2f6a2342f4a8e70271fdfa Mon Sep 17 00:00:00 2001 From: vs Date: Sat, 18 Oct 2025 01:05:18 +0300 Subject: [PATCH 1/4] The assembly module has been completed, statistics have been corrected --- .../AdminCompilationController.java | 32 +++++ .../PublicCompilationController.java | 29 +++++ .../compilation/dto/CompilationDto.java | 7 +- .../dto/UpdateCompilationRequest.java | 2 - .../compilation/mapper/CompilationMapper.java | 30 ++++- .../compilation/model/Compilation.java | 2 +- .../repository/CompilationRepository.java | 29 +++++ .../service/CompilationService.java | 16 +++ .../service/CompilationServiceImpl.java | 121 ++++++++++++++++++ .../practicum/event/mapper/EventMapper.java | 4 + .../practicum/controller/StatsController.java | 5 + .../exception/BadRequestException.java | 7 + .../ru/practicum/exception/ErrorHandler.java | 29 +++++ .../ru/practicum/exception/ErrorResponse.java | 10 ++ 14 files changed, 317 insertions(+), 6 deletions(-) create mode 100644 stats/stats-server/src/main/java/ru/practicum/exception/BadRequestException.java create mode 100644 stats/stats-server/src/main/java/ru/practicum/exception/ErrorHandler.java create mode 100644 stats/stats-server/src/main/java/ru/practicum/exception/ErrorResponse.java 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..f88e1b4 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,36 @@ package ru.practicum.compilation.controller; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +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; + +@RestController +@RequestMapping("/admin/compilations") +@RequiredArgsConstructor public class AdminCompilationController { + private final CompilationService compilationService; + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public CompilationDto createCompilation(@RequestBody @Valid NewCompilationDto request) { + return compilationService.createCompilation(request); + } + + @DeleteMapping("/{compId}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteCompilation(@PathVariable Long compId) { + compilationService.deleteCompilation(compId); + } + + @PatchMapping("/{compId}") + public CompilationDto updateCompilation( + @PathVariable Long compId, + @RequestBody @Valid UpdateCompilationRequest 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..92373dd 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,33 @@ package ru.practicum.compilation.controller; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; +import lombok.RequiredArgsConstructor; +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; + +@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); + return compilationService.getCompilations(pinned, pageable); + } + + @GetMapping("/{compId}") + public CompilationDto getCompilation(@PathVariable Long 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..97e401b 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,12 +2,15 @@ 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..1dc5d05 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,32 @@ package ru.practicum.compilation.mapper; -public class CompilationMapper { +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import ru.practicum.compilation.dto.NewCompilationDto; +import ru.practicum.compilation.dto.CompilationDto; +import ru.practicum.compilation.model.Compilation; +import ru.practicum.event.dto.EventShortDto; +import ru.practicum.event.mapper.EventMapper; +import ru.practicum.event.model.Event; + +import java.util.Set; +import java.util.stream.Collectors; + +@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", source = "events", qualifiedByName = "eventsToEventShortDos") + CompilationDto toDto(Compilation compilation); + + @Named("eventsToEventShortDos") + default Set eventsToEventShortDos(Set events) { + if (events == null) return null; + return events.stream() + .map(EventMapper.eventMapper::toEventShortWithoutRequestsAndViewsDto) + .collect(Collectors.toSet()); + } } 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..8932f5f 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 @@ -32,4 +32,4 @@ public class Compilation { inverseJoinColumns = @JoinColumn(name = "event_id") ) private Set events; -} +} \ No newline at end of file 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..b7407c3 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,36 @@ package ru.practicum.compilation.repository; +import io.micrometer.common.lang.NonNull; +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.List; +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); + + boolean existsById(@NonNull Long compId); + + @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 IN :ids""") + List findAllByIdWithEvents(@Param("ids") List ids); } 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..ad3b2d4 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,125 @@ package ru.practicum.compilation.service; +import lombok.RequiredArgsConstructor; +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; +import java.util.Map; +import java.util.stream.Collectors; + +@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); + return compilationMapper.toDto(savedCompilation); + } + + @Override + @Transactional + public void deleteCompilation(Long compId) { + if (!compilationRepository.existsById(compId)) + throw new NotFoundException("Сборник с идентификатором " + compId + " не найден"); + + compilationRepository.deleteById(compId); + } + + @Override + @Transactional + public CompilationDto updateCompilation(Long compId, UpdateCompilationRequest request) { + Compilation compilation = compilationRepository.findByIdWithEvents(compId) + .orElseThrow(() -> new NotFoundException("Сборник с идентификатором " + 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)); + } + } + + 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()) { + List compilationIds = compilations.stream() + .map(Compilation::getId) + .collect(Collectors.toList()); + + List compilationsWithEvents = compilationRepository.findAllByIdWithEvents(compilationIds); + Map compilationMap = compilationsWithEvents.stream() + .collect(Collectors.toMap(Compilation::getId, c -> c)); + + return compilations.stream() + .map(compilationMapper::toDto) + .collect(Collectors.toList()); + } + return List.of(); + } + + @Override + public CompilationDto getCompilationById(Long compId) { + Compilation compilation = compilationRepository.findByIdWithEvents(compId) + .orElseThrow(() -> new NotFoundException("Сборник с идентификатором " + compId + " не найдена")); + return compilationMapper.toDto(compilation); + } } 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..ac55d1b 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 @@ -1,6 +1,7 @@ package ru.practicum.event.mapper; import org.mapstruct.*; +import org.mapstruct.factory.Mappers; import ru.practicum.category.model.Category; import ru.practicum.event.dto.*; import ru.practicum.event.model.Event; @@ -9,6 +10,7 @@ @Mapper(componentModel = "spring", uses = {LocationMapper.class}) public interface EventMapper { + EventMapper eventMapper = Mappers.getMapper(EventMapper.class); @Mapping(target = "id", ignore = true) @Mapping(target = "publishedOn", ignore = true) @@ -18,6 +20,8 @@ public interface EventMapper { EventShortDto toEventShortDto(Event event, Integer confirmedRequests, Long views); + EventShortDto toEventShortWithoutRequestsAndViewsDto(Event event); + EventFullDto toEventFullDto(Event event, Integer confirmedRequests, Long views); @Mapping(target = "id", ignore = true) 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..2e4d0bf 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,10 @@ 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 From 7771179f170e4f7b7fdc67b4e34d777ba4afb406 Mon Sep 17 00:00:00 2001 From: vs Date: Sat, 18 Oct 2025 01:05:18 +0300 Subject: [PATCH 2/4] The assembly module has been completed, statistics have been corrected --- .github/copilot-instructions.md | 2 +- compose.yaml | 17 +-- .../AdminCompilationController.java | 32 +++++ .../PublicCompilationController.java | 29 +++++ .../compilation/dto/CompilationDto.java | 7 +- .../dto/UpdateCompilationRequest.java | 2 - .../compilation/mapper/CompilationMapper.java | 30 ++++- .../compilation/model/Compilation.java | 2 +- .../repository/CompilationRepository.java | 29 +++++ .../service/CompilationService.java | 16 +++ .../service/CompilationServiceImpl.java | 121 ++++++++++++++++++ .../practicum/event/mapper/EventMapper.java | 4 + .../src/main/resources/application.properties | 2 +- .../practicum/controller/StatsController.java | 5 + .../exception/BadRequestException.java | 7 + .../ru/practicum/exception/ErrorHandler.java | 29 +++++ .../ru/practicum/exception/ErrorResponse.java | 10 ++ 17 files changed, 328 insertions(+), 16 deletions(-) create mode 100644 stats/stats-server/src/main/java/ru/practicum/exception/BadRequestException.java create mode 100644 stats/stats-server/src/main/java/ru/practicum/exception/ErrorHandler.java create mode 100644 stats/stats-server/src/main/java/ru/practicum/exception/ErrorResponse.java 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..b41d5cc 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,15 @@ 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 + - 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/compilation/controller/AdminCompilationController.java b/main-service/src/main/java/ru/practicum/compilation/controller/AdminCompilationController.java index 1897cf1..f88e1b4 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,36 @@ package ru.practicum.compilation.controller; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +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; + +@RestController +@RequestMapping("/admin/compilations") +@RequiredArgsConstructor public class AdminCompilationController { + private final CompilationService compilationService; + + @PostMapping + @ResponseStatus(HttpStatus.CREATED) + public CompilationDto createCompilation(@RequestBody @Valid NewCompilationDto request) { + return compilationService.createCompilation(request); + } + + @DeleteMapping("/{compId}") + @ResponseStatus(HttpStatus.NO_CONTENT) + public void deleteCompilation(@PathVariable Long compId) { + compilationService.deleteCompilation(compId); + } + + @PatchMapping("/{compId}") + public CompilationDto updateCompilation( + @PathVariable Long compId, + @RequestBody @Valid UpdateCompilationRequest 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..92373dd 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,33 @@ package ru.practicum.compilation.controller; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; +import lombok.RequiredArgsConstructor; +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; + +@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); + return compilationService.getCompilations(pinned, pageable); + } + + @GetMapping("/{compId}") + public CompilationDto getCompilation(@PathVariable Long 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..97e401b 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,12 +2,15 @@ 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..1dc5d05 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,32 @@ package ru.practicum.compilation.mapper; -public class CompilationMapper { +import org.mapstruct.Mapper; +import org.mapstruct.Mapping; +import org.mapstruct.Named; +import ru.practicum.compilation.dto.NewCompilationDto; +import ru.practicum.compilation.dto.CompilationDto; +import ru.practicum.compilation.model.Compilation; +import ru.practicum.event.dto.EventShortDto; +import ru.practicum.event.mapper.EventMapper; +import ru.practicum.event.model.Event; + +import java.util.Set; +import java.util.stream.Collectors; + +@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", source = "events", qualifiedByName = "eventsToEventShortDos") + CompilationDto toDto(Compilation compilation); + + @Named("eventsToEventShortDos") + default Set eventsToEventShortDos(Set events) { + if (events == null) return null; + return events.stream() + .map(EventMapper.eventMapper::toEventShortWithoutRequestsAndViewsDto) + .collect(Collectors.toSet()); + } } 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..8932f5f 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 @@ -32,4 +32,4 @@ public class Compilation { inverseJoinColumns = @JoinColumn(name = "event_id") ) private Set events; -} +} \ No newline at end of file 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..b7407c3 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,36 @@ package ru.practicum.compilation.repository; +import io.micrometer.common.lang.NonNull; +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.List; +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); + + boolean existsById(@NonNull Long compId); + + @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 IN :ids""") + List findAllByIdWithEvents(@Param("ids") List ids); } 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..ad3b2d4 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,125 @@ package ru.practicum.compilation.service; +import lombok.RequiredArgsConstructor; +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; +import java.util.Map; +import java.util.stream.Collectors; + +@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); + return compilationMapper.toDto(savedCompilation); + } + + @Override + @Transactional + public void deleteCompilation(Long compId) { + if (!compilationRepository.existsById(compId)) + throw new NotFoundException("Сборник с идентификатором " + compId + " не найден"); + + compilationRepository.deleteById(compId); + } + + @Override + @Transactional + public CompilationDto updateCompilation(Long compId, UpdateCompilationRequest request) { + Compilation compilation = compilationRepository.findByIdWithEvents(compId) + .orElseThrow(() -> new NotFoundException("Сборник с идентификатором " + 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)); + } + } + + 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()) { + List compilationIds = compilations.stream() + .map(Compilation::getId) + .collect(Collectors.toList()); + + List compilationsWithEvents = compilationRepository.findAllByIdWithEvents(compilationIds); + Map compilationMap = compilationsWithEvents.stream() + .collect(Collectors.toMap(Compilation::getId, c -> c)); + + return compilations.stream() + .map(compilationMapper::toDto) + .collect(Collectors.toList()); + } + return List.of(); + } + + @Override + public CompilationDto getCompilationById(Long compId) { + Compilation compilation = compilationRepository.findByIdWithEvents(compId) + .orElseThrow(() -> new NotFoundException("Сборник с идентификатором " + compId + " не найдена")); + return compilationMapper.toDto(compilation); + } } 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..ac55d1b 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 @@ -1,6 +1,7 @@ package ru.practicum.event.mapper; import org.mapstruct.*; +import org.mapstruct.factory.Mappers; import ru.practicum.category.model.Category; import ru.practicum.event.dto.*; import ru.practicum.event.model.Event; @@ -9,6 +10,7 @@ @Mapper(componentModel = "spring", uses = {LocationMapper.class}) public interface EventMapper { + EventMapper eventMapper = Mappers.getMapper(EventMapper.class); @Mapping(target = "id", ignore = true) @Mapping(target = "publishedOn", ignore = true) @@ -18,6 +20,8 @@ public interface EventMapper { EventShortDto toEventShortDto(Event event, Integer confirmedRequests, Long views); + EventShortDto toEventShortWithoutRequestsAndViewsDto(Event event); + EventFullDto toEventFullDto(Event event, Integer confirmedRequests, Long views); @Mapping(target = "id", ignore = true) 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-server/src/main/java/ru/practicum/controller/StatsController.java b/stats/stats-server/src/main/java/ru/practicum/controller/StatsController.java index f5bc0a6..2e4d0bf 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,10 @@ 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 From b5395fa19e376d4edf65221e50cbef738d003ee4 Mon Sep 17 00:00:00 2001 From: vs Date: Sat, 18 Oct 2025 19:45:21 +0300 Subject: [PATCH 3/4] Edits have been made based on the review --- compose.yaml | 1 + .../AdminCompilationController.java | 11 ++++-- .../PublicCompilationController.java | 9 +++-- .../repository/CompilationRepository.java | 3 -- .../service/CompilationServiceImpl.java | 34 ++++++++++++++----- .../practicum/event/mapper/EventMapper.java | 2 ++ .../practicum/controller/StatsController.java | 6 ++-- 7 files changed, 46 insertions(+), 20 deletions(-) diff --git a/compose.yaml b/compose.yaml index b41d5cc..07cc69d 100644 --- a/compose.yaml +++ b/compose.yaml @@ -48,6 +48,7 @@ services: ports: - "8080:8080" depends_on: + - stats-server - ewm-db environment: - SPRING_DATASOURCE_URL=jdbc:postgresql://ewm-db:5432/ewmDB 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 f88e1b4..170a311 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,7 +1,9 @@ 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; @@ -9,6 +11,7 @@ import ru.practicum.compilation.dto.UpdateCompilationRequest; import ru.practicum.compilation.service.CompilationService; +@Slf4j @RestController @RequestMapping("/admin/compilations") @RequiredArgsConstructor @@ -18,19 +21,21 @@ public class AdminCompilationController { @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 Long compId) { + public void deleteCompilation(@PathVariable @Positive Long compId) { + log.debug("Controller: deleteCompilation compId={}", compId); compilationService.deleteCompilation(compId); } @PatchMapping("/{compId}") - public CompilationDto updateCompilation( - @PathVariable Long compId, + public CompilationDto updateCompilation(@PathVariable 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 92373dd..afb3457 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 @@ -3,6 +3,7 @@ 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.*; @@ -11,6 +12,7 @@ import java.util.List; +@Slf4j @RestController @RequestMapping("/compilations") @RequiredArgsConstructor @@ -18,16 +20,17 @@ public class PublicCompilationController { private final CompilationService compilationService; @GetMapping - public List getCompilations( - @RequestParam(required = false) Boolean pinned, + 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 Long 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/repository/CompilationRepository.java b/main-service/src/main/java/ru/practicum/compilation/repository/CompilationRepository.java index b7407c3..6b90a2a 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,6 +1,5 @@ package ru.practicum.compilation.repository; -import io.micrometer.common.lang.NonNull; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; @@ -24,8 +23,6 @@ public interface CompilationRepository extends JpaRepository boolean existsByTitle(String title); - boolean existsById(@NonNull Long compId); - @Query(""" SELECT c FROM Compilation c LEFT JOIN FETCH c.events e 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 ad3b2d4..55955a9 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,6 +1,7 @@ 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; @@ -21,9 +22,9 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.Map; import java.util.stream.Collectors; +@Slf4j @Service @RequiredArgsConstructor @Transactional(readOnly = true) @@ -42,10 +43,14 @@ public CompilationDto createCompilation(NewCompilationDto 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); } @@ -55,14 +60,15 @@ 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 = compilationRepository.findByIdWithEvents(compId) - .orElseThrow(() -> new NotFoundException("Сборник с идентификатором " + compId + " не найден")); + Compilation compilation = getCompilationOrThrow(compId); if (request.title() != null && !request.title().equals(compilation.getTitle()) && compilationRepository.existsByTitle(request.title())) @@ -70,6 +76,7 @@ public CompilationDto updateCompilation(Long compId, UpdateCompilationRequest re if (request.title() != null) compilation.setTitle(request.title()); compilation.setPinned(request.pinned()); + if (request.events() != null) { if (request.events().isEmpty()) { compilation.setEvents(new HashSet<>()); @@ -81,6 +88,8 @@ public CompilationDto updateCompilation(Long compId, UpdateCompilationRequest re } } + log.info("Обновлен сборник request={}", request); + return compilationMapper.toDto(compilationRepository.save(compilation)); } @@ -103,23 +112,30 @@ public List getCompilations(Boolean pinned, Pageable pageable) { if (!compilations.isEmpty()) { List compilationIds = compilations.stream() .map(Compilation::getId) - .collect(Collectors.toList()); + .toList(); - List compilationsWithEvents = compilationRepository.findAllByIdWithEvents(compilationIds); - Map compilationMap = compilationsWithEvents.stream() - .collect(Collectors.toMap(Compilation::getId, c -> c)); + log.info("Получен список сборников pinned={}, pageable={}", pinned, pageable); return compilations.stream() .map(compilationMapper::toDto) .collect(Collectors.toList()); } + log.info("Список сборников пуст"); + return List.of(); } @Override public CompilationDto getCompilationById(Long compId) { - Compilation compilation = compilationRepository.findByIdWithEvents(compId) - .orElseThrow(() -> new NotFoundException("Сборник с идентификатором " + 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/mapper/EventMapper.java b/main-service/src/main/java/ru/practicum/event/mapper/EventMapper.java index ac55d1b..1fc3d07 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 @@ -20,6 +20,8 @@ public interface EventMapper { EventShortDto toEventShortDto(Event event, Integer confirmedRequests, Long views); + @Mapping(target = "confirmedRequests", ignore = true) + @Mapping(target = "views", ignore = true) EventShortDto toEventShortWithoutRequestsAndViewsDto(Event event); EventFullDto toEventFullDto(Event event, Integer confirmedRequests, Long views); 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 2e4d0bf..9393fe0 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 @@ -32,10 +32,12 @@ public List getStats(@RequestParam("start") String start, @Request @RequestParam(value = "unique", defaultValue = "false") boolean unique) { StatsRequest request = StatsRequest.of(start, end, uris, unique); - if (request.start().isAfter(request.end())) + if (request.start().isAfter(request.end())) { throw new BadRequestException("Дата начала должна быть раньше даты окончания"); + } + + log.debug("Controller: getStats1 request={}", request); - log.debug("Controller: getStats request={}", request); return statsService.getStats(request); } } From b66cec69b8cdff82c0cd4a626c6d593a7ddc9cc9 Mon Sep 17 00:00:00 2001 From: Alexander Date: Sat, 18 Oct 2025 21:25:15 +0300 Subject: [PATCH 4/4] Refactor controller methods for improved readability; update parameter annotations for consistency --- .../controller/AdminCategoryController.java | 5 +-- .../AdminCompilationController.java | 4 +- .../PublicCompilationController.java | 4 +- .../compilation/dto/CompilationDto.java | 3 -- .../compilation/mapper/CompilationMapper.java | 19 ++-------- .../compilation/model/Compilation.java | 4 +- .../repository/CompilationRepository.java | 9 ----- .../service/CompilationServiceImpl.java | 18 ++------- .../controller/AdminEventController.java | 5 +-- .../controller/PrivateEventController.java | 38 ++++++++----------- .../practicum/event/mapper/EventMapper.java | 5 +-- .../user/controller/AdminUserController.java | 7 ++-- .../src/main/resources/application.properties | 1 - .../practicum/controller/StatsController.java | 3 +- 14 files changed, 39 insertions(+), 86 deletions(-) delete mode 100644 stats/stats-client/src/main/resources/application.properties 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 170a311..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 @@ -33,8 +33,8 @@ public void deleteCompilation(@PathVariable @Positive Long compId) { } @PatchMapping("/{compId}") - public CompilationDto updateCompilation(@PathVariable Long compId, - @RequestBody @Valid UpdateCompilationRequest request) { + 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 afb3457..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 @@ -21,8 +21,8 @@ public class PublicCompilationController { @GetMapping public List getCompilations(@RequestParam(required = false) Boolean pinned, - @RequestParam(defaultValue = "0") @PositiveOrZero Integer from, - @RequestParam(defaultValue = "10") @Positive Integer size) { + @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); 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 97e401b..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 @@ -6,11 +6,8 @@ public record CompilationDto( Long id, - Set events, - boolean pinned, - 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 1dc5d05..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 @@ -2,31 +2,18 @@ import org.mapstruct.Mapper; import org.mapstruct.Mapping; -import org.mapstruct.Named; -import ru.practicum.compilation.dto.NewCompilationDto; import ru.practicum.compilation.dto.CompilationDto; +import ru.practicum.compilation.dto.NewCompilationDto; import ru.practicum.compilation.model.Compilation; -import ru.practicum.event.dto.EventShortDto; import ru.practicum.event.mapper.EventMapper; -import ru.practicum.event.model.Event; - -import java.util.Set; -import java.util.stream.Collectors; @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", source = "events", qualifiedByName = "eventsToEventShortDos") + @Mapping(target = "events", qualifiedByName = "toEventShortWithoutStats") CompilationDto toDto(Compilation compilation); - - @Named("eventsToEventShortDos") - default Set eventsToEventShortDos(Set events) { - if (events == null) return null; - return events.stream() - .map(EventMapper.eventMapper::toEventShortWithoutRequestsAndViewsDto) - .collect(Collectors.toSet()); - } } 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 8932f5f..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( @@ -32,4 +32,4 @@ public class Compilation { inverseJoinColumns = @JoinColumn(name = "event_id") ) private Set events; -} \ No newline at end of file +} 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 6b90a2a..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 @@ -7,7 +7,6 @@ import org.springframework.data.repository.query.Param; import ru.practicum.compilation.model.Compilation; -import java.util.List; import java.util.Optional; public interface CompilationRepository extends JpaRepository { @@ -22,12 +21,4 @@ public interface CompilationRepository extends JpaRepository Optional findByIdWithEvents(@Param("id") Long id); boolean existsByTitle(String title); - - @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 IN :ids""") - List findAllByIdWithEvents(@Param("ids") List ids); } 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 55955a9..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 @@ -22,7 +22,6 @@ import java.util.ArrayList; import java.util.HashSet; import java.util.List; -import java.util.stream.Collectors; @Slf4j @Service @@ -46,11 +45,11 @@ public CompilationDto createCompilation(NewCompilationDto request) { 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); } @@ -61,7 +60,6 @@ public void deleteCompilation(Long compId) { throw new NotFoundException("Сборник с идентификатором " + compId + " не найден"); log.info("Удален сборник compId={}", compId); - compilationRepository.deleteById(compId); } @@ -70,8 +68,8 @@ public void deleteCompilation(Long compId) { public CompilationDto updateCompilation(Long compId, UpdateCompilationRequest request) { Compilation compilation = getCompilationOrThrow(compId); - if (request.title() != null && !request.title().equals(compilation.getTitle()) && - compilationRepository.existsByTitle(request.title())) + 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()); @@ -89,7 +87,6 @@ public CompilationDto updateCompilation(Long compId, UpdateCompilationRequest re } log.info("Обновлен сборник request={}", request); - return compilationMapper.toDto(compilationRepository.save(compilation)); } @@ -110,18 +107,12 @@ public List getCompilations(Boolean pinned, Pageable pageable) { List compilations = compilationsPage.getContent(); if (!compilations.isEmpty()) { - List compilationIds = compilations.stream() - .map(Compilation::getId) - .toList(); - log.info("Получен список сборников pinned={}, pageable={}", pinned, pageable); - return compilations.stream() .map(compilationMapper::toDto) - .collect(Collectors.toList()); + .toList(); } log.info("Список сборников пуст"); - return List.of(); } @@ -130,7 +121,6 @@ public CompilationDto getCompilationById(Long compId) { Compilation compilation = getCompilationOrThrow(compId); log.info("Получен сборник compId={}", compId); - return compilationMapper.toDto(compilation); } 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 1fc3d07..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 @@ -1,7 +1,6 @@ package ru.practicum.event.mapper; import org.mapstruct.*; -import org.mapstruct.factory.Mappers; import ru.practicum.category.model.Category; import ru.practicum.event.dto.*; import ru.practicum.event.model.Event; @@ -10,7 +9,6 @@ @Mapper(componentModel = "spring", uses = {LocationMapper.class}) public interface EventMapper { - EventMapper eventMapper = Mappers.getMapper(EventMapper.class); @Mapping(target = "id", ignore = true) @Mapping(target = "publishedOn", ignore = true) @@ -20,9 +18,10 @@ public interface EventMapper { EventShortDto toEventShortDto(Event event, Integer confirmedRequests, Long views); + @Named("toEventShortWithoutStats") @Mapping(target = "confirmedRequests", ignore = true) @Mapping(target = "views", ignore = true) - EventShortDto toEventShortWithoutRequestsAndViewsDto(Event event); + EventShortDto toEventShortWithoutStats(Event event); EventFullDto toEventFullDto(Event event, Integer confirmedRequests, Long views); 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/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 9393fe0..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 @@ -36,8 +36,7 @@ public List getStats(@RequestParam("start") String start, @Request throw new BadRequestException("Дата начала должна быть раньше даты окончания"); } - log.debug("Controller: getStats1 request={}", request); - + log.debug("Controller: getStats request={}", request); return statsService.getStats(request); } }