Files
mic-after-work-server-impl-…/src/main/java/com/lions/dev/service/EventService.java

510 lines
21 KiB
Java

package com.lions.dev.service;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import jakarta.transaction.Transactional;
import com.lions.dev.dto.request.events.EventCreateRequestDTO;
import com.lions.dev.entity.events.Events;
import com.lions.dev.entity.friends.Friendship;
import com.lions.dev.entity.users.Users;
import com.lions.dev.exception.EventNotFoundException;
import com.lions.dev.exception.UserNotFoundException;
import com.lions.dev.repository.EventsRepository;
import com.lions.dev.repository.FriendshipRepository;
import com.lions.dev.repository.UsersRepository;
import com.lions.dev.repository.EstablishmentRepository;
import com.lions.dev.dto.events.NotificationEvent;
import org.eclipse.microprofile.reactive.messaging.Channel;
import org.eclipse.microprofile.reactive.messaging.Emitter;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import java.time.LocalDateTime;
import java.util.List;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* Service de gestion des événements.
*
* Version 2.0 - Architecture refactorée avec nommage standardisé.
* Conforme à l'architecture de données AfterWork v2.0 (Ultra-Compétitive).
*
* Ce service contient la logique métier pour la création, récupération, mise à jour et suppression des événements.
* Chaque méthode est loguée pour assurer une traçabilité exhaustive des actions effectuées.
*/
@ApplicationScoped
public class EventService {
@Inject
EventsRepository eventsRepository;
@Inject
FriendshipRepository friendshipRepository;
@Inject
UsersRepository usersRepository;
@Inject
EstablishmentRepository establishmentRepository; // v2.0
@Inject
NotificationService notificationService;
@Inject
@Channel("notifications")
Emitter<NotificationEvent> notificationEmitter; // v2.0 - Publie dans Kafka
private static final Logger logger = LoggerFactory.getLogger(EventService.class);
/**
* Crée un nouvel événement dans le système (v2.0).
*
* @param eventCreateRequestDTO Le DTO contenant les informations de l'événement à créer.
* @param creator L'utilisateur créateur de l'événement.
* @return L'événement créé.
*/
public Events createEvent(EventCreateRequestDTO eventCreateRequestDTO, Users creator) {
// Initialisation de l'entité Event avec les détails fournis
Events event = new Events();
event.setTitle(eventCreateRequestDTO.getTitle());
event.setDescription(eventCreateRequestDTO.getDescription());
event.setStartDate(eventCreateRequestDTO.getStartDate());
event.setEndDate(eventCreateRequestDTO.getEndDate());
// v2.0 - Établissement au lieu de location
if (eventCreateRequestDTO.getEstablishmentId() != null) {
com.lions.dev.entity.establishment.Establishment establishment =
establishmentRepository.findById(eventCreateRequestDTO.getEstablishmentId());
if (establishment != null) {
event.setEstablishment(establishment);
} else {
logger.warn("[WARN] Établissement non trouvé avec l'ID : {}", eventCreateRequestDTO.getEstablishmentId());
}
}
event.setCategory(eventCreateRequestDTO.getCategory());
event.setLink(eventCreateRequestDTO.getLink());
event.setImageUrl(eventCreateRequestDTO.getImageUrl());
event.setMaxParticipants(eventCreateRequestDTO.getMaxParticipants());
event.setTags(eventCreateRequestDTO.getTags());
event.setOrganizer(eventCreateRequestDTO.getOrganizer());
event.setParticipationFee(eventCreateRequestDTO.getParticipationFee());
// v2.0 - Nouveaux champs
if (eventCreateRequestDTO.getIsPrivate() != null) {
event.setIsPrivate(eventCreateRequestDTO.getIsPrivate());
}
if (eventCreateRequestDTO.getWaitlistEnabled() != null) {
event.setWaitlistEnabled(eventCreateRequestDTO.getWaitlistEnabled());
}
event.setPrivacyRules(eventCreateRequestDTO.getPrivacyRules());
event.setTransportInfo(eventCreateRequestDTO.getTransportInfo());
event.setAccommodationInfo(eventCreateRequestDTO.getAccommodationInfo());
event.setAccessibilityInfo(eventCreateRequestDTO.getAccessibilityInfo());
event.setParkingInfo(eventCreateRequestDTO.getParkingInfo());
event.setSecurityProtocol(eventCreateRequestDTO.getSecurityProtocol());
event.setCreator(creator);
event.setStatus("OPEN"); // v2.0 - Statut standardisé
// Persiste l'événement dans la base de données
eventsRepository.persist(event);
logger.info("[logger] Événement créé avec succès : {}", event.getTitle());
// Créer des notifications pour tous les amis (v2.0 - avec Kafka)
try {
List<Friendship> friendships = friendshipRepository.findFriendsByUser(creator, 0, Integer.MAX_VALUE);
// v2.0 - Utiliser les nouveaux noms de champs
String creatorName = creator.getFirstName() + " " + creator.getLastName();
for (Friendship friendship : friendships) {
Users friend = friendship.getUser().equals(creator)
? friendship.getFriend()
: friendship.getUser();
String notificationTitle = "Nouvel événement de " + creatorName;
String notificationMessage = creatorName + " a créé un nouvel événement : " + event.getTitle();
// Créer notification en base
notificationService.createNotification(
notificationTitle,
notificationMessage,
"event",
friend.getId(),
event.getId()
);
// TEMPS RÉEL: Publier dans Kafka (v2.0)
try {
java.util.Map<String, Object> notificationData = new java.util.HashMap<>();
notificationData.put("eventId", event.getId().toString());
notificationData.put("eventTitle", event.getTitle());
notificationData.put("creatorId", creator.getId().toString());
notificationData.put("creatorName", creatorName);
notificationData.put("startDate", event.getStartDate().toString());
NotificationEvent kafkaEvent = new NotificationEvent(
friend.getId().toString(), // userId destinataire
"event_created",
notificationData
);
notificationEmitter.send(kafkaEvent);
logger.debug("[logger] Événement event_created publié dans Kafka pour: {}", friend.getId());
} catch (Exception kafkaEx) {
logger.error("[ERROR] Erreur publication Kafka pour événement {}", event.getId(), kafkaEx);
// Ne pas bloquer si Kafka échoue
}
}
logger.info("[logger] Notifications créées pour {} ami(s)", friendships.size());
} catch (Exception e) {
logger.error("[ERROR] Erreur lors de la création des notifications : {}", e.getMessage());
}
return event;
}
/**
* Récupère un événement par son ID.
*
* @param id L'ID de l'événement.
* @return L'événement trouvé.
* @throws EventNotFoundException Si l'événement n'est pas trouvé.
*/
public Events getEventById(UUID id) {
logger.info("[logger] Tentative de récupération de l'événement avec l'ID : {}", id);
Events event = eventsRepository.findById(id);
if (event == null) {
logger.error("[ERROR] Événement non trouvé avec l'ID : {}", id);
throw new EventNotFoundException(id);
}
logger.info("[logger] Événement trouvé avec l'ID : {}", id);
return event;
}
/**
* Récupère tous les événements après une date donnée.
*
* @param startDate La date de début pour filtrer les événements.
* @return Une liste d'événements après cette date.
*/
public List<Events> getEventsAfterDate(LocalDateTime startDate) {
logger.info("[logger] Récupération des événements après la date : {}", startDate);
List<Events> events = eventsRepository.findEventsAfterDate(startDate);
logger.info("[logger] Nombre d'événements trouvés après la date {} : {}", startDate, events.size());
return events;
}
/**
* Supprime un événement par son ID.
*
* @param id L'ID de l'événement à supprimer.
* @param userId L'ID de l'utilisateur qui tente de supprimer l'événement.
* @return true si l'événement a été supprimé, false sinon.
* @throws EventNotFoundException Si l'événement n'est pas trouvé.
* @throws SecurityException Si l'utilisateur n'est pas le créateur de l'événement.
*/
@Transactional
public boolean deleteEvent(UUID id, UUID userId) {
logger.info("[logger] Tentative de suppression de l'événement avec l'ID : {} par l'utilisateur : {}", id, userId);
Events event = eventsRepository.findById(id);
if (event == null) {
logger.warn("[logger] Échec de la suppression : événement avec l'ID {} introuvable.", id);
throw new EventNotFoundException(id);
}
// Vérifier que l'utilisateur est le créateur
if (!canModifyEvent(event, userId)) {
logger.error("[ERROR] L'utilisateur {} n'a pas les permissions pour supprimer l'événement {}", userId, id);
throw new SecurityException("Vous n'avez pas les permissions pour supprimer cet événement");
}
boolean deleted = eventsRepository.deleteById(id);
if (deleted) {
logger.info("[logger] Événement avec l'ID {} supprimé avec succès.", id);
}
return deleted;
}
/**
* Vérifie si un utilisateur peut modifier un événement.
*
* @param event L'événement à vérifier.
* @param userId L'ID de l'utilisateur.
* @return true si l'utilisateur peut modifier l'événement, false sinon.
*/
public boolean canModifyEvent(Events event, UUID userId) {
if (event == null || event.getCreator() == null) {
return false;
}
return event.getCreator().getId().equals(userId);
}
/**
* Met à jour un événement dans le système.
*
* @param event L'événement contenant les détails mis à jour.
* @param userId L'ID de l'utilisateur qui tente de mettre à jour l'événement.
* @return L'événement mis à jour.
* @throws EventNotFoundException Si l'événement n'est pas trouvé.
* @throws SecurityException Si l'utilisateur n'est pas le créateur de l'événement.
*/
@Transactional
public Events updateEvent(Events event, UUID userId) {
logger.info("[logger] Tentative de mise à jour de l'événement avec l'ID : {} par l'utilisateur : {}", event.getId(), userId);
Events existingEvent = eventsRepository.findById(event.getId());
if (existingEvent == null) {
logger.error("[ERROR] Événement non trouvé avec l'ID : {}", event.getId());
throw new EventNotFoundException(event.getId());
}
// Vérifier que l'utilisateur est le créateur
if (!canModifyEvent(existingEvent, userId)) {
logger.error("[ERROR] L'utilisateur {} n'a pas les permissions pour modifier l'événement {}", userId, event.getId());
throw new SecurityException("Vous n'avez pas les permissions pour modifier cet événement");
}
// v2.0 - Mettre à jour les détails de l'événement
existingEvent.setTitle(event.getTitle());
existingEvent.setDescription(event.getDescription());
existingEvent.setStartDate(event.getStartDate());
existingEvent.setEndDate(event.getEndDate());
// v2.0 - Établissement au lieu de location
if (event.getEstablishment() != null) {
existingEvent.setEstablishment(event.getEstablishment());
}
existingEvent.setCategory(event.getCategory());
existingEvent.setLink(event.getLink());
existingEvent.setImageUrl(event.getImageUrl());
existingEvent.setMaxParticipants(event.getMaxParticipants());
existingEvent.setTags(event.getTags());
existingEvent.setOrganizer(event.getOrganizer());
existingEvent.setParticipationFee(event.getParticipationFee());
// v2.0 - Nouveaux champs
if (event.getIsPrivate() != null) {
existingEvent.setIsPrivate(event.getIsPrivate());
}
if (event.getWaitlistEnabled() != null) {
existingEvent.setWaitlistEnabled(event.getWaitlistEnabled());
}
existingEvent.setPrivacyRules(event.getPrivacyRules());
existingEvent.setTransportInfo(event.getTransportInfo());
existingEvent.setAccommodationInfo(event.getAccommodationInfo());
existingEvent.setAccessibilityInfo(event.getAccessibilityInfo());
existingEvent.setParkingInfo(event.getParkingInfo());
existingEvent.setSecurityProtocol(event.getSecurityProtocol());
existingEvent.setStatus(event.getStatus());
// Persiste les modifications dans la base de données
eventsRepository.persist(existingEvent);
logger.info("[logger] Événement mis à jour avec succès : {}", existingEvent.getTitle());
return existingEvent;
}
/**
* Récupère les événements par catégorie.
*
* @param category La catégorie des événements.
* @return La liste des événements dans cette catégorie.
*/
public List<Events> findEventsByCategory(String category) {
logger.info("[logger] Récupération des événements dans la catégorie : {}", category);
List<Events> events = eventsRepository.find("category", category).list();
logger.info("[logger] Nombre d'événements trouvés dans la catégorie '{}' : {}", category, events.size());
return events;
}
/**
* Recherche des événements par mot-clé dans le titre ou la description.
*
* @param keyword Le mot-clé à rechercher.
* @return La liste des événements correspondant au mot-clé.
*/
public List<Events> searchEvents(String keyword) {
logger.info("[logger] Recherche d'événements avec le mot-clé : {}", keyword);
List<Events> events = eventsRepository.find("title like ?1 or description like ?1", "%" + keyword + "%").list();
logger.info("[logger] Nombre d'événements trouvés pour le mot-clé '{}' : {}", keyword, events.size());
return events;
}
/**
* Récupère les événements auxquels un utilisateur participe.
*
* @param user L'utilisateur pour lequel récupérer les événements.
* @return La liste des événements auxquels l'utilisateur participe.
*/
public List<Events> findEventsByUser(Users user) {
return findEventsByUser(user, 0, Integer.MAX_VALUE);
}
/**
* Récupère les événements auxquels un utilisateur participe avec pagination.
*
* @param user L'utilisateur pour lequel récupérer les événements.
* @param page Le numéro de la page (0-indexé)
* @param size La taille de la page
* @return La liste paginée des événements auxquels l'utilisateur participe.
*/
public List<Events> findEventsByUser(Users user, int page, int size) {
logger.info("[logger] Récupération des événements pour l'utilisateur avec l'ID : {} (page: {}, size: {})", user.getId(), page, size);
List<Events> events = eventsRepository.find("participants", user)
.page(page, size)
.list();
logger.info("[logger] Nombre d'événements pour l'utilisateur avec l'ID {} : {}", user.getId(), events.size());
return events;
}
/**
* Récupère les événements par statut.
*
* @param status Le statut des événements (en cours, fermé, etc.).
* @return La liste des événements ayant ce statut.
*/
public List<Events> findEventsByStatus(String status) {
logger.info("[logger] Récupération des événements avec le statut : {}", status);
List<Events> events = eventsRepository.find("status", status).list();
logger.info("[logger] Nombre d'événements avec le statut '{}' : {}", status, events.size());
return events;
}
/**
* Récupère les événements qui se déroulent entre deux dates spécifiques.
*
* @param startDate La date de début.
* @param endDate La date de fin.
* @return La liste des événements entre ces deux dates.
*/
public List<Events> findEventsBetweenDates(LocalDateTime startDate, LocalDateTime endDate) {
logger.info("[logger] Récupération des événements entre les dates : {} et {}", startDate, endDate);
// Vérifie la validité des dates fournies
if (startDate == null || endDate == null || endDate.isBefore(startDate)) {
logger.error("[ERROR] Dates invalides fournies : startDate={}, endDate={}", startDate, endDate);
throw new IllegalArgumentException("Les dates sont invalides ou mal formatées.");
}
List<Events> events = eventsRepository.findEventsBetweenDates(startDate, endDate);
logger.info("[logger] Nombre d'événements trouvés entre les dates : {}", events.size());
return events;
}
/**
* Récupère les événements futurs.
*
* @return Une liste d'événements à venir.
*/
public List<Events> findUpcomingEvents() {
logger.info("[logger] Récupération des événements futurs.");
LocalDateTime now = LocalDateTime.now();
List<Events> events = eventsRepository.find("startDate > ?1", now).list();
logger.info("[logger] Nombre d'événements futurs trouvés : " + events.size());
return events;
}
/**
* Récupère les événements passés.
*
* @return Une liste d'événements passés.
*/
public List<Events> findPastEvents() {
logger.info("[logger] Récupération des événements passés.");
LocalDateTime now = LocalDateTime.now();
List<Events> events = eventsRepository.find("endDate < ?1", now).list();
logger.info("[logger] Nombre d'événements passés trouvés : " + events.size());
return events;
}
/**
* Récupère les événements par localisation (ville ou adresse de l'établissement).
* v2.0 : plus de colonne location ; recherche sur establishment.address et establishment.city.
*
* @param location Fragment de localisation (ville ou adresse).
* @return La liste des événements dont l'établissement matche.
*/
public List<Events> findEventsByLocation(String location) {
logger.info("[logger] Récupération des événements pour la localisation : " + location);
List<Events> events = eventsRepository.findEventsByEstablishmentLocation(location);
logger.info("[logger] Nombre d'événements trouvés pour la localisation '" + location + "' : " + events.size());
return events;
}
/**
* Récupère les événements populaires en fonction du nombre de participants.
*
* @return Une liste d'événements populaires.
*/
public List<Events> findPopularEvents() {
logger.info("[logger] Récupération des événements populaires.");
List<Events> events = eventsRepository.listAll().stream()
.sorted((e1, e2) -> Integer.compare(e2.getNumberOfParticipants(), e1.getNumberOfParticipants()))
.limit(10)
.toList();
logger.info("[logger] Nombre d'événements populaires trouvés : " + events.size());
return events;
}
/**
* Recommande des événements pour un utilisateur spécifique.
*
* @param user L'utilisateur pour lequel recommander des événements.
* @return La liste des événements recommandés.
*/
public List<Events> recommendEventsForUser(Users user) {
logger.info("[logger] Recommandation d'événements pour l'utilisateur : " + user.getEmail());
// v2.0 - Utiliser preferences pour preferredCategory
String preferredCategory = user.getPreferredCategory(); // Méthode qui utilise preferences JSONB
List<Events> events = preferredCategory != null
? eventsRepository.find("category", preferredCategory).list()
: eventsRepository.findAll().list();
logger.info("[logger] Nombre d'événements recommandés pour l'utilisateur : " + events.size());
return events;
}
/**
* Récupère les événements de l'utilisateur et de ses amis (relations d'amitié acceptées).
*
* @param userId L'ID de l'utilisateur
* @param page Le numéro de la page (0-indexé)
* @param size La taille de la page
* @return Liste paginée des événements de l'utilisateur et de ses amis
* @throws UserNotFoundException Si l'utilisateur n'existe pas
*/
public List<Events> getEventsByFriends(UUID userId, int page, int size) {
logger.info("[logger] Récupération des événements des amis pour l'utilisateur ID : " + userId);
Users user = usersRepository.findById(userId);
if (user == null) {
logger.error("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
}
// Récupérer toutes les relations d'amitié acceptées
List<Friendship> friendships = friendshipRepository.findFriendsByUser(user, 0, Integer.MAX_VALUE);
// Extraire les IDs des amis
List<UUID> friendIds = friendships.stream()
.map(friendship -> {
// L'ami est soit dans 'user' soit dans 'friend', selon qui a initié la relation
return friendship.getUser().equals(user)
? friendship.getFriend().getId()
: friendship.getUser().getId();
})
.distinct()
.collect(Collectors.toList());
logger.info("[logger] " + friendIds.size() + " ami(s) trouvé(s) pour l'utilisateur ID : " + userId);
// Récupérer les événements de l'utilisateur et de ses amis
return eventsRepository.findEventsByFriends(userId, friendIds, page, size);
}
}