diff --git a/docker-compose.yml b/docker-compose.yml
index 1e26a3e..ab52e69 100644
--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -2,16 +2,17 @@ version: '3.1'
services:
stats-server:
- build: stats/stats-server
+ build: ./stats/stats-server
container_name: stat-server
ports:
- "9090:9090"
+ depends_on:
+ - stats-db
environment:
- SPRING_DATASOURCE_URL=jdbc:postgresql://stats-db:5432/ewm-stats
- SPRING_DATASOURCE_USERNAME=stat
- SPRING_DATASOURCE_PASSWORD=stat
- depends_on:
- - stats-db
+
stats-db:
image: postgres:16.1
@@ -24,7 +25,7 @@ services:
- POSTGRES_DB=ewm-stats
ewm-service:
- build: main-service
+ build: ./main-service
ports:
- "8080:8080"
environment:
diff --git a/main-service/Dockerfile b/main-service/Dockerfile
index 0ff1817..7ae68e5 100644
--- a/main-service/Dockerfile
+++ b/main-service/Dockerfile
@@ -1,5 +1,3 @@
FROM eclipse-temurin:21-jre-jammy
-VOLUME /tmp
-ARG JAR_FILE=target/*.jar
-COPY ${JAR_FILE} app.jar
-ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"]
\ No newline at end of file
+COPY ./target/*.jar main-service.jar
+ENTRYPOINT ["java","-jar","/main-service.jar"]
\ No newline at end of file
diff --git a/main-service/pom.xml b/main-service/pom.xml
index dea6df8..2711657 100644
--- a/main-service/pom.xml
+++ b/main-service/pom.xml
@@ -18,6 +18,12 @@
+
+ ru.practicum
+ stats-client
+ 0.0.1-SNAPSHOT
+ compile
+
org.zalando
logbook-spring-boot-starter
diff --git a/main-service/src/main/java/ewm/DateFormatConfig.java b/main-service/src/main/java/ewm/DateFormatConfig.java
new file mode 100644
index 0000000..414a0f1
--- /dev/null
+++ b/main-service/src/main/java/ewm/DateFormatConfig.java
@@ -0,0 +1,34 @@
+package ewm;
+
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.time.format.DateTimeFormatter;
+
+@Configuration
+public class DateFormatConfig {
+
+ @Bean
+ public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
+
+ return builder -> {
+
+ // formatter
+ DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+ // deserializers
+ builder.deserializers(new LocalDateDeserializer(dateFormatter));
+ builder.deserializers(new LocalDateTimeDeserializer(dateTimeFormatter));
+
+ // serializers
+ builder.serializers(new LocalDateSerializer(dateFormatter));
+ builder.serializers(new LocalDateTimeSerializer(dateTimeFormatter));
+ };
+ }
+}
diff --git a/main-service/src/main/java/ewm/MainServiceApplication.java b/main-service/src/main/java/ewm/MainServiceApplication.java
index fce1434..0788c03 100644
--- a/main-service/src/main/java/ewm/MainServiceApplication.java
+++ b/main-service/src/main/java/ewm/MainServiceApplication.java
@@ -2,7 +2,9 @@
import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.context.annotation.ComponentScan;
+@ComponentScan(basePackages = {"ewm", "client"})
@SpringBootApplication
public class MainServiceApplication {
public static void main(String[] args) {
diff --git a/main-service/src/main/java/ewm/categories/Category.java b/main-service/src/main/java/ewm/categories/Category.java
new file mode 100644
index 0000000..dee827e
--- /dev/null
+++ b/main-service/src/main/java/ewm/categories/Category.java
@@ -0,0 +1,21 @@
+package ewm.categories;
+
+import jakarta.persistence.*;
+import lombok.*;
+
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@Getter
+@Setter
+@ToString
+@Entity
+@Table(name = "categories")
+public class Category {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private long id;
+
+ @Column(nullable = false, unique = true)
+ private String name;
+}
diff --git a/main-service/src/main/java/ewm/categories/CategoryMapper.java b/main-service/src/main/java/ewm/categories/CategoryMapper.java
new file mode 100644
index 0000000..bd7b5e8
--- /dev/null
+++ b/main-service/src/main/java/ewm/categories/CategoryMapper.java
@@ -0,0 +1,24 @@
+package ewm.categories;
+
+import ewm.categories.dto.CategoryCreateDto;
+import ewm.categories.dto.CategoryDto;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CategoryMapper {
+
+ public Category categoryCreateDtoToModel(CategoryCreateDto dto) {
+ return Category.builder()
+ .name(dto.getName())
+ .build();
+ }
+
+ public CategoryDto modelToDto(Category category) {
+ return CategoryDto.builder()
+ .id(category.getId())
+ .name(category.getName())
+ .build();
+ }
+
+
+}
diff --git a/main-service/src/main/java/ewm/categories/CategoryRepository.java b/main-service/src/main/java/ewm/categories/CategoryRepository.java
new file mode 100644
index 0000000..acdd9e3
--- /dev/null
+++ b/main-service/src/main/java/ewm/categories/CategoryRepository.java
@@ -0,0 +1,6 @@
+package ewm.categories;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+
+public interface CategoryRepository extends JpaRepository {
+}
diff --git a/main-service/src/main/java/ewm/categories/controller/CategoriesAdminController.java b/main-service/src/main/java/ewm/categories/controller/CategoriesAdminController.java
new file mode 100644
index 0000000..50e8b39
--- /dev/null
+++ b/main-service/src/main/java/ewm/categories/controller/CategoriesAdminController.java
@@ -0,0 +1,37 @@
+package ewm.categories.controller;
+
+import ewm.categories.dto.CategoryCreateDto;
+import ewm.categories.dto.CategoryDto;
+import ewm.categories.service.CategoryService;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+@Validated
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/admin/categories")
+public class CategoriesAdminController {
+ private final CategoryService service;
+
+ @PostMapping
+ @ResponseStatus(value = HttpStatus.CREATED)
+ public CategoryDto create(@RequestBody @Valid CategoryCreateDto createDto) {
+ return service.create(createDto);
+ }
+
+ @PatchMapping("/{catId}")
+ public CategoryDto update(@PathVariable Long catId,
+ @RequestBody @Valid CategoryCreateDto createDto) {
+ return service.update(catId, createDto);
+ }
+
+ @DeleteMapping("/{catId}")
+ @ResponseStatus(value = HttpStatus.NO_CONTENT)
+ public void deleteById(@PathVariable Long catId) {
+ service.deleteById(catId);
+ }
+
+}
diff --git a/main-service/src/main/java/ewm/categories/controller/CategoriesPublicController.java b/main-service/src/main/java/ewm/categories/controller/CategoriesPublicController.java
new file mode 100644
index 0000000..f8240fd
--- /dev/null
+++ b/main-service/src/main/java/ewm/categories/controller/CategoriesPublicController.java
@@ -0,0 +1,30 @@
+package ewm.categories.controller;
+
+import ewm.categories.dto.CategoryDto;
+import ewm.categories.service.CategoryService;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@Validated
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/categories")
+public class CategoriesPublicController {
+ private final CategoryService service;
+
+ @GetMapping
+ public List getCategories(
+ @RequestParam(value = "from", defaultValue = "0") Integer from,
+ @RequestParam(value = "size", defaultValue = "10") Integer size) {
+ return service.getCategories(from, size);
+ }
+
+ @GetMapping("/{catId}")
+ public CategoryDto getById(@PathVariable Long catId) {
+ return service.getById(catId);
+ }
+
+}
diff --git a/main-service/src/main/java/ewm/categories/dto/CategoryCreateDto.java b/main-service/src/main/java/ewm/categories/dto/CategoryCreateDto.java
new file mode 100644
index 0000000..d16d387
--- /dev/null
+++ b/main-service/src/main/java/ewm/categories/dto/CategoryCreateDto.java
@@ -0,0 +1,14 @@
+package ewm.categories.dto;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.NotNull;
+import jakarta.validation.constraints.Size;
+import lombok.Data;
+
+@Data
+public class CategoryCreateDto {
+ @NotNull
+ @NotBlank
+ @Size(min = 1, max = 50)
+ private String name;
+}
diff --git a/main-service/src/main/java/ewm/categories/dto/CategoryDto.java b/main-service/src/main/java/ewm/categories/dto/CategoryDto.java
new file mode 100644
index 0000000..7341704
--- /dev/null
+++ b/main-service/src/main/java/ewm/categories/dto/CategoryDto.java
@@ -0,0 +1,15 @@
+package ewm.categories.dto;
+
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+@AllArgsConstructor
+@NoArgsConstructor
+@Data
+@Builder
+public class CategoryDto {
+ private long id;
+ private String name;
+}
diff --git a/main-service/src/main/java/ewm/categories/service/CategoryService.java b/main-service/src/main/java/ewm/categories/service/CategoryService.java
new file mode 100644
index 0000000..9bee337
--- /dev/null
+++ b/main-service/src/main/java/ewm/categories/service/CategoryService.java
@@ -0,0 +1,21 @@
+package ewm.categories.service;
+
+import ewm.categories.Category;
+import ewm.categories.dto.CategoryCreateDto;
+import ewm.categories.dto.CategoryDto;
+
+import java.util.List;
+
+public interface CategoryService {
+ CategoryDto create(CategoryCreateDto createDto);
+
+ CategoryDto update(long categoryId, CategoryCreateDto createDto);
+
+ void deleteById(long categoryId);
+
+ List getCategories(Integer from, Integer size);
+
+ CategoryDto getById(long categoryId);
+
+ Category checkAndReturnCategory(long id);
+}
diff --git a/main-service/src/main/java/ewm/categories/service/CategoryServiceImpl.java b/main-service/src/main/java/ewm/categories/service/CategoryServiceImpl.java
new file mode 100644
index 0000000..0fe731d
--- /dev/null
+++ b/main-service/src/main/java/ewm/categories/service/CategoryServiceImpl.java
@@ -0,0 +1,62 @@
+package ewm.categories.service;
+
+import ewm.categories.Category;
+import ewm.categories.CategoryMapper;
+import ewm.categories.CategoryRepository;
+import ewm.categories.dto.CategoryCreateDto;
+import ewm.categories.dto.CategoryDto;
+import ewm.exception.NotFoundException;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class CategoryServiceImpl implements CategoryService {
+ private final CategoryRepository repository;
+ private final CategoryMapper mapper;
+
+ @Override
+ public CategoryDto create(CategoryCreateDto createDto) {
+ Category category = mapper.categoryCreateDtoToModel(createDto);
+ return mapper.modelToDto(repository.save(category));
+ }
+
+ @Override
+ public CategoryDto update(long categoryId, CategoryCreateDto createDto) {
+ Category savedCategory = checkAndReturnCategory(categoryId);
+ savedCategory.setName(createDto.getName());
+ return mapper.modelToDto(repository.save(savedCategory));
+ }
+
+ @Override
+ public void deleteById(long categoryId) {
+ checkAndReturnCategory(categoryId);
+ repository.deleteById(categoryId);
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public List getCategories(Integer from, Integer size) {
+ Pageable pageable = PageRequest.of(from / size, size);
+ return repository.findAll(pageable).map(mapper::modelToDto).toList();
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public CategoryDto getById(long categoryId) {
+ Category category = checkAndReturnCategory(categoryId);
+ return mapper.modelToDto(category);
+ }
+
+ public Category checkAndReturnCategory(long id) {
+ return repository.findById(id).orElseThrow(() ->
+ new NotFoundException("Category with id=" + id + " was not found"));
+ }
+
+}
diff --git a/main-service/src/main/java/ewm/compilations/Compilation.java b/main-service/src/main/java/ewm/compilations/Compilation.java
new file mode 100644
index 0000000..0ef17e4
--- /dev/null
+++ b/main-service/src/main/java/ewm/compilations/Compilation.java
@@ -0,0 +1,39 @@
+package ewm.compilations;
+
+import ewm.events.Event;
+import jakarta.persistence.*;
+import lombok.*;
+
+import java.util.Set;
+
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@Getter
+@Setter
+@ToString
+@Entity
+@Table(name = "compilations")
+public class Compilation {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private long id;
+
+ @Column(name = "title", nullable = false)
+ private String title;
+
+ @Column(name = "pinned", nullable = false)
+ private Boolean pinned;
+
+ @ManyToMany
+ @JoinTable(name = "compilation_event",
+ joinColumns = @JoinColumn(name = "compilation_id"),
+ inverseJoinColumns = @JoinColumn(name = "event_id"))
+ private Set events;
+
+ public Compilation(String title, Boolean pinned) {
+ this.title = title;
+ this.pinned = pinned;
+ }
+
+}
diff --git a/main-service/src/main/java/ewm/compilations/CompilationMapper.java b/main-service/src/main/java/ewm/compilations/CompilationMapper.java
new file mode 100644
index 0000000..0c3d3ea
--- /dev/null
+++ b/main-service/src/main/java/ewm/compilations/CompilationMapper.java
@@ -0,0 +1,23 @@
+package ewm.compilations;
+
+import ewm.compilations.dto.CompilationDto;
+import ewm.compilations.dto.CreateCompilationDto;
+import org.springframework.stereotype.Component;
+
+@Component
+public class CompilationMapper {
+ public Compilation toCompilationEntity(CreateCompilationDto createCompilationDto) {
+ return new Compilation(
+ createCompilationDto.getTitle(),
+ createCompilationDto.isPinned()
+ );
+ }
+
+ public CompilationDto toCompilationDto(Compilation compilation) {
+ return new CompilationDto(
+ compilation.getId(),
+ compilation.getPinned(),
+ compilation.getTitle()
+ );
+ }
+}
diff --git a/main-service/src/main/java/ewm/compilations/CompilationRepository.java b/main-service/src/main/java/ewm/compilations/CompilationRepository.java
new file mode 100644
index 0000000..7b587e5
--- /dev/null
+++ b/main-service/src/main/java/ewm/compilations/CompilationRepository.java
@@ -0,0 +1,10 @@
+package ewm.compilations;
+
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.repository.JpaRepository;
+
+import java.util.List;
+
+public interface CompilationRepository extends JpaRepository {
+ List findAllByPinned(Boolean pinned, Pageable pageable);
+}
diff --git a/main-service/src/main/java/ewm/compilations/controller/CompilationAdminController.java b/main-service/src/main/java/ewm/compilations/controller/CompilationAdminController.java
new file mode 100644
index 0000000..872490d
--- /dev/null
+++ b/main-service/src/main/java/ewm/compilations/controller/CompilationAdminController.java
@@ -0,0 +1,35 @@
+package ewm.compilations.controller;
+
+import ewm.compilations.dto.CompilationDto;
+import ewm.compilations.dto.CreateCompilationDto;
+import ewm.compilations.dto.UpdateCompilationRequest;
+import ewm.compilations.service.CompilationService;
+import jakarta.validation.Valid;
+import lombok.RequiredArgsConstructor;
+import org.springframework.http.HttpStatus;
+import org.springframework.web.bind.annotation.*;
+
+@RestController
+@RequestMapping("/admin/compilations")
+@RequiredArgsConstructor
+public class CompilationAdminController {
+ private final CompilationService compilationService;
+
+ @PostMapping
+ @ResponseStatus(value = HttpStatus.CREATED)
+ public CompilationDto addCompilation(@RequestBody @Valid CreateCompilationDto createCompilationDto) {
+ return compilationService.addCompilation(createCompilationDto);
+ }
+
+ @PatchMapping("/{compilationId}")
+ public CompilationDto updateCompilation(@PathVariable Long compilationId,
+ @RequestBody @Valid UpdateCompilationRequest updateCompilation) {
+ return compilationService.updateCompilation(compilationId, updateCompilation);
+ }
+
+ @DeleteMapping("/{compilationId}")
+ @ResponseStatus(value = HttpStatus.NO_CONTENT)
+ public void deleteCompilation(@PathVariable long compilationId) {
+ compilationService.deleteCompilation(compilationId);
+ }
+}
\ No newline at end of file
diff --git a/main-service/src/main/java/ewm/compilations/controller/CompilationPublicController.java b/main-service/src/main/java/ewm/compilations/controller/CompilationPublicController.java
new file mode 100644
index 0000000..8b535d7
--- /dev/null
+++ b/main-service/src/main/java/ewm/compilations/controller/CompilationPublicController.java
@@ -0,0 +1,32 @@
+package ewm.compilations.controller;
+
+import ewm.compilations.dto.CompilationDto;
+import ewm.compilations.service.CompilationService;
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.PositiveOrZero;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@Validated
+@RestController
+@RequestMapping("/compilations")
+@RequiredArgsConstructor
+public class CompilationPublicController {
+ 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) {
+ return compilationService.getCompilations(pinned, from, size);
+ }
+
+ @GetMapping("/{compilationId}")
+ public CompilationDto getCompilationById(@PathVariable Long compilationId) {
+ return compilationService.getCompilationById(compilationId);
+ }
+
+}
diff --git a/main-service/src/main/java/ewm/compilations/dto/CompilationDto.java b/main-service/src/main/java/ewm/compilations/dto/CompilationDto.java
new file mode 100644
index 0000000..8924015
--- /dev/null
+++ b/main-service/src/main/java/ewm/compilations/dto/CompilationDto.java
@@ -0,0 +1,24 @@
+package ewm.compilations.dto;
+
+import ewm.events.dto.EventShortDto;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class CompilationDto {
+ private Long id;
+ private List events;
+ private Boolean pinned;
+ private String title;
+
+ public CompilationDto(Long id, boolean pinned, String title) {
+ this.id = id;
+ this.pinned = pinned;
+ this.title = title;
+ }
+}
diff --git a/main-service/src/main/java/ewm/compilations/dto/CreateCompilationDto.java b/main-service/src/main/java/ewm/compilations/dto/CreateCompilationDto.java
new file mode 100644
index 0000000..e1ca206
--- /dev/null
+++ b/main-service/src/main/java/ewm/compilations/dto/CreateCompilationDto.java
@@ -0,0 +1,25 @@
+package ewm.compilations.dto;
+
+import jakarta.validation.constraints.NotBlank;
+import jakarta.validation.constraints.Size;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class CreateCompilationDto {
+
+ private List events;
+
+ private boolean pinned;
+
+ @NotBlank
+ @Size(min = 1, max = 50)
+ private String title;
+
+
+}
diff --git a/main-service/src/main/java/ewm/compilations/dto/UpdateCompilationRequest.java b/main-service/src/main/java/ewm/compilations/dto/UpdateCompilationRequest.java
new file mode 100644
index 0000000..1367bca
--- /dev/null
+++ b/main-service/src/main/java/ewm/compilations/dto/UpdateCompilationRequest.java
@@ -0,0 +1,21 @@
+package ewm.compilations.dto;
+
+import jakarta.validation.constraints.Size;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.util.List;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class UpdateCompilationRequest {
+ private List events;
+
+ private Boolean pinned;
+
+ @Size(min = 1, max = 50)
+ private String title;
+
+}
\ No newline at end of file
diff --git a/main-service/src/main/java/ewm/compilations/service/CompilationService.java b/main-service/src/main/java/ewm/compilations/service/CompilationService.java
new file mode 100644
index 0000000..57777ad
--- /dev/null
+++ b/main-service/src/main/java/ewm/compilations/service/CompilationService.java
@@ -0,0 +1,19 @@
+package ewm.compilations.service;
+
+import ewm.compilations.dto.CompilationDto;
+import ewm.compilations.dto.CreateCompilationDto;
+import ewm.compilations.dto.UpdateCompilationRequest;
+
+import java.util.List;
+
+public interface CompilationService {
+ List getCompilations(Boolean pinned, Integer from, Integer size);
+
+ CompilationDto getCompilationById(Long compilationId);
+
+ CompilationDto addCompilation(CreateCompilationDto newCompilationDto);
+
+ CompilationDto updateCompilation(Long compId, UpdateCompilationRequest updateCompilation);
+
+ void deleteCompilation(Long compilationId);
+}
diff --git a/main-service/src/main/java/ewm/compilations/service/CompilationServiceImpl.java b/main-service/src/main/java/ewm/compilations/service/CompilationServiceImpl.java
new file mode 100644
index 0000000..51b3986
--- /dev/null
+++ b/main-service/src/main/java/ewm/compilations/service/CompilationServiceImpl.java
@@ -0,0 +1,155 @@
+package ewm.compilations.service;
+
+import ewm.compilations.Compilation;
+import ewm.compilations.CompilationMapper;
+import ewm.compilations.CompilationRepository;
+import ewm.compilations.dto.CompilationDto;
+import ewm.compilations.dto.CreateCompilationDto;
+import ewm.compilations.dto.UpdateCompilationRequest;
+import ewm.events.Event;
+import ewm.events.EventMapper;
+import ewm.events.EventRepository;
+import ewm.events.dto.EventShortDto;
+import ewm.exception.NotFoundException;
+import ewm.requests.RequestRepository;
+import ewm.requests.dto.ConfirmedRequestsDto;
+import lombok.RequiredArgsConstructor;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Pageable;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.util.List;
+import java.util.Map;
+import java.util.Set;
+import java.util.stream.Collectors;
+
+import static ewm.requests.RequestStatus.CONFIRMED;
+
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class CompilationServiceImpl implements CompilationService {
+ private final CompilationRepository compilationRepository;
+ private final EventRepository eventRepository;
+ private final RequestRepository requestRepository;
+ private final EventMapper eventMapper;
+ private final CompilationMapper compilationMapper;
+
+ @Override
+ @Transactional(readOnly = true)
+ public List getCompilations(Boolean pinned, Integer from, Integer size) {
+ Pageable pageable = PageRequest.of(from / size, size);
+ List compilations;
+
+ if (pinned != null) {
+ compilations = compilationRepository.findAllByPinned(pinned, pageable);
+ } else {
+ compilations = compilationRepository.findAll(pageable).getContent();
+ }
+
+ List compilationIds = compilations.stream()
+ .map(Compilation::getId)
+ .toList();
+
+ List events = eventRepository.findAllByInitiatorIdIn(compilationIds);
+
+ List eventIds = events.stream()
+ .map(Event::getId)
+ .toList();
+
+ Map confirmedRequests = requestRepository.findAllByEventIdInAndStatus(eventIds, CONFIRMED)
+ .stream()
+ .collect(Collectors.toMap(ConfirmedRequestsDto::getEvent, ConfirmedRequestsDto::getCount));
+
+ List result = compilations.stream()
+ .map(compilation -> {
+ CompilationDto compilationDto = compilationMapper.toCompilationDto(compilation);
+ List eventShortDtos = compilation.getEvents().stream()
+ .map(event -> eventMapper.toEventShortDto(event, confirmedRequests.get(event.getId())))
+ .collect(Collectors.toList());
+ compilationDto.setEvents(eventShortDtos);
+ return compilationDto;
+ })
+ .toList();
+ return result;
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public CompilationDto getCompilationById(Long compilationId) {
+ Compilation compilation = getCompilation(compilationId);
+ CompilationDto compilationDto = compilationMapper.toCompilationDto(compilation);
+ if (compilation.getEvents() != null) {
+ List ids = compilation.getEvents().stream().map(Event::getId).collect(Collectors.toList());
+ Map confirmedRequests = requestRepository.findAllByEventIdInAndStatus(ids, CONFIRMED)
+ .stream()
+ .collect(Collectors.toMap(ConfirmedRequestsDto::getEvent, ConfirmedRequestsDto::getCount));
+ compilationDto.setEvents(compilation.getEvents().stream()
+ .map(event -> eventMapper.toEventShortDto(event, confirmedRequests.get(event.getId())))
+ .collect(Collectors.toList()));
+ }
+ return compilationDto;
+ }
+
+ @Override
+ public CompilationDto addCompilation(CreateCompilationDto newCompilationDto) {
+ Compilation compilation = compilationMapper.toCompilationEntity(newCompilationDto);
+ setEvents(compilation, newCompilationDto.getEvents());
+ return setCompilationDto(compilation);
+ }
+
+ @Override
+ public CompilationDto updateCompilation(Long compId, UpdateCompilationRequest updateCompilation) {
+ Compilation compilation = getCompilation(compId);
+ if (updateCompilation.getEvents() != null) {
+ Set events = updateCompilation.getEvents().stream()
+ .map(id -> {
+ Event event = new Event();
+ event.setId(id);
+ return event;
+ }).collect(Collectors.toSet());
+ compilation.setEvents(events);
+ }
+ if (updateCompilation.getPinned() != null) {
+ compilation.setPinned(updateCompilation.getPinned());
+ }
+ String title = updateCompilation.getTitle();
+ if (title != null && !title.isBlank()) {
+ compilation.setTitle(title);
+ }
+ return setCompilationDto(compilation);
+ }
+
+ @Override
+ public void deleteCompilation(Long compilationId) {
+ getCompilation(compilationId);
+ compilationRepository.deleteById(compilationId);
+ }
+
+ private void setEvents(Compilation compilation, List eventIds) {
+ if (eventIds != null) {
+ compilation.setEvents(eventRepository.findAllByIdIn(eventIds));
+ }
+ }
+
+ private CompilationDto setCompilationDto(Compilation compilation) {
+ CompilationDto compilationDto = compilationMapper.toCompilationDto(compilationRepository.save(compilation));
+ if (compilation.getEvents() != null) {
+ List ids = compilation.getEvents().stream().map(Event::getId).collect(Collectors.toList());
+ Map confirmedRequests = requestRepository.findAllByEventIdInAndStatus(ids, CONFIRMED)
+ .stream()
+ .collect(Collectors.toMap(ConfirmedRequestsDto::getEvent, ConfirmedRequestsDto::getCount));
+ compilationDto.setEvents(compilation.getEvents().stream()
+ .map(event -> eventMapper.toEventShortDto(event, confirmedRequests.get(event.getId())))
+ .collect(Collectors.toList()));
+ }
+ return compilationDto;
+ }
+
+ private Compilation getCompilation(Long compilationId) {
+ return compilationRepository.findById(compilationId).orElseThrow(() ->
+ new NotFoundException("Compilation id=" + compilationId + " not found"));
+ }
+
+}
diff --git a/main-service/src/main/java/ewm/events/Event.java b/main-service/src/main/java/ewm/events/Event.java
new file mode 100644
index 0000000..986464f
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/Event.java
@@ -0,0 +1,70 @@
+package ewm.events;
+
+import ewm.categories.Category;
+import ewm.events.enums.State;
+import ewm.locations.Location;
+import ewm.user.User;
+import jakarta.persistence.*;
+import lombok.*;
+
+import java.time.LocalDateTime;
+
+@NoArgsConstructor
+@AllArgsConstructor
+@Builder
+@Getter
+@Setter
+@ToString
+@Entity
+@Table(name = "events")
+public class Event {
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ private long id;
+
+ @Column(nullable = false)
+ private String annotation;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "category_id", nullable = false)
+ @ToString.Exclude
+ private Category category;
+
+ @Column(name = "created_on", nullable = false, columnDefinition = "TIMESTAMP")
+ private LocalDateTime createdOn;
+
+ @Column(nullable = false)
+ private String description;
+
+ @Column(name = "event_date", nullable = false)
+ private LocalDateTime eventDate;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "initiator_id", nullable = false)
+ @ToString.Exclude
+ private User initiator;
+
+ @ManyToOne(fetch = FetchType.LAZY)
+ @JoinColumn(name = "location_id", nullable = false)
+ @ToString.Exclude
+ private Location location;
+
+ private Boolean paid;
+
+ @Column(name = "participant_limit")
+ private Integer participantLimit;
+
+ @Column(name = "published_on")
+ private LocalDateTime publishedOn;
+
+ @Column(name = "request_moderation")
+ private Boolean requestModeration;
+
+ @Enumerated(EnumType.STRING)
+ private State state;
+
+ @Column(nullable = false)
+ private String title;
+
+
+}
diff --git a/main-service/src/main/java/ewm/events/EventMapper.java b/main-service/src/main/java/ewm/events/EventMapper.java
new file mode 100644
index 0000000..be9ef68
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/EventMapper.java
@@ -0,0 +1,98 @@
+package ewm.events;
+
+import ewm.categories.CategoryMapper;
+import ewm.events.dto.*;
+import ewm.locations.LocationMapper;
+import ewm.user.UserMapper;
+import lombok.RequiredArgsConstructor;
+import org.springframework.stereotype.Component;
+
+@Component
+@RequiredArgsConstructor
+public class EventMapper {
+ private final CategoryMapper categoryMapper;
+ private final UserMapper userMapper;
+ private final LocationMapper locationMapper;
+
+ public Event eventCreateDtoToModel(EventCreateDto dto) {
+ return Event.builder()
+ .annotation(dto.getAnnotation())
+ .description(dto.getDescription())
+ .eventDate(dto.getEventDate())
+ .paid(dto.isPaid())
+ .participantLimit(dto.getParticipantLimit())
+ .requestModeration(dto.isRequestModeration())
+ .title(dto.getTitle())
+ .build();
+ }
+
+ public EventFullDto modelToEventFullDto(Event event, Long confirmedRequests) {
+ return EventFullDto.builder()
+ .id(event.getId())
+ .annotation(event.getAnnotation())
+ .category(categoryMapper.modelToDto(event.getCategory()))
+ .confirmedRequests(confirmedRequests)
+ .createdOn(event.getCreatedOn())
+ .description(event.getDescription())
+ .eventDate(event.getEventDate())
+ .initiator(userMapper.modelToUserShortDto(event.getInitiator()))
+ .location(locationMapper.modelToDto(event.getLocation()))
+ .paid(event.getPaid())
+ .participantLimit(event.getParticipantLimit())
+ .publishedOn(event.getPublishedOn())
+ .requestModeration(event.getRequestModeration())
+ .state(event.getState())
+ .title(event.getTitle())
+ .build();
+ }
+
+ public EventShortDto toEventShortDto(Event event, Long confirmedRequests) {
+ return EventShortDto.builder()
+ .id(event.getId())
+ .annotation(event.getAnnotation())
+ .category(categoryMapper.modelToDto(event.getCategory()))
+ .confirmedRequests(confirmedRequests)
+ .eventDate(event.getEventDate())
+ .initiator(userMapper.modelToUserShortDto(event.getInitiator()))
+ .paid(event.getPaid())
+ .title(event.getTitle())
+ .build();
+ }
+
+
+ public EventViewsFullDto toEventFullDtoWithViews(Event event, Long views, Long confirmedRequests) {
+ return EventViewsFullDto.builder()
+ .id(event.getId())
+ .annotation(event.getAnnotation())
+ .category(categoryMapper.modelToDto(event.getCategory()))
+ .confirmedRequests(confirmedRequests)
+ .createdOn(event.getCreatedOn())
+ .description(event.getDescription())
+ .eventDate(event.getEventDate())
+ .initiator(userMapper.modelToUserShortDto(event.getInitiator()))
+ .location(locationMapper.modelToDto(event.getLocation()))
+ .paid(event.getPaid())
+ .participantLimit(event.getParticipantLimit())
+ .publishedOn(event.getPublishedOn())
+ .requestModeration(event.getRequestModeration())
+ .state(event.getState())
+ .title(event.getTitle())
+ .views(views)
+ .build();
+ }
+
+ public EventViewsShortDto toEventShortDtoWithViews(Event event, Long views, Long confirmedRequests) {
+ return EventViewsShortDto.builder()
+ .id(event.getId())
+ .annotation(event.getAnnotation())
+ .category(categoryMapper.modelToDto(event.getCategory()))
+ .confirmedRequests(confirmedRequests)
+ .eventDate(event.getEventDate())
+ .initiator(userMapper.modelToUserShortDto(event.getInitiator()))
+ .paid(event.getPaid())
+ .title(event.getTitle())
+ .views(views)
+ .build();
+ }
+
+}
diff --git a/main-service/src/main/java/ewm/events/EventRepository.java b/main-service/src/main/java/ewm/events/EventRepository.java
new file mode 100644
index 0000000..226f9c2
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/EventRepository.java
@@ -0,0 +1,25 @@
+package ewm.events;
+
+import org.springframework.data.domain.Page;
+import org.springframework.data.domain.Pageable;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+
+import java.util.List;
+import java.util.Optional;
+import java.util.Set;
+
+public interface EventRepository extends JpaRepository {
+
+ List findAllByInitiatorId(Long userId, Pageable pageable);
+
+ Optional findByIdAndInitiatorId(Long eventId, Long userId);
+
+ @Query("SELECT e FROM Event e WHERE e.initiator.id IN :initiatorIds")
+ List findAllByInitiatorIdIn(List initiatorIds);
+
+ Page findAll(Specification specification, Pageable pageable);
+
+ Set findAllByIdIn(List events);
+}
diff --git a/main-service/src/main/java/ewm/events/controller/EventControllerAdmin.java b/main-service/src/main/java/ewm/events/controller/EventControllerAdmin.java
new file mode 100644
index 0000000..66301c5
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/controller/EventControllerAdmin.java
@@ -0,0 +1,46 @@
+package ewm.events.controller;
+
+import ewm.events.dto.EventFullDto;
+import ewm.events.dto.EventUpdateAdminDto;
+import ewm.events.dto.EventViewsFullDto;
+import ewm.events.service.EventService;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.PositiveOrZero;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Slf4j
+@Validated
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/admin/events")
+public class EventControllerAdmin {
+ private final EventService eventService;
+
+ @PatchMapping("/{eventId}")
+ public EventFullDto updateEventByAdmin(@PathVariable Long eventId,
+ @RequestBody @Valid EventUpdateAdminDto eventUpdateAdminDto) {
+ return eventService.updateEventByAdmin(eventId, eventUpdateAdminDto);
+ }
+
+ @GetMapping
+ public List getEvents(
+ @RequestParam(required = false) List users,
+ @RequestParam(required = false) List states,
+ @RequestParam(required = false) List categories,
+ @RequestParam(required = false) LocalDateTime rangeStart,
+ @RequestParam(required = false) LocalDateTime rangeEnd,
+ @RequestParam(value = "from", defaultValue = "0")
+ @PositiveOrZero Integer from,
+ @RequestParam(value = "size", defaultValue = "10")
+ @Positive Integer size) {
+ return eventService.getEventsByAdmin(users, states, categories, rangeStart, rangeEnd, from, size);
+ }
+
+}
diff --git a/main-service/src/main/java/ewm/events/controller/EventControllerPrivate.java b/main-service/src/main/java/ewm/events/controller/EventControllerPrivate.java
new file mode 100644
index 0000000..8c45312
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/controller/EventControllerPrivate.java
@@ -0,0 +1,68 @@
+package ewm.events.controller;
+
+import ewm.events.dto.EventCreateDto;
+import ewm.events.dto.EventFullDto;
+import ewm.events.dto.EventShortDto;
+import ewm.events.dto.EventUpdateUserDto;
+import ewm.events.service.EventService;
+import ewm.requests.dto.EventRequestStatusUpdateRequest;
+import ewm.requests.dto.EventRequestStatusUpdateResult;
+import ewm.requests.dto.RequestDto;
+import ewm.requests.service.RequestService;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.PositiveOrZero;
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.springframework.http.HttpStatus;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.util.List;
+
+@Slf4j
+@Validated
+@RestController
+@RequestMapping("/users/{userId}/events")
+@RequiredArgsConstructor
+public class EventControllerPrivate {
+ private final EventService eventService;
+ private final RequestService requestService;
+
+ @PostMapping
+ @ResponseStatus(value = HttpStatus.CREATED)
+ public EventFullDto addEvent(@PathVariable Long userId, @RequestBody @Valid EventCreateDto dto) {
+ return eventService.create(userId, dto);
+ }
+
+ @PatchMapping("/{eventId}")
+ public EventFullDto updateEventByOwner(@PathVariable Long userId,
+ @PathVariable Long eventId,
+ @RequestBody @Valid EventUpdateUserDto updateEvent) {
+ return eventService.updateEventByOwner(userId, eventId, updateEvent);
+ }
+
+ @PatchMapping("/{eventId}/requests")
+ public EventRequestStatusUpdateResult updateRequestsStatus(@PathVariable Long userId,
+ @PathVariable Long eventId,
+ @RequestBody EventRequestStatusUpdateRequest request) {
+ return requestService.updateRequestsStatus(userId, eventId, request);
+ }
+
+ @GetMapping
+ List getEventsByOwner(@PathVariable Long userId,
+ @RequestParam(value = "from", defaultValue = "0") @PositiveOrZero Integer from,
+ @RequestParam(value = "size", defaultValue = "10") @Positive Integer size) {
+ return eventService.getEventsByOwnerId(userId, from, size);
+ }
+
+ @GetMapping("/{eventId}")
+ public EventFullDto getEventByOwner(@PathVariable Long userId, @PathVariable Long eventId) {
+ return eventService.getEventByOwner(userId, eventId);
+ }
+
+ @GetMapping("/{eventId}/requests")
+ public List getRequestsByEventOwner(@PathVariable Long userId, @PathVariable Long eventId) {
+ return requestService.getRequestsByEventOwner(userId, eventId);
+ }
+}
diff --git a/main-service/src/main/java/ewm/events/controller/EventControllerPublic.java b/main-service/src/main/java/ewm/events/controller/EventControllerPublic.java
new file mode 100644
index 0000000..299a959
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/controller/EventControllerPublic.java
@@ -0,0 +1,49 @@
+package ewm.events.controller;
+
+
+import ewm.events.dto.EventViewsFullDto;
+import ewm.events.dto.EventViewsShortDto;
+import ewm.events.service.EventService;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.constraints.Positive;
+import jakarta.validation.constraints.PositiveOrZero;
+import lombok.RequiredArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.*;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+@Validated
+@RestController
+@RequiredArgsConstructor
+@RequestMapping("/events")
+public class EventControllerPublic {
+ private final EventService eventService;
+
+ @GetMapping
+ public List getEvents(
+ @RequestParam(required = false) String text,
+ @RequestParam(required = false) List categories,
+ @RequestParam(required = false) Boolean paid,
+ @RequestParam(required = false)
+ LocalDateTime rangeStart,
+ @RequestParam(required = false)
+ LocalDateTime rangeEnd,
+ @RequestParam(defaultValue = "false") Boolean onlyAvailable,
+ @RequestParam(defaultValue = "EVENT_DATE") String sort,
+ @RequestParam(value = "from", defaultValue = "0") @PositiveOrZero
+ Integer from,
+ @RequestParam(value = "size", defaultValue = "10") @Positive
+ Integer size,
+ HttpServletRequest request) {
+ return eventService.getEvents(text, categories, paid, rangeStart, rangeEnd, onlyAvailable,
+ sort, from, size, request);
+ }
+
+ @GetMapping("/{eventId}")
+ public EventViewsFullDto getEventById(@PathVariable Long eventId, HttpServletRequest request) {
+ return eventService.getEventById(eventId, request);
+ }
+
+}
\ No newline at end of file
diff --git a/main-service/src/main/java/ewm/events/dto/EventCreateDto.java b/main-service/src/main/java/ewm/events/dto/EventCreateDto.java
new file mode 100644
index 0000000..3c08c62
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/dto/EventCreateDto.java
@@ -0,0 +1,46 @@
+package ewm.events.dto;
+
+import ewm.locations.LocationDto;
+import jakarta.validation.Valid;
+import jakarta.validation.constraints.*;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class EventCreateDto {
+ @Size(min = 20, max = 2000)
+ @NotBlank
+ private String annotation;
+
+ @NotNull
+ private Long category;
+
+ @Size(min = 20, max = 7000)
+ @NotBlank
+ private String description;
+
+ @NotNull
+ @Future
+ private LocalDateTime eventDate;
+
+ @NotNull
+ @Valid
+ private LocationDto location;
+
+ private boolean paid;
+
+ @PositiveOrZero
+ private int participantLimit = 0;
+
+ private boolean requestModeration = true;
+
+ @Size(min = 3, max = 120)
+ @NotBlank
+ private String title;
+
+}
diff --git a/main-service/src/main/java/ewm/events/dto/EventFullDto.java b/main-service/src/main/java/ewm/events/dto/EventFullDto.java
new file mode 100644
index 0000000..817aa6c
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/dto/EventFullDto.java
@@ -0,0 +1,34 @@
+package ewm.events.dto;
+
+import ewm.categories.dto.CategoryDto;
+import ewm.events.enums.State;
+import ewm.locations.LocationDto;
+import ewm.user.dto.UserShortDto;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Builder
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class EventFullDto {
+ private Long id;
+ private String annotation;
+ private CategoryDto category;
+ private Long confirmedRequests;
+ private LocalDateTime createdOn;
+ private String description;
+ private LocalDateTime eventDate;
+ private UserShortDto initiator;
+ private LocationDto location;
+ private Boolean paid;
+ private Integer participantLimit;
+ private LocalDateTime publishedOn;
+ private Boolean requestModeration;
+ private State state;
+ private String title;
+}
diff --git a/main-service/src/main/java/ewm/events/dto/EventShortDto.java b/main-service/src/main/java/ewm/events/dto/EventShortDto.java
new file mode 100644
index 0000000..0c99401
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/dto/EventShortDto.java
@@ -0,0 +1,32 @@
+package ewm.events.dto;
+
+import ewm.categories.dto.CategoryDto;
+import ewm.user.dto.UserShortDto;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Builder
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class EventShortDto {
+ private Long id;
+
+ private String annotation;
+
+ private CategoryDto category;
+
+ private Long confirmedRequests;
+
+ private LocalDateTime eventDate;
+
+ private UserShortDto initiator;
+
+ private Boolean paid;
+
+ private String title;
+}
diff --git a/main-service/src/main/java/ewm/events/dto/EventUpdateAdminDto.java b/main-service/src/main/java/ewm/events/dto/EventUpdateAdminDto.java
new file mode 100644
index 0000000..469282a
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/dto/EventUpdateAdminDto.java
@@ -0,0 +1,44 @@
+package ewm.events.dto;
+
+import ewm.locations.LocationDto;
+import jakarta.validation.constraints.Future;
+import jakarta.validation.constraints.PositiveOrZero;
+import jakarta.validation.constraints.Size;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Builder
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class EventUpdateAdminDto {
+
+ @Size(min = 20, max = 2000)
+ private String annotation;
+
+ private Long category;
+
+ @Size(min = 20, max = 7000)
+ private String description;
+
+ @Future
+ private LocalDateTime eventDate;
+
+ private LocationDto location;
+
+ private Boolean paid;
+
+ @PositiveOrZero
+ private Integer participantLimit;
+
+ private Boolean requestModeration;
+
+ private String stateAction;
+
+ @Size(min = 3, max = 120)
+ private String title;
+}
diff --git a/main-service/src/main/java/ewm/events/dto/EventUpdateUserDto.java b/main-service/src/main/java/ewm/events/dto/EventUpdateUserDto.java
new file mode 100644
index 0000000..c11dc45
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/dto/EventUpdateUserDto.java
@@ -0,0 +1,44 @@
+package ewm.events.dto;
+
+import ewm.locations.LocationDto;
+import jakarta.validation.constraints.Future;
+import jakarta.validation.constraints.PositiveOrZero;
+import jakarta.validation.constraints.Size;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Builder
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class EventUpdateUserDto {
+
+ @Size(min = 20, max = 2000)
+ private String annotation;
+
+ private Long category;
+
+ @Size(min = 20, max = 7000)
+ private String description;
+
+ @Future
+ private LocalDateTime eventDate;
+
+ private LocationDto location;
+
+ private Boolean paid;
+
+ @PositiveOrZero
+ private Integer participantLimit;
+
+ private Boolean requestModeration;
+
+ private String stateAction;
+
+ @Size(min = 3, max = 120)
+ private String title;
+}
diff --git a/main-service/src/main/java/ewm/events/dto/EventViewsFullDto.java b/main-service/src/main/java/ewm/events/dto/EventViewsFullDto.java
new file mode 100644
index 0000000..4d86921
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/dto/EventViewsFullDto.java
@@ -0,0 +1,51 @@
+package ewm.events.dto;
+
+import ewm.categories.dto.CategoryDto;
+import ewm.events.enums.State;
+import ewm.locations.LocationDto;
+import ewm.user.dto.UserShortDto;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Builder
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class EventViewsFullDto {
+ private Long id;
+
+ private String title;
+
+ private String annotation;
+
+ private CategoryDto category;
+
+ private boolean paid;
+
+ private LocalDateTime eventDate;
+
+ private UserShortDto initiator;
+
+ private Long views;
+
+ private Long confirmedRequests;
+
+ private String description;
+
+ private Integer participantLimit;
+
+ private State state;
+
+ private LocalDateTime createdOn;
+
+ private LocalDateTime publishedOn;
+
+ private LocationDto location;
+
+ private boolean requestModeration;
+
+}
diff --git a/main-service/src/main/java/ewm/events/dto/EventViewsShortDto.java b/main-service/src/main/java/ewm/events/dto/EventViewsShortDto.java
new file mode 100644
index 0000000..abebf31
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/dto/EventViewsShortDto.java
@@ -0,0 +1,34 @@
+package ewm.events.dto;
+
+import ewm.categories.dto.CategoryDto;
+import ewm.user.dto.UserShortDto;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+
+import java.time.LocalDateTime;
+
+@Builder
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
+public class EventViewsShortDto {
+ private Long id;
+
+ private String annotation;
+
+ private CategoryDto category;
+
+ private Long confirmedRequests;
+
+ private LocalDateTime eventDate;
+
+ private UserShortDto initiator;
+
+ private Boolean paid;
+
+ private String title;
+
+ private Long views;
+}
diff --git a/main-service/src/main/java/ewm/events/enums/State.java b/main-service/src/main/java/ewm/events/enums/State.java
new file mode 100644
index 0000000..5cf3232
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/enums/State.java
@@ -0,0 +1,7 @@
+package ewm.events.enums;
+
+public enum State {
+ PENDING,
+ PUBLISHED,
+ CANCELED;
+}
diff --git a/main-service/src/main/java/ewm/events/enums/StateActionAdmin.java b/main-service/src/main/java/ewm/events/enums/StateActionAdmin.java
new file mode 100644
index 0000000..7f01a8c
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/enums/StateActionAdmin.java
@@ -0,0 +1,6 @@
+package ewm.events.enums;
+
+public enum StateActionAdmin {
+ PUBLISH_EVENT,
+ REJECT_EVENT
+}
diff --git a/main-service/src/main/java/ewm/events/enums/StateActionPrivate.java b/main-service/src/main/java/ewm/events/enums/StateActionPrivate.java
new file mode 100644
index 0000000..499b713
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/enums/StateActionPrivate.java
@@ -0,0 +1,6 @@
+package ewm.events.enums;
+
+public enum StateActionPrivate {
+ SEND_TO_REVIEW,
+ CANCEL_REVIEW
+}
diff --git a/main-service/src/main/java/ewm/events/service/EventService.java b/main-service/src/main/java/ewm/events/service/EventService.java
new file mode 100644
index 0000000..516dc45
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/service/EventService.java
@@ -0,0 +1,31 @@
+package ewm.events.service;
+
+import ewm.events.dto.*;
+import jakarta.servlet.http.HttpServletRequest;
+
+import java.time.LocalDateTime;
+import java.util.List;
+
+public interface EventService {
+ EventFullDto create(long userId, EventCreateDto dto);
+
+ EventFullDto updateEventByOwner(Long userId, Long eventId, EventUpdateUserDto dto);
+
+ EventFullDto updateEventByAdmin(Long eventId, EventUpdateAdminDto dto);
+
+ List getEventsByOwnerId(Long userId, Integer from, Integer size);
+
+ EventFullDto getEventByOwner(Long userId, Long eventId);
+
+ List getEventsByAdmin(List users, List states, List categories,
+ LocalDateTime rangeStart, LocalDateTime rangeEnd,
+ Integer from, Integer size);
+
+ List getEvents(String text, List categories, Boolean paid, LocalDateTime rangeStart,
+ LocalDateTime rangeEnd, Boolean onlyAvailable, String sort, Integer from,
+ Integer size, HttpServletRequest request);
+
+ EventViewsFullDto getEventById(Long eventId, HttpServletRequest request);
+
+
+}
diff --git a/main-service/src/main/java/ewm/events/service/EventServiceImpl.java b/main-service/src/main/java/ewm/events/service/EventServiceImpl.java
new file mode 100644
index 0000000..16c643b
--- /dev/null
+++ b/main-service/src/main/java/ewm/events/service/EventServiceImpl.java
@@ -0,0 +1,388 @@
+package ewm.events.service;
+
+import client.StatsClient;
+import com.fasterxml.jackson.core.type.TypeReference;
+import com.fasterxml.jackson.databind.ObjectMapper;
+import dto.EndpointHitDto;
+import dto.ViewStatsDto;
+import ewm.categories.Category;
+import ewm.categories.service.CategoryService;
+import ewm.events.Event;
+import ewm.events.EventMapper;
+import ewm.events.EventRepository;
+import ewm.events.dto.*;
+import ewm.events.enums.State;
+import ewm.events.enums.StateActionAdmin;
+import ewm.events.enums.StateActionPrivate;
+import ewm.exception.BadRequestException;
+import ewm.exception.ForbiddenException;
+import ewm.exception.NotFoundException;
+import ewm.locations.Location;
+import ewm.locations.service.LocationService;
+import ewm.requests.RequestRepository;
+import ewm.requests.dto.ConfirmedRequestsDto;
+import ewm.user.User;
+import ewm.user.service.UserService;
+import jakarta.servlet.http.HttpServletRequest;
+import jakarta.validation.ValidationException;
+import lombok.RequiredArgsConstructor;
+import org.springframework.beans.factory.annotation.Value;
+import org.springframework.data.domain.PageRequest;
+import org.springframework.data.domain.Sort;
+import org.springframework.data.jpa.domain.Specification;
+import org.springframework.http.ResponseEntity;
+import org.springframework.stereotype.Service;
+import org.springframework.transaction.annotation.Transactional;
+
+import java.time.LocalDateTime;
+import java.util.ArrayList;
+import java.util.List;
+import java.util.Map;
+import java.util.Objects;
+import java.util.stream.Collectors;
+
+import static ewm.events.enums.State.*;
+import static ewm.events.enums.StateActionAdmin.PUBLISH_EVENT;
+import static ewm.events.enums.StateActionAdmin.REJECT_EVENT;
+import static ewm.events.enums.StateActionPrivate.CANCEL_REVIEW;
+import static ewm.events.enums.StateActionPrivate.SEND_TO_REVIEW;
+import static ewm.requests.RequestStatus.CONFIRMED;
+
+@Transactional
+@Service
+@RequiredArgsConstructor
+public class EventServiceImpl implements EventService {
+ private final EventRepository eventRepository;
+ private final EventMapper eventMapper;
+ private final UserService userService;
+ private final CategoryService categoryService;
+ private final LocationService locationService;
+ private final RequestRepository requestRepository;
+ private final StatsClient statsClient;
+ private final ObjectMapper objectMapper = new ObjectMapper();
+ @Value("${app}")
+ private String app;
+
+ @Override
+ public EventFullDto create(long userId, EventCreateDto dto) {
+ validateEventTime(dto.getEventDate());
+ User user = userService.getModelById(userId);
+ Category category = categoryService.checkAndReturnCategory(dto.getCategory());
+ Location location = locationService.getOrSave(dto.getLocation());
+
+ Event event = eventMapper.eventCreateDtoToModel(dto);
+ event.setInitiator(user);
+ event.setCategory(category);
+ event.setLocation(location);
+ event.setCreatedOn(LocalDateTime.now());
+ event.setState(PENDING);
+
+ return eventMapper.modelToEventFullDto(eventRepository.save(event), 0L);
+ }
+
+ @Override
+ public EventFullDto updateEventByOwner(Long userId, Long eventId, EventUpdateUserDto dto) {
+ Event event = checkAndReturnEvent(eventId);
+ if (event.getInitiator().getId() != userId) {
+ throw new BadRequestException("Event must not be published");
+ }
+ if (event.getState() == PUBLISHED) {
+ throw new ForbiddenException("Cannot update the event because it's not in the right state: PUBLISHED");
+ }
+ String annotation = dto.getAnnotation();
+ if (annotation != null && !annotation.isBlank()) {
+ event.setAnnotation(annotation);
+ }
+ if (dto.getCategory() != null) {
+ event.setCategory(categoryService.checkAndReturnCategory(dto.getCategory()));
+ }
+ String description = dto.getDescription();
+ if (description != null && !description.isBlank()) {
+ event.setDescription(description);
+ }
+ LocalDateTime eventDate = dto.getEventDate();
+ if (eventDate != null) {
+ validateEventTime(eventDate);
+ event.setEventDate(eventDate);
+ }
+ if (dto.getLocation() != null) {
+ event.setLocation(locationService.getOrSave(dto.getLocation()));
+ }
+ if (dto.getPaid() != null) {
+ event.setPaid(dto.getPaid());
+ }
+ if (dto.getParticipantLimit() != null) {
+ event.setParticipantLimit(dto.getParticipantLimit());
+ }
+ if (dto.getRequestModeration() != null) {
+ event.setRequestModeration(dto.getRequestModeration());
+ }
+ String title = dto.getTitle();
+ if (title != null && !title.isBlank()) {
+ event.setTitle(title);
+ }
+ if (dto.getStateAction() != null) {
+ StateActionPrivate stateActionPrivate = StateActionPrivate.valueOf(dto.getStateAction());
+ if (stateActionPrivate.equals(SEND_TO_REVIEW)) {
+ event.setState(PENDING);
+ } else if (stateActionPrivate.equals(CANCEL_REVIEW)) {
+ event.setState(CANCELED);
+ }
+ }
+ event = eventRepository.save(event);
+ return eventMapper.modelToEventFullDto(event, requestRepository.countByEventIdAndStatus(eventId, CONFIRMED));
+ }
+
+ @Override
+ public EventFullDto updateEventByAdmin(Long eventId, EventUpdateAdminDto dto) {
+ Event event = checkAndReturnEvent(eventId);
+
+ if (dto.getStateAction() != null) {
+ StateActionAdmin stateAction = StateActionAdmin.valueOf(dto.getStateAction());
+ if (!event.getState().equals(PENDING) && stateAction.equals(PUBLISH_EVENT)) {
+ throw new ForbiddenException("Cannot publish the event because it's not in the right state: not PENDING");
+ }
+ if (event.getState().equals(PUBLISHED) && stateAction.equals(REJECT_EVENT)) {
+ throw new ForbiddenException("Cannot reject the event because it's not in the right state: PUBLISHED");
+ }
+ if (stateAction.equals(PUBLISH_EVENT)) {
+ event.setState(PUBLISHED);
+ event.setPublishedOn(LocalDateTime.now());
+ } else if (stateAction.equals(REJECT_EVENT)) {
+ event.setState(State.CANCELED);
+ }
+ }
+
+ String annotation = dto.getAnnotation();
+ if (annotation != null && !annotation.isBlank()) {
+ event.setAnnotation(annotation);
+ }
+ if (dto.getCategory() != null) {
+ event.setCategory(categoryService.checkAndReturnCategory(dto.getCategory()));
+ }
+ String description = dto.getDescription();
+ if (description != null && !description.isBlank()) {
+ event.setDescription(description);
+ }
+ LocalDateTime eventDate = dto.getEventDate();
+ if (eventDate != null) {
+ validateEventTime(eventDate);
+ event.setEventDate(eventDate);
+ }
+ if (dto.getLocation() != null) {
+ event.setLocation(locationService.getOrSave(dto.getLocation()));
+ }
+ if (dto.getPaid() != null) {
+ event.setPaid(dto.getPaid());
+ }
+ if (dto.getParticipantLimit() != null) {
+ event.setParticipantLimit(dto.getParticipantLimit());
+ }
+ if (dto.getRequestModeration() != null) {
+ event.setRequestModeration(dto.getRequestModeration());
+ }
+ String title = dto.getTitle();
+ if (title != null && !title.isBlank()) {
+ event.setTitle(title);
+ }
+
+ event = eventRepository.save(event);
+ return eventMapper.modelToEventFullDto(event, requestRepository.countByEventIdAndStatus(eventId, CONFIRMED));
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public List getEventsByOwnerId(Long userId, Integer from, Integer size) {
+ List events = eventRepository.findAllByInitiatorId(userId, PageRequest.of(from / size, size));
+ List ids = events.stream().map(Event::getId).collect(Collectors.toList());
+ Map confirmedRequests = requestRepository.findAllByEventIdInAndStatus(ids, CONFIRMED)
+ .stream()
+ .collect(Collectors.toMap(ConfirmedRequestsDto::getEvent, ConfirmedRequestsDto::getCount));
+ return events.stream()
+ .map(event -> eventMapper.toEventShortDto(event, confirmedRequests.getOrDefault(event.getId(), 0L)))
+ .collect(Collectors.toList());
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public EventFullDto getEventByOwner(Long userId, Long eventId) {
+ Event event = checkAndReturnEvent(eventId);
+ if (event.getInitiator().getId() == userId) {
+ return eventMapper.modelToEventFullDto(event, requestRepository.countByEventIdAndStatus(eventId, CONFIRMED));
+ } else {
+ throw new NotFoundException("userId не верный");
+ }
+ }
+
+ @Override
+ @Transactional(readOnly = true)
+ public List getEventsByAdmin(List users, List states, List categories,
+ LocalDateTime rangeStart, LocalDateTime rangeEnd,
+ Integer from, Integer size) {
+ if (rangeStart != null && rangeEnd != null && rangeStart.isAfter(rangeEnd)) {
+ throw new ValidationException("Incorrectly made request, Start is after End");
+ }
+ Specification specification = Specification.where(null);
+ if (users != null) {
+ specification = specification.and((root, query, criteriaBuilder) ->
+ root.get("initiator").get("id").in(users));
+ }
+ if (states != null) {
+ specification = specification.and((root, query, criteriaBuilder) ->
+ root.get("state").as(String.class).in(states));
+ }
+ if (categories != null) {
+ specification = specification.and((root, query, criteriaBuilder) ->
+ root.get("category").get("id").in(categories));
+ }
+ if (rangeStart != null) {
+ specification = specification.and((root, query, criteriaBuilder) ->
+ criteriaBuilder.greaterThanOrEqualTo(root.get("eventDate"), rangeStart));
+ }
+ if (rangeEnd != null) {
+ specification = specification.and((root, query, criteriaBuilder) ->
+ criteriaBuilder.lessThanOrEqualTo(root.get("eventDate"), rangeEnd));
+ }
+ List events = eventRepository.findAll(specification, PageRequest.of(from / size, size)).getContent();
+ List result = new ArrayList<>();
+ List uris = events.stream()
+ .map(event -> String.format("/events/%s", event.getId()))
+ .collect(Collectors.toList());
+ LocalDateTime start = events.stream()
+ .map(Event::getCreatedOn)
+ .min(LocalDateTime::compareTo)
+ .orElseThrow(() -> new NotFoundException("Start not found"));
+ ResponseEntity
-
- org.springframework.boot
- spring-boot-starter-web
-
- org.springframework.boot
- spring-boot-starter-validation
+ org.springframework
+ spring-web
@@ -34,8 +29,9 @@
- org.hibernate.validator
- hibernate-validator
+ org.apache.httpcomponents
+ httpclient
+ 4.5.14
@@ -43,35 +39,13 @@
httpclient5
-
- org.springframework.boot
- spring-boot-configuration-processor
- true
-
-
-
- org.projectlombok
- lombok
- true
-
-
-
- org.springframework.boot
- spring-boot-starter-test
- test
-
ru.practicum
stats-dto
0.0.1-SNAPSHOT
compile
-
- ru.practicum
- stats-server
- 0.0.1-SNAPSHOT
- compile
-
+
diff --git a/stats/stats-client/src/main/java/client/StatsClient.java b/stats/stats-client/src/main/java/client/StatsClient.java
index 79a3337..2d4c6e0 100644
--- a/stats/stats-client/src/main/java/client/StatsClient.java
+++ b/stats/stats-client/src/main/java/client/StatsClient.java
@@ -1,6 +1,6 @@
package client;
-import dto.CreateDto;
+import dto.EndpointHitDto;
import org.apache.hc.client5.http.classic.HttpClient;
import org.apache.hc.client5.http.impl.classic.HttpClientBuilder;
import org.springframework.beans.factory.annotation.Value;
@@ -28,10 +28,10 @@ public StatsClient() {
rest.setRequestFactory(requestFactory);
}
- public ResponseEntity save(CreateDto createDto) {
+ public ResponseEntity save(EndpointHitDto endpointHitDto) {
ResponseEntity response;
try {
- response = rest.postForEntity(serverUrl + "/createDto", createDto, Object.class);
+ response = rest.postForEntity(serverUrl + "/hit", endpointHitDto, Object.class);
} catch (HttpStatusCodeException e) {
return ResponseEntity.status(e.getStatusCode()).body(e.getResponseBodyAsByteArray());
}
diff --git a/stats/stats-dto/pom.xml b/stats/stats-dto/pom.xml
index 26d9ae0..259fc97 100644
--- a/stats/stats-dto/pom.xml
+++ b/stats/stats-dto/pom.xml
@@ -5,9 +5,8 @@
4.0.0
ru.practicum
- explore-with-me
+ stats
0.0.1-SNAPSHOT
- ../../pom.xml
stats-dto
diff --git a/stats/stats-dto/src/main/java/dto/CreateDto.java b/stats/stats-dto/src/main/java/dto/EndpointHitDto.java
similarity index 75%
rename from stats/stats-dto/src/main/java/dto/CreateDto.java
rename to stats/stats-dto/src/main/java/dto/EndpointHitDto.java
index e2f6159..6e1ba88 100644
--- a/stats/stats-dto/src/main/java/dto/CreateDto.java
+++ b/stats/stats-dto/src/main/java/dto/EndpointHitDto.java
@@ -1,6 +1,5 @@
package dto;
-import com.fasterxml.jackson.annotation.JsonFormat;
import jakarta.validation.constraints.NotNull;
import jakarta.validation.constraints.Past;
import lombok.*;
@@ -12,7 +11,7 @@
@Getter
@NoArgsConstructor
@AllArgsConstructor
-public class CreateDto {
+public class EndpointHitDto {
@NotNull
private String app;
@@ -24,7 +23,7 @@ public class CreateDto {
@NotNull
@Past
- @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
+ // @JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime timestamp;
}
diff --git a/stats/stats-dto/src/main/java/dto/ViewStatsDto.java b/stats/stats-dto/src/main/java/dto/ViewStatsDto.java
index 23044f8..9295c89 100644
--- a/stats/stats-dto/src/main/java/dto/ViewStatsDto.java
+++ b/stats/stats-dto/src/main/java/dto/ViewStatsDto.java
@@ -1,19 +1,15 @@
package dto;
-import lombok.Getter;
-import lombok.Setter;
+import lombok.AllArgsConstructor;
+import lombok.Data;
+import lombok.NoArgsConstructor;
-@Getter
-@Setter
+@Data
+@AllArgsConstructor
+@NoArgsConstructor
public class ViewStatsDto {
private String app;
private String uri;
private Long hits;
-
- public ViewStatsDto(String app, String uri, Long hits) {
- this.app = app;
- this.uri = uri;
- this.hits = hits;
- }
}
diff --git a/stats/stats-server/Dockerfile b/stats/stats-server/Dockerfile
index 0ff1817..63576b9 100644
--- a/stats/stats-server/Dockerfile
+++ b/stats/stats-server/Dockerfile
@@ -1,5 +1,3 @@
FROM eclipse-temurin:21-jre-jammy
-VOLUME /tmp
-ARG JAR_FILE=target/*.jar
-COPY ${JAR_FILE} app.jar
-ENTRYPOINT ["sh", "-c", "java ${JAVA_OPTS} -jar /app.jar"]
\ No newline at end of file
+COPY ./target/*.jar stats-server.jar
+ENTRYPOINT ["java","-jar","/stats-server.jar"]
\ No newline at end of file
diff --git a/stats/stats-server/pom.xml b/stats/stats-server/pom.xml
index af87956..8764a27 100644
--- a/stats/stats-server/pom.xml
+++ b/stats/stats-server/pom.xml
@@ -5,9 +5,8 @@
4.0.0
ru.practicum
- explore-with-me
+ stats
0.0.1-SNAPSHOT
- ../../pom.xml
stats-server
diff --git a/stats/stats-server/src/main/java/ewm/DateFormatConfig.java b/stats/stats-server/src/main/java/ewm/DateFormatConfig.java
new file mode 100644
index 0000000..414a0f1
--- /dev/null
+++ b/stats/stats-server/src/main/java/ewm/DateFormatConfig.java
@@ -0,0 +1,34 @@
+package ewm;
+
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.deser.LocalDateTimeDeserializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateSerializer;
+import com.fasterxml.jackson.datatype.jsr310.ser.LocalDateTimeSerializer;
+import org.springframework.boot.autoconfigure.jackson.Jackson2ObjectMapperBuilderCustomizer;
+import org.springframework.context.annotation.Bean;
+import org.springframework.context.annotation.Configuration;
+
+import java.time.format.DateTimeFormatter;
+
+@Configuration
+public class DateFormatConfig {
+
+ @Bean
+ public Jackson2ObjectMapperBuilderCustomizer jackson2ObjectMapperBuilderCustomizer() {
+
+ return builder -> {
+
+ // formatter
+ DateTimeFormatter dateFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
+ DateTimeFormatter dateTimeFormatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
+
+ // deserializers
+ builder.deserializers(new LocalDateDeserializer(dateFormatter));
+ builder.deserializers(new LocalDateTimeDeserializer(dateTimeFormatter));
+
+ // serializers
+ builder.serializers(new LocalDateSerializer(dateFormatter));
+ builder.serializers(new LocalDateTimeSerializer(dateTimeFormatter));
+ };
+ }
+}
diff --git a/stats/stats-server/src/main/java/ewm/exception/ConflictException.java b/stats/stats-server/src/main/java/ewm/exception/ConflictException.java
deleted file mode 100644
index 6bcc2f4..0000000
--- a/stats/stats-server/src/main/java/ewm/exception/ConflictException.java
+++ /dev/null
@@ -1,8 +0,0 @@
-package ewm.exception;
-
-public class ConflictException extends RuntimeException {
- public ConflictException(String message) {
- super(message);
- }
-}
-
diff --git a/stats/stats-server/src/main/java/ewm/exception/ErrorHandler.java b/stats/stats-server/src/main/java/ewm/exception/ErrorHandler.java
index 3154594..dfcf251 100644
--- a/stats/stats-server/src/main/java/ewm/exception/ErrorHandler.java
+++ b/stats/stats-server/src/main/java/ewm/exception/ErrorHandler.java
@@ -1,39 +1,35 @@
package ewm.exception;
+import lombok.extern.slf4j.Slf4j;
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;
+import java.time.LocalDateTime;
+
+@Slf4j
@RestControllerAdvice
public class ErrorHandler {
-
@ExceptionHandler
- @ResponseStatus(HttpStatus.NOT_FOUND)
- public ErrorResponse handleNotFoundException(final NotFoundException e) {
- return new ErrorResponse("Error", e.getMessage());
+ @ResponseStatus(HttpStatus.BAD_REQUEST)
+ public ErrorResponse handleValidationException(final BadRequestException e) {
+ return ErrorResponse.builder()
+ .message(e.getMessage())
+ .reason("Incorrectly made request.")
+ .status("BAD_REQUEST")
+ .timestamp(LocalDateTime.now())
+ .build();
}
@ExceptionHandler
@ResponseStatus(HttpStatus.INTERNAL_SERVER_ERROR)
- public ErrorResponse handleInternalServerErrorException(final InternalServerErrorException e) {
- return new ErrorResponse("Error", e.getMessage());
- }
-
- @ExceptionHandler
- @ResponseStatus(HttpStatus.CONFLICT)
- public ErrorResponse handleConflictException(final ConflictException e) {
- return new ErrorResponse("Error", e.getMessage());
- }
-
- @ExceptionHandler
- @ResponseStatus(HttpStatus.BAD_REQUEST)
- public ErrorResponse handleBadRequestException(final BadRequestException e) {
- return new ErrorResponse("Error", e.getMessage());
+ public ErrorResponse handleThrowable(final InternalServerException e) {
+ return ErrorResponse.builder()
+ .message(e.getMessage())
+ .status("INTERNAL_SERVER_ERROR")
+ .timestamp(LocalDateTime.now())
+ .build();
}
-
}
-
-
-
diff --git a/stats/stats-server/src/main/java/ewm/exception/ErrorResponse.java b/stats/stats-server/src/main/java/ewm/exception/ErrorResponse.java
index 2fdf34c..1ff7622 100644
--- a/stats/stats-server/src/main/java/ewm/exception/ErrorResponse.java
+++ b/stats/stats-server/src/main/java/ewm/exception/ErrorResponse.java
@@ -1,19 +1,17 @@
package ewm.exception;
-public class ErrorResponse {
- private final String error;
- private final String description;
-
- public ErrorResponse(String error, String description) {
- this.error = error;
- this.description = description;
- }
+import lombok.Builder;
+import lombok.Data;
- public String getError() {
- return error;
- }
+import java.time.LocalDateTime;
+import java.util.List;
- public String getDescription() {
- return description;
- }
-}
\ No newline at end of file
+@Data
+@Builder
+public class ErrorResponse {
+ private final List errors;
+ private final String message;
+ private final String reason;
+ private final String status;
+ private final LocalDateTime timestamp;
+}
diff --git a/stats/stats-server/src/main/java/ewm/exception/InternalServerException.java b/stats/stats-server/src/main/java/ewm/exception/InternalServerException.java
new file mode 100644
index 0000000..e3aa867
--- /dev/null
+++ b/stats/stats-server/src/main/java/ewm/exception/InternalServerException.java
@@ -0,0 +1,7 @@
+package ewm.exception;
+
+public class InternalServerException extends RuntimeException {
+ public InternalServerException(String message) {
+ super(message);
+ }
+}
diff --git a/stats/stats-server/src/main/java/ewm/stat/EndpointHitController.java b/stats/stats-server/src/main/java/ewm/stat/EndpointHitController.java
index c0d0752..add7b04 100644
--- a/stats/stats-server/src/main/java/ewm/stat/EndpointHitController.java
+++ b/stats/stats-server/src/main/java/ewm/stat/EndpointHitController.java
@@ -1,6 +1,6 @@
package ewm.stat;
-import dto.CreateDto;
+import dto.EndpointHitDto;
import dto.ViewStatsDto;
import ewm.stat.service.EndpointHitService;
import jakarta.validation.Valid;
@@ -21,8 +21,8 @@ public class EndpointHitController {
@ResponseStatus(HttpStatus.CREATED)
@PostMapping("/hit")
- public void create(@RequestBody @Valid CreateDto createDto) {
- service.create(createDto);
+ public void create(@RequestBody @Valid EndpointHitDto endpointHitDto) {
+ service.create(endpointHitDto);
}
@GetMapping("/stats")
diff --git a/stats/stats-server/src/main/java/ewm/stat/EndpointHitMapper.java b/stats/stats-server/src/main/java/ewm/stat/EndpointHitMapper.java
index fe03da4..5a9789e 100644
--- a/stats/stats-server/src/main/java/ewm/stat/EndpointHitMapper.java
+++ b/stats/stats-server/src/main/java/ewm/stat/EndpointHitMapper.java
@@ -1,6 +1,6 @@
package ewm.stat;
-import dto.CreateDto;
+import dto.EndpointHitDto;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;
@@ -8,7 +8,7 @@
@Component
public class EndpointHitMapper {
- public EndpointHit dtoToModel(CreateDto dto) {
+ public EndpointHit dtoToModel(EndpointHitDto dto) {
return EndpointHit.builder()
.app(dto.getApp())
.uri(dto.getUri())
@@ -16,4 +16,5 @@ public EndpointHit dtoToModel(CreateDto dto) {
.timestamp(dto.getTimestamp())
.build();
}
+
}
diff --git a/stats/stats-server/src/main/java/ewm/stat/EndpointHitRepository.java b/stats/stats-server/src/main/java/ewm/stat/EndpointHitRepository.java
index 5075d2b..6ae95bd 100644
--- a/stats/stats-server/src/main/java/ewm/stat/EndpointHitRepository.java
+++ b/stats/stats-server/src/main/java/ewm/stat/EndpointHitRepository.java
@@ -3,34 +3,38 @@
import dto.ViewStatsDto;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
import java.time.LocalDateTime;
import java.util.List;
public interface EndpointHitRepository extends JpaRepository {
- @Query("SELECT new dto.ViewStatsDto(e.app, e.uri, COUNT(e.ip) as hits) " +
- "FROM EndpointHit e " +
- "WHERE e.timestamp BETWEEN :start AND :end " +
- "AND (:uris IS NULL OR e.uri IN :uris) " +
- "GROUP BY e.app, e.uri " +
- "ORDER BY COUNT(e.ip) DESC")
- List getStats(
- @Param("start") LocalDateTime start,
- @Param("end") LocalDateTime end,
- @Param("uris") List uris);
-
- @Query("SELECT new dto.ViewStatsDto(e.app, e.uri, COUNT(DISTINCT e.ip) as hits) " +
- "FROM EndpointHit e " +
- "WHERE e.timestamp BETWEEN :start AND :end " +
- "AND (:uris IS NULL OR e.uri IN :uris) " +
- "GROUP BY e.app, e.uri " +
- "ORDER BY COUNT(DISTINCT e.ip) DESC")
- List getUniqueStats(
- @Param("start") LocalDateTime start,
- @Param("end") LocalDateTime end,
- @Param("uris") List uris);
+ @Query("SELECT new dto.ViewStatsDto(h.app, h.uri, COUNT(h.uri)) " +
+ "FROM EndpointHit AS h " +
+ "WHERE h.timestamp BETWEEN ?1 AND ?2 " +
+ "GROUP BY h.app, h.uri " +
+ "ORDER BY COUNT (h.uri) DESC")
+ List getAllHitsWithoutUris(LocalDateTime start, LocalDateTime end);
+ @Query("SELECT new dto.ViewStatsDto(h.app, h.uri, COUNT(h.uri)) " +
+ "FROM EndpointHit AS h " +
+ "WHERE h.uri IN (?1) AND h.timestamp BETWEEN ?2 AND ?3 " +
+ "GROUP BY h.app, h.uri " +
+ "ORDER BY COUNT (h.uri) DESC")
+ List getAllHitsWithUris(List uris, LocalDateTime start, LocalDateTime end);
+
+ @Query("SELECT new dto.ViewStatsDto(h.app, h.uri, COUNT(DISTINCT h.ip)) " +
+ "FROM EndpointHit AS h " +
+ "WHERE h.timestamp BETWEEN ?1 AND ?2 " +
+ "GROUP BY h.app, h.uri " +
+ "ORDER BY COUNT(DISTINCT h.ip) DESC")
+ List getHitsWithoutUrisWithUniqueIp(LocalDateTime start, LocalDateTime end);
+
+ @Query("SELECT new dto.ViewStatsDto(h.app, h.uri, COUNT(DISTINCT h.ip)) " +
+ "FROM EndpointHit AS h " +
+ "WHERE h.uri IN (?1) AND h.timestamp BETWEEN ?2 AND ?3 " +
+ "GROUP BY h.app, h.uri " +
+ "ORDER BY COUNT(DISTINCT h.ip) DESC")
+ List getHitsWithUrisWithUniqueIp(List uris, LocalDateTime start, LocalDateTime end);
}
diff --git a/stats/stats-server/src/main/java/ewm/stat/service/EndpointHitService.java b/stats/stats-server/src/main/java/ewm/stat/service/EndpointHitService.java
index 8ea6ec3..d8e1763 100644
--- a/stats/stats-server/src/main/java/ewm/stat/service/EndpointHitService.java
+++ b/stats/stats-server/src/main/java/ewm/stat/service/EndpointHitService.java
@@ -1,6 +1,6 @@
package ewm.stat.service;
-import dto.CreateDto;
+import dto.EndpointHitDto;
import dto.ViewStatsDto;
import java.time.LocalDateTime;
@@ -8,7 +8,7 @@
public interface EndpointHitService {
- void create(CreateDto createDto);
+ void create(EndpointHitDto endpointHitDto);
List getStats(LocalDateTime start, LocalDateTime end, List uris, boolean unique);
}
diff --git a/stats/stats-server/src/main/java/ewm/stat/service/EndpointHitServiceImpl.java b/stats/stats-server/src/main/java/ewm/stat/service/EndpointHitServiceImpl.java
index 5059a7f..b169639 100644
--- a/stats/stats-server/src/main/java/ewm/stat/service/EndpointHitServiceImpl.java
+++ b/stats/stats-server/src/main/java/ewm/stat/service/EndpointHitServiceImpl.java
@@ -1,7 +1,8 @@
package ewm.stat.service;
-import dto.CreateDto;
+import dto.EndpointHitDto;
import dto.ViewStatsDto;
+import ewm.exception.BadRequestException;
import ewm.stat.EndpointHit;
import ewm.stat.EndpointHitMapper;
import ewm.stat.EndpointHitRepository;
@@ -20,17 +21,26 @@ public class EndpointHitServiceImpl implements EndpointHitService {
private final EndpointHitMapper mapper;
@Override
- public void create(CreateDto createDto) {
- EndpointHit endpointHit = mapper.dtoToModel(createDto);
+ public void create(EndpointHitDto endpointHitDto) {
+ EndpointHit endpointHit = mapper.dtoToModel(endpointHitDto);
repository.save(endpointHit);
}
@Override
public List getStats(LocalDateTime start, LocalDateTime end, List uris, boolean unique) {
+ if (start.isAfter(end)) {
+ throw new BadRequestException("Время указанно не верно");
+ }
if (unique) {
- return repository.getUniqueStats(start, end, uris);
+ if (uris != null) {
+ return repository.getHitsWithUrisWithUniqueIp(uris, start, end);
+ }
+ return repository.getHitsWithoutUrisWithUniqueIp(start, end);
} else {
- return repository.getStats(start, end, uris);
+ if (uris != null) {
+ return repository.getAllHitsWithUris(uris, start, end);
+ }
+ return repository.getAllHitsWithoutUris(start, end);
}
}
diff --git a/stats/stats-server/src/main/resources/schema.sql b/stats/stats-server/src/main/resources/schema.sql
index cb63d1d..1c82b34 100644
--- a/stats/stats-server/src/main/resources/schema.sql
+++ b/stats/stats-server/src/main/resources/schema.sql
@@ -1,8 +1,9 @@
+DROP TABLE IF EXISTS endpoint_hits CASCADE;
CREATE TABLE IF NOT EXISTS endpoint_hits (
id INTEGER GENERATED BY DEFAULT AS IDENTITY PRIMARY KEY,
- app VARCHAR(200) NOT NULL,
- uri VARCHAR(200) NOT NULL,
- ip VARCHAR(200) NOT NULL,
+ app VARCHAR(200),
+ uri VARCHAR(200),
+ ip VARCHAR(200),
timestamp TIMESTAMP WITHOUT TIME ZONE NOT NULL
);