diff --git a/.gitignore b/.gitignore
index 64703cb..977bbab 100644
--- a/.gitignore
+++ b/.gitignore
@@ -15,8 +15,13 @@ target/
!**/src/main/**/target/
!**/src/test/**/target/
+tester-0.0.1.jar
+/tester.bat
+tester.txt
+
### IntelliJ IDEA ###
.idea
*.iws
*.iml
*.ipr
+/execution-report.txt
\ No newline at end of file
diff --git a/API-event-service-specification.json b/API-event-service-specification.json
index 3de2ee2..c02dbcb 100644
--- a/API-event-service-specification.json
+++ b/API-event-service-specification.json
@@ -1571,7 +1571,7 @@
},
"paid": true,
"title": "Знаменитое шоу 'Летающая кукуруза'",
- "views": 999
+ "rating": 6.6
},
{
"annotation": "За почти три десятилетия группа 'Java Core' закрепились на сцене как группа, объединяющая поколения.",
@@ -1588,7 +1588,7 @@
},
"paid": true,
"title": "Концерт рок-группы 'Java Core'",
- "views": 991
+ "rating": 3.7
}
],
"items": {
@@ -1705,11 +1705,11 @@
"description": "Заголовок",
"example": "Знаменитое шоу 'Летающая кукуруза'"
},
- "views": {
- "type": "integer",
- "description": "Количество просмотрев события",
- "format": "int64",
- "example": 999
+ "rating": {
+ "type": "number",
+ "description": "Рейтинг события",
+ "format": "float8",
+ "example": 4.8
}
}
},
@@ -1762,11 +1762,11 @@
"description": "Заголовок",
"example": "Знаменитое шоу 'Летающая кукуруза'"
},
- "views": {
- "type": "integer",
+ "rating": {
+ "type": "number",
"description": "Количество просмотрев события",
- "format": "int64",
- "example": 999
+ "format": "float8",
+ "example": 5.4
}
},
"description": "Краткая информация о событии",
@@ -1786,7 +1786,7 @@
},
"paid": true,
"title": "Знаменитое шоу 'Летающая кукуруза'",
- "views": 999
+ "rating": 9.8
},
{
"annotation": "За почти три десятилетия группа 'Java Core' закрепились на сцене как группа, объединяющая поколения.",
@@ -1803,7 +1803,7 @@
},
"paid": true,
"title": "Концерт рок-группы 'Java Core'",
- "views": 991
+ "rating": 6.8
}
]
},
diff --git a/core/comment-service/src/main/java/ru/practicum/CommentServiceApplication.java b/core/comment-service/src/main/java/ru/practicum/CommentServiceApplication.java
index 5b05018..a468853 100644
--- a/core/comment-service/src/main/java/ru/practicum/CommentServiceApplication.java
+++ b/core/comment-service/src/main/java/ru/practicum/CommentServiceApplication.java
@@ -12,4 +12,4 @@ public static void main(String[] args) {
SpringApplication.run(CommentServiceApplication.class, args);
}
-}
+}
\ No newline at end of file
diff --git a/core/comment-service/src/main/java/ru/practicum/client/UserClient.java b/core/comment-service/src/main/java/ru/practicum/client/UserClient.java
index 50ca2b2..35907ef 100644
--- a/core/comment-service/src/main/java/ru/practicum/client/UserClient.java
+++ b/core/comment-service/src/main/java/ru/practicum/client/UserClient.java
@@ -5,4 +5,4 @@
@FeignClient(name = "user-service")
public interface UserClient extends UserApi {
-}
+}
\ No newline at end of file
diff --git a/core/comment-service/src/main/java/ru/practicum/comment/service/CommentPublicServiceImpl.java b/core/comment-service/src/main/java/ru/practicum/comment/service/CommentPublicServiceImpl.java
index 0c5a1f2..405d397 100644
--- a/core/comment-service/src/main/java/ru/practicum/comment/service/CommentPublicServiceImpl.java
+++ b/core/comment-service/src/main/java/ru/practicum/comment/service/CommentPublicServiceImpl.java
@@ -87,5 +87,4 @@ public CommentDto getCommentByEventAndCommentId(Long eventId, Long comId) {
return CommentMapper.toCommentDto(comment, userDto, eventCommentDto);
}
-
}
\ No newline at end of file
diff --git a/core/comment-service/src/main/resources/application.yaml b/core/comment-service/src/main/resources/application.yaml
index 931c1b1..e6db48a 100644
--- a/core/comment-service/src/main/resources/application.yaml
+++ b/core/comment-service/src/main/resources/application.yaml
@@ -22,4 +22,4 @@ eureka:
instance:
instance-id: ${spring.application.name}${random.int}
preferIpAddress: false
- hostname: localhost
+ hostname: localhost
\ No newline at end of file
diff --git a/core/comment-service/src/main/resources/schema.sql b/core/comment-service/src/main/resources/schema.sql
index 0e28872..84b18e6 100644
--- a/core/comment-service/src/main/resources/schema.sql
+++ b/core/comment-service/src/main/resources/schema.sql
@@ -1 +1 @@
-CREATE SCHEMA IF NOT EXISTS comment_service;
+CREATE SCHEMA IF NOT EXISTS comment_service;
\ No newline at end of file
diff --git a/core/core-common/pom.xml b/core/core-common/pom.xml
index 09963fa..10d86be 100644
--- a/core/core-common/pom.xml
+++ b/core/core-common/pom.xml
@@ -14,13 +14,6 @@
-
-
-
-
-
-
-
@@ -34,11 +27,6 @@
lombok
-
-
-
-
-
@@ -57,24 +45,6 @@
2.0.1.Final
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
@@ -87,29 +57,6 @@
spring-cloud-starter-circuitbreaker-resilience4j
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
diff --git a/core/core-common/src/main/java/ru/practicum/api/compilation/CompilationPublicApi.java b/core/core-common/src/main/java/ru/practicum/api/compilation/CompilationPublicApi.java
index 071559d..a707e38 100644
--- a/core/core-common/src/main/java/ru/practicum/api/compilation/CompilationPublicApi.java
+++ b/core/core-common/src/main/java/ru/practicum/api/compilation/CompilationPublicApi.java
@@ -27,4 +27,4 @@ CompilationDto getCompilationById(
@PathVariable Long compId
);
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/api/event/EventAdminApi.java b/core/core-common/src/main/java/ru/practicum/api/event/EventAdminApi.java
index 24022a1..a5df78e 100644
--- a/core/core-common/src/main/java/ru/practicum/api/event/EventAdminApi.java
+++ b/core/core-common/src/main/java/ru/practicum/api/event/EventAdminApi.java
@@ -37,4 +37,4 @@ EventFullDto updateEventByAdmin(
@RequestBody @Valid UpdateEventDto updateEventDto
);
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/api/event/EventAllApi.java b/core/core-common/src/main/java/ru/practicum/api/event/EventAllApi.java
index dc37691..fc1c48b 100644
--- a/core/core-common/src/main/java/ru/practicum/api/event/EventAllApi.java
+++ b/core/core-common/src/main/java/ru/practicum/api/event/EventAllApi.java
@@ -1,4 +1,4 @@
package ru.practicum.api.event;
public interface EventAllApi extends EventPublicApi, EventPrivateApi, EventAdminApi {
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/api/event/EventPrivateApi.java b/core/core-common/src/main/java/ru/practicum/api/event/EventPrivateApi.java
index 8302fef..926d11a 100644
--- a/core/core-common/src/main/java/ru/practicum/api/event/EventPrivateApi.java
+++ b/core/core-common/src/main/java/ru/practicum/api/event/EventPrivateApi.java
@@ -47,4 +47,4 @@ EventFullDto updateEventByUserIdAndEventId(
@Valid @RequestBody UpdateEventDto updateEventDto
);
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/api/event/EventPublicApi.java b/core/core-common/src/main/java/ru/practicum/api/event/EventPublicApi.java
index 7119219..baf5ff5 100644
--- a/core/core-common/src/main/java/ru/practicum/api/event/EventPublicApi.java
+++ b/core/core-common/src/main/java/ru/practicum/api/event/EventPublicApi.java
@@ -30,32 +30,48 @@ List getAllEventsByParams(
);
// Получение подробной информации об опубликованном событии по его идентификатору
- @GetMapping("/events/{id}")
+ @GetMapping("/events/{eventId}")
@ResponseStatus(HttpStatus.OK)
EventFullDto getInformationAboutEventByEventId(
- @PathVariable @Positive Long id,
+ @RequestHeader("X-EWM-USER-ID") @Positive Long userId,
+ @PathVariable @Positive Long eventId,
HttpServletRequest request
);
// Получение информации о событии для сервиса комментариев
- @GetMapping("/events/{id}/dto/comment")
+ @GetMapping("/events/{eventId}/dto/comment")
@ResponseStatus(HttpStatus.OK)
EventCommentDto getEventCommentDto(
- @PathVariable @Positive Long id
+ @PathVariable @Positive Long eventId
);
// Получение информации о списке событий для сервиса комментариев
@PostMapping("/events/dto/list/comment")
@ResponseStatus(HttpStatus.OK)
Collection getEventCommentDtoList(
- @RequestBody Collection ids
+ @RequestBody Collection eventIds
);
// Получение информации о событии для сервиса заявок
- @GetMapping("/events/{id}/dto/interaction")
+ @GetMapping("/events/{eventId}/dto/interaction")
@ResponseStatus(HttpStatus.OK)
EventInteractionDto getEventInteractionDto(
- @PathVariable @Positive Long id
+ @PathVariable @Positive Long eventId
+ );
+
+ // рекомендации мероприятий для пользователя
+ @GetMapping("/events/recommendations")
+ @ResponseStatus(HttpStatus.OK)
+ Collection getRecommendations(
+ @RequestHeader("X-EWM-USER-ID") @Positive Long userId,
+ @RequestParam(defaultValue = "10") Integer size
+ );
+
+ @PutMapping("/events/{eventId}/like")
+ @ResponseStatus(HttpStatus.OK)
+ String sendLike(
+ @RequestHeader("X-EWM-USER-ID") @Positive Long userId,
+ @PathVariable @Positive Long eventId
);
}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/api/request/RequestApi.java b/core/core-common/src/main/java/ru/practicum/api/request/RequestApi.java
index 163c60a..c079c78 100644
--- a/core/core-common/src/main/java/ru/practicum/api/request/RequestApi.java
+++ b/core/core-common/src/main/java/ru/practicum/api/request/RequestApi.java
@@ -66,4 +66,12 @@ Map getConfirmedRequestsByEventIds(
@RequestBody Collection eventIds
);
+ // Проверка участия пользователя в конкретном событии перед лайком
+ @GetMapping("/users/{userId}/events/{eventId}/check/participation")
+ @ResponseStatus(HttpStatus.OK)
+ String checkParticipation(
+ @PathVariable @Positive(message = "User Id not valid") Long userId,
+ @PathVariable @Positive(message = "Event Id not valid") Long eventId
+ );
+
}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/client/EventClientAbstractHelper.java b/core/core-common/src/main/java/ru/practicum/client/EventClientAbstractHelper.java
index ba6620d..a5e9196 100644
--- a/core/core-common/src/main/java/ru/practicum/client/EventClientAbstractHelper.java
+++ b/core/core-common/src/main/java/ru/practicum/client/EventClientAbstractHelper.java
@@ -86,4 +86,4 @@ private boolean isNotFoundCode(RuntimeException e) {
return false;
}
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/client/RequestClientAbstractHelper.java b/core/core-common/src/main/java/ru/practicum/client/RequestClientAbstractHelper.java
index d00145e..c16f12f 100644
--- a/core/core-common/src/main/java/ru/practicum/client/RequestClientAbstractHelper.java
+++ b/core/core-common/src/main/java/ru/practicum/client/RequestClientAbstractHelper.java
@@ -1,8 +1,10 @@
package ru.practicum.client;
+import feign.FeignException;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import ru.practicum.api.request.RequestApi;
+import ru.practicum.exception.ServiceInteractionException;
import java.util.Collection;
import java.util.Map;
@@ -24,4 +26,25 @@ public Map retrieveConfirmedRequestsMapByEventIdList(Collection comments;
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/event/EventInteractionDto.java b/core/core-common/src/main/java/ru/practicum/dto/event/EventInteractionDto.java
index 706ba0d..3254fb5 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/event/EventInteractionDto.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/event/EventInteractionDto.java
@@ -51,4 +51,4 @@ public static EventInteractionDto makeDummy(Long id) {
return dto;
}
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/event/EventParams.java b/core/core-common/src/main/java/ru/practicum/dto/event/EventParams.java
index 73e141f..35cb098 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/event/EventParams.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/event/EventParams.java
@@ -32,4 +32,4 @@ public class EventParams {
private Integer size;
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/event/EventRequestStatusUpdateRequest.java b/core/core-common/src/main/java/ru/practicum/dto/event/EventRequestStatusUpdateRequest.java
index 859a768..6cedfb0 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/event/EventRequestStatusUpdateRequest.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/event/EventRequestStatusUpdateRequest.java
@@ -11,4 +11,4 @@ public class EventRequestStatusUpdateRequest {
private State state;
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/event/EventRequestStatusUpdateResult.java b/core/core-common/src/main/java/ru/practicum/dto/event/EventRequestStatusUpdateResult.java
index 3be6bc9..f47028b 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/event/EventRequestStatusUpdateResult.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/event/EventRequestStatusUpdateResult.java
@@ -12,4 +12,4 @@ public class EventRequestStatusUpdateResult {
private List rejectedRequests;
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/event/EventShortDto.java b/core/core-common/src/main/java/ru/practicum/dto/event/EventShortDto.java
index 7f3843c..250a8dc 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/event/EventShortDto.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/event/EventShortDto.java
@@ -30,6 +30,6 @@ public class EventShortDto {
private LocalDateTime eventDate;
private Long confirmedRequests;
- private Long views;
+ private Double rating;
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/event/EventSort.java b/core/core-common/src/main/java/ru/practicum/dto/event/EventSort.java
index ea36265..830c6b0 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/event/EventSort.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/event/EventSort.java
@@ -1,5 +1,5 @@
package ru.practicum.dto.event;
public enum EventSort {
- EVENT_DATE, VIEWS
-}
+ EVENT_DATE, VIEWS, RATING
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/event/LocationDto.java b/core/core-common/src/main/java/ru/practicum/dto/event/LocationDto.java
index 3b6c1b9..864323a 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/event/LocationDto.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/event/LocationDto.java
@@ -14,4 +14,4 @@ public class LocationDto {
private Float lat;
private Float lon;
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/event/NewEventDto.java b/core/core-common/src/main/java/ru/practicum/dto/event/NewEventDto.java
index 7e43c7e..043ba29 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/event/NewEventDto.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/event/NewEventDto.java
@@ -39,4 +39,4 @@ public class NewEventDto {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime eventDate;
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/event/State.java b/core/core-common/src/main/java/ru/practicum/dto/event/State.java
index 976062e..c664a68 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/event/State.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/event/State.java
@@ -2,4 +2,4 @@
public enum State {
PENDING, PUBLISHED, CANCELED
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/event/StateAction.java b/core/core-common/src/main/java/ru/practicum/dto/event/StateAction.java
index c35bda8..8c691a9 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/event/StateAction.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/event/StateAction.java
@@ -5,4 +5,4 @@ public enum StateAction {
CANCEL_REVIEW,
PUBLISH_EVENT,
REJECT_EVENT
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/event/UpdateEventDto.java b/core/core-common/src/main/java/ru/practicum/dto/event/UpdateEventDto.java
index 1725e05..47215b5 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/event/UpdateEventDto.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/event/UpdateEventDto.java
@@ -48,4 +48,4 @@ public class UpdateEventDto {
@JsonFormat(pattern = "yyyy-MM-dd HH:mm:ss")
private LocalDateTime eventDate;
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/request/EventRequestStatusUpdateRequestDto.java b/core/core-common/src/main/java/ru/practicum/dto/request/EventRequestStatusUpdateRequestDto.java
index 4af1b9b..2a3d7f0 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/request/EventRequestStatusUpdateRequestDto.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/request/EventRequestStatusUpdateRequestDto.java
@@ -21,4 +21,4 @@ public class EventRequestStatusUpdateRequestDto {
@NotNull(message = "Field 'status' shouldn't be null")
private ParticipationRequestStatus status;
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/request/EventRequestStatusUpdateResultDto.java b/core/core-common/src/main/java/ru/practicum/dto/request/EventRequestStatusUpdateResultDto.java
index b678c65..81fd5a0 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/request/EventRequestStatusUpdateResultDto.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/request/EventRequestStatusUpdateResultDto.java
@@ -20,4 +20,4 @@ public class EventRequestStatusUpdateResultDto {
@Builder.Default
private List rejectedRequests = new ArrayList<>();
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/request/ParticipationRequestDto.java b/core/core-common/src/main/java/ru/practicum/dto/request/ParticipationRequestDto.java
index 25b8afb..b46c319 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/request/ParticipationRequestDto.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/request/ParticipationRequestDto.java
@@ -25,4 +25,4 @@ public class ParticipationRequestDto {
@JsonFormat(pattern = "yyyy-MM-dd'T'HH:mm:ss.SSS")
private LocalDateTime created;
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/request/ParticipationRequestStatus.java b/core/core-common/src/main/java/ru/practicum/dto/request/ParticipationRequestStatus.java
index e2f335e..88b0d23 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/request/ParticipationRequestStatus.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/request/ParticipationRequestStatus.java
@@ -4,4 +4,4 @@ public enum ParticipationRequestStatus {
PENDING, CONFIRMED, CANCELED, REJECTED
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/user/NewUserRequestDto.java b/core/core-common/src/main/java/ru/practicum/dto/user/NewUserRequestDto.java
index c2fecdf..5ea5305 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/user/NewUserRequestDto.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/user/NewUserRequestDto.java
@@ -23,4 +23,4 @@ public class NewUserRequestDto {
@Size(min = 2, max = 250, message = "Field 'name' should be from 2 to 250 characters")
private String name;
-}
+}
\ No newline at end of file
diff --git a/core/core-common/src/main/java/ru/practicum/dto/user/UserDto.java b/core/core-common/src/main/java/ru/practicum/dto/user/UserDto.java
index 88b1dd5..bbee84c 100644
--- a/core/core-common/src/main/java/ru/practicum/dto/user/UserDto.java
+++ b/core/core-common/src/main/java/ru/practicum/dto/user/UserDto.java
@@ -23,4 +23,4 @@ public static UserDto makeDummy(Long id) {
return dto;
}
-}
+}
\ No newline at end of file
diff --git a/core/event-service/src/main/java/ru/practicum/compilation/service/CompilationMapper.java b/core/event-service/src/main/java/ru/practicum/compilation/service/CompilationMapper.java
index 8c961d6..60f4894 100644
--- a/core/event-service/src/main/java/ru/practicum/compilation/service/CompilationMapper.java
+++ b/core/event-service/src/main/java/ru/practicum/compilation/service/CompilationMapper.java
@@ -13,7 +13,7 @@ public class CompilationMapper {
public static CompilationDto toCompilationDto(Compilation compilation, Map userMap) {
List eventShortDtoList = compilation.getEvents().stream()
- .map(e -> EventMapper.toEventShortDto(e, userMap.get(e.getInitiatorId()), 0L, 0L))
+ .map(e -> EventMapper.toEventShortDto(e, userMap.get(e.getInitiatorId()), 0L, 0.0))
.toList();
return CompilationDto.builder()
diff --git a/core/event-service/src/main/java/ru/practicum/event/controller/EventPublicController.java b/core/event-service/src/main/java/ru/practicum/event/controller/EventPublicController.java
index f249745..e4f58b8 100644
--- a/core/event-service/src/main/java/ru/practicum/event/controller/EventPublicController.java
+++ b/core/event-service/src/main/java/ru/practicum/event/controller/EventPublicController.java
@@ -49,23 +49,33 @@ public List getAllEventsByParams(
// Получение подробной информации об опубликованном событии по его идентификатору
@Override
- public EventFullDto getInformationAboutEventByEventId(Long id, HttpServletRequest request) {
- return eventPublicService.getEventById(id, request);
+ public EventFullDto getInformationAboutEventByEventId(Long userId, Long eventId, HttpServletRequest request) {
+ return eventPublicService.getEventById(userId, eventId, request);
}
@Override
- public EventCommentDto getEventCommentDto(Long id) {
- return eventPublicService.getEventCommentDto(id);
+ public EventCommentDto getEventCommentDto(Long eventId) {
+ return eventPublicService.getEventCommentDto(eventId);
}
@Override
- public Collection getEventCommentDtoList(Collection ids) {
- return eventPublicService.getEventCommentDtoList(ids);
+ public Collection getEventCommentDtoList(Collection eventIds) {
+ return eventPublicService.getEventCommentDtoList(eventIds);
}
@Override
- public EventInteractionDto getEventInteractionDto(Long id) {
- return eventPublicService.getEventInteractionDto(id);
+ public EventInteractionDto getEventInteractionDto(Long eventId) {
+ return eventPublicService.getEventInteractionDto(eventId);
+ }
+
+ @Override
+ public Collection getRecommendations(Long userId, Integer size) {
+ return eventPublicService.getRecommendations(userId, size);
+ }
+
+ @Override
+ public String sendLike(Long userId, Long eventId) {
+ return eventPublicService.sendLike(userId, eventId);
}
}
\ No newline at end of file
diff --git a/core/event-service/src/main/java/ru/practicum/event/dal/View.java b/core/event-service/src/main/java/ru/practicum/event/dal/View.java
deleted file mode 100644
index 4cce663..0000000
--- a/core/event-service/src/main/java/ru/practicum/event/dal/View.java
+++ /dev/null
@@ -1,33 +0,0 @@
-package ru.practicum.event.dal;
-
-import jakarta.persistence.*;
-import lombok.*;
-import org.hibernate.annotations.OnDelete;
-import org.hibernate.annotations.OnDeleteAction;
-
-@Getter
-@Setter
-@EqualsAndHashCode(onlyExplicitlyIncluded = true)
-@ToString
-@Entity
-@Builder
-@NoArgsConstructor
-@AllArgsConstructor
-@Table(name = "views", indexes = {@Index(name = "idx_views_event_id", columnList = "event_id")})
-public class View {
-
- @Id
- @GeneratedValue(strategy = GenerationType.IDENTITY)
- @EqualsAndHashCode.Include
- @Column(name = "id")
- private Long id;
-
- @ManyToOne
- @JoinColumn(name = "event_id", nullable = false)
- @OnDelete(action = OnDeleteAction.RESTRICT)
- private Event event;
-
- @Column(name = "ip", length = 15, nullable = false)
- private String ip;
-
-}
diff --git a/core/event-service/src/main/java/ru/practicum/event/dal/ViewRepository.java b/core/event-service/src/main/java/ru/practicum/event/dal/ViewRepository.java
deleted file mode 100644
index 2d2110e..0000000
--- a/core/event-service/src/main/java/ru/practicum/event/dal/ViewRepository.java
+++ /dev/null
@@ -1,25 +0,0 @@
-package ru.practicum.event.dal;
-
-import org.springframework.data.jpa.repository.JpaRepository;
-import org.springframework.data.jpa.repository.Query;
-import org.springframework.data.repository.query.Param;
-
-import java.util.List;
-
-public interface ViewRepository extends JpaRepository {
-
- long countByEventId(Long eventId);
-
- @Query("""
- SELECT v.event.id, count(v)
- FROM View v
- WHERE v.event.id IN :eventIds
- GROUP BY v.event.id
- """)
- List
diff --git a/stats/aggregator/pom.xml b/stats/aggregator/pom.xml
new file mode 100644
index 0000000..0617d57
--- /dev/null
+++ b/stats/aggregator/pom.xml
@@ -0,0 +1,100 @@
+
+
+ 4.0.0
+
+
+ ru.practicum
+ stats
+ 0.0.1-SNAPSHOT
+
+
+ aggregator
+
+
+
+
+
+
+ ru.practicum
+ avro-schemas
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-config
+
+
+
+ org.springframework.retry
+ spring-retry
+
+
+
+
+
+ org.springframework.kafka
+ spring-kafka
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
+
+
diff --git a/stats/aggregator/src/main/java/ru/practicum/AggregatorApplication.java b/stats/aggregator/src/main/java/ru/practicum/AggregatorApplication.java
new file mode 100644
index 0000000..8599363
--- /dev/null
+++ b/stats/aggregator/src/main/java/ru/practicum/AggregatorApplication.java
@@ -0,0 +1,15 @@
+package ru.practicum;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
+
+@SpringBootApplication
+@ConfigurationPropertiesScan
+public class AggregatorApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(AggregatorApplication.class, args);
+ }
+
+}
diff --git a/stats/aggregator/src/main/java/ru/practicum/kafka/KafkaController.java b/stats/aggregator/src/main/java/ru/practicum/kafka/KafkaController.java
new file mode 100644
index 0000000..af19112
--- /dev/null
+++ b/stats/aggregator/src/main/java/ru/practicum/kafka/KafkaController.java
@@ -0,0 +1,36 @@
+package ru.practicum.kafka;
+
+import lombok.RequiredArgsConstructor;
+import org.apache.avro.specific.SpecificRecordBase;
+import org.springframework.boot.context.event.ApplicationReadyEvent;
+import org.springframework.context.event.EventListener;
+import org.springframework.kafka.annotation.KafkaListener;
+import org.springframework.kafka.config.KafkaListenerEndpointRegistry;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.stereotype.Service;
+import ru.practicum.ewm.stats.avro.UserActionAvro;
+import ru.practicum.properties.CustomProperties;
+import ru.practicum.service.UserActionService;
+
+@Service
+@RequiredArgsConstructor
+public class KafkaController {
+
+ private final KafkaTemplate kafkaTemplate;
+ private final KafkaListenerEndpointRegistry kafkaRegistry;
+
+ private final CustomProperties customProperties;
+ private final UserActionService userActionService;
+
+ @EventListener(ApplicationReadyEvent.class)
+ public void initKafkaProducer() {
+ kafkaTemplate.flush();
+ kafkaRegistry.start();
+ }
+
+ @KafkaListener(topics = "#{customProperties.kafka.userActionTopic}")
+ public void listen(UserActionAvro userActionAvro) {
+ userActionService.handleUserAction(userActionAvro);
+ }
+
+}
diff --git a/stats/aggregator/src/main/java/ru/practicum/properties/CustomProperties.java b/stats/aggregator/src/main/java/ru/practicum/properties/CustomProperties.java
new file mode 100644
index 0000000..3129526
--- /dev/null
+++ b/stats/aggregator/src/main/java/ru/practicum/properties/CustomProperties.java
@@ -0,0 +1,50 @@
+package ru.practicum.properties;
+
+import lombok.Getter;
+import lombok.Setter;
+import org.springframework.boot.context.properties.ConfigurationProperties;
+import org.springframework.stereotype.Component;
+import ru.practicum.ewm.stats.avro.UserActionAvro;
+
+import java.math.BigDecimal;
+
+@Getter
+@Setter
+@ConfigurationProperties("my-area-guide")
+@Component
+public class CustomProperties {
+
+ private final Kafka kafka = new Kafka();
+ private final Aggregator aggregator = new Aggregator();
+
+ @Getter
+ @Setter
+ public static class Kafka {
+ private String userActionTopic = "user-actions";
+ private String eventsSimilarityTopic = "events-similarity";
+ }
+
+ @Getter
+ @Setter
+ public static class Aggregator {
+ private final Weights weights = new Weights();
+ private String minimumSumAlgorithm = "optimized";
+ }
+
+ @Getter
+ @Setter
+ public static class Weights {
+ private String like = "0.9";
+ private String register = "0.7";
+ private String view = "0.3";
+
+ public BigDecimal ofUserAction(UserActionAvro userActionAvro) {
+ return switch (userActionAvro.getActionType()) {
+ case LIKE -> new BigDecimal(like);
+ case REGISTER -> new BigDecimal(register);
+ default -> new BigDecimal(view);
+ };
+ }
+ }
+
+}
\ No newline at end of file
diff --git a/stats/aggregator/src/main/java/ru/practicum/service/UserActionService.java b/stats/aggregator/src/main/java/ru/practicum/service/UserActionService.java
new file mode 100644
index 0000000..3b92a12
--- /dev/null
+++ b/stats/aggregator/src/main/java/ru/practicum/service/UserActionService.java
@@ -0,0 +1,147 @@
+package ru.practicum.service;
+
+import lombok.RequiredArgsConstructor;
+import lombok.extern.slf4j.Slf4j;
+import org.apache.avro.specific.SpecificRecordBase;
+import org.springframework.kafka.core.KafkaTemplate;
+import org.springframework.stereotype.Service;
+import ru.practicum.ewm.stats.avro.EventSimilarityAvro;
+import ru.practicum.ewm.stats.avro.UserActionAvro;
+import ru.practicum.properties.CustomProperties;
+
+import java.math.BigDecimal;
+import java.time.Instant;
+import java.util.*;
+
+@Slf4j
+@Service
+@RequiredArgsConstructor
+public class UserActionService {
+
+ private final KafkaTemplate kafkaTemplate;
+ private final CustomProperties customProperties;
+
+ // Map> - таблица весов с быстрым доступом по userId
+ private final Map> weightsByUser = new HashMap<>();
+ // Map> - таблица весов с быстрым доступом по eventId
+ private final Map> weightsByEvent = new HashMap<>();
+
+ // Map - таблица сумм векторов для каждого события
+ private final Map eventSums = new HashMap<>();
+
+ // Map> - таблица сумм минимумов пар векторов событий
+ private final Map> minWeightSums = new HashMap<>();
+
+ public void handleUserAction(UserActionAvro userActionAvro) {
+ Long userId = userActionAvro.getUserId();
+ Long eventId = userActionAvro.getEventId();
+ BigDecimal oldWeight = BigDecimal.ZERO;
+ BigDecimal newWeight = customProperties.getAggregator().getWeights().ofUserAction(userActionAvro);
+ log.debug("IN: W[{},{}] = {}", userId, eventId, newWeight);
+
+ Map userWeights = weightsByUser.computeIfAbsent(userId, id -> new HashMap<>());
+ Map eventWeights = weightsByEvent.computeIfAbsent(eventId, id -> new HashMap<>());
+
+ if (userWeights.containsKey(eventId)) {
+ oldWeight = userWeights.get(eventId);
+ if (newWeight.compareTo(oldWeight) <= 0) {
+ log.debug("WEIGHT: {} <= {}, ничего не делаем!", newWeight, oldWeight);
+ return;
+ }
+ log.debug("WEIGHT: {} > {}, кладем {}", newWeight, oldWeight, newWeight);
+ } else {
+ log.debug("WEIGHT: первое взаимодействие юзера {} и события {}, кладем {}", userId, eventId, newWeight);
+ }
+
+ // обновляем веса
+ userWeights.put(eventId, newWeight);
+ eventWeights.put(userId, newWeight);
+ // обновляем сумму вектора события
+ recountEventSum(eventId, oldWeight, newWeight);
+ // обновляем суммы минимумов векторов событий
+ if (customProperties.getAggregator().getMinimumSumAlgorithm().toLowerCase().contains("naive")) {
+ recountEventMinWeightsNaive(userId, eventId);
+ } else {
+ recountEventMinWeightsOptimized(userId, eventId, oldWeight, newWeight);
+ }
+ // считаем подобие и отправляем в кафку
+ sendSimilarity(userId, eventId);
+ }
+
+ // вычисление и отправка подобия на основе имеющихся таблиц
+ private void sendSimilarity(Long userId, Long eventId) {
+ for (Long anotherEventId : weightsByUser.get(userId).keySet()) {
+ if (!Objects.equals(eventId, anotherEventId)) {
+ long first = Math.min(eventId, anotherEventId);
+ long second = Math.max(eventId, anotherEventId);
+
+ double numerator = minWeightSums.get(first).get(second).doubleValue();
+ double sqrt1 = Math.sqrt(eventSums.get(first).doubleValue());
+ double sqrt2 = Math.sqrt(eventSums.get(second).doubleValue());
+ double denominator = sqrt1 * sqrt2;
+ double similarity = numerator / denominator;
+
+ EventSimilarityAvro eventSimilarityAvro = EventSimilarityAvro.newBuilder()
+ .setEventA(first)
+ .setEventB(second)
+ .setScore(similarity)
+ .setTimestamp(Instant.now())
+ .build();
+
+ kafkaTemplate.send(customProperties.getKafka().getEventsSimilarityTopic(), eventSimilarityAvro);
+ log.debug("SENT: {}", eventSimilarityAvro);
+ }
+ }
+ }
+
+ // пересчет таблицы сумм векторов события
+ private void recountEventSum(Long eventId, BigDecimal oldWeight, BigDecimal newWeight) {
+ BigDecimal delta = newWeight.subtract(oldWeight);
+ BigDecimal prevSum = eventSums.get(eventId);
+ eventSums.merge(eventId, delta, BigDecimal::add);
+ log.debug("SUM: Для события {} пересчитана сумма {} + {} = {}", eventId, prevSum, delta, eventSums.get(eventId));
+ }
+
+ // пересчет таблицы сумм минимумов двух векторов событий - версия 1, наивная
+ private void recountEventMinWeightsNaive(Long userId, Long eventId) {
+ for (Long secondEventId : weightsByUser.get(userId).keySet()) {
+ if (!Objects.equals(secondEventId, eventId)) {
+ Map eventWeights1 = weightsByEvent.get(eventId);
+ Map eventWeights2 = weightsByEvent.get(secondEventId);
+
+ Set userIds = new HashSet<>(eventWeights1.keySet());
+ userIds.retainAll(eventWeights2.keySet());
+
+ BigDecimal sum = userIds.stream()
+ .map(id -> eventWeights1.get(id).min(eventWeights2.get(id)))
+ .reduce(BigDecimal.ZERO, BigDecimal::add);
+
+ Long first = Math.min(eventId, secondEventId);
+ Long second = Math.max(eventId, secondEventId);
+ minWeightSums.computeIfAbsent(first, k -> new HashMap<>()).put(second, sum);;
+ log.debug("MIN1: Для событий {} и {} посчитана сумма минимумов {}", first, second, sum);
+ }
+ }
+ }
+
+ // пересчет таблицы сумм минимумов двух векторов событий - версия 2, оптимизированная
+ private void recountEventMinWeightsOptimized(Long userId, Long eventId, BigDecimal oldWeight, BigDecimal newWeight) {
+ for (Map.Entry anotherEventEntry : weightsByUser.get(userId).entrySet()) {
+ if (!Objects.equals(eventId, anotherEventEntry.getKey())) {
+ Long first = Math.min(eventId, anotherEventEntry.getKey());
+ Long second = Math.max(eventId, anotherEventEntry.getKey());
+
+ Map firstEventSums = minWeightSums.computeIfAbsent(first, k -> new HashMap<>());
+ BigDecimal oldSum = firstEventSums.getOrDefault(second, BigDecimal.ZERO);
+
+ BigDecimal oldMinimum = oldWeight.min(anotherEventEntry.getValue());
+ BigDecimal newMinimum = newWeight.min(anotherEventEntry.getValue());
+ BigDecimal newSum = oldSum.subtract(oldMinimum).add(newMinimum);
+
+ firstEventSums.put(second, newSum);
+ log.debug("MIN2: Для событий {} и {} посчитана сумма минимумов {}", first, second, newSum);
+ }
+ }
+ }
+
+}
diff --git a/stats/aggregator/src/main/resources/application.yaml b/stats/aggregator/src/main/resources/application.yaml
new file mode 100644
index 0000000..f057e85
--- /dev/null
+++ b/stats/aggregator/src/main/resources/application.yaml
@@ -0,0 +1,25 @@
+spring:
+ application:
+ name: aggregator
+ config:
+ import: "configserver:"
+ cloud:
+ config:
+ discovery:
+ enabled: true
+ serviceId: config-server
+ fail-fast: true
+ retry:
+ useRandomPolicy: true
+ max-interval: 10000
+ max-attempts: 100
+
+eureka:
+ client:
+ registerWithEureka: true
+ serviceUrl:
+ defaultZone: http://localhost:8761/eureka/
+ instance:
+ instance-id: ${spring.application.name}${random.int}
+ preferIpAddress: false
+ hostname: localhost
\ No newline at end of file
diff --git a/stats/analyzer/pom.xml b/stats/analyzer/pom.xml
new file mode 100644
index 0000000..3f45efd
--- /dev/null
+++ b/stats/analyzer/pom.xml
@@ -0,0 +1,124 @@
+
+
+ 4.0.0
+
+
+ ru.practicum
+ stats
+ 0.0.1-SNAPSHOT
+
+
+ analyzer
+
+
+
+
+
+
+ ru.practicum
+ proto-schemas
+
+
+
+ ru.practicum
+ avro-schemas
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-configuration-processor
+ true
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+ org.springframework.boot
+ spring-boot-starter-actuator
+
+
+
+
+
+ org.postgresql
+ postgresql
+
+
+
+ org.springframework.boot
+ spring-boot-starter-data-jpa
+
+
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-netflix-eureka-client
+
+
+
+ org.springframework.cloud
+ spring-cloud-starter-config
+
+
+
+ org.springframework.retry
+ spring-retry
+
+
+
+
+
+ net.devh
+ grpc-server-spring-boot-starter
+
+
+
+
+
+ org.springframework.kafka
+ spring-kafka
+
+
+
+
+
+
+
+
+ org.springframework.boot
+ spring-boot-maven-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+ org.apache.maven.plugins
+ maven-compiler-plugin
+
+
+
+ org.projectlombok
+ lombok
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/stats/analyzer/src/main/java/ru/practicum/AnalyzerApplication.java b/stats/analyzer/src/main/java/ru/practicum/AnalyzerApplication.java
new file mode 100644
index 0000000..fab04a9
--- /dev/null
+++ b/stats/analyzer/src/main/java/ru/practicum/AnalyzerApplication.java
@@ -0,0 +1,15 @@
+package ru.practicum;
+
+import org.springframework.boot.SpringApplication;
+import org.springframework.boot.autoconfigure.SpringBootApplication;
+import org.springframework.boot.context.properties.ConfigurationPropertiesScan;
+
+@SpringBootApplication
+@ConfigurationPropertiesScan
+public class AnalyzerApplication {
+
+ public static void main(String[] args) {
+ SpringApplication.run(AnalyzerApplication.class, args);
+ }
+
+}
diff --git a/stats/analyzer/src/main/java/ru/practicum/dal/EventSimilarity.java b/stats/analyzer/src/main/java/ru/practicum/dal/EventSimilarity.java
new file mode 100644
index 0000000..300d596
--- /dev/null
+++ b/stats/analyzer/src/main/java/ru/practicum/dal/EventSimilarity.java
@@ -0,0 +1,43 @@
+package ru.practicum.dal;
+
+import jakarta.persistence.*;
+import lombok.*;
+import org.hibernate.annotations.Check;
+
+import java.time.Instant;
+
+@Getter
+@Setter
+@EqualsAndHashCode(onlyExplicitlyIncluded = true)
+@ToString
+@Entity
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+@Table(
+ name = "similarities",
+ indexes = {@Index(name = "idx_similarities_event_a_event_b", columnList = "event_a, event_b")},
+ uniqueConstraints = {@UniqueConstraint(name = "unique_event_a_event_b", columnNames = {"event_a", "event_b"})}
+)
+@Check(constraints = "event_a < event_b")
+public class EventSimilarity {
+
+ @Id
+ @GeneratedValue(strategy = GenerationType.IDENTITY)
+ @EqualsAndHashCode.Include
+ @Column(name = "id")
+ private Long id;
+
+ @Column(name = "event_a", nullable = false)
+ private Long eventA;
+
+ @Column(name = "event_b", nullable = false)
+ private Long eventB;
+
+ @Column(name = "score", nullable = false)
+ private Double score;
+
+ @Column(name = "timestamp", nullable = false)
+ private Instant timestamp;
+
+}
diff --git a/stats/analyzer/src/main/java/ru/practicum/dal/EventSimilarityRepository.java b/stats/analyzer/src/main/java/ru/practicum/dal/EventSimilarityRepository.java
new file mode 100644
index 0000000..8e2acab
--- /dev/null
+++ b/stats/analyzer/src/main/java/ru/practicum/dal/EventSimilarityRepository.java
@@ -0,0 +1,61 @@
+package ru.practicum.dal;
+
+import org.springframework.data.jpa.repository.JpaRepository;
+import org.springframework.data.jpa.repository.Query;
+import org.springframework.data.repository.query.Param;
+
+import java.util.List;
+
+public interface EventSimilarityRepository extends JpaRepository {
+
+ EventSimilarity findByEventAAndEventB(Long eventA, Long eventB);
+
+ @Query(nativeQuery = true, value = """
+ (
+ SELECT s.event_b event_id, s.score score
+ FROM similarities s
+ WHERE s.event_a IN :eventList
+ AND NOT EXISTS (SELECT 1 FROM actions a WHERE a.event_id = s.event_b AND a.user_id = :userId)
+ )
+ UNION ALL
+ (
+ SELECT s.event_a event_id, s.score score
+ FROM similarities s
+ WHERE s.event_b IN :eventList
+ AND NOT EXISTS (SELECT 1 FROM actions a WHERE a.event_id = s.event_a AND a.user_id = :userId)
+ )
+ ORDER BY score DESC
+ LIMIT :limit;
+ """)
+ List