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

@@ -3,11 +3,14 @@ package dev.lions.unionflow.server.resource;
import dev.lions.unionflow.server.api.dto.common.PagedResponse;
import dev.lions.unionflow.server.dto.EvenementMobileDTO;
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.service.EvenementService;
import io.quarkus.panache.common.Page;
import io.quarkus.panache.common.Sort;
import jakarta.annotation.security.RolesAllowed;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import jakarta.validation.constraints.Min;
import jakarta.ws.rs.*;
@@ -286,4 +289,112 @@ public class EvenementResource {
boolean inscrit = evenementService.isUserInscrit(id);
return Response.ok(Map.of("inscrit", inscrit)).build();
}
// === GESTION DES INSCRIPTIONS ===
/** S'inscrire à un événement */
@POST
@Path("/{id}/inscriptions")
@Operation(summary = "S'inscrire à un événement")
@APIResponse(responseCode = "201", description = "Inscription créée")
@APIResponse(responseCode = "400", description = "Déjà inscrit ou événement complet")
@APIResponse(responseCode = "404", description = "Événement non trouvé")
@RolesAllowed({ "ADMIN", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE", "USER" })
@Transactional
public Response inscrireEvenement(@PathParam("id") UUID evenementId) {
try {
InscriptionEvenement inscription = evenementService.inscrireEvenement(evenementId);
return Response.status(Response.Status.CREATED).entity(inscription).build();
} catch (IllegalStateException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(Map.of("error", e.getMessage()))
.build();
}
}
/** Se désinscrire d'un événement */
@DELETE
@Path("/{id}/inscriptions")
@Operation(summary = "Se désinscrire d'un événement")
@APIResponse(responseCode = "204", description = "Désinscription effectuée")
@APIResponse(responseCode = "404", description = "Inscription non trouvée")
@RolesAllowed({ "ADMIN", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE", "USER" })
@Transactional
public Response desinscrireEvenement(@PathParam("id") UUID evenementId) {
evenementService.desinscrireEvenement(evenementId);
return Response.noContent().build();
}
/** Liste des participants d'un événement */
@GET
@Path("/{id}/participants")
@Operation(summary = "Liste des participants")
@APIResponse(responseCode = "200", description = "Liste des participants")
@RolesAllowed({ "ADMIN", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE" })
public Response getParticipants(@PathParam("id") UUID evenementId) {
List<InscriptionEvenement> participants = evenementService.getParticipants(evenementId);
return Response.ok(participants).build();
}
/** Mes inscriptions */
@GET
@Path("/mes-inscriptions")
@Operation(summary = "Mes inscriptions aux événements")
@APIResponse(responseCode = "200", description = "Liste de mes inscriptions")
@RolesAllowed({ "ADMIN", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE", "USER" })
public Response getMesInscriptions() {
List<InscriptionEvenement> inscriptions = evenementService.getMesInscriptions();
return Response.ok(inscriptions).build();
}
// === GESTION DES FEEDBACKS ===
/** Soumettre un feedback */
@POST
@Path("/{id}/feedback")
@Operation(summary = "Soumettre un feedback sur l'événement")
@APIResponse(responseCode = "201", description = "Feedback créé")
@APIResponse(responseCode = "400", description = "Données invalides ou feedback déjà soumis")
@APIResponse(responseCode = "404", description = "Événement non trouvé")
@RolesAllowed({ "ADMIN", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE", "USER" })
@Transactional
public Response soumetteFeedback(
@PathParam("id") UUID evenementId, Map<String, Object> requestBody) {
Integer note = (Integer) requestBody.get("note");
String commentaire = (String) requestBody.get("commentaire");
if (note == null || note < 1 || note > 5) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(Map.of("error", "La note doit être entre 1 et 5"))
.build();
}
try {
FeedbackEvenement feedback =
evenementService.soumetteFeedback(evenementId, note, commentaire);
return Response.status(Response.Status.CREATED).entity(feedback).build();
} catch (IllegalStateException e) {
return Response.status(Response.Status.BAD_REQUEST)
.entity(Map.of("error", e.getMessage()))
.build();
}
}
/** Liste des feedbacks d'un événement */
@GET
@Path("/{id}/feedbacks")
@Operation(summary = "Liste des feedbacks de l'événement")
@APIResponse(responseCode = "200", description = "Liste des feedbacks")
public Response getFeedbacks(@PathParam("id") UUID evenementId) {
List<FeedbackEvenement> feedbacks = evenementService.getFeedbacks(evenementId);
Map<String, Object> stats = evenementService.getStatistiquesFeedback(evenementId);
return Response.ok(
Map.of(
"feedbacks", feedbacks,
"noteMoyenne", stats.get("noteMoyenne"),
"nombreFeedbacks", stats.get("nombreFeedbacks")))
.build();
}
}