package dev.lions.repositories; import jakarta.enterprise.context.ApplicationScoped; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import jakarta.persistence.TypedQuery; import jakarta.persistence.criteria.CriteriaBuilder; import jakarta.persistence.criteria.CriteriaQuery; import jakarta.persistence.criteria.Root; import jakarta.transaction.Transactional; import jakarta.validation.constraints.NotNull; import lombok.extern.slf4j.Slf4j; import dev.lions.events.AnalyticsEvent; import dev.lions.exceptions.RepositoryException; import java.time.LocalDateTime; import java.util.List; import java.util.Map; import java.util.Optional; import java.util.stream.Collectors; /** * Repository gérant la persistance des événements analytiques. * Cette classe assure le stockage, la récupération et l'analyse * des événements analytiques de l'application. * * @author Lions Dev Team * @version 1.1 */ @Slf4j @ApplicationScoped public class AnalyticsRepository extends BaseRepository { @PersistenceContext private EntityManager entityManager; /** * Recherche tous les événements pour une période donnée. * * @param startDate Date de début de la période * @param endDate Date de fin de la période * @return Liste des événements trouvés * @throws RepositoryException En cas d'erreur de requête */ public List findEventsByDateRange(@NotNull LocalDateTime startDate, @NotNull LocalDateTime endDate) { log.debug("Recherche des événements entre {} et {}", startDate, endDate); try { TypedQuery query = entityManager.createQuery( "SELECT e FROM AnalyticsEvent e " + "WHERE e.timestamp BETWEEN :startDate AND :endDate " + "ORDER BY e.timestamp DESC", AnalyticsEvent.class); query.setParameter("startDate", startDate); query.setParameter("endDate", endDate); List events = query.getResultList(); log.info("Trouvé {} événements pour la période demandée", events.size()); return events; } catch (Exception e) { log.error("Erreur lors de la recherche des événements par période", e); throw new RepositoryException( "Erreur lors de la recherche des événements par période", e); } } /** * Recherche les événements analytiques par type pour une période donnée. * * @param eventType Type d'événement recherché * @param startDate Date de début * @param endDate Date de fin * @return Liste des événements trouvés */ public List findEventsByType(@NotNull String eventType, @NotNull LocalDateTime startDate, @NotNull LocalDateTime endDate) { log.debug("Recherche des événements de type {} entre {} et {}", eventType, startDate, endDate); try { TypedQuery query = entityManager.createQuery( "SELECT e FROM AnalyticsEvent e " + "WHERE e.eventType = :eventType " + "AND e.timestamp BETWEEN :startDate AND :endDate " + "ORDER BY e.timestamp DESC", AnalyticsEvent.class); query.setParameter("eventType", eventType); query.setParameter("startDate", startDate); query.setParameter("endDate", endDate); return query.getResultList(); } catch (Exception e) { throw new RepositoryException( "Erreur lors de la recherche des événements par type", e); } } /** * Enregistre un nouvel événement analytique. * * @param event Événement à sauvegarder * @return Événement sauvegardé */ @Transactional public AnalyticsEvent save(AnalyticsEvent event) { log.debug("Sauvegarde d'un nouvel événement de type: {}", event.getEventType()); try { if (event.getId() == null) { entityManager.persist(event); log.info("Nouvel événement créé avec l'ID: {}", event.getId()); } else { event = entityManager.merge(event); log.info("Événement mis à jour avec l'ID: {}", event.getId()); } return event; } catch (Exception e) { log.error("Erreur lors de la sauvegarde de l'événement", e); throw new RepositoryException("Erreur lors de la sauvegarde de l'événement", e); } } /** * Calcule le nombre d'événements par type pour une période donnée. * * @param startDate Date de début * @param endDate Date de fin * @return Map des compteurs par type d'événement */ public Map getEventCountByType(@NotNull LocalDateTime startDate, @NotNull LocalDateTime endDate) { log.debug("Calcul du nombre d'événements entre {} et {}", startDate, endDate); try { List results = entityManager.createQuery( "SELECT e.eventType, COUNT(e) FROM AnalyticsEvent e " + "WHERE e.timestamp BETWEEN :startDate AND :endDate " + "GROUP BY e.eventType ORDER BY COUNT(e) DESC", Object[].class) .setParameter("startDate", startDate) .setParameter("endDate", endDate) .getResultList(); return results.stream() .collect(Collectors.toMap( row -> (String) row[0], row -> (Long) row[1] )); } catch (Exception e) { throw new RepositoryException( "Erreur lors du calcul du nombre d'événements", e); } } /** * Recherche les événements associés à un contact spécifique. * * @param contactId Identifiant du contact * @return Liste des événements associés */ public List findEventsByContactId(@NotNull String contactId) { log.debug("Recherche des événements pour le contact {}", contactId); try { TypedQuery query = entityManager.createQuery( "SELECT e FROM AnalyticsEvent e " + "WHERE e.contactId = :contactId " + "ORDER BY e.timestamp DESC", AnalyticsEvent.class); query.setParameter("contactId", contactId); return query.getResultList(); } catch (Exception e) { throw new RepositoryException( "Erreur lors de la recherche des événements par contact", e); } } /** * Supprime les événements antérieurs à une date donnée. * * @param retentionDate Date limite de conservation * @return Nombre d'événements supprimés */ @Transactional public int deleteEventsOlderThan(@NotNull LocalDateTime retentionDate) { log.info("Suppression des événements antérieurs à {}", retentionDate); try { int deletedCount = entityManager.createQuery( "DELETE FROM AnalyticsEvent e WHERE e.timestamp < :retentionDate") .setParameter("retentionDate", retentionDate) .executeUpdate(); log.info("{} événements supprimés", deletedCount); return deletedCount; } catch (Exception e) { throw new RepositoryException( "Erreur lors de la suppression des anciens événements", e); } } /** * Recherche un événement par son identifiant. * * @param id Identifiant de l'événement * @return Optional contenant l'événement s'il existe */ public Optional findById(Long id) { log.debug("Recherche de l'événement avec l'ID: {}", id); try { AnalyticsEvent event = entityManager.find(AnalyticsEvent.class, id); return Optional.ofNullable(event); } catch (Exception e) { throw new RepositoryException( "Erreur lors de la recherche de l'événement par ID", e); } } /** * Calcule les statistiques d'événements par environnement. * * @param startDate Date de début * @param endDate Date de fin * @return Map des statistiques par environnement */ public Map getEventStatistics(LocalDateTime startDate, LocalDateTime endDate) { log.debug("Calcul des statistiques entre {} et {}", startDate, endDate); try { List results = entityManager.createQuery( "SELECT e.environment, COUNT(e) FROM AnalyticsEvent e " + "WHERE e.timestamp BETWEEN :startDate AND :endDate " + "GROUP BY e.environment", Object[].class) .setParameter("startDate", startDate) .setParameter("endDate", endDate) .getResultList(); return results.stream() .collect(Collectors.toMap( row -> (String) row[0], row -> (Long) row[1] )); } catch (Exception e) { throw new RepositoryException( "Erreur lors du calcul des statistiques", e); } } }