diff --git a/analytics-service/src/main/java/com/posthub/analytics/controller/AnalyticsController.java b/analytics-service/src/main/java/com/posthub/analytics/controller/AnalyticsController.java index 989f993..6ed8525 100644 --- a/analytics-service/src/main/java/com/posthub/analytics/controller/AnalyticsController.java +++ b/analytics-service/src/main/java/com/posthub/analytics/controller/AnalyticsController.java @@ -2,12 +2,16 @@ package com.posthub.analytics.controller; import com.posthub.analytics.dto.DailyStatsResponse; import com.posthub.analytics.dto.DashboardResponse; +import com.posthub.analytics.dto.EventLogResponse; import com.posthub.analytics.model.UserStats; import com.posthub.analytics.service.AnalyticsQueryService; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.*; +import java.time.LocalDate; import java.util.List; @RestController @@ -34,4 +38,14 @@ public class AnalyticsController { return ResponseEntity.ok(queryService.getDailyStats(days)); } + @GetMapping("/events") + public ResponseEntity> getEvents( + @RequestParam(required = false) String eventType, + @RequestParam(required = false) String userId, + @RequestParam(required = false) LocalDate dateFrom, + @RequestParam(required = false) LocalDate dateTo, + Pageable pageable) { + return ResponseEntity.ok(queryService.getEvents(eventType, userId, dateFrom, dateTo, pageable)); + } + } \ No newline at end of file diff --git a/analytics-service/src/main/java/com/posthub/analytics/dto/EventLogResponse.java b/analytics-service/src/main/java/com/posthub/analytics/dto/EventLogResponse.java new file mode 100644 index 0000000..64328b9 --- /dev/null +++ b/analytics-service/src/main/java/com/posthub/analytics/dto/EventLogResponse.java @@ -0,0 +1,23 @@ +package com.posthub.analytics.dto; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Data; +import lombok.NoArgsConstructor; + +import java.time.Instant; +import java.time.LocalDate; + +@Data +@Builder +@NoArgsConstructor +@AllArgsConstructor +public class EventLogResponse { + + private Long id; + private String eventType; + private String userId; + private String chatId; + private Instant eventTimestamp; + private LocalDate eventDate; +} \ No newline at end of file diff --git a/analytics-service/src/main/java/com/posthub/analytics/repository/EventLogRepository.java b/analytics-service/src/main/java/com/posthub/analytics/repository/EventLogRepository.java index 20c10fc..c15753c 100644 --- a/analytics-service/src/main/java/com/posthub/analytics/repository/EventLogRepository.java +++ b/analytics-service/src/main/java/com/posthub/analytics/repository/EventLogRepository.java @@ -2,6 +2,7 @@ package com.posthub.analytics.repository; import com.posthub.analytics.model.EventLog; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.JpaSpecificationExecutor; import org.springframework.data.jpa.repository.Query; import org.springframework.data.repository.query.Param; import org.springframework.stereotype.Repository; @@ -10,7 +11,7 @@ import java.time.LocalDate; import java.util.List; @Repository -public interface EventLogRepository extends JpaRepository { +public interface EventLogRepository extends JpaRepository, JpaSpecificationExecutor { @Query("SELECT COUNT(DISTINCT e.userId) FROM EventLog e " + "WHERE e.eventDate BETWEEN :from AND :to") diff --git a/analytics-service/src/main/java/com/posthub/analytics/repository/EventLogSpecification.java b/analytics-service/src/main/java/com/posthub/analytics/repository/EventLogSpecification.java new file mode 100644 index 0000000..190642a --- /dev/null +++ b/analytics-service/src/main/java/com/posthub/analytics/repository/EventLogSpecification.java @@ -0,0 +1,35 @@ +package com.posthub.analytics.repository; + +import com.posthub.analytics.model.EventLog; +import org.springframework.data.jpa.domain.Specification; + +import java.time.LocalDate; + +public final class EventLogSpecification { + + private EventLogSpecification() {} + + public static Specification hasEventType(String eventType) { + return (root, query, cb) -> eventType == null || eventType.isBlank() + ? null + : cb.equal(root.get("eventType"), eventType); + } + + public static Specification hasUserId(String userId) { + return (root, query, cb) -> userId == null || userId.isBlank() + ? null + : cb.equal(root.get("userId"), userId); + } + + public static Specification dateFrom(LocalDate dateFrom) { + return (root, query, cb) -> dateFrom == null + ? null + : cb.greaterThanOrEqualTo(root.get("eventDate"), dateFrom); + } + + public static Specification dateTo(LocalDate dateTo) { + return (root, query, cb) -> dateTo == null + ? null + : cb.lessThanOrEqualTo(root.get("eventDate"), dateTo); + } +} \ No newline at end of file diff --git a/analytics-service/src/main/java/com/posthub/analytics/service/AnalyticsQueryService.java b/analytics-service/src/main/java/com/posthub/analytics/service/AnalyticsQueryService.java index 7eac9cf..3e9051a 100644 --- a/analytics-service/src/main/java/com/posthub/analytics/service/AnalyticsQueryService.java +++ b/analytics-service/src/main/java/com/posthub/analytics/service/AnalyticsQueryService.java @@ -2,12 +2,18 @@ package com.posthub.analytics.service; import com.posthub.analytics.dto.DailyStatsResponse; import com.posthub.analytics.dto.DashboardResponse; +import com.posthub.analytics.dto.EventLogResponse; import com.posthub.analytics.model.DailyStats; +import com.posthub.analytics.model.EventLog; import com.posthub.analytics.model.UserStats; import com.posthub.analytics.repository.DailyStatsRepository; import com.posthub.analytics.repository.EventLogRepository; +import com.posthub.analytics.repository.EventLogSpecification; import com.posthub.analytics.repository.UserStatsRepository; import lombok.RequiredArgsConstructor; +import org.springframework.data.domain.Page; +import org.springframework.data.domain.Pageable; +import org.springframework.data.jpa.domain.Specification; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -76,4 +82,28 @@ public class AnalyticsQueryService { .activeUsers(ds.getActiveUsers()) .build(); } + + public Page getEvents(String eventType, String userId, + LocalDate dateFrom, LocalDate dateTo, + Pageable pageable) { + Specification spec = Specification + .where(EventLogSpecification.hasEventType(eventType)) + .and(EventLogSpecification.hasUserId(userId)) + .and(EventLogSpecification.dateFrom(dateFrom)) + .and(EventLogSpecification.dateTo(dateTo)); + + return eventLogRepository.findAll(spec, pageable) + .map(this::toEventResponse); + } + + private EventLogResponse toEventResponse(EventLog e) { + return EventLogResponse.builder() + .id(e.getId()) + .eventType(e.getEventType()) + .userId(e.getUserId()) + .chatId(e.getChatId()) + .eventTimestamp(e.getEventTimestamp()) + .eventDate(e.getEventDate()) + .build(); + } } \ No newline at end of file diff --git a/gateway-service/src/main/resources/application.yml b/gateway-service/src/main/resources/application.yml index 0ff4bd1..c609ba4 100644 --- a/gateway-service/src/main/resources/application.yml +++ b/gateway-service/src/main/resources/application.yml @@ -61,6 +61,15 @@ spring: - RewritePath=/api/rag(?/?.*), ${segment} - AddRequestHeader=X-Forwarded-Prefix, /api/rag + # Analytics Service - API endpoints + - id: analytics-service-api + uri: lb://analytics-service + predicates: + - Path=/api/analytics/** + - Method=GET,POST + filters: + - AddRequestHeader=X-Forwarded-Prefix, /api/analytics + # ---- JWT ---- jwt: secret: ${JWT_SECRET:}