|
@@ -1,37 +1,90 @@
|
|
|
package com.pharmacopoeia.service;
|
|
package com.pharmacopoeia.service;
|
|
|
|
|
|
|
|
|
|
+import com.pharmacopoeia.entity.UserEvent;
|
|
|
|
|
+import com.pharmacopoeia.repository.UserEventRepository;
|
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.Logger;
|
|
|
import org.slf4j.LoggerFactory;
|
|
import org.slf4j.LoggerFactory;
|
|
|
-import org.springframework.jdbc.core.JdbcTemplate;
|
|
|
|
|
|
|
+import org.springframework.data.domain.PageRequest;
|
|
|
import org.springframework.scheduling.annotation.Async;
|
|
import org.springframework.scheduling.annotation.Async;
|
|
|
import org.springframework.stereotype.Service;
|
|
import org.springframework.stereotype.Service;
|
|
|
|
|
|
|
|
|
|
+import java.time.Instant;
|
|
|
|
|
+import java.time.LocalDate;
|
|
|
|
|
+import java.time.ZoneOffset;
|
|
|
|
|
+import java.util.LinkedHashMap;
|
|
|
|
|
+import java.util.List;
|
|
|
import java.util.Map;
|
|
import java.util.Map;
|
|
|
|
|
+import java.util.stream.Collectors;
|
|
|
|
|
|
|
|
@Service
|
|
@Service
|
|
|
public class AnalyticsService {
|
|
public class AnalyticsService {
|
|
|
|
|
|
|
|
private static final Logger log = LoggerFactory.getLogger(AnalyticsService.class);
|
|
private static final Logger log = LoggerFactory.getLogger(AnalyticsService.class);
|
|
|
- private final JdbcTemplate jdbc;
|
|
|
|
|
|
|
+ private final UserEventRepository repo;
|
|
|
|
|
|
|
|
- public AnalyticsService(JdbcTemplate jdbc) {
|
|
|
|
|
- this.jdbc = jdbc;
|
|
|
|
|
|
|
+ public AnalyticsService(UserEventRepository repo) {
|
|
|
|
|
+ this.repo = repo;
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
@Async
|
|
@Async
|
|
|
public void saveEvent(String userKey, String eventType, Map<String, Object> eventData,
|
|
public void saveEvent(String userKey, String eventType, Map<String, Object> eventData,
|
|
|
String pageUrl, String referrer, String ip, String userAgent) {
|
|
String pageUrl, String referrer, String ip, String userAgent) {
|
|
|
try {
|
|
try {
|
|
|
- String json = eventData != null && !eventData.isEmpty()
|
|
|
|
|
- ? new com.fasterxml.jackson.databind.ObjectMapper().writeValueAsString(eventData)
|
|
|
|
|
- : "{}";
|
|
|
|
|
- jdbc.update(
|
|
|
|
|
- "INSERT INTO user_events (user_key, event_type, event_data, page_url, referrer, ip, user_agent) " +
|
|
|
|
|
- "VALUES (?, ?, CAST(? AS jsonb), ?, ?, ?, ?)",
|
|
|
|
|
- userKey, eventType, json, pageUrl, referrer, ip, userAgent
|
|
|
|
|
- );
|
|
|
|
|
|
|
+ UserEvent event = UserEvent.builder()
|
|
|
|
|
+ .userKey(userKey)
|
|
|
|
|
+ .eventType(eventType)
|
|
|
|
|
+ .eventData(eventData != null ? eventData : Map.of())
|
|
|
|
|
+ .pageUrl(pageUrl)
|
|
|
|
|
+ .referrer(referrer)
|
|
|
|
|
+ .ip(ip)
|
|
|
|
|
+ .userAgent(userAgent)
|
|
|
|
|
+ .build();
|
|
|
|
|
+ repo.save(event);
|
|
|
} catch (Exception e) {
|
|
} catch (Exception e) {
|
|
|
log.warn("埋点写入失败: type={}, error={}", eventType, e.getMessage());
|
|
log.warn("埋点写入失败: type={}, error={}", eventType, e.getMessage());
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|
|
|
|
|
+
|
|
|
|
|
+ // ==================== 埋点查询 ====================
|
|
|
|
|
+
|
|
|
|
|
+ public Map<String, Object> queryEvents(String eventType, String startDate, String endDate,
|
|
|
|
|
+ int page, int pageSize) {
|
|
|
|
|
+ Instant startTime = parseDate(startDate);
|
|
|
|
|
+ Instant endTime = endDate != null && !endDate.isBlank()
|
|
|
|
|
+ ? LocalDate.parse(endDate).plusDays(1).atStartOfDay(ZoneOffset.UTC).toInstant()
|
|
|
|
|
+ : null;
|
|
|
|
|
+
|
|
|
|
|
+ var pageable = PageRequest.of(page - 1, pageSize);
|
|
|
|
|
+ var result = repo.findWithFilters(
|
|
|
|
|
+ eventType != null && !eventType.isBlank() ? eventType : null,
|
|
|
|
|
+ startTime, endTime, pageable);
|
|
|
|
|
+
|
|
|
|
|
+ List<Map<String, Object>> items = result.getContent().stream()
|
|
|
|
|
+ .map(e -> {
|
|
|
|
|
+ Map<String, Object> m = new LinkedHashMap<>();
|
|
|
|
|
+ m.put("id", e.getId());
|
|
|
|
|
+ m.put("user_key", e.getUserKey());
|
|
|
|
|
+ m.put("event_type", e.getEventType());
|
|
|
|
|
+ m.put("event_data", e.getEventData());
|
|
|
|
|
+ m.put("page_url", e.getPageUrl());
|
|
|
|
|
+ m.put("referrer", e.getReferrer());
|
|
|
|
|
+ m.put("ip", e.getIp());
|
|
|
|
|
+ m.put("user_agent", e.getUserAgent());
|
|
|
|
|
+ m.put("created_at", e.getCreatedAt());
|
|
|
|
|
+ return m;
|
|
|
|
|
+ })
|
|
|
|
|
+ .collect(Collectors.toList());
|
|
|
|
|
+
|
|
|
|
|
+ Map<String, Object> response = new LinkedHashMap<>();
|
|
|
|
|
+ response.put("items", items);
|
|
|
|
|
+ response.put("total", result.getTotalElements());
|
|
|
|
|
+ response.put("page", page);
|
|
|
|
|
+ response.put("page_size", pageSize);
|
|
|
|
|
+ return response;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private Instant parseDate(String date) {
|
|
|
|
|
+ if (date == null || date.isBlank()) return null;
|
|
|
|
|
+ return LocalDate.parse(date).atStartOfDay(ZoneOffset.UTC).toInstant();
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|