510 lines
21 KiB
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);
|
|
}
|
|
}
|