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

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
@@ -0,0 +1,43 @@
package ru.practicum.compilation.controller;

import jakarta.validation.Valid;

import ru.practicum.compilation.dto.CompilationDto;
import ru.practicum.compilation.dto.NewCompilationDto;
import ru.practicum.compilation.dto.UpdateCompilationRequest;
import ru.practicum.compilation.service.CompilationsService;

import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.*;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/admin/compilations")
public class CompilationsAdminController {
private final CompilationsService compService;

@PostMapping
@ResponseStatus(HttpStatus.CREATED)
public CompilationDto saveCompilation(@RequestBody @Valid NewCompilationDto newCompilationDto) {
log.info("Admin save compilation requested with body={}", newCompilationDto);
return compService.save(newCompilationDto);
}

@DeleteMapping("/{compId}")
@ResponseStatus(HttpStatus.NO_CONTENT)
public void deleteCompilation(@PathVariable long compId) {
log.info("Admin delete compilation requested with id={}", compId);
compService.deleteById(compId);
}

@PatchMapping("/{compId}")
public CompilationDto updateCompilation(
@PathVariable long compId, @RequestBody @Valid UpdateCompilationRequest updateRequest) {
log.info("Admin update compilation requested with id={}, body={}", compId, updateRequest);
return compService.update(compId, updateRequest);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package ru.practicum.compilation.controller;

import java.util.Collection;

import ru.practicum.compilation.dto.CompilationDto;
import ru.practicum.compilation.service.CompilationsPublicGetRequest;
import ru.practicum.compilation.service.CompilationsService;

import org.springframework.web.bind.annotation.*;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@RestController
@RequiredArgsConstructor
@RequestMapping("/compilations")
public class CompilationsPublicController {
private final CompilationsService compService;

@GetMapping
public Collection<CompilationDto> getCompilations(
@RequestParam(required = false) Boolean pinned,
@RequestParam(defaultValue = "0") int from,
@RequestParam(defaultValue = "10") int size) {
CompilationsPublicGetRequest getRequest =
new CompilationsPublicGetRequest(pinned, from, size);
log.info("Public get compilations requested with params= {}", getRequest);
return compService.findAll(getRequest);
}

@GetMapping("/{compId}")
public CompilationDto getById(@PathVariable long compId) {
log.info("Public get compilation by id requested with id={}", compId);
return compService.findById(compId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package ru.practicum.compilation.mapper;

import java.util.List;
import java.util.Set;

import ru.practicum.compilation.dto.CompilationDto;
import ru.practicum.compilation.dto.NewCompilationDto;
import ru.practicum.compilation.dto.UpdateCompilationRequest;
import ru.practicum.compilation.model.Compilation;
import ru.practicum.event.dto.EventShortDto;
import ru.practicum.event.model.Event;

import lombok.experimental.UtilityClass;

@UtilityClass
public class CompilationsMapper {

public Compilation mapToEntity(NewCompilationDto newCompilationDto, Set<Event> events) {
return new Compilation(null, newCompilationDto.title(), newCompilationDto.pinned(), events);
}

public CompilationDto mapToDto(Compilation compilation, List<EventShortDto> events) {
return new CompilationDto(
events,
compilation.getId(),
Boolean.TRUE.equals(compilation.getPinned()),
compilation.getTitle());
}

public void updateEntity(
Compilation compilation, UpdateCompilationRequest updateRequest, Set<Event> events) {

if (updateRequest.hasTitle()) {
compilation.setTitle(updateRequest.title());
}

if (updateRequest.hasPinned()) {
compilation.setPinned(updateRequest.pinned());
}

if (updateRequest.hasEvents()) {
compilation.setEvents(events);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package ru.practicum.compilation.repository;

import java.util.Optional;

import ru.practicum.compilation.model.Compilation;

import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.EntityGraph;
import org.springframework.data.jpa.repository.JpaRepository;

public interface CompilationsRepository extends JpaRepository<Compilation, Long> {

Page<Compilation> findAllByPinned(boolean pinned, Pageable pageable);

@EntityGraph(attributePaths = "events")
Optional<Compilation> findWithEventsById(Long id);

boolean existsByTitle(String title);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ru.practicum.compilation.service;

import org.springframework.data.domain.PageRequest;
import org.springframework.data.domain.Pageable;

public record CompilationsPublicGetRequest(Boolean pinned, int from, int size) {

public CompilationsPublicGetRequest {
if (pinned == null) pinned = false;
}

public Pageable getPageable() {
int page = from / size;
return PageRequest.of(page, size);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package ru.practicum.compilation.service;

import java.util.Collection;

import jakarta.validation.Valid;

import ru.practicum.compilation.dto.CompilationDto;
import ru.practicum.compilation.dto.NewCompilationDto;
import ru.practicum.compilation.dto.UpdateCompilationRequest;

public interface CompilationsService {
Collection<CompilationDto> findAll(CompilationsPublicGetRequest getRequest);

CompilationDto findById(long compId);

CompilationDto save(@Valid NewCompilationDto newCompilationDto);

void deleteById(long compId);

CompilationDto update(long compId, UpdateCompilationRequest updateRequest);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,202 @@
package ru.practicum.compilation.service;

import java.time.LocalDateTime;
import java.util.Collection;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.stream.Collectors;

import ru.practicum.client.StatsClient;
import ru.practicum.compilation.dto.CompilationDto;
import ru.practicum.compilation.dto.NewCompilationDto;
import ru.practicum.compilation.dto.UpdateCompilationRequest;
import ru.practicum.compilation.mapper.CompilationsMapper;
import ru.practicum.compilation.model.Compilation;
import ru.practicum.compilation.repository.CompilationsRepository;
import ru.practicum.dto.ViewStatsDto;
import ru.practicum.event.dto.EventShortDto;
import ru.practicum.event.mapper.EventMapper;
import ru.practicum.event.model.Event;
import ru.practicum.event.repository.EventRepository;
import ru.practicum.exception.ConflictException;
import ru.practicum.exception.NotFoundException;
import ru.practicum.request.repository.ParticipationRequestRepository;

import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;

@Slf4j
@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class CompilationsServiceImpl implements CompilationsService {
private static final LocalDateTime MINIMAL_LOCAL_DATE_TIME =
LocalDateTime.of(1000, 1, 1, 0, 0, 0);
private final CompilationsRepository compRepository;
private final EventRepository eventRepository;
private final ParticipationRequestRepository requestRepository;
private final StatsClient statsClient;

@Override
public Collection<CompilationDto> findAll(CompilationsPublicGetRequest getRequest) {

Page<Compilation> page =
compRepository.findAllByPinned(getRequest.pinned(), getRequest.getPageable());

Set<Event> events =
page.stream().flatMap(c -> c.getEvents().stream()).collect(Collectors.toSet());

Map<Long, Long> confirmedRequests = getConfirmedRequests(events);
Map<Long, Long> views = getViews(events);

return page.stream().map(c -> toDto(c, confirmedRequests, views)).toList();
}

@Override
public CompilationDto findById(long compId) {

Compilation compilation =
compRepository
.findWithEventsById(compId)
.orElseThrow(
NotFoundException.supplier(
"Compilation with id=%d was not found", compId));

Set<Event> events = compilation.getEvents();

Map<Long, Long> confirmedRequests = getConfirmedRequests(events);
Map<Long, Long> views = getViews(events);

return toDto(compilation, confirmedRequests, views);
}

@Override
@Transactional
public CompilationDto save(NewCompilationDto newCompilationDto) {

if (compRepository.existsByTitle(newCompilationDto.title())) {
throw new ConflictException(
"Compilation with title=" + newCompilationDto.title() + " already exists");
}

Set<Event> events = getEvents(newCompilationDto.events());

Compilation compilation = CompilationsMapper.mapToEntity(newCompilationDto, events);

Compilation saved = compRepository.save(compilation);

Map<Long, Long> confirmedRequests = getConfirmedRequests(events);
Map<Long, Long> views = getViews(events);

return toDto(saved, confirmedRequests, views);
}

@Override
@Transactional
public void deleteById(long compId) {
if (!compRepository.existsById(compId)) {
throw new NotFoundException("Compilation with id=" + compId + " was not found");
}

compRepository.deleteById(compId);
}

@Override
@Transactional
public CompilationDto update(long compId, UpdateCompilationRequest updateRequest) {

Compilation compilation =
compRepository
.findWithEventsById(compId)
.orElseThrow(
NotFoundException.supplier(
"Compilation with id=%d was not found", compId));

Set<Event> events = null;
if (updateRequest.hasEvents()) {
events = getEvents(updateRequest.events());
}

CompilationsMapper.updateEntity(compilation, updateRequest, events);

Compilation updated = compRepository.save(compilation);

Set<Event> actualEvents = updated.getEvents();

Map<Long, Long> confirmedRequests = getConfirmedRequests(actualEvents);
Map<Long, Long> views = getViews(actualEvents);

return toDto(updated, confirmedRequests, views);
}

private CompilationDto toDto(
Compilation compilation, Map<Long, Long> confirmedRequests, Map<Long, Long> views) {
List<EventShortDto> events =
compilation.getEvents().stream()
.map(
event ->
EventMapper.mapToShortDto(
event,
confirmedRequests.getOrDefault(event.getId(), 0L),
views.get(event.getId())))
.toList();

return CompilationsMapper.mapToDto(compilation, events);
}

private Set<Event> getEvents(Collection<Long> eventIds) {
if (eventIds == null || eventIds.isEmpty()) {
return Set.of();
}

Set<Event> events = eventRepository.findAllByIdIn(eventIds);

if (events.size() != eventIds.size()) {
throw new NotFoundException("One or more events were not found");
}

return events;
}

private Map<Long, Long> getConfirmedRequests(Set<Event> events) {
if (events.isEmpty()) {
return Map.of();
}

List<Long> eventIds = events.stream().map(Event::getId).toList();

return requestRepository.countConfirmedByEventIds(eventIds);
}

private Map<Long, Long> getViews(Set<Event> events) {
if (events.isEmpty()) {
return Map.of();
}

List<String> uris = events.stream().map(e -> "/events/%s".formatted(e.getId())).toList();

try {
List<ViewStatsDto> stats =
statsClient.getStats(MINIMAL_LOCAL_DATE_TIME, LocalDateTime.now(), uris, true);

return stats.stream()
.collect(
Collectors.toMap(
s -> {
String uri = s.uri();
return Long.parseLong(
uri.substring(uri.lastIndexOf("/") + 1));
},
ViewStatsDto::hits));

} catch (Exception e) {
log.error("Error during getting stats for events", e);
return Map.of();
}
}
}
Loading