feat(backend): endpoints inscriptions + feedback événements

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>
This commit is contained in:
dahoud
2026-03-16 20:11:03 +00:00
parent 3be01e28a7
commit a7bcaf9277
6 changed files with 690 additions and 0 deletions

View File

@@ -0,0 +1,117 @@
package dev.lions.unionflow.server.entity;
import jakarta.persistence.*;
import jakarta.validation.constraints.Max;
import jakarta.validation.constraints.Min;
import jakarta.validation.constraints.NotNull;
import java.time.LocalDateTime;
import lombok.*;
/**
* Entité FeedbackEvenement représentant l'évaluation d'un membre sur un événement
*
* @author UnionFlow Team
* @version 1.0
* @since 2026-03-16
*/
@Entity
@Table(
name = "feedbacks_evenement",
indexes = {
@Index(name = "idx_feedback_membre", columnList = "membre_id"),
@Index(name = "idx_feedback_evenement", columnList = "evenement_id"),
@Index(name = "idx_feedback_date", columnList = "date_feedback")
},
uniqueConstraints = {
@UniqueConstraint(
name = "uk_feedback_membre_evenement",
columnNames = {"membre_id", "evenement_id"})
})
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
@EqualsAndHashCode(callSuper = true)
public class FeedbackEvenement extends BaseEntity {
@NotNull
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "membre_id", nullable = false)
private Membre membre;
@NotNull
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "evenement_id", nullable = false)
private Evenement evenement;
@NotNull
@Min(1)
@Max(5)
@Column(name = "note", nullable = false)
private Integer note;
@Column(name = "commentaire", length = 1000)
private String commentaire;
@Builder.Default
@Column(name = "date_feedback", nullable = false)
private LocalDateTime dateFeedback = LocalDateTime.now();
@Column(name = "moderation_statut", length = 20)
@Builder.Default
private String moderationStatut = ModerationStatut.PUBLIE.name();
@Column(name = "raison_moderation", length = 500)
private String raisonModeration;
/** Énumération des statuts de modération */
public enum ModerationStatut {
PUBLIE, // Visible publiquement
EN_ATTENTE, // En attente de modération
REJETE // Rejeté par modération
}
// Méthodes utilitaires
/** Vérifie si le feedback est publié */
public boolean isPublie() {
return ModerationStatut.PUBLIE.name().equals(this.moderationStatut);
}
/** Marque le feedback comme en attente de modération */
public void mettreEnAttente(String raison) {
this.moderationStatut = ModerationStatut.EN_ATTENTE.name();
this.raisonModeration = raison;
setDateModification(LocalDateTime.now());
}
/** Publie le feedback */
public void publier() {
this.moderationStatut = ModerationStatut.PUBLIE.name();
this.raisonModeration = null;
setDateModification(LocalDateTime.now());
}
/** Rejette le feedback */
public void rejeter(String raison) {
this.moderationStatut = ModerationStatut.REJETE.name();
this.raisonModeration = raison;
setDateModification(LocalDateTime.now());
}
@PreUpdate
public void preUpdate() {
super.onUpdate();
}
@Override
public String toString() {
return String.format(
"FeedbackEvenement{id=%s, membre=%s, evenement=%s, note=%d, dateFeedback=%s}",
getId(),
membre != null ? membre.getEmail() : "null",
evenement != null ? evenement.getTitre() : "null",
note,
dateFeedback);
}
}