Ajoute infrastructure complète pour gérer inscriptions et feedbacks événements.
## Entités
- FeedbackEvenement : note 1-5, commentaire, modération (PUBLIE/EN_ATTENTE/REJETE)
- InscriptionEvenement : déjà existait, utilisation ajoutée
## Repositories
- InscriptionEvenementRepository : findByMembreAndEvenement, findByEvenement, countConfirmees, isMembreInscrit
- FeedbackEvenementRepository : findByMembreAndEvenement, findPubliesByEvenement, calculateAverageNote
## Service (EvenementService)
Inscriptions :
- inscrireEvenement() : vérifie capacité, crée inscription CONFIRMEE
- desinscrireEvenement() : soft delete inscription
- getParticipants() : liste inscriptions confirmées
- getMesInscriptions() : inscriptions du membre connecté
Feedbacks :
- soumetteFeedback() : note 1-5 + commentaire, vérifie participation, événement terminé
- getFeedbacks() : liste feedbacks publiés
- getStatistiquesFeedback() : note moyenne + nombre feedbacks
## REST Endpoints (6 total)
Inscriptions :
- POST /api/evenements/{id}/inscriptions - S'inscrire
- DELETE /api/evenements/{id}/inscriptions - Se désinscrire
- GET /api/evenements/{id}/participants - Liste participants
- GET /api/evenements/mes-inscriptions - Mes inscriptions
Feedbacks :
- POST /api/evenements/{id}/feedback - Soumettre feedback (note+commentaire)
- GET /api/evenements/{id}/feedbacks - Liste feedbacks + stats
## Database
- Migration V7 : table feedbacks_evenement
- Contrainte unique: un feedback par membre/événement
- Index: membre_id, evenement_id, date_feedback, moderation_statut
Débloquer fonctionnalités événements mobile.
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
579 lines
20 KiB
Java
579 lines
20 KiB
Java
package dev.lions.unionflow.server.service;
|
|
|
|
import dev.lions.unionflow.server.entity.Evenement;
|
|
import dev.lions.unionflow.server.entity.FeedbackEvenement;
|
|
import dev.lions.unionflow.server.entity.InscriptionEvenement;
|
|
import dev.lions.unionflow.server.entity.Membre;
|
|
import dev.lions.unionflow.server.repository.EvenementRepository;
|
|
import dev.lions.unionflow.server.repository.FeedbackEvenementRepository;
|
|
import dev.lions.unionflow.server.repository.InscriptionEvenementRepository;
|
|
import dev.lions.unionflow.server.repository.MembreRepository;
|
|
import dev.lions.unionflow.server.repository.OrganisationRepository;
|
|
import dev.lions.unionflow.server.service.KeycloakService;
|
|
import io.quarkus.panache.common.Page;
|
|
import io.quarkus.panache.common.Sort;
|
|
import jakarta.enterprise.context.ApplicationScoped;
|
|
import jakarta.inject.Inject;
|
|
import jakarta.transaction.Transactional;
|
|
import java.time.LocalDateTime;
|
|
import jakarta.ws.rs.NotFoundException;
|
|
import org.hibernate.Hibernate;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.Optional;
|
|
import java.util.UUID;
|
|
import org.jboss.logging.Logger;
|
|
|
|
/**
|
|
* Service métier pour la gestion des événements Version simplifiée pour tester
|
|
* les imports et
|
|
* Lombok
|
|
*
|
|
* @author UnionFlow Team
|
|
* @version 1.0
|
|
* @since 2025-01-15
|
|
*/
|
|
@ApplicationScoped
|
|
public class EvenementService {
|
|
|
|
private static final Logger LOG = Logger.getLogger(EvenementService.class);
|
|
|
|
@Inject
|
|
EvenementRepository evenementRepository;
|
|
|
|
@Inject
|
|
MembreRepository membreRepository;
|
|
|
|
@Inject
|
|
OrganisationRepository organisationRepository;
|
|
|
|
@Inject
|
|
KeycloakService keycloakService;
|
|
|
|
@Inject
|
|
InscriptionEvenementRepository inscriptionRepository;
|
|
|
|
@Inject
|
|
FeedbackEvenementRepository feedbackRepository;
|
|
|
|
/**
|
|
* Crée un nouvel événement
|
|
*
|
|
* @param evenement l'événement à créer
|
|
* @return l'événement créé
|
|
* @throws IllegalArgumentException si les données sont invalides
|
|
*/
|
|
@Transactional
|
|
public Evenement creerEvenement(Evenement evenement) {
|
|
LOG.infof("Création événement: %s", evenement.getTitre());
|
|
|
|
// Validation des données
|
|
validerEvenement(evenement);
|
|
|
|
// Vérifier l'unicité du titre dans l'organisation
|
|
if (evenement.getOrganisation() != null) {
|
|
Optional<Evenement> existant = evenementRepository.findByTitre(evenement.getTitre());
|
|
if (existant.isPresent()
|
|
&& existant.get().getOrganisation().getId().equals(evenement.getOrganisation().getId())) {
|
|
throw new IllegalArgumentException(
|
|
"Un événement avec ce titre existe déjà dans cette organisation");
|
|
}
|
|
}
|
|
|
|
// Métadonnées de création
|
|
evenement.setCreePar(keycloakService.getCurrentUserEmail());
|
|
|
|
// Valeurs par défaut
|
|
if (evenement.getStatut() == null) {
|
|
evenement.setStatut("PLANIFIE");
|
|
}
|
|
if (evenement.getActif() == null) {
|
|
evenement.setActif(true);
|
|
}
|
|
if (evenement.getVisiblePublic() == null) {
|
|
evenement.setVisiblePublic(true);
|
|
}
|
|
if (evenement.getInscriptionRequise() == null) {
|
|
evenement.setInscriptionRequise(true);
|
|
}
|
|
|
|
evenementRepository.persist(evenement);
|
|
|
|
LOG.infof("Événement créé avec succès: ID=%s, Titre=%s", evenement.getId(), evenement.getTitre());
|
|
return evenement;
|
|
}
|
|
|
|
/**
|
|
* Met à jour un événement existant
|
|
*
|
|
* @param id l'UUID de l'événement
|
|
* @param evenementMisAJour les nouvelles données
|
|
* @return l'événement mis à jour
|
|
* @throws IllegalArgumentException si l'événement n'existe pas
|
|
*/
|
|
@Transactional
|
|
public Evenement mettreAJourEvenement(UUID id, Evenement evenementMisAJour) {
|
|
LOG.infof("Mise à jour événement ID: %s", id);
|
|
|
|
Evenement evenementExistant = evenementRepository
|
|
.findByIdOptional(id)
|
|
.orElseThrow(
|
|
() -> new NotFoundException("Événement non trouvé avec l'ID: " + id));
|
|
|
|
// Vérifier les permissions
|
|
if (!peutModifierEvenement(evenementExistant)) {
|
|
throw new SecurityException("Vous n'avez pas les permissions pour modifier cet événement");
|
|
}
|
|
|
|
// Validation des nouvelles données
|
|
validerEvenement(evenementMisAJour);
|
|
|
|
// Mise à jour des champs
|
|
evenementExistant.setTitre(evenementMisAJour.getTitre());
|
|
evenementExistant.setDescription(evenementMisAJour.getDescription());
|
|
evenementExistant.setDateDebut(evenementMisAJour.getDateDebut());
|
|
evenementExistant.setDateFin(evenementMisAJour.getDateFin());
|
|
evenementExistant.setLieu(evenementMisAJour.getLieu());
|
|
evenementExistant.setAdresse(evenementMisAJour.getAdresse());
|
|
evenementExistant.setTypeEvenement(evenementMisAJour.getTypeEvenement());
|
|
evenementExistant.setCapaciteMax(evenementMisAJour.getCapaciteMax());
|
|
evenementExistant.setPrix(evenementMisAJour.getPrix());
|
|
evenementExistant.setInscriptionRequise(evenementMisAJour.getInscriptionRequise());
|
|
evenementExistant.setDateLimiteInscription(evenementMisAJour.getDateLimiteInscription());
|
|
evenementExistant.setInstructionsParticulieres(
|
|
evenementMisAJour.getInstructionsParticulieres());
|
|
evenementExistant.setContactOrganisateur(evenementMisAJour.getContactOrganisateur());
|
|
evenementExistant.setMaterielRequis(evenementMisAJour.getMaterielRequis());
|
|
evenementExistant.setVisiblePublic(evenementMisAJour.getVisiblePublic());
|
|
if (evenementMisAJour.getStatut() != null) {
|
|
evenementExistant.setStatut(evenementMisAJour.getStatut());
|
|
}
|
|
|
|
// Métadonnées de modification
|
|
evenementExistant.setModifiePar(keycloakService.getCurrentUserEmail());
|
|
|
|
evenementRepository.update(evenementExistant);
|
|
|
|
LOG.infof("Événement mis à jour avec succès: ID=%s", id);
|
|
// Initialiser les relations lazy pour éviter LazyInitializationException lors
|
|
// de la sérialisation JSON
|
|
Hibernate.initialize(evenementExistant.getOrganisation());
|
|
Hibernate.initialize(evenementExistant.getOrganisateur());
|
|
return evenementExistant;
|
|
}
|
|
|
|
/** Trouve un événement par ID */
|
|
public Optional<Evenement> trouverParId(UUID id) {
|
|
return evenementRepository.findByIdOptional(id);
|
|
}
|
|
|
|
/** Liste tous les événements actifs avec pagination */
|
|
public List<Evenement> listerEvenementsActifs(Page page, Sort sort) {
|
|
return evenementRepository.findAllActifs(page, sort);
|
|
}
|
|
|
|
/** Liste les événements à venir */
|
|
public List<Evenement> listerEvenementsAVenir(Page page, Sort sort) {
|
|
return evenementRepository.findEvenementsAVenir(page, sort);
|
|
}
|
|
|
|
/** Liste les événements publics */
|
|
public List<Evenement> listerEvenementsPublics(Page page, Sort sort) {
|
|
return evenementRepository.findEvenementsPublics(page, sort);
|
|
}
|
|
|
|
/** Recherche d'événements par terme */
|
|
public List<Evenement> rechercherEvenements(String terme, Page page, Sort sort) {
|
|
return evenementRepository.rechercheAvancee(
|
|
terme, null, null, null, null, null, null, null, null, null, page, sort);
|
|
}
|
|
|
|
/** Liste les événements par type */
|
|
public List<Evenement> listerParType(String type, Page page, Sort sort) {
|
|
return evenementRepository.findByType(type, page, sort);
|
|
}
|
|
|
|
/**
|
|
* Supprime logiquement un événement
|
|
*
|
|
* @param id l'UUID de l'événement à supprimer
|
|
* @throws IllegalArgumentException si l'événement n'existe pas
|
|
*/
|
|
@Transactional
|
|
public void supprimerEvenement(UUID id) {
|
|
LOG.infof("Suppression événement ID: %s", id);
|
|
|
|
Evenement evenement = evenementRepository
|
|
.findByIdOptional(id)
|
|
.orElseThrow(
|
|
() -> new NotFoundException("Événement non trouvé avec l'ID: " + id));
|
|
|
|
// Vérifier les permissions
|
|
if (!peutModifierEvenement(evenement)) {
|
|
throw new SecurityException("Vous n'avez pas les permissions pour supprimer cet événement");
|
|
}
|
|
|
|
// Vérifier s'il y a des inscriptions
|
|
if (evenement.getNombreInscrits() > 0) {
|
|
throw new IllegalStateException("Impossible de supprimer un événement avec des inscriptions");
|
|
}
|
|
|
|
// Suppression logique
|
|
evenement.setActif(false);
|
|
evenement.setModifiePar(keycloakService.getCurrentUserEmail());
|
|
|
|
evenementRepository.update(evenement);
|
|
|
|
LOG.infof("Événement supprimé avec succès: ID=%s", id);
|
|
}
|
|
|
|
/**
|
|
* Change le statut d'un événement
|
|
*
|
|
* @param id l'UUID de l'événement
|
|
* @param nouveauStatut le nouveau statut
|
|
* @return l'événement mis à jour
|
|
*/
|
|
@Transactional
|
|
public Evenement changerStatut(UUID id, String nouveauStatut) {
|
|
LOG.infof("Changement statut événement ID: %s vers %s", id, nouveauStatut);
|
|
|
|
Evenement evenement = evenementRepository
|
|
.findByIdOptional(id)
|
|
.orElseThrow(
|
|
() -> new NotFoundException("Événement non trouvé avec l'ID: " + id));
|
|
|
|
// Vérifier les permissions
|
|
if (!peutModifierEvenement(evenement)) {
|
|
throw new SecurityException("Vous n'avez pas les permissions pour modifier cet événement");
|
|
}
|
|
|
|
// Valider le changement de statut
|
|
validerChangementStatut(evenement.getStatut(), nouveauStatut);
|
|
|
|
evenement.setStatut(nouveauStatut);
|
|
evenement.setModifiePar(keycloakService.getCurrentUserEmail());
|
|
|
|
evenementRepository.update(evenement);
|
|
|
|
LOG.infof("Statut événement changé avec succès: ID=%s, Nouveau statut=%s", id, nouveauStatut);
|
|
return evenement;
|
|
}
|
|
|
|
/**
|
|
* Compte le nombre total d'événements
|
|
*
|
|
* @return le nombre total d'événements
|
|
*/
|
|
public long countEvenements() {
|
|
return evenementRepository.count();
|
|
}
|
|
|
|
/**
|
|
* Compte le nombre d'événements actifs
|
|
*
|
|
* @return le nombre d'événements actifs
|
|
*/
|
|
public long countEvenementsActifs() {
|
|
return evenementRepository.countActifs();
|
|
}
|
|
|
|
/**
|
|
* Obtient les statistiques des événements
|
|
*
|
|
* @return les statistiques sous forme de Map
|
|
*/
|
|
public Map<String, Object> obtenirStatistiques() {
|
|
Map<String, Long> statsBase = evenementRepository.getStatistiques();
|
|
|
|
long total = statsBase.getOrDefault("total", 0L);
|
|
long actifs = statsBase.getOrDefault("actifs", 0L);
|
|
long aVenir = statsBase.getOrDefault("aVenir", 0L);
|
|
long enCours = statsBase.getOrDefault("enCours", 0L);
|
|
|
|
Map<String, Object> result = new java.util.HashMap<>();
|
|
result.put("total", total);
|
|
result.put("actifs", actifs);
|
|
result.put("aVenir", aVenir);
|
|
result.put("enCours", enCours);
|
|
result.put("passes", statsBase.getOrDefault("passes", 0L));
|
|
result.put("publics", statsBase.getOrDefault("publics", 0L));
|
|
result.put("avecInscription", statsBase.getOrDefault("avecInscription", 0L));
|
|
result.put("tauxActivite", total > 0 ? (actifs * 100.0 / total) : 0.0);
|
|
result.put("tauxEvenementsAVenir", total > 0 ? (aVenir * 100.0 / total) : 0.0);
|
|
result.put("tauxEvenementsEnCours", total > 0 ? (enCours * 100.0 / total) : 0.0);
|
|
result.put("timestamp", LocalDateTime.now());
|
|
return result;
|
|
}
|
|
|
|
// Méthodes privées de validation et permissions
|
|
|
|
/** Valide les données d'un événement */
|
|
private void validerEvenement(Evenement evenement) {
|
|
if (evenement.getTitre() == null || evenement.getTitre().trim().isEmpty()) {
|
|
throw new IllegalArgumentException("Le titre de l'événement est obligatoire");
|
|
}
|
|
|
|
if (evenement.getDateDebut() == null) {
|
|
throw new IllegalArgumentException("La date de début est obligatoire");
|
|
}
|
|
|
|
if (evenement.getDateDebut().isBefore(LocalDateTime.now().minusHours(1))) {
|
|
throw new IllegalArgumentException("La date de début ne peut pas être dans le passé");
|
|
}
|
|
|
|
if (evenement.getDateFin() != null
|
|
&& evenement.getDateFin().isBefore(evenement.getDateDebut())) {
|
|
throw new IllegalArgumentException(
|
|
"La date de fin ne peut pas être antérieure à la date de début");
|
|
}
|
|
|
|
if (evenement.getCapaciteMax() != null && evenement.getCapaciteMax() <= 0) {
|
|
throw new IllegalArgumentException("La capacité maximale doit être positive");
|
|
}
|
|
|
|
if (evenement.getPrix() != null
|
|
&& evenement.getPrix().compareTo(java.math.BigDecimal.ZERO) < 0) {
|
|
throw new IllegalArgumentException("Le prix ne peut pas être négatif");
|
|
}
|
|
}
|
|
|
|
/** Valide un changement de statut */
|
|
private void validerChangementStatut(
|
|
String statutActuel, String nouveauStatut) {
|
|
// Règles de transition simplifiées pour la version mobile
|
|
if ("TERMINE".equals(statutActuel) || "ANNULE".equals(statutActuel)) {
|
|
throw new IllegalArgumentException(
|
|
"Impossible de changer le statut d'un événement terminé ou annulé");
|
|
}
|
|
}
|
|
|
|
/** Vérifie les permissions de modification pour l'application mobile */
|
|
private boolean peutModifierEvenement(Evenement evenement) {
|
|
if (keycloakService.hasRole("ADMIN") || keycloakService.hasRole("ORGANISATEUR_EVENEMENT")) {
|
|
return true;
|
|
}
|
|
|
|
String utilisateurActuel = keycloakService.getCurrentUserEmail();
|
|
return utilisateurActuel != null && utilisateurActuel.equals(evenement.getCreePar());
|
|
}
|
|
|
|
/**
|
|
* Indique si l'utilisateur connecté est inscrit à l'événement.
|
|
* Utilisé par l'app mobile pour afficher le statut d'inscription sur la page détail.
|
|
*/
|
|
public boolean isUserInscrit(UUID evenementId) {
|
|
Evenement evenement = evenementRepository.findByIdOptional(evenementId).orElse(null);
|
|
if (evenement == null) {
|
|
return false;
|
|
}
|
|
String email = keycloakService.getCurrentUserEmail();
|
|
if (email == null || email.isBlank()) {
|
|
return false;
|
|
}
|
|
return membreRepository.findByEmail(email)
|
|
.map(m -> evenement.isMemberInscrit(m.getId()))
|
|
.orElse(false);
|
|
}
|
|
|
|
// === GESTION DES INSCRIPTIONS ===
|
|
|
|
/**
|
|
* Inscrit l'utilisateur connecté à un événement
|
|
*
|
|
* @param evenementId UUID de l'événement
|
|
* @return L'inscription créée
|
|
*/
|
|
@Transactional
|
|
public InscriptionEvenement inscrireEvenement(UUID evenementId) {
|
|
String email = keycloakService.getCurrentUserEmail();
|
|
if (email == null || email.isBlank()) {
|
|
throw new IllegalStateException("Utilisateur non authentifié");
|
|
}
|
|
|
|
Membre membre =
|
|
membreRepository
|
|
.findByEmail(email)
|
|
.orElseThrow(() -> new NotFoundException("Membre non trouvé"));
|
|
|
|
Evenement evenement =
|
|
evenementRepository
|
|
.findByIdOptional(evenementId)
|
|
.orElseThrow(() -> new NotFoundException("Événement non trouvé"));
|
|
|
|
// Vérifier si déjà inscrit
|
|
Optional<InscriptionEvenement> existante =
|
|
inscriptionRepository.findByMembreAndEvenement(membre.getId(), evenementId);
|
|
if (existante.isPresent()) {
|
|
throw new IllegalStateException("Vous êtes déjà inscrit à cet événement");
|
|
}
|
|
|
|
// Vérifier capacité
|
|
if (evenement.getCapaciteMax() != null) {
|
|
long nbInscrits = inscriptionRepository.countConfirmeesByEvenement(evenementId);
|
|
if (nbInscrits >= evenement.getCapaciteMax()) {
|
|
throw new IllegalStateException("L'événement est complet");
|
|
}
|
|
}
|
|
|
|
InscriptionEvenement inscription =
|
|
InscriptionEvenement.builder()
|
|
.membre(membre)
|
|
.evenement(evenement)
|
|
.statut(InscriptionEvenement.StatutInscription.CONFIRMEE.name())
|
|
.dateInscription(LocalDateTime.now())
|
|
.build();
|
|
|
|
inscriptionRepository.persist(inscription);
|
|
LOG.infof(
|
|
"Inscription créée: membre=%s, événement=%s", membre.getEmail(), evenement.getTitre());
|
|
return inscription;
|
|
}
|
|
|
|
/**
|
|
* Désinscrit l'utilisateur connecté d'un événement
|
|
*
|
|
* @param evenementId UUID de l'événement
|
|
*/
|
|
@Transactional
|
|
public void desinscrireEvenement(UUID evenementId) {
|
|
String email = keycloakService.getCurrentUserEmail();
|
|
if (email == null || email.isBlank()) {
|
|
throw new IllegalStateException("Utilisateur non authentifié");
|
|
}
|
|
|
|
Membre membre =
|
|
membreRepository
|
|
.findByEmail(email)
|
|
.orElseThrow(() -> new NotFoundException("Membre non trouvé"));
|
|
|
|
InscriptionEvenement inscription =
|
|
inscriptionRepository
|
|
.findByMembreAndEvenement(membre.getId(), evenementId)
|
|
.orElseThrow(() -> new NotFoundException("Inscription non trouvée"));
|
|
|
|
inscriptionRepository.softDelete(inscription);
|
|
LOG.infof("Désinscription: membre=%s, événement=%s", membre.getEmail(), evenementId);
|
|
}
|
|
|
|
/**
|
|
* Liste les participants d'un événement
|
|
*
|
|
* @param evenementId UUID de l'événement
|
|
* @return Liste des inscriptions confirmées
|
|
*/
|
|
public List<InscriptionEvenement> getParticipants(UUID evenementId) {
|
|
return inscriptionRepository.findConfirmeesByEvenement(evenementId);
|
|
}
|
|
|
|
/**
|
|
* Liste les inscriptions de l'utilisateur connecté
|
|
*
|
|
* @return Liste des inscriptions du membre
|
|
*/
|
|
public List<InscriptionEvenement> getMesInscriptions() {
|
|
String email = keycloakService.getCurrentUserEmail();
|
|
if (email == null || email.isBlank()) {
|
|
throw new IllegalStateException("Utilisateur non authentifié");
|
|
}
|
|
|
|
Membre membre =
|
|
membreRepository
|
|
.findByEmail(email)
|
|
.orElseThrow(() -> new NotFoundException("Membre non trouvé"));
|
|
|
|
return inscriptionRepository.findByMembre(membre.getId());
|
|
}
|
|
|
|
// === GESTION DES FEEDBACKS ===
|
|
|
|
/**
|
|
* Soumet un feedback pour un événement
|
|
*
|
|
* @param evenementId UUID de l'événement
|
|
* @param note Note de 1 à 5
|
|
* @param commentaire Commentaire optionnel
|
|
* @return Le feedback créé
|
|
*/
|
|
@Transactional
|
|
public FeedbackEvenement soumetteFeedback(
|
|
UUID evenementId, Integer note, String commentaire) {
|
|
String email = keycloakService.getCurrentUserEmail();
|
|
if (email == null || email.isBlank()) {
|
|
throw new IllegalStateException("Utilisateur non authentifié");
|
|
}
|
|
|
|
Membre membre =
|
|
membreRepository
|
|
.findByEmail(email)
|
|
.orElseThrow(() -> new NotFoundException("Membre non trouvé"));
|
|
|
|
Evenement evenement =
|
|
evenementRepository
|
|
.findByIdOptional(evenementId)
|
|
.orElseThrow(() -> new NotFoundException("Événement non trouvé"));
|
|
|
|
// Vérifier si déjà soumis
|
|
Optional<FeedbackEvenement> existant =
|
|
feedbackRepository.findByMembreAndEvenement(membre.getId(), evenementId);
|
|
if (existant.isPresent()) {
|
|
throw new IllegalStateException("Vous avez déjà soumis un feedback pour cet événement");
|
|
}
|
|
|
|
// Vérifier que le membre était inscrit
|
|
boolean etaitInscrit =
|
|
inscriptionRepository.isMembreInscrit(membre.getId(), evenementId);
|
|
if (!etaitInscrit) {
|
|
throw new IllegalStateException(
|
|
"Seuls les participants peuvent donner un feedback");
|
|
}
|
|
|
|
// Vérifier que l'événement est terminé
|
|
if (evenement.getDateFin() == null || evenement.getDateFin().isAfter(LocalDateTime.now())) {
|
|
throw new IllegalStateException(
|
|
"Vous ne pouvez donner un feedback qu'après la fin de l'événement");
|
|
}
|
|
|
|
FeedbackEvenement feedback =
|
|
FeedbackEvenement.builder()
|
|
.membre(membre)
|
|
.evenement(evenement)
|
|
.note(note)
|
|
.commentaire(commentaire)
|
|
.dateFeedback(LocalDateTime.now())
|
|
.moderationStatut(FeedbackEvenement.ModerationStatut.PUBLIE.name())
|
|
.build();
|
|
|
|
feedbackRepository.persist(feedback);
|
|
LOG.infof(
|
|
"Feedback créé: membre=%s, événement=%s, note=%d",
|
|
membre.getEmail(), evenement.getTitre(), note);
|
|
return feedback;
|
|
}
|
|
|
|
/**
|
|
* Liste les feedbacks d'un événement
|
|
*
|
|
* @param evenementId UUID de l'événement
|
|
* @return Liste des feedbacks publiés
|
|
*/
|
|
public List<FeedbackEvenement> getFeedbacks(UUID evenementId) {
|
|
return feedbackRepository.findPubliesByEvenement(evenementId);
|
|
}
|
|
|
|
/**
|
|
* Calcule les statistiques de feedback pour un événement
|
|
*
|
|
* @param evenementId UUID de l'événement
|
|
* @return Map contenant noteMovenne et nombreFeedbacks
|
|
*/
|
|
public Map<String, Object> getStatistiquesFeedback(UUID evenementId) {
|
|
Double noteMoyenne = feedbackRepository.calculateAverageNote(evenementId);
|
|
long nombreFeedbacks = feedbackRepository.countPubliesByEvenement(evenementId);
|
|
|
|
return Map.of(
|
|
"noteMoyenne", noteMoyenne,
|
|
"nombreFeedbacks", nombreFeedbacks);
|
|
}
|
|
}
|