Configure Maven repository for unionflow-server-api dependency
This commit is contained in:
@@ -0,0 +1,705 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.finance.AdhesionDTO;
|
||||
import dev.lions.unionflow.server.service.AdhesionService;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Content;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
/**
|
||||
* Resource REST pour la gestion des adhésions
|
||||
* Expose les endpoints API pour les opérations CRUD sur les adhésions
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-17
|
||||
*/
|
||||
@Path("/api/adhesions")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Adhésions", description = "Gestion des demandes d'adhésion des membres")
|
||||
@Slf4j
|
||||
@RolesAllowed({"ADMIN", "MEMBRE", "USER"})
|
||||
public class AdhesionResource {
|
||||
|
||||
@Inject AdhesionService adhesionService;
|
||||
|
||||
/** Récupère toutes les adhésions avec pagination */
|
||||
@GET
|
||||
@Operation(
|
||||
summary = "Lister toutes les adhésions",
|
||||
description = "Récupère la liste paginée de toutes les adhésions")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "Liste des adhésions récupérée avec succès",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = AdhesionDTO.class))),
|
||||
@APIResponse(responseCode = "400", description = "Paramètres de pagination invalides"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getAllAdhesions(
|
||||
@Parameter(description = "Numéro de page (0-based)", example = "0")
|
||||
@QueryParam("page")
|
||||
@DefaultValue("0")
|
||||
@Min(0)
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page", example = "20")
|
||||
@QueryParam("size")
|
||||
@DefaultValue("20")
|
||||
@Min(1)
|
||||
int size) {
|
||||
|
||||
try {
|
||||
log.info("GET /api/adhesions - page: {}, size: {}", page, size);
|
||||
|
||||
List<AdhesionDTO> adhesions = adhesionService.getAllAdhesions(page, size);
|
||||
|
||||
log.info("Récupération réussie de {} adhésions", adhesions.size());
|
||||
return Response.ok(adhesions).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des adhésions", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error", "Erreur lors de la récupération des adhésions", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère une adhésion par son ID */
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(
|
||||
summary = "Récupérer une adhésion par ID",
|
||||
description = "Récupère les détails d'une adhésion spécifique")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "Adhésion trouvée",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = AdhesionDTO.class))),
|
||||
@APIResponse(responseCode = "404", description = "Adhésion non trouvée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getAdhesionById(
|
||||
@Parameter(description = "Identifiant de l'adhésion", required = true)
|
||||
@PathParam("id")
|
||||
@NotNull
|
||||
UUID id) {
|
||||
|
||||
try {
|
||||
log.info("GET /api/adhesions/{}", id);
|
||||
|
||||
AdhesionDTO adhesion = adhesionService.getAdhesionById(id);
|
||||
|
||||
log.info("Adhésion récupérée avec succès - ID: {}", id);
|
||||
return Response.ok(adhesion).build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Adhésion non trouvée - ID: {}", id);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Adhésion non trouvée", "id", id))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération de l'adhésion - ID: " + id, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error", "Erreur lors de la récupération de l'adhésion", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère une adhésion par son numéro de référence */
|
||||
@GET
|
||||
@Path("/reference/{numeroReference}")
|
||||
@Operation(
|
||||
summary = "Récupérer une adhésion par référence",
|
||||
description = "Récupère une adhésion par son numéro de référence unique")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Adhésion trouvée"),
|
||||
@APIResponse(responseCode = "404", description = "Adhésion non trouvée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getAdhesionByReference(
|
||||
@Parameter(description = "Numéro de référence de l'adhésion", required = true)
|
||||
@PathParam("numeroReference")
|
||||
@NotNull
|
||||
String numeroReference) {
|
||||
|
||||
try {
|
||||
log.info("GET /api/adhesions/reference/{}", numeroReference);
|
||||
|
||||
AdhesionDTO adhesion = adhesionService.getAdhesionByReference(numeroReference);
|
||||
|
||||
log.info("Adhésion récupérée avec succès - Référence: {}", numeroReference);
|
||||
return Response.ok(adhesion).build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Adhésion non trouvée - Référence: {}", numeroReference);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Adhésion non trouvée", "reference", numeroReference))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error(
|
||||
"Erreur lors de la récupération de l'adhésion - Référence: " + numeroReference, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error", "Erreur lors de la récupération de l'adhésion", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Crée une nouvelle adhésion */
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Operation(
|
||||
summary = "Créer une nouvelle adhésion",
|
||||
description = "Crée une nouvelle demande d'adhésion pour un membre")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "201",
|
||||
description = "Adhésion créée avec succès",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = AdhesionDTO.class))),
|
||||
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||
@APIResponse(responseCode = "404", description = "Membre ou organisation non trouvé"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response createAdhesion(
|
||||
@Parameter(description = "Données de l'adhésion à créer", required = true) @Valid
|
||||
AdhesionDTO adhesionDTO) {
|
||||
|
||||
try {
|
||||
log.info(
|
||||
"POST /api/adhesions - Création adhésion pour membre: {} et organisation: {}",
|
||||
adhesionDTO.getMembreId(),
|
||||
adhesionDTO.getOrganisationId());
|
||||
|
||||
AdhesionDTO nouvelleAdhesion = adhesionService.createAdhesion(adhesionDTO);
|
||||
|
||||
log.info(
|
||||
"Adhésion créée avec succès - ID: {}, Référence: {}",
|
||||
nouvelleAdhesion.getId(),
|
||||
nouvelleAdhesion.getNumeroReference());
|
||||
|
||||
return Response.status(Response.Status.CREATED).entity(nouvelleAdhesion).build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Membre ou organisation non trouvé lors de la création d'adhésion");
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Membre ou organisation non trouvé", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Données invalides pour la création d'adhésion: {}", e.getMessage());
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Données invalides", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la création de l'adhésion", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of("error", "Erreur lors de la création de l'adhésion", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Met à jour une adhésion existante */
|
||||
@PUT
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/{id}")
|
||||
@Operation(
|
||||
summary = "Mettre à jour une adhésion",
|
||||
description = "Met à jour les données d'une adhésion existante")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Adhésion mise à jour avec succès"),
|
||||
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||
@APIResponse(responseCode = "404", description = "Adhésion non trouvée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response updateAdhesion(
|
||||
@Parameter(description = "Identifiant de l'adhésion", required = true)
|
||||
@PathParam("id")
|
||||
@NotNull
|
||||
UUID id,
|
||||
@Parameter(description = "Nouvelles données de l'adhésion", required = true) @Valid
|
||||
AdhesionDTO adhesionDTO) {
|
||||
|
||||
try {
|
||||
log.info("PUT /api/adhesions/{}", id);
|
||||
|
||||
AdhesionDTO adhesionMiseAJour = adhesionService.updateAdhesion(id, adhesionDTO);
|
||||
|
||||
log.info("Adhésion mise à jour avec succès - ID: {}", id);
|
||||
return Response.ok(adhesionMiseAJour).build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Adhésion non trouvée pour mise à jour - ID: {}", id);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Adhésion non trouvée", "id", id))
|
||||
.build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn(
|
||||
"Données invalides pour la mise à jour d'adhésion - ID: {}, Erreur: {}", id, e.getMessage());
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Données invalides", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la mise à jour de l'adhésion - ID: " + id, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of("error", "Erreur lors de la mise à jour de l'adhésion", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Supprime une adhésion */
|
||||
@DELETE
|
||||
@RolesAllowed({"ADMIN"})
|
||||
@Path("/{id}")
|
||||
@Operation(
|
||||
summary = "Supprimer une adhésion",
|
||||
description = "Supprime (annule) une adhésion")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "204", description = "Adhésion supprimée avec succès"),
|
||||
@APIResponse(responseCode = "404", description = "Adhésion non trouvée"),
|
||||
@APIResponse(
|
||||
responseCode = "409",
|
||||
description = "Impossible de supprimer une adhésion payée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response deleteAdhesion(
|
||||
@Parameter(description = "Identifiant de l'adhésion", required = true)
|
||||
@PathParam("id")
|
||||
@NotNull
|
||||
UUID id) {
|
||||
|
||||
try {
|
||||
log.info("DELETE /api/adhesions/{}", id);
|
||||
|
||||
adhesionService.deleteAdhesion(id);
|
||||
|
||||
log.info("Adhésion supprimée avec succès - ID: {}", id);
|
||||
return Response.noContent().build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Adhésion non trouvée pour suppression - ID: {}", id);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Adhésion non trouvée", "id", id))
|
||||
.build();
|
||||
} catch (IllegalStateException e) {
|
||||
log.warn("Impossible de supprimer l'adhésion - ID: {}, Raison: {}", id, e.getMessage());
|
||||
return Response.status(Response.Status.CONFLICT)
|
||||
.entity(Map.of("error", "Impossible de supprimer l'adhésion", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la suppression de l'adhésion - ID: " + id, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of("error", "Erreur lors de la suppression de l'adhésion", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Approuve une adhésion */
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/{id}/approuver")
|
||||
@Operation(
|
||||
summary = "Approuver une adhésion",
|
||||
description = "Approuve une demande d'adhésion en attente")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Adhésion approuvée avec succès"),
|
||||
@APIResponse(responseCode = "400", description = "L'adhésion ne peut pas être approuvée"),
|
||||
@APIResponse(responseCode = "404", description = "Adhésion non trouvée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response approuverAdhesion(
|
||||
@Parameter(description = "Identifiant de l'adhésion", required = true)
|
||||
@PathParam("id")
|
||||
@NotNull
|
||||
UUID id,
|
||||
@Parameter(description = "Nom de l'utilisateur qui approuve")
|
||||
@QueryParam("approuvePar")
|
||||
String approuvePar) {
|
||||
|
||||
try {
|
||||
log.info("POST /api/adhesions/{}/approuver", id);
|
||||
|
||||
AdhesionDTO adhesion = adhesionService.approuverAdhesion(id, approuvePar);
|
||||
|
||||
log.info("Adhésion approuvée avec succès - ID: {}", id);
|
||||
return Response.ok(adhesion).build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Adhésion non trouvée pour approbation - ID: {}", id);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Adhésion non trouvée", "id", id))
|
||||
.build();
|
||||
} catch (IllegalStateException e) {
|
||||
log.warn("Impossible d'approuver l'adhésion - ID: {}, Raison: {}", id, e.getMessage());
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Impossible d'approuver l'adhésion", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de l'approbation de l'adhésion - ID: " + id, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of("error", "Erreur lors de l'approbation de l'adhésion", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Rejette une adhésion */
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/{id}/rejeter")
|
||||
@Operation(
|
||||
summary = "Rejeter une adhésion",
|
||||
description = "Rejette une demande d'adhésion en attente")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Adhésion rejetée avec succès"),
|
||||
@APIResponse(responseCode = "400", description = "L'adhésion ne peut pas être rejetée"),
|
||||
@APIResponse(responseCode = "404", description = "Adhésion non trouvée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response rejeterAdhesion(
|
||||
@Parameter(description = "Identifiant de l'adhésion", required = true)
|
||||
@PathParam("id")
|
||||
@NotNull
|
||||
UUID id,
|
||||
@Parameter(description = "Motif du rejet", required = true) @QueryParam("motifRejet")
|
||||
@NotNull
|
||||
String motifRejet) {
|
||||
|
||||
try {
|
||||
log.info("POST /api/adhesions/{}/rejeter", id);
|
||||
|
||||
AdhesionDTO adhesion = adhesionService.rejeterAdhesion(id, motifRejet);
|
||||
|
||||
log.info("Adhésion rejetée avec succès - ID: {}", id);
|
||||
return Response.ok(adhesion).build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Adhésion non trouvée pour rejet - ID: {}", id);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Adhésion non trouvée", "id", id))
|
||||
.build();
|
||||
} catch (IllegalStateException e) {
|
||||
log.warn("Impossible de rejeter l'adhésion - ID: {}, Raison: {}", id, e.getMessage());
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Impossible de rejeter l'adhésion", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors du rejet de l'adhésion - ID: " + id, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of("error", "Erreur lors du rejet de l'adhésion", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Enregistre un paiement pour une adhésion */
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/{id}/paiement")
|
||||
@Operation(
|
||||
summary = "Enregistrer un paiement",
|
||||
description = "Enregistre un paiement pour une adhésion approuvée")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Paiement enregistré avec succès"),
|
||||
@APIResponse(responseCode = "400", description = "L'adhésion ne peut pas recevoir de paiement"),
|
||||
@APIResponse(responseCode = "404", description = "Adhésion non trouvée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response enregistrerPaiement(
|
||||
@Parameter(description = "Identifiant de l'adhésion", required = true)
|
||||
@PathParam("id")
|
||||
@NotNull
|
||||
UUID id,
|
||||
@Parameter(description = "Montant payé", required = true) @QueryParam("montantPaye")
|
||||
@NotNull
|
||||
BigDecimal montantPaye,
|
||||
@Parameter(description = "Méthode de paiement") @QueryParam("methodePaiement")
|
||||
String methodePaiement,
|
||||
@Parameter(description = "Référence du paiement") @QueryParam("referencePaiement")
|
||||
String referencePaiement) {
|
||||
|
||||
try {
|
||||
log.info("POST /api/adhesions/{}/paiement", id);
|
||||
|
||||
AdhesionDTO adhesion =
|
||||
adhesionService.enregistrerPaiement(id, montantPaye, methodePaiement, referencePaiement);
|
||||
|
||||
log.info("Paiement enregistré avec succès pour l'adhésion - ID: {}", id);
|
||||
return Response.ok(adhesion).build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Adhésion non trouvée pour paiement - ID: {}", id);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Adhésion non trouvée", "id", id))
|
||||
.build();
|
||||
} catch (IllegalStateException e) {
|
||||
log.warn("Impossible d'enregistrer le paiement - ID: {}, Raison: {}", id, e.getMessage());
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(
|
||||
Map.of("error", "Impossible d'enregistrer le paiement", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de l'enregistrement du paiement - ID: " + id, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of("error", "Erreur lors de l'enregistrement du paiement", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère les adhésions d'un membre */
|
||||
@GET
|
||||
@Path("/membre/{membreId}")
|
||||
@Operation(
|
||||
summary = "Lister les adhésions d'un membre",
|
||||
description = "Récupère toutes les adhésions d'un membre spécifique")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste des adhésions du membre"),
|
||||
@APIResponse(responseCode = "404", description = "Membre non trouvé"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getAdhesionsByMembre(
|
||||
@Parameter(description = "Identifiant du membre", required = true)
|
||||
@PathParam("membreId")
|
||||
@NotNull
|
||||
UUID membreId,
|
||||
@Parameter(description = "Numéro de page", example = "0")
|
||||
@QueryParam("page")
|
||||
@DefaultValue("0")
|
||||
@Min(0)
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page", example = "20")
|
||||
@QueryParam("size")
|
||||
@DefaultValue("20")
|
||||
@Min(1)
|
||||
int size) {
|
||||
|
||||
try {
|
||||
log.info("GET /api/adhesions/membre/{} - page: {}, size: {}", membreId, page, size);
|
||||
|
||||
List<AdhesionDTO> adhesions = adhesionService.getAdhesionsByMembre(membreId, page, size);
|
||||
|
||||
log.info(
|
||||
"Récupération réussie de {} adhésions pour le membre {}", adhesions.size(), membreId);
|
||||
return Response.ok(adhesions).build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Membre non trouvé - ID: {}", membreId);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Membre non trouvé", "membreId", membreId))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des adhésions du membre - ID: " + membreId, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of("error", "Erreur lors de la récupération des adhésions", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère les adhésions d'une organisation */
|
||||
@GET
|
||||
@Path("/organisation/{organisationId}")
|
||||
@Operation(
|
||||
summary = "Lister les adhésions d'une organisation",
|
||||
description = "Récupère toutes les adhésions d'une organisation spécifique")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste des adhésions de l'organisation"),
|
||||
@APIResponse(responseCode = "404", description = "Organisation non trouvée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getAdhesionsByOrganisation(
|
||||
@Parameter(description = "Identifiant de l'organisation", required = true)
|
||||
@PathParam("organisationId")
|
||||
@NotNull
|
||||
UUID organisationId,
|
||||
@Parameter(description = "Numéro de page", example = "0")
|
||||
@QueryParam("page")
|
||||
@DefaultValue("0")
|
||||
@Min(0)
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page", example = "20")
|
||||
@QueryParam("size")
|
||||
@DefaultValue("20")
|
||||
@Min(1)
|
||||
int size) {
|
||||
|
||||
try {
|
||||
log.info(
|
||||
"GET /api/adhesions/organisation/{} - page: {}, size: {}", organisationId, page, size);
|
||||
|
||||
List<AdhesionDTO> adhesions =
|
||||
adhesionService.getAdhesionsByOrganisation(organisationId, page, size);
|
||||
|
||||
log.info(
|
||||
"Récupération réussie de {} adhésions pour l'organisation {}",
|
||||
adhesions.size(),
|
||||
organisationId);
|
||||
return Response.ok(adhesions).build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Organisation non trouvée - ID: {}", organisationId);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Organisation non trouvée", "organisationId", organisationId))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error(
|
||||
"Erreur lors de la récupération des adhésions de l'organisation - ID: " + organisationId, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of("error", "Erreur lors de la récupération des adhésions", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère les adhésions par statut */
|
||||
@GET
|
||||
@Path("/statut/{statut}")
|
||||
@Operation(
|
||||
summary = "Lister les adhésions par statut",
|
||||
description = "Récupère toutes les adhésions ayant un statut spécifique")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "Liste des adhésions avec le statut spécifié"),
|
||||
@APIResponse(responseCode = "400", description = "Statut invalide"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getAdhesionsByStatut(
|
||||
@Parameter(description = "Statut des adhésions", required = true, example = "EN_ATTENTE")
|
||||
@PathParam("statut")
|
||||
@NotNull
|
||||
String statut,
|
||||
@Parameter(description = "Numéro de page", example = "0")
|
||||
@QueryParam("page")
|
||||
@DefaultValue("0")
|
||||
@Min(0)
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page", example = "20")
|
||||
@QueryParam("size")
|
||||
@DefaultValue("20")
|
||||
@Min(1)
|
||||
int size) {
|
||||
|
||||
try {
|
||||
log.info("GET /api/adhesions/statut/{} - page: {}, size: {}", statut, page, size);
|
||||
|
||||
List<AdhesionDTO> adhesions = adhesionService.getAdhesionsByStatut(statut, page, size);
|
||||
|
||||
log.info("Récupération réussie de {} adhésions avec statut {}", adhesions.size(), statut);
|
||||
return Response.ok(adhesions).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des adhésions par statut - Statut: " + statut, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of("error", "Erreur lors de la récupération des adhésions", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère les adhésions en attente */
|
||||
@GET
|
||||
@Path("/en-attente")
|
||||
@Operation(
|
||||
summary = "Lister les adhésions en attente",
|
||||
description = "Récupère toutes les adhésions en attente d'approbation")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste des adhésions en attente"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getAdhesionsEnAttente(
|
||||
@Parameter(description = "Numéro de page", example = "0")
|
||||
@QueryParam("page")
|
||||
@DefaultValue("0")
|
||||
@Min(0)
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page", example = "20")
|
||||
@QueryParam("size")
|
||||
@DefaultValue("20")
|
||||
@Min(1)
|
||||
int size) {
|
||||
|
||||
try {
|
||||
log.info("GET /api/adhesions/en-attente - page: {}, size: {}", page, size);
|
||||
|
||||
List<AdhesionDTO> adhesions = adhesionService.getAdhesionsEnAttente(page, size);
|
||||
|
||||
log.info("Récupération réussie de {} adhésions en attente", adhesions.size());
|
||||
return Response.ok(adhesions).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des adhésions en attente", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error", "Erreur lors de la récupération des adhésions en attente", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère les statistiques des adhésions */
|
||||
@GET
|
||||
@Path("/stats")
|
||||
@Operation(
|
||||
summary = "Statistiques des adhésions",
|
||||
description = "Récupère les statistiques globales des adhésions")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Statistiques récupérées avec succès"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getStatistiquesAdhesions() {
|
||||
try {
|
||||
log.info("GET /api/adhesions/stats");
|
||||
|
||||
Map<String, Object> statistiques = adhesionService.getStatistiquesAdhesions();
|
||||
|
||||
log.info("Statistiques récupérées avec succès");
|
||||
return Response.ok(statistiques).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des statistiques", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error", "Erreur lors de la récupération des statistiques", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@@ -0,0 +1,345 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.analytics.AnalyticsDataDTO;
|
||||
import dev.lions.unionflow.server.api.dto.analytics.DashboardWidgetDTO;
|
||||
import dev.lions.unionflow.server.api.dto.analytics.KPITrendDTO;
|
||||
import dev.lions.unionflow.server.api.enums.analytics.PeriodeAnalyse;
|
||||
import dev.lions.unionflow.server.api.enums.analytics.TypeMetrique;
|
||||
import dev.lions.unionflow.server.service.AnalyticsService;
|
||||
import dev.lions.unionflow.server.service.KPICalculatorService;
|
||||
import io.quarkus.security.Authenticated;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Ressource REST pour les analytics et métriques UnionFlow
|
||||
*
|
||||
* <p>Cette ressource expose les APIs pour accéder aux données analytics, KPI, tendances et widgets
|
||||
* de tableau de bord.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-16
|
||||
*/
|
||||
@Path("/api/v1/analytics")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Authenticated
|
||||
@Tag(name = "Analytics", description = "APIs pour les analytics et métriques")
|
||||
public class AnalyticsResource {
|
||||
|
||||
private static final Logger log = Logger.getLogger(AnalyticsResource.class);
|
||||
|
||||
@Inject AnalyticsService analyticsService;
|
||||
|
||||
@Inject KPICalculatorService kpiCalculatorService;
|
||||
|
||||
/** Calcule une métrique analytics pour une période donnée */
|
||||
@GET
|
||||
@Path("/metriques/{typeMetrique}")
|
||||
@RolesAllowed({"ADMIN", "MANAGER", "MEMBER"})
|
||||
@Operation(
|
||||
summary = "Calculer une métrique analytics",
|
||||
description = "Calcule une métrique spécifique pour une période et organisation données")
|
||||
@APIResponse(responseCode = "200", description = "Métrique calculée avec succès")
|
||||
@APIResponse(responseCode = "400", description = "Paramètres invalides")
|
||||
@APIResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
public Response calculerMetrique(
|
||||
@Parameter(description = "Type de métrique à calculer", required = true)
|
||||
@PathParam("typeMetrique")
|
||||
TypeMetrique typeMetrique,
|
||||
@Parameter(description = "Période d'analyse", required = true) @QueryParam("periode") @NotNull
|
||||
PeriodeAnalyse periodeAnalyse,
|
||||
@Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("organisationId")
|
||||
UUID organisationId) {
|
||||
|
||||
try {
|
||||
log.infof(
|
||||
"Calcul de la métrique %s pour la période %s et l'organisation %s",
|
||||
typeMetrique, periodeAnalyse, organisationId);
|
||||
|
||||
AnalyticsDataDTO result =
|
||||
analyticsService.calculerMetrique(typeMetrique, periodeAnalyse, organisationId);
|
||||
|
||||
return Response.ok(result).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.errorf(e, "Erreur lors du calcul de la métrique %s: %s", typeMetrique, e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of("error", "Erreur lors du calcul de la métrique", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Calcule les tendances d'un KPI sur une période */
|
||||
@GET
|
||||
@Path("/tendances/{typeMetrique}")
|
||||
@RolesAllowed({"ADMIN", "MANAGER", "MEMBER"})
|
||||
@Operation(
|
||||
summary = "Calculer la tendance d'un KPI",
|
||||
description = "Calcule l'évolution et les tendances d'un KPI sur une période donnée")
|
||||
@APIResponse(responseCode = "200", description = "Tendance calculée avec succès")
|
||||
@APIResponse(responseCode = "400", description = "Paramètres invalides")
|
||||
@APIResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
public Response calculerTendanceKPI(
|
||||
@Parameter(description = "Type de métrique pour la tendance", required = true)
|
||||
@PathParam("typeMetrique")
|
||||
TypeMetrique typeMetrique,
|
||||
@Parameter(description = "Période d'analyse", required = true) @QueryParam("periode") @NotNull
|
||||
PeriodeAnalyse periodeAnalyse,
|
||||
@Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("organisationId")
|
||||
UUID organisationId) {
|
||||
|
||||
try {
|
||||
log.infof(
|
||||
"Calcul de la tendance KPI %s pour la période %s et l'organisation %s",
|
||||
typeMetrique, periodeAnalyse, organisationId);
|
||||
|
||||
KPITrendDTO result =
|
||||
analyticsService.calculerTendanceKPI(typeMetrique, periodeAnalyse, organisationId);
|
||||
|
||||
return Response.ok(result).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.errorf(
|
||||
e, "Erreur lors du calcul de la tendance KPI %s: %s", typeMetrique, e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of("error", "Erreur lors du calcul de la tendance", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Obtient tous les KPI pour une organisation */
|
||||
@GET
|
||||
@Path("/kpis")
|
||||
@RolesAllowed({"ADMIN", "MANAGER", "MEMBER"})
|
||||
@Operation(
|
||||
summary = "Obtenir tous les KPI",
|
||||
description = "Récupère tous les KPI calculés pour une organisation et période données")
|
||||
@APIResponse(responseCode = "200", description = "KPI récupérés avec succès")
|
||||
@APIResponse(responseCode = "400", description = "Paramètres invalides")
|
||||
@APIResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
public Response obtenirTousLesKPI(
|
||||
@Parameter(description = "Période d'analyse", required = true) @QueryParam("periode") @NotNull
|
||||
PeriodeAnalyse periodeAnalyse,
|
||||
@Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("organisationId")
|
||||
UUID organisationId) {
|
||||
|
||||
try {
|
||||
log.infof(
|
||||
"Récupération de tous les KPI pour la période %s et l'organisation %s",
|
||||
periodeAnalyse, organisationId);
|
||||
|
||||
Map<TypeMetrique, BigDecimal> kpis =
|
||||
kpiCalculatorService.calculerTousLesKPI(
|
||||
organisationId, periodeAnalyse.getDateDebut(), periodeAnalyse.getDateFin());
|
||||
|
||||
return Response.ok(kpis).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des KPI: {}", e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of("error", "Erreur lors de la récupération des KPI", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Calcule le KPI de performance globale */
|
||||
@GET
|
||||
@Path("/performance-globale")
|
||||
@RolesAllowed({"ADMIN", "MANAGER"})
|
||||
@Operation(
|
||||
summary = "Calculer la performance globale",
|
||||
description = "Calcule le score de performance globale de l'organisation")
|
||||
@APIResponse(responseCode = "200", description = "Performance globale calculée avec succès")
|
||||
@APIResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
public Response calculerPerformanceGlobale(
|
||||
@Parameter(description = "Période d'analyse", required = true) @QueryParam("periode") @NotNull
|
||||
PeriodeAnalyse periodeAnalyse,
|
||||
@Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("organisationId")
|
||||
UUID organisationId) {
|
||||
|
||||
try {
|
||||
log.infof(
|
||||
"Calcul de la performance globale pour la période %s et l'organisation %s",
|
||||
periodeAnalyse, organisationId);
|
||||
|
||||
BigDecimal performanceGlobale =
|
||||
kpiCalculatorService.calculerKPIPerformanceGlobale(
|
||||
organisationId, periodeAnalyse.getDateDebut(), periodeAnalyse.getDateFin());
|
||||
|
||||
return Response.ok(
|
||||
Map.of(
|
||||
"performanceGlobale", performanceGlobale,
|
||||
"periode", periodeAnalyse,
|
||||
"organisationId", organisationId,
|
||||
"dateCalcul", java.time.LocalDateTime.now()))
|
||||
.build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors du calcul de la performance globale: {}", e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error",
|
||||
"Erreur lors du calcul de la performance globale",
|
||||
"message",
|
||||
e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Obtient les évolutions des KPI par rapport à la période précédente */
|
||||
@GET
|
||||
@Path("/evolutions")
|
||||
@RolesAllowed({"ADMIN", "MANAGER", "MEMBER"})
|
||||
@Operation(
|
||||
summary = "Obtenir les évolutions des KPI",
|
||||
description = "Récupère les évolutions des KPI par rapport à la période précédente")
|
||||
@APIResponse(responseCode = "200", description = "Évolutions récupérées avec succès")
|
||||
@APIResponse(responseCode = "400", description = "Paramètres invalides")
|
||||
@APIResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
public Response obtenirEvolutionsKPI(
|
||||
@Parameter(description = "Période d'analyse", required = true) @QueryParam("periode") @NotNull
|
||||
PeriodeAnalyse periodeAnalyse,
|
||||
@Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("organisationId")
|
||||
UUID organisationId) {
|
||||
|
||||
try {
|
||||
log.infof(
|
||||
"Récupération des évolutions KPI pour la période %s et l'organisation %s",
|
||||
periodeAnalyse, organisationId);
|
||||
|
||||
Map<TypeMetrique, BigDecimal> evolutions =
|
||||
kpiCalculatorService.calculerEvolutionsKPI(
|
||||
organisationId, periodeAnalyse.getDateDebut(), periodeAnalyse.getDateFin());
|
||||
|
||||
return Response.ok(evolutions).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des évolutions KPI: {}", e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error",
|
||||
"Erreur lors de la récupération des évolutions",
|
||||
"message",
|
||||
e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Obtient les widgets du tableau de bord pour un utilisateur */
|
||||
@GET
|
||||
@Path("/dashboard/widgets")
|
||||
@RolesAllowed({"ADMIN", "MANAGER", "MEMBER"})
|
||||
@Operation(
|
||||
summary = "Obtenir les widgets du tableau de bord",
|
||||
description = "Récupère tous les widgets configurés pour le tableau de bord de l'utilisateur")
|
||||
@APIResponse(responseCode = "200", description = "Widgets récupérés avec succès")
|
||||
@APIResponse(responseCode = "403", description = "Accès non autorisé")
|
||||
public Response obtenirWidgetsTableauBord(
|
||||
@Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("organisationId")
|
||||
UUID organisationId,
|
||||
@Parameter(description = "ID de l'utilisateur", required = true)
|
||||
@QueryParam("utilisateurId")
|
||||
@NotNull
|
||||
UUID utilisateurId) {
|
||||
|
||||
try {
|
||||
log.infof(
|
||||
"Récupération des widgets du tableau de bord pour l'organisation %s et l'utilisateur %s",
|
||||
organisationId, utilisateurId);
|
||||
|
||||
List<DashboardWidgetDTO> widgets =
|
||||
analyticsService.obtenirMetriquesTableauBord(organisationId, utilisateurId);
|
||||
|
||||
return Response.ok(widgets).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des widgets: {}", e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error", "Erreur lors de la récupération des widgets", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Obtient les types de métriques disponibles */
|
||||
@GET
|
||||
@Path("/types-metriques")
|
||||
@RolesAllowed({"ADMIN", "MANAGER", "MEMBER"})
|
||||
@Operation(
|
||||
summary = "Obtenir les types de métriques disponibles",
|
||||
description = "Récupère la liste de tous les types de métriques disponibles")
|
||||
@APIResponse(responseCode = "200", description = "Types de métriques récupérés avec succès")
|
||||
public Response obtenirTypesMetriques() {
|
||||
try {
|
||||
log.info("Récupération des types de métriques disponibles");
|
||||
|
||||
TypeMetrique[] typesMetriques = TypeMetrique.values();
|
||||
|
||||
return Response.ok(Map.of("typesMetriques", typesMetriques, "total", typesMetriques.length))
|
||||
.build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des types de métriques: {}", e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error",
|
||||
"Erreur lors de la récupération des types de métriques",
|
||||
"message",
|
||||
e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Obtient les périodes d'analyse disponibles */
|
||||
@GET
|
||||
@Path("/periodes-analyse")
|
||||
@RolesAllowed({"ADMIN", "MANAGER", "MEMBER"})
|
||||
@Operation(
|
||||
summary = "Obtenir les périodes d'analyse disponibles",
|
||||
description = "Récupère la liste de toutes les périodes d'analyse disponibles")
|
||||
@APIResponse(responseCode = "200", description = "Périodes d'analyse récupérées avec succès")
|
||||
public Response obtenirPeriodesAnalyse() {
|
||||
try {
|
||||
log.info("Récupération des périodes d'analyse disponibles");
|
||||
|
||||
PeriodeAnalyse[] periodesAnalyse = PeriodeAnalyse.values();
|
||||
|
||||
return Response.ok(
|
||||
Map.of("periodesAnalyse", periodesAnalyse, "total", periodesAnalyse.length))
|
||||
.build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des périodes d'analyse: {}", e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error",
|
||||
"Erreur lors de la récupération des périodes d'analyse",
|
||||
"message",
|
||||
e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,112 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.admin.AuditLogDTO;
|
||||
import dev.lions.unionflow.server.service.AuditService;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
/**
|
||||
* Resource REST pour la gestion des logs d'audit
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-17
|
||||
*/
|
||||
@Path("/api/audit")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Audit", description = "Gestion des logs d'audit")
|
||||
@Slf4j
|
||||
@RolesAllowed({"ADMIN", "MEMBRE", "USER"})
|
||||
public class AuditResource {
|
||||
|
||||
@Inject
|
||||
AuditService auditService;
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Liste tous les logs d'audit", description = "Récupère tous les logs avec pagination")
|
||||
public Response listerTous(
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("50") int size,
|
||||
@QueryParam("sortBy") @DefaultValue("dateHeure") String sortBy,
|
||||
@QueryParam("sortOrder") @DefaultValue("desc") String sortOrder) {
|
||||
|
||||
try {
|
||||
Map<String, Object> result = auditService.listerTous(page, size, sortBy, sortOrder);
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des logs d'audit", e);
|
||||
return Response.serverError()
|
||||
.entity(Map.of("error", "Erreur lors de la récupération des logs: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/rechercher")
|
||||
@Operation(summary = "Recherche des logs avec filtres", description = "Recherche avancée avec filtres multiples")
|
||||
public Response rechercher(
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr,
|
||||
@QueryParam("typeAction") String typeAction,
|
||||
@QueryParam("severite") String severite,
|
||||
@QueryParam("utilisateur") String utilisateur,
|
||||
@QueryParam("module") String module,
|
||||
@QueryParam("ipAddress") String ipAddress,
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("50") int size) {
|
||||
|
||||
try {
|
||||
LocalDateTime dateDebut = dateDebutStr != null ? LocalDateTime.parse(dateDebutStr) : null;
|
||||
LocalDateTime dateFin = dateFinStr != null ? LocalDateTime.parse(dateFinStr) : null;
|
||||
|
||||
Map<String, Object> result = auditService.rechercher(
|
||||
dateDebut, dateFin, typeAction, severite, utilisateur, module, ipAddress, page, size);
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la recherche des logs d'audit", e);
|
||||
return Response.serverError()
|
||||
.entity(Map.of("error", "Erreur lors de la recherche: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Operation(summary = "Enregistre un nouveau log d'audit", description = "Crée une nouvelle entrée dans le journal d'audit")
|
||||
public Response enregistrerLog(@Valid AuditLogDTO dto) {
|
||||
try {
|
||||
AuditLogDTO result = auditService.enregistrerLog(dto);
|
||||
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de l'enregistrement du log d'audit", e);
|
||||
return Response.serverError()
|
||||
.entity(Map.of("error", "Erreur lors de l'enregistrement: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/statistiques")
|
||||
@Operation(summary = "Récupère les statistiques d'audit", description = "Retourne les statistiques globales des logs")
|
||||
public Response getStatistiques() {
|
||||
try {
|
||||
Map<String, Object> stats = auditService.getStatistiques();
|
||||
return Response.ok(stats).build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des statistiques", e);
|
||||
return Response.serverError()
|
||||
.entity(Map.of("error", "Erreur lors de la récupération des statistiques: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,278 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.comptabilite.*;
|
||||
import dev.lions.unionflow.server.service.ComptabiliteService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Resource REST pour la gestion comptable
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Path("/api/comptabilite")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@RolesAllowed({"ADMIN", "MEMBRE", "USER"})
|
||||
public class ComptabiliteResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(ComptabiliteResource.class);
|
||||
|
||||
@Inject ComptabiliteService comptabiliteService;
|
||||
|
||||
// ========================================
|
||||
// COMPTES COMPTABLES
|
||||
// ========================================
|
||||
|
||||
/**
|
||||
* Crée un nouveau compte comptable
|
||||
*
|
||||
* @param compteDTO DTO du compte à créer
|
||||
* @return Compte créé
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/comptes")
|
||||
public Response creerCompteComptable(@Valid CompteComptableDTO compteDTO) {
|
||||
try {
|
||||
CompteComptableDTO result = comptabiliteService.creerCompteComptable(compteDTO);
|
||||
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la création du compte comptable");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la création du compte comptable: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve un compte comptable par son ID
|
||||
*
|
||||
* @param id ID du compte
|
||||
* @return Compte comptable
|
||||
*/
|
||||
@GET
|
||||
@Path("/comptes/{id}")
|
||||
public Response trouverCompteParId(@PathParam("id") UUID id) {
|
||||
try {
|
||||
CompteComptableDTO result = comptabiliteService.trouverCompteParId(id);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Compte comptable non trouvé"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la recherche du compte comptable");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la recherche du compte comptable: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste tous les comptes comptables actifs
|
||||
*
|
||||
* @return Liste des comptes
|
||||
*/
|
||||
@GET
|
||||
@Path("/comptes")
|
||||
public Response listerTousLesComptes() {
|
||||
try {
|
||||
List<CompteComptableDTO> result = comptabiliteService.listerTousLesComptes();
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la liste des comptes comptables");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la liste des comptes comptables: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// JOURNAUX COMPTABLES
|
||||
// ========================================
|
||||
|
||||
/**
|
||||
* Crée un nouveau journal comptable
|
||||
*
|
||||
* @param journalDTO DTO du journal à créer
|
||||
* @return Journal créé
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/journaux")
|
||||
public Response creerJournalComptable(@Valid JournalComptableDTO journalDTO) {
|
||||
try {
|
||||
JournalComptableDTO result = comptabiliteService.creerJournalComptable(journalDTO);
|
||||
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la création du journal comptable");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la création du journal comptable: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve un journal comptable par son ID
|
||||
*
|
||||
* @param id ID du journal
|
||||
* @return Journal comptable
|
||||
*/
|
||||
@GET
|
||||
@Path("/journaux/{id}")
|
||||
public Response trouverJournalParId(@PathParam("id") UUID id) {
|
||||
try {
|
||||
JournalComptableDTO result = comptabiliteService.trouverJournalParId(id);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Journal comptable non trouvé"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la recherche du journal comptable");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la recherche du journal comptable: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste tous les journaux comptables actifs
|
||||
*
|
||||
* @return Liste des journaux
|
||||
*/
|
||||
@GET
|
||||
@Path("/journaux")
|
||||
public Response listerTousLesJournaux() {
|
||||
try {
|
||||
List<JournalComptableDTO> result = comptabiliteService.listerTousLesJournaux();
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la liste des journaux comptables");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la liste des journaux comptables: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// ÉCRITURES COMPTABLES
|
||||
// ========================================
|
||||
|
||||
/**
|
||||
* Crée une nouvelle écriture comptable
|
||||
*
|
||||
* @param ecritureDTO DTO de l'écriture à créer
|
||||
* @return Écriture créée
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/ecritures")
|
||||
public Response creerEcritureComptable(@Valid EcritureComptableDTO ecritureDTO) {
|
||||
try {
|
||||
EcritureComptableDTO result = comptabiliteService.creerEcritureComptable(ecritureDTO);
|
||||
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la création de l'écriture comptable");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la création de l'écriture comptable: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve une écriture comptable par son ID
|
||||
*
|
||||
* @param id ID de l'écriture
|
||||
* @return Écriture comptable
|
||||
*/
|
||||
@GET
|
||||
@Path("/ecritures/{id}")
|
||||
public Response trouverEcritureParId(@PathParam("id") UUID id) {
|
||||
try {
|
||||
EcritureComptableDTO result = comptabiliteService.trouverEcritureParId(id);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Écriture comptable non trouvée"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la recherche de l'écriture comptable");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la recherche de l'écriture comptable: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste les écritures d'un journal
|
||||
*
|
||||
* @param journalId ID du journal
|
||||
* @return Liste des écritures
|
||||
*/
|
||||
@GET
|
||||
@Path("/ecritures/journal/{journalId}")
|
||||
public Response listerEcrituresParJournal(@PathParam("journalId") UUID journalId) {
|
||||
try {
|
||||
List<EcritureComptableDTO> result = comptabiliteService.listerEcrituresParJournal(journalId);
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la liste des écritures");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la liste des écritures: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste les écritures d'une organisation
|
||||
*
|
||||
* @param organisationId ID de l'organisation
|
||||
* @return Liste des écritures
|
||||
*/
|
||||
@GET
|
||||
@Path("/ecritures/organisation/{organisationId}")
|
||||
public Response listerEcrituresParOrganisation(@PathParam("organisationId") UUID organisationId) {
|
||||
try {
|
||||
List<EcritureComptableDTO> result = comptabiliteService.listerEcrituresParOrganisation(organisationId);
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la liste des écritures");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la liste des écritures: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Classe interne pour les réponses d'erreur */
|
||||
public static class ErrorResponse {
|
||||
public String error;
|
||||
|
||||
public ErrorResponse(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,674 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.finance.CotisationDTO;
|
||||
import dev.lions.unionflow.server.service.CotisationService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Content;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
/**
|
||||
* Resource REST pour la gestion des cotisations Expose les endpoints API pour les opérations CRUD
|
||||
* sur les cotisations
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-15
|
||||
*/
|
||||
@Path("/api/cotisations")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Cotisations", description = "Gestion des cotisations des membres")
|
||||
@RolesAllowed({"ADMIN", "MEMBRE", "USER"})
|
||||
@Slf4j
|
||||
public class CotisationResource {
|
||||
|
||||
@Inject CotisationService cotisationService;
|
||||
|
||||
/** Endpoint public pour les cotisations (test) */
|
||||
@GET
|
||||
@Path("/public")
|
||||
@Operation(
|
||||
summary = "Cotisations publiques",
|
||||
description = "Liste des cotisations sans authentification")
|
||||
@APIResponse(responseCode = "200", description = "Liste des cotisations")
|
||||
public Response getCotisationsPublic(
|
||||
@QueryParam("page") @DefaultValue("0") @Min(0) int page,
|
||||
@QueryParam("size") @DefaultValue("20") @Min(1) int size) {
|
||||
|
||||
try {
|
||||
log.info("GET /api/cotisations/public - page: {}, size: {}", page, size);
|
||||
|
||||
// Récupérer les cotisations depuis la base de données
|
||||
List<CotisationDTO> cotisationsDTO = cotisationService.getAllCotisations(page, size);
|
||||
|
||||
// Convertir en format pour l'application mobile
|
||||
List<Map<String, Object>> cotisations = cotisationsDTO.stream()
|
||||
.map(c -> {
|
||||
Map<String, Object> map = new java.util.HashMap<>();
|
||||
map.put("id", c.getId() != null ? c.getId().toString() : "");
|
||||
map.put("nom", c.getDescription() != null ? c.getDescription() : "Cotisation");
|
||||
map.put("description", c.getDescription() != null ? c.getDescription() : "");
|
||||
map.put("montant", c.getMontantDu() != null ? c.getMontantDu().doubleValue() : 0.0);
|
||||
map.put("devise", c.getCodeDevise() != null ? c.getCodeDevise() : "XOF");
|
||||
map.put("dateEcheance", c.getDateEcheance() != null ? c.getDateEcheance().toString() : "");
|
||||
map.put("statut", c.getStatut() != null ? c.getStatut() : "EN_ATTENTE");
|
||||
map.put("type", c.getTypeCotisation() != null ? c.getTypeCotisation() : "MENSUELLE");
|
||||
return map;
|
||||
})
|
||||
.collect(Collectors.toList());
|
||||
|
||||
long totalElements = cotisationService.getStatistiquesCotisations().get("totalCotisations") != null
|
||||
? ((Number) cotisationService.getStatistiquesCotisations().get("totalCotisations")).longValue()
|
||||
: cotisations.size();
|
||||
int totalPages = (int) Math.ceil((double) totalElements / size);
|
||||
|
||||
Map<String, Object> response =
|
||||
Map.of(
|
||||
"content", cotisations,
|
||||
"totalElements", totalElements,
|
||||
"totalPages", totalPages,
|
||||
"size", size,
|
||||
"number", page);
|
||||
|
||||
return Response.ok(response).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des cotisations publiques", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de la récupération des cotisations"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère toutes les cotisations avec pagination */
|
||||
@GET
|
||||
@Operation(
|
||||
summary = "Lister toutes les cotisations",
|
||||
description = "Récupère la liste paginée de toutes les cotisations")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "Liste des cotisations récupérée avec succès",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = CotisationDTO.class))),
|
||||
@APIResponse(responseCode = "400", description = "Paramètres de pagination invalides"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getAllCotisations(
|
||||
@Parameter(description = "Numéro de page (0-based)", example = "0")
|
||||
@QueryParam("page")
|
||||
@DefaultValue("0")
|
||||
@Min(0)
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page", example = "20")
|
||||
@QueryParam("size")
|
||||
@DefaultValue("20")
|
||||
@Min(1)
|
||||
int size) {
|
||||
|
||||
try {
|
||||
log.info("GET /api/cotisations - page: {}, size: {}", page, size);
|
||||
|
||||
List<CotisationDTO> cotisations = cotisationService.getAllCotisations(page, size);
|
||||
|
||||
log.info("Récupération réussie de {} cotisations", cotisations.size());
|
||||
return Response.ok(cotisations).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des cotisations", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error",
|
||||
"Erreur lors de la récupération des cotisations",
|
||||
"message",
|
||||
e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère une cotisation par son ID */
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(
|
||||
summary = "Récupérer une cotisation par ID",
|
||||
description = "Récupère les détails d'une cotisation spécifique")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "Cotisation trouvée",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = CotisationDTO.class))),
|
||||
@APIResponse(responseCode = "404", description = "Cotisation non trouvée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getCotisationById(
|
||||
@Parameter(description = "Identifiant de la cotisation", required = true)
|
||||
@PathParam("id")
|
||||
@NotNull
|
||||
UUID id) {
|
||||
|
||||
try {
|
||||
log.info("GET /api/cotisations/{}", id);
|
||||
|
||||
CotisationDTO cotisation = cotisationService.getCotisationById(id);
|
||||
|
||||
log.info("Cotisation récupérée avec succès - ID: {}", id);
|
||||
return Response.ok(cotisation).build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Cotisation non trouvée - ID: {}", id);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Cotisation non trouvée", "id", id))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération de la cotisation - ID: " + id, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error",
|
||||
"Erreur lors de la récupération de la cotisation",
|
||||
"message",
|
||||
e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère une cotisation par son numéro de référence */
|
||||
@GET
|
||||
@Path("/reference/{numeroReference}")
|
||||
@Operation(
|
||||
summary = "Récupérer une cotisation par référence",
|
||||
description = "Récupère une cotisation par son numéro de référence unique")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Cotisation trouvée"),
|
||||
@APIResponse(responseCode = "404", description = "Cotisation non trouvée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getCotisationByReference(
|
||||
@Parameter(description = "Numéro de référence de la cotisation", required = true)
|
||||
@PathParam("numeroReference")
|
||||
@NotNull
|
||||
String numeroReference) {
|
||||
|
||||
try {
|
||||
log.info("GET /api/cotisations/reference/{}", numeroReference);
|
||||
|
||||
CotisationDTO cotisation = cotisationService.getCotisationByReference(numeroReference);
|
||||
|
||||
log.info("Cotisation récupérée avec succès - Référence: {}", numeroReference);
|
||||
return Response.ok(cotisation).build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Cotisation non trouvée - Référence: {}", numeroReference);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Cotisation non trouvée", "reference", numeroReference))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error(
|
||||
"Erreur lors de la récupération de la cotisation - Référence: " + numeroReference, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error",
|
||||
"Erreur lors de la récupération de la cotisation",
|
||||
"message",
|
||||
e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Crée une nouvelle cotisation */
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Operation(
|
||||
summary = "Créer une nouvelle cotisation",
|
||||
description = "Crée une nouvelle cotisation pour un membre")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "201",
|
||||
description = "Cotisation créée avec succès",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = CotisationDTO.class))),
|
||||
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||
@APIResponse(responseCode = "404", description = "Membre non trouvé"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response createCotisation(
|
||||
@Parameter(description = "Données de la cotisation à créer", required = true) @Valid
|
||||
CotisationDTO cotisationDTO) {
|
||||
|
||||
try {
|
||||
log.info(
|
||||
"POST /api/cotisations - Création cotisation pour membre: {}",
|
||||
cotisationDTO.getMembreId());
|
||||
|
||||
CotisationDTO nouvelleCotisation = cotisationService.createCotisation(cotisationDTO);
|
||||
|
||||
log.info(
|
||||
"Cotisation créée avec succès - ID: {}, Référence: {}",
|
||||
nouvelleCotisation.getId(),
|
||||
nouvelleCotisation.getNumeroReference());
|
||||
|
||||
return Response.status(Response.Status.CREATED).entity(nouvelleCotisation).build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn(
|
||||
"Membre non trouvé lors de la création de cotisation: {}", cotisationDTO.getMembreId());
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Membre non trouvé", "membreId", cotisationDTO.getMembreId()))
|
||||
.build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn("Données invalides pour la création de cotisation: {}", e.getMessage());
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Données invalides", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la création de la cotisation", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error",
|
||||
"Erreur lors de la création de la cotisation",
|
||||
"message",
|
||||
e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Met à jour une cotisation existante */
|
||||
@PUT
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/{id}")
|
||||
@Operation(
|
||||
summary = "Mettre à jour une cotisation",
|
||||
description = "Met à jour les données d'une cotisation existante")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Cotisation mise à jour avec succès"),
|
||||
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||
@APIResponse(responseCode = "404", description = "Cotisation non trouvée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response updateCotisation(
|
||||
@Parameter(description = "Identifiant de la cotisation", required = true)
|
||||
@PathParam("id")
|
||||
@NotNull
|
||||
UUID id,
|
||||
@Parameter(description = "Nouvelles données de la cotisation", required = true) @Valid
|
||||
CotisationDTO cotisationDTO) {
|
||||
|
||||
try {
|
||||
log.info("PUT /api/cotisations/{}", id);
|
||||
|
||||
CotisationDTO cotisationMiseAJour = cotisationService.updateCotisation(id, cotisationDTO);
|
||||
|
||||
log.info("Cotisation mise à jour avec succès - ID: {}", id);
|
||||
return Response.ok(cotisationMiseAJour).build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Cotisation non trouvée pour mise à jour - ID: {}", id);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Cotisation non trouvée", "id", id))
|
||||
.build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
log.warn(
|
||||
"Données invalides pour la mise à jour de cotisation - ID: {}, Erreur: {}",
|
||||
id,
|
||||
e.getMessage());
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Données invalides", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la mise à jour de la cotisation - ID: " + id, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error",
|
||||
"Erreur lors de la mise à jour de la cotisation",
|
||||
"message",
|
||||
e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Supprime une cotisation */
|
||||
@DELETE
|
||||
@RolesAllowed({"ADMIN"})
|
||||
@Path("/{id}")
|
||||
@Operation(
|
||||
summary = "Supprimer une cotisation",
|
||||
description = "Supprime (désactive) une cotisation")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "204", description = "Cotisation supprimée avec succès"),
|
||||
@APIResponse(responseCode = "404", description = "Cotisation non trouvée"),
|
||||
@APIResponse(
|
||||
responseCode = "409",
|
||||
description = "Impossible de supprimer une cotisation payée"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response deleteCotisation(
|
||||
@Parameter(description = "Identifiant de la cotisation", required = true)
|
||||
@PathParam("id")
|
||||
@NotNull
|
||||
UUID id) {
|
||||
|
||||
try {
|
||||
log.info("DELETE /api/cotisations/{}", id);
|
||||
|
||||
cotisationService.deleteCotisation(id);
|
||||
|
||||
log.info("Cotisation supprimée avec succès - ID: {}", id);
|
||||
return Response.noContent().build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Cotisation non trouvée pour suppression - ID: {}", id);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Cotisation non trouvée", "id", id))
|
||||
.build();
|
||||
} catch (IllegalStateException e) {
|
||||
log.warn("Impossible de supprimer la cotisation - ID: {}, Raison: {}", id, e.getMessage());
|
||||
return Response.status(Response.Status.CONFLICT)
|
||||
.entity(
|
||||
Map.of("error", "Impossible de supprimer la cotisation", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la suppression de la cotisation - ID: " + id, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error",
|
||||
"Erreur lors de la suppression de la cotisation",
|
||||
"message",
|
||||
e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère les cotisations d'un membre */
|
||||
@GET
|
||||
@Path("/membre/{membreId}")
|
||||
@Operation(
|
||||
summary = "Lister les cotisations d'un membre",
|
||||
description = "Récupère toutes les cotisations d'un membre spécifique")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste des cotisations du membre"),
|
||||
@APIResponse(responseCode = "404", description = "Membre non trouvé"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getCotisationsByMembre(
|
||||
@Parameter(description = "Identifiant du membre", required = true)
|
||||
@PathParam("membreId")
|
||||
@NotNull
|
||||
UUID membreId,
|
||||
@Parameter(description = "Numéro de page", example = "0")
|
||||
@QueryParam("page")
|
||||
@DefaultValue("0")
|
||||
@Min(0)
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page", example = "20")
|
||||
@QueryParam("size")
|
||||
@DefaultValue("20")
|
||||
@Min(1)
|
||||
int size) {
|
||||
|
||||
try {
|
||||
log.info("GET /api/cotisations/membre/{} - page: {}, size: {}", membreId, page, size);
|
||||
|
||||
List<CotisationDTO> cotisations =
|
||||
cotisationService.getCotisationsByMembre(membreId, page, size);
|
||||
|
||||
log.info(
|
||||
"Récupération réussie de {} cotisations pour le membre {}", cotisations.size(), membreId);
|
||||
return Response.ok(cotisations).build();
|
||||
|
||||
} catch (NotFoundException e) {
|
||||
log.warn("Membre non trouvé - ID: {}", membreId);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Membre non trouvé", "membreId", membreId))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des cotisations du membre - ID: " + membreId, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error",
|
||||
"Erreur lors de la récupération des cotisations",
|
||||
"message",
|
||||
e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère les cotisations par statut */
|
||||
@GET
|
||||
@Path("/statut/{statut}")
|
||||
@Operation(
|
||||
summary = "Lister les cotisations par statut",
|
||||
description = "Récupère toutes les cotisations ayant un statut spécifique")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "Liste des cotisations avec le statut spécifié"),
|
||||
@APIResponse(responseCode = "400", description = "Statut invalide"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getCotisationsByStatut(
|
||||
@Parameter(description = "Statut des cotisations", required = true, example = "EN_ATTENTE")
|
||||
@PathParam("statut")
|
||||
@NotNull
|
||||
String statut,
|
||||
@Parameter(description = "Numéro de page", example = "0")
|
||||
@QueryParam("page")
|
||||
@DefaultValue("0")
|
||||
@Min(0)
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page", example = "20")
|
||||
@QueryParam("size")
|
||||
@DefaultValue("20")
|
||||
@Min(1)
|
||||
int size) {
|
||||
|
||||
try {
|
||||
log.info("GET /api/cotisations/statut/{} - page: {}, size: {}", statut, page, size);
|
||||
|
||||
List<CotisationDTO> cotisations =
|
||||
cotisationService.getCotisationsByStatut(statut, page, size);
|
||||
|
||||
log.info("Récupération réussie de {} cotisations avec statut {}", cotisations.size(), statut);
|
||||
return Response.ok(cotisations).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des cotisations par statut - Statut: " + statut, e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error",
|
||||
"Erreur lors de la récupération des cotisations",
|
||||
"message",
|
||||
e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère les cotisations en retard */
|
||||
@GET
|
||||
@Path("/en-retard")
|
||||
@Operation(
|
||||
summary = "Lister les cotisations en retard",
|
||||
description = "Récupère toutes les cotisations dont la date d'échéance est dépassée")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste des cotisations en retard"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getCotisationsEnRetard(
|
||||
@Parameter(description = "Numéro de page", example = "0")
|
||||
@QueryParam("page")
|
||||
@DefaultValue("0")
|
||||
@Min(0)
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page", example = "20")
|
||||
@QueryParam("size")
|
||||
@DefaultValue("20")
|
||||
@Min(1)
|
||||
int size) {
|
||||
|
||||
try {
|
||||
log.info("GET /api/cotisations/en-retard - page: {}, size: {}", page, size);
|
||||
|
||||
List<CotisationDTO> cotisations = cotisationService.getCotisationsEnRetard(page, size);
|
||||
|
||||
log.info("Récupération réussie de {} cotisations en retard", cotisations.size());
|
||||
return Response.ok(cotisations).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des cotisations en retard", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error",
|
||||
"Erreur lors de la récupération des cotisations en retard",
|
||||
"message",
|
||||
e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Recherche avancée de cotisations */
|
||||
@GET
|
||||
@Path("/recherche")
|
||||
@Operation(
|
||||
summary = "Recherche avancée de cotisations",
|
||||
description = "Recherche de cotisations avec filtres multiples")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Résultats de la recherche"),
|
||||
@APIResponse(responseCode = "400", description = "Paramètres de recherche invalides"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response rechercherCotisations(
|
||||
@Parameter(description = "Identifiant du membre") @QueryParam("membreId") UUID membreId,
|
||||
@Parameter(description = "Statut de la cotisation") @QueryParam("statut") String statut,
|
||||
@Parameter(description = "Type de cotisation") @QueryParam("typeCotisation")
|
||||
String typeCotisation,
|
||||
@Parameter(description = "Année") @QueryParam("annee") Integer annee,
|
||||
@Parameter(description = "Mois") @QueryParam("mois") Integer mois,
|
||||
@Parameter(description = "Numéro de page", example = "0")
|
||||
@QueryParam("page")
|
||||
@DefaultValue("0")
|
||||
@Min(0)
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page", example = "20")
|
||||
@QueryParam("size")
|
||||
@DefaultValue("20")
|
||||
@Min(1)
|
||||
int size) {
|
||||
|
||||
try {
|
||||
log.info(
|
||||
"GET /api/cotisations/recherche - Filtres: membreId={}, statut={}, type={}, annee={},"
|
||||
+ " mois={}",
|
||||
membreId,
|
||||
statut,
|
||||
typeCotisation,
|
||||
annee,
|
||||
mois);
|
||||
|
||||
List<CotisationDTO> cotisations =
|
||||
cotisationService.rechercherCotisations(
|
||||
membreId, statut, typeCotisation, annee, mois, page, size);
|
||||
|
||||
log.info("Recherche réussie - {} cotisations trouvées", cotisations.size());
|
||||
return Response.ok(cotisations).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la recherche de cotisations", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error", "Erreur lors de la recherche de cotisations", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère les statistiques des cotisations */
|
||||
@GET
|
||||
@Path("/stats")
|
||||
@Operation(
|
||||
summary = "Statistiques des cotisations",
|
||||
description = "Récupère les statistiques globales des cotisations")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Statistiques récupérées avec succès"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
public Response getStatistiquesCotisations() {
|
||||
try {
|
||||
log.info("GET /api/cotisations/stats");
|
||||
|
||||
Map<String, Object> statistiques = cotisationService.getStatistiquesCotisations();
|
||||
|
||||
log.info("Statistiques récupérées avec succès");
|
||||
return Response.ok(statistiques).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de la récupération des statistiques", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(
|
||||
Map.of(
|
||||
"error",
|
||||
"Erreur lors de la récupération des statistiques",
|
||||
"message",
|
||||
e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie des rappels de cotisations groupés à plusieurs membres (WOU/DRY)
|
||||
*
|
||||
* @param membreIds Liste des IDs des membres destinataires
|
||||
* @return Nombre de rappels envoyés
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/rappels/groupes")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Envoyer des rappels de cotisations groupés")
|
||||
@APIResponse(responseCode = "200", description = "Rappels envoyés avec succès")
|
||||
public Response envoyerRappelsGroupes(List<UUID> membreIds) {
|
||||
try {
|
||||
int rappelsEnvoyes = cotisationService.envoyerRappelsCotisationsGroupes(membreIds);
|
||||
return Response.ok(Map.of("rappelsEnvoyes", rappelsEnvoyes)).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur lors de l'envoi des rappels groupés", e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de l'envoi des rappels: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,251 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.dashboard.DashboardDataDTO;
|
||||
import dev.lions.unionflow.server.api.dto.dashboard.DashboardStatsDTO;
|
||||
import dev.lions.unionflow.server.api.dto.dashboard.RecentActivityDTO;
|
||||
import dev.lions.unionflow.server.api.dto.dashboard.UpcomingEventDTO;
|
||||
import dev.lions.unionflow.server.api.service.dashboard.DashboardService;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Resource REST pour les APIs du dashboard
|
||||
*
|
||||
* <p>Cette ressource expose les endpoints pour récupérer les données du dashboard,
|
||||
* incluant les statistiques, activités récentes et événements à venir.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-15
|
||||
*/
|
||||
@Path("/api/v1/dashboard")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Dashboard", description = "APIs pour la gestion du dashboard")
|
||||
@RolesAllowed({"ADMIN", "MEMBRE", "USER"})
|
||||
public class DashboardResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(DashboardResource.class);
|
||||
|
||||
@Inject
|
||||
DashboardService dashboardService;
|
||||
|
||||
/**
|
||||
* Récupère toutes les données du dashboard
|
||||
*/
|
||||
@GET
|
||||
@Path("/data")
|
||||
@Operation(
|
||||
summary = "Récupérer toutes les données du dashboard",
|
||||
description = "Retourne les statistiques, activités récentes et événements à venir"
|
||||
)
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Données récupérées avec succès"),
|
||||
@APIResponse(responseCode = "400", description = "Paramètres invalides"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
public Response getDashboardData(
|
||||
@Parameter(description = "ID de l'organisation", required = true)
|
||||
@QueryParam("organizationId") @NotNull String organizationId,
|
||||
@Parameter(description = "ID de l'utilisateur", required = true)
|
||||
@QueryParam("userId") @NotNull String userId) {
|
||||
|
||||
LOG.infof("GET /api/v1/dashboard/data - org: %s, user: %s", organizationId, userId);
|
||||
|
||||
try {
|
||||
DashboardDataDTO dashboardData = dashboardService.getDashboardData(organizationId, userId);
|
||||
return Response.ok(dashboardData).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la récupération des données dashboard");
|
||||
return Response.serverError().entity(Map.of("error", "Erreur serveur")).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère uniquement les statistiques du dashboard
|
||||
*/
|
||||
@GET
|
||||
@Path("/stats")
|
||||
@Operation(
|
||||
summary = "Récupérer les statistiques du dashboard",
|
||||
description = "Retourne uniquement les statistiques principales"
|
||||
)
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Statistiques récupérées avec succès"),
|
||||
@APIResponse(responseCode = "400", description = "Paramètres invalides"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
public Response getDashboardStats(
|
||||
@Parameter(description = "ID de l'organisation", required = true)
|
||||
@QueryParam("organizationId") @NotNull String organizationId,
|
||||
@Parameter(description = "ID de l'utilisateur", required = true)
|
||||
@QueryParam("userId") @NotNull String userId) {
|
||||
|
||||
LOG.infof("GET /api/v1/dashboard/stats - org: %s, user: %s", organizationId, userId);
|
||||
|
||||
try {
|
||||
DashboardStatsDTO stats = dashboardService.getDashboardStats(organizationId, userId);
|
||||
return Response.ok(stats).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la récupération des statistiques dashboard");
|
||||
return Response.serverError().entity(Map.of("error", "Erreur serveur")).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les activités récentes
|
||||
*/
|
||||
@GET
|
||||
@Path("/activities")
|
||||
@Operation(
|
||||
summary = "Récupérer les activités récentes",
|
||||
description = "Retourne la liste des activités récentes avec pagination"
|
||||
)
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Activités récupérées avec succès"),
|
||||
@APIResponse(responseCode = "400", description = "Paramètres invalides"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
public Response getRecentActivities(
|
||||
@Parameter(description = "ID de l'organisation", required = true)
|
||||
@QueryParam("organizationId") @NotNull String organizationId,
|
||||
@Parameter(description = "ID de l'utilisateur", required = true)
|
||||
@QueryParam("userId") @NotNull String userId,
|
||||
@Parameter(description = "Nombre maximum d'activités à retourner", required = false)
|
||||
@QueryParam("limit") @DefaultValue("10") int limit) {
|
||||
|
||||
LOG.infof("GET /api/v1/dashboard/activities - org: %s, user: %s, limit: %d",
|
||||
organizationId, userId, limit);
|
||||
|
||||
try {
|
||||
List<RecentActivityDTO> activities = dashboardService.getRecentActivities(
|
||||
organizationId, userId, limit);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("activities", activities);
|
||||
response.put("total", activities.size());
|
||||
response.put("limit", limit);
|
||||
|
||||
return Response.ok(response).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la récupération des activités récentes");
|
||||
return Response.serverError().entity(Map.of("error", "Erreur serveur")).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les événements à venir
|
||||
*/
|
||||
@GET
|
||||
@Path("/events/upcoming")
|
||||
@Operation(
|
||||
summary = "Récupérer les événements à venir",
|
||||
description = "Retourne la liste des événements à venir avec pagination"
|
||||
)
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Événements récupérés avec succès"),
|
||||
@APIResponse(responseCode = "400", description = "Paramètres invalides"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
public Response getUpcomingEvents(
|
||||
@Parameter(description = "ID de l'organisation", required = true)
|
||||
@QueryParam("organizationId") @NotNull String organizationId,
|
||||
@Parameter(description = "ID de l'utilisateur", required = true)
|
||||
@QueryParam("userId") @NotNull String userId,
|
||||
@Parameter(description = "Nombre maximum d'événements à retourner", required = false)
|
||||
@QueryParam("limit") @DefaultValue("5") int limit) {
|
||||
|
||||
LOG.infof("GET /api/v1/dashboard/events/upcoming - org: %s, user: %s, limit: %d",
|
||||
organizationId, userId, limit);
|
||||
|
||||
try {
|
||||
List<UpcomingEventDTO> events = dashboardService.getUpcomingEvents(
|
||||
organizationId, userId, limit);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("events", events);
|
||||
response.put("total", events.size());
|
||||
response.put("limit", limit);
|
||||
|
||||
return Response.ok(response).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la récupération des événements à venir");
|
||||
return Response.serverError().entity(Map.of("error", "Erreur serveur")).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint de santé pour vérifier le statut du dashboard
|
||||
*/
|
||||
@GET
|
||||
@Path("/health")
|
||||
@Operation(
|
||||
summary = "Vérifier la santé du service dashboard",
|
||||
description = "Retourne le statut de santé du service dashboard"
|
||||
)
|
||||
@APIResponse(responseCode = "200", description = "Service en bonne santé")
|
||||
public Response healthCheck() {
|
||||
LOG.debug("GET /api/v1/dashboard/health");
|
||||
|
||||
Map<String, Object> health = new HashMap<>();
|
||||
health.put("status", "UP");
|
||||
health.put("service", "dashboard");
|
||||
health.put("timestamp", System.currentTimeMillis());
|
||||
health.put("version", "1.0.0");
|
||||
|
||||
return Response.ok(health).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint pour rafraîchir les données du dashboard
|
||||
*/
|
||||
@POST
|
||||
@Path("/refresh")
|
||||
@Operation(
|
||||
summary = "Rafraîchir les données du dashboard",
|
||||
description = "Force la mise à jour des données du dashboard"
|
||||
)
|
||||
@APIResponses(value = {
|
||||
@APIResponse(responseCode = "200", description = "Données rafraîchies avec succès"),
|
||||
@APIResponse(responseCode = "400", description = "Paramètres invalides"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur serveur")
|
||||
})
|
||||
public Response refreshDashboard(
|
||||
@Parameter(description = "ID de l'organisation", required = true)
|
||||
@QueryParam("organizationId") @NotNull String organizationId,
|
||||
@Parameter(description = "ID de l'utilisateur", required = true)
|
||||
@QueryParam("userId") @NotNull String userId) {
|
||||
|
||||
LOG.infof("POST /api/v1/dashboard/refresh - org: %s, user: %s", organizationId, userId);
|
||||
|
||||
try {
|
||||
// Simuler un rafraîchissement (dans un vrai système, cela pourrait vider le cache)
|
||||
DashboardDataDTO dashboardData = dashboardService.getDashboardData(organizationId, userId);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("status", "refreshed");
|
||||
response.put("timestamp", System.currentTimeMillis());
|
||||
response.put("data", dashboardData);
|
||||
|
||||
return Response.ok(response).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors du rafraîchissement du dashboard");
|
||||
return Response.serverError().entity(Map.of("error", "Erreur serveur")).build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,158 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.document.DocumentDTO;
|
||||
import dev.lions.unionflow.server.api.dto.document.PieceJointeDTO;
|
||||
import dev.lions.unionflow.server.service.DocumentService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Resource REST pour la gestion documentaire
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Path("/api/documents")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@RolesAllowed({"ADMIN", "MEMBRE", "USER"})
|
||||
public class DocumentResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(DocumentResource.class);
|
||||
|
||||
@Inject DocumentService documentService;
|
||||
|
||||
/**
|
||||
* Crée un nouveau document
|
||||
*
|
||||
* @param documentDTO DTO du document à créer
|
||||
* @return Document créé
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
public Response creerDocument(@Valid DocumentDTO documentDTO) {
|
||||
try {
|
||||
DocumentDTO result = documentService.creerDocument(documentDTO);
|
||||
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la création du document");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la création du document: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve un document par son ID
|
||||
*
|
||||
* @param id ID du document
|
||||
* @return Document
|
||||
*/
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
public Response trouverParId(@PathParam("id") UUID id) {
|
||||
try {
|
||||
DocumentDTO result = documentService.trouverParId(id);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Document non trouvé"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la recherche du document");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la recherche du document: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Enregistre un téléchargement de document
|
||||
*
|
||||
* @param id ID du document
|
||||
* @return Succès
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/{id}/telechargement")
|
||||
public Response enregistrerTelechargement(@PathParam("id") UUID id) {
|
||||
try {
|
||||
documentService.enregistrerTelechargement(id);
|
||||
return Response.ok().build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Document non trouvé"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'enregistrement du téléchargement");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(
|
||||
new ErrorResponse(
|
||||
"Erreur lors de l'enregistrement du téléchargement: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une pièce jointe
|
||||
*
|
||||
* @param pieceJointeDTO DTO de la pièce jointe à créer
|
||||
* @return Pièce jointe créée
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/pieces-jointes")
|
||||
public Response creerPieceJointe(@Valid PieceJointeDTO pieceJointeDTO) {
|
||||
try {
|
||||
PieceJointeDTO result = documentService.creerPieceJointe(pieceJointeDTO);
|
||||
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la création de la pièce jointe");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la création de la pièce jointe: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste toutes les pièces jointes d'un document
|
||||
*
|
||||
* @param documentId ID du document
|
||||
* @return Liste des pièces jointes
|
||||
*/
|
||||
@GET
|
||||
@Path("/{documentId}/pieces-jointes")
|
||||
public Response listerPiecesJointesParDocument(@PathParam("documentId") UUID documentId) {
|
||||
try {
|
||||
List<PieceJointeDTO> result = documentService.listerPiecesJointesParDocument(documentId);
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la liste des pièces jointes");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la liste des pièces jointes: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Classe interne pour les réponses d'erreur */
|
||||
public static class ErrorResponse {
|
||||
public String error;
|
||||
|
||||
public ErrorResponse(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,452 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.dto.EvenementMobileDTO;
|
||||
import dev.lions.unionflow.server.entity.Evenement;
|
||||
import dev.lions.unionflow.server.entity.Evenement.StatutEvenement;
|
||||
import dev.lions.unionflow.server.entity.Evenement.TypeEvenement;
|
||||
import dev.lions.unionflow.server.service.EvenementService;
|
||||
import java.util.stream.Collectors;
|
||||
import io.quarkus.panache.common.Page;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.validation.constraints.Min;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Resource REST pour la gestion des événements
|
||||
*
|
||||
* <p>Fournit les endpoints API pour les opérations CRUD sur les événements, optimisé pour
|
||||
* l'intégration avec l'application mobile UnionFlow.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-15
|
||||
*/
|
||||
@Path("/api/evenements")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Événements", description = "Gestion des événements de l'union")
|
||||
public class EvenementResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(EvenementResource.class);
|
||||
|
||||
@Inject EvenementService evenementService;
|
||||
|
||||
/** Endpoint de test public pour vérifier la connectivité */
|
||||
@GET
|
||||
@Path("/test")
|
||||
@Operation(
|
||||
summary = "Test de connectivité",
|
||||
description = "Endpoint public pour tester la connectivité")
|
||||
@APIResponse(responseCode = "200", description = "Test réussi")
|
||||
public Response testConnectivity() {
|
||||
LOG.info("Test de connectivité appelé depuis l'application mobile");
|
||||
return Response.ok(
|
||||
Map.of(
|
||||
"status", "success",
|
||||
"message", "Serveur UnionFlow opérationnel",
|
||||
"timestamp", System.currentTimeMillis(),
|
||||
"version", "1.0.0"))
|
||||
.build();
|
||||
}
|
||||
|
||||
/** Endpoint de debug pour vérifier le chargement des données */
|
||||
@GET
|
||||
@Path("/count")
|
||||
@Operation(summary = "Compter les événements", description = "Compte le nombre d'événements dans la base")
|
||||
@APIResponse(responseCode = "200", description = "Nombre d'événements")
|
||||
public Response countEvenements() {
|
||||
try {
|
||||
long count = evenementService.countEvenements();
|
||||
return Response.ok(Map.of("count", count, "status", "success")).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf("Erreur count: %s", e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Liste tous les événements actifs avec pagination */
|
||||
@GET
|
||||
@Operation(
|
||||
summary = "Lister tous les événements actifs",
|
||||
description = "Récupère la liste paginée des événements actifs")
|
||||
@APIResponse(responseCode = "200", description = "Liste des événements actifs")
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié")
|
||||
// @RolesAllowed({"ADMIN", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE"}) // Temporairement désactivé
|
||||
public Response listerEvenements(
|
||||
@Parameter(description = "Numéro de page (0-based)", example = "0")
|
||||
@QueryParam("page")
|
||||
@DefaultValue("0")
|
||||
@Min(0)
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page", example = "20")
|
||||
@QueryParam("size")
|
||||
@DefaultValue("20")
|
||||
@Min(1)
|
||||
int size,
|
||||
@Parameter(description = "Champ de tri", example = "dateDebut")
|
||||
@QueryParam("sort")
|
||||
@DefaultValue("dateDebut")
|
||||
String sortField,
|
||||
@Parameter(description = "Direction du tri (asc/desc)", example = "asc")
|
||||
@QueryParam("direction")
|
||||
@DefaultValue("asc")
|
||||
String sortDirection) {
|
||||
|
||||
try {
|
||||
LOG.infof("GET /api/evenements - page: %d, size: %d", page, size);
|
||||
|
||||
Sort sort =
|
||||
sortDirection.equalsIgnoreCase("desc")
|
||||
? Sort.by(sortField).descending()
|
||||
: Sort.by(sortField).ascending();
|
||||
|
||||
List<Evenement> evenements =
|
||||
evenementService.listerEvenementsActifs(Page.of(page, size), sort);
|
||||
|
||||
LOG.infof("Nombre d'événements récupérés: %d", evenements.size());
|
||||
|
||||
// Convertir en DTO mobile
|
||||
List<EvenementMobileDTO> evenementsDTOs = new ArrayList<>();
|
||||
for (Evenement evenement : evenements) {
|
||||
try {
|
||||
EvenementMobileDTO dto = EvenementMobileDTO.fromEntity(evenement);
|
||||
evenementsDTOs.add(dto);
|
||||
} catch (Exception e) {
|
||||
LOG.errorf("Erreur lors de la conversion de l'événement %s: %s", evenement.getId(), e.getMessage());
|
||||
// Continuer avec les autres événements
|
||||
}
|
||||
}
|
||||
|
||||
LOG.infof("Nombre de DTOs créés: %d", evenementsDTOs.size());
|
||||
|
||||
// Compter le total d'événements actifs
|
||||
long total = evenementService.countEvenementsActifs();
|
||||
int totalPages = total > 0 ? (int) Math.ceil((double) total / size) : 0;
|
||||
|
||||
// Retourner la structure paginée attendue par le mobile
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("data", evenementsDTOs);
|
||||
response.put("total", total);
|
||||
response.put("page", page);
|
||||
response.put("size", size);
|
||||
response.put("totalPages", totalPages);
|
||||
|
||||
LOG.infof("Réponse prête: %d événements, total=%d, pages=%d", evenementsDTOs.size(), total, totalPages);
|
||||
|
||||
return Response.ok(response)
|
||||
.header("Content-Type", "application/json;charset=UTF-8")
|
||||
.build();
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.errorf("Erreur lors de la récupération des événements: %s", e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de la récupération des événements: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère un événement par son ID */
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Récupérer un événement par ID")
|
||||
@APIResponse(responseCode = "200", description = "Événement trouvé")
|
||||
@APIResponse(responseCode = "404", description = "Événement non trouvé")
|
||||
@RolesAllowed({"ADMIN", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE"})
|
||||
public Response obtenirEvenement(
|
||||
@Parameter(description = "UUID de l'événement", required = true) @PathParam("id") UUID id) {
|
||||
|
||||
try {
|
||||
LOG.infof("GET /api/evenements/%s", id);
|
||||
|
||||
Optional<Evenement> evenement = evenementService.trouverParId(id);
|
||||
|
||||
if (evenement.isPresent()) {
|
||||
return Response.ok(evenement.get()).build();
|
||||
} else {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Événement non trouvé"))
|
||||
.build();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.errorf("Erreur lors de la récupération de l'événement %d: %s", id, e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de la récupération de l'événement"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Crée un nouvel événement */
|
||||
@POST
|
||||
@Operation(summary = "Créer un nouvel événement")
|
||||
@APIResponse(responseCode = "201", description = "Événement créé avec succès")
|
||||
@APIResponse(responseCode = "400", description = "Données invalides")
|
||||
@RolesAllowed({"ADMIN", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT"})
|
||||
public Response creerEvenement(
|
||||
@Parameter(description = "Données de l'événement à créer", required = true) @Valid
|
||||
Evenement evenement) {
|
||||
|
||||
try {
|
||||
LOG.infof("POST /api/evenements - Création événement: %s", evenement.getTitre());
|
||||
|
||||
Evenement evenementCree = evenementService.creerEvenement(evenement);
|
||||
|
||||
return Response.status(Response.Status.CREATED).entity(evenementCree).build();
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warnf("Données invalides: %s", e.getMessage());
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (SecurityException e) {
|
||||
LOG.warnf("Permissions insuffisantes: %s", e.getMessage());
|
||||
return Response.status(Response.Status.FORBIDDEN)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf("Erreur lors de la création: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de la création de l'événement"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Met à jour un événement existant */
|
||||
@PUT
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Mettre à jour un événement")
|
||||
@APIResponse(responseCode = "200", description = "Événement mis à jour avec succès")
|
||||
@APIResponse(responseCode = "404", description = "Événement non trouvé")
|
||||
@RolesAllowed({"ADMIN", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT"})
|
||||
public Response mettreAJourEvenement(@PathParam("id") UUID id, @Valid Evenement evenement) {
|
||||
|
||||
try {
|
||||
LOG.infof("PUT /api/evenements/%s", id);
|
||||
|
||||
Evenement evenementMisAJour = evenementService.mettreAJourEvenement(id, evenement);
|
||||
|
||||
return Response.ok(evenementMisAJour).build();
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (SecurityException e) {
|
||||
return Response.status(Response.Status.FORBIDDEN)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf("Erreur lors de la mise à jour: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de la mise à jour"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Supprime un événement */
|
||||
@DELETE
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Supprimer un événement")
|
||||
@APIResponse(responseCode = "204", description = "Événement supprimé avec succès")
|
||||
@RolesAllowed({"ADMIN", "PRESIDENT", "ORGANISATEUR_EVENEMENT"})
|
||||
public Response supprimerEvenement(@PathParam("id") UUID id) {
|
||||
|
||||
try {
|
||||
LOG.infof("DELETE /api/evenements/%s", id);
|
||||
|
||||
evenementService.supprimerEvenement(id);
|
||||
|
||||
return Response.noContent().build();
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (IllegalStateException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (SecurityException e) {
|
||||
return Response.status(Response.Status.FORBIDDEN)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf("Erreur lors de la suppression: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de la suppression"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Endpoints spécialisés pour l'application mobile */
|
||||
|
||||
/** Liste les événements à venir */
|
||||
@GET
|
||||
@Path("/a-venir")
|
||||
@Operation(summary = "Événements à venir")
|
||||
@RolesAllowed({"ADMIN", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE"})
|
||||
public Response evenementsAVenir(
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("10") int size) {
|
||||
|
||||
try {
|
||||
List<Evenement> evenements =
|
||||
evenementService.listerEvenementsAVenir(
|
||||
Page.of(page, size), Sort.by("dateDebut").ascending());
|
||||
|
||||
return Response.ok(evenements).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf("Erreur événements à venir: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de la récupération"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Liste les événements publics */
|
||||
@GET
|
||||
@Path("/publics")
|
||||
@Operation(summary = "Événements publics")
|
||||
public Response evenementsPublics(
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size) {
|
||||
|
||||
try {
|
||||
List<Evenement> evenements =
|
||||
evenementService.listerEvenementsPublics(
|
||||
Page.of(page, size), Sort.by("dateDebut").ascending());
|
||||
|
||||
return Response.ok(evenements).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf("Erreur événements publics: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de la récupération"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Recherche d'événements */
|
||||
@GET
|
||||
@Path("/recherche")
|
||||
@Operation(summary = "Rechercher des événements")
|
||||
@RolesAllowed({"ADMIN", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE"})
|
||||
public Response rechercherEvenements(
|
||||
@QueryParam("q") String recherche,
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size) {
|
||||
|
||||
try {
|
||||
if (recherche == null || recherche.trim().isEmpty()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Le terme de recherche est obligatoire"))
|
||||
.build();
|
||||
}
|
||||
|
||||
List<Evenement> evenements =
|
||||
evenementService.rechercherEvenements(
|
||||
recherche, Page.of(page, size), Sort.by("dateDebut").ascending());
|
||||
|
||||
return Response.ok(evenements).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf("Erreur recherche: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de la recherche"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Événements par type */
|
||||
@GET
|
||||
@Path("/type/{type}")
|
||||
@Operation(summary = "Événements par type")
|
||||
@RolesAllowed({"ADMIN", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT", "MEMBRE"})
|
||||
public Response evenementsParType(
|
||||
@PathParam("type") TypeEvenement type,
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size) {
|
||||
|
||||
try {
|
||||
List<Evenement> evenements =
|
||||
evenementService.listerParType(
|
||||
type, Page.of(page, size), Sort.by("dateDebut").ascending());
|
||||
|
||||
return Response.ok(evenements).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf("Erreur événements par type: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de la récupération"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Change le statut d'un événement */
|
||||
@PATCH
|
||||
@Path("/{id}/statut")
|
||||
@Operation(summary = "Changer le statut d'un événement")
|
||||
@RolesAllowed({"ADMIN", "PRESIDENT", "ORGANISATEUR_EVENEMENT"})
|
||||
public Response changerStatut(
|
||||
@PathParam("id") UUID id, @QueryParam("statut") StatutEvenement nouveauStatut) {
|
||||
|
||||
try {
|
||||
if (nouveauStatut == null) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Le nouveau statut est obligatoire"))
|
||||
.build();
|
||||
}
|
||||
|
||||
Evenement evenement = evenementService.changerStatut(id, nouveauStatut);
|
||||
|
||||
return Response.ok(evenement).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (SecurityException e) {
|
||||
return Response.status(Response.Status.FORBIDDEN)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf("Erreur changement statut: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors du changement de statut"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Statistiques des événements */
|
||||
@GET
|
||||
@Path("/statistiques")
|
||||
@Operation(summary = "Statistiques des événements")
|
||||
@RolesAllowed({"ADMIN", "PRESIDENT", "SECRETAIRE", "ORGANISATEUR_EVENEMENT"})
|
||||
public Response obtenirStatistiques() {
|
||||
|
||||
try {
|
||||
Map<String, Object> statistiques = evenementService.obtenirStatistiques();
|
||||
|
||||
return Response.ok(statistiques).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf("Erreur statistiques: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors du calcul des statistiques"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,119 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.service.ExportService;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/** Resource REST pour l'export des données */
|
||||
@Path("/api/export")
|
||||
@ApplicationScoped
|
||||
@Tag(name = "Export", description = "API d'export des données")
|
||||
@RolesAllowed({"ADMIN", "MEMBRE", "USER"})
|
||||
public class ExportResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(ExportResource.class);
|
||||
|
||||
@Inject ExportService exportService;
|
||||
|
||||
@GET
|
||||
@Path("/cotisations/csv")
|
||||
@Produces("text/csv")
|
||||
@Operation(summary = "Exporter les cotisations en CSV")
|
||||
@APIResponse(responseCode = "200", description = "Fichier CSV généré")
|
||||
public Response exporterCotisationsCSV(
|
||||
@QueryParam("statut") String statut,
|
||||
@QueryParam("type") String type,
|
||||
@QueryParam("associationId") UUID associationId) {
|
||||
LOG.info("Export CSV des cotisations");
|
||||
|
||||
byte[] csv = exportService.exporterToutesCotisationsCSV(statut, type, associationId);
|
||||
|
||||
return Response.ok(csv)
|
||||
.header("Content-Disposition", "attachment; filename=\"cotisations.csv\"")
|
||||
.header("Content-Type", "text/csv; charset=UTF-8")
|
||||
.build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/cotisations/csv")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces("text/csv")
|
||||
@Operation(summary = "Exporter des cotisations spécifiques en CSV")
|
||||
@APIResponse(responseCode = "200", description = "Fichier CSV généré")
|
||||
public Response exporterCotisationsSelectionneesCSV(List<UUID> cotisationIds) {
|
||||
LOG.infof("Export CSV de %d cotisations", cotisationIds.size());
|
||||
|
||||
byte[] csv = exportService.exporterCotisationsCSV(cotisationIds);
|
||||
|
||||
return Response.ok(csv)
|
||||
.header("Content-Disposition", "attachment; filename=\"cotisations.csv\"")
|
||||
.header("Content-Type", "text/csv; charset=UTF-8")
|
||||
.build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/cotisations/{cotisationId}/recu")
|
||||
@Produces("text/plain")
|
||||
@Operation(summary = "Générer un reçu de paiement")
|
||||
@APIResponse(responseCode = "200", description = "Reçu généré")
|
||||
public Response genererRecu(@PathParam("cotisationId") UUID cotisationId) {
|
||||
LOG.infof("Génération reçu pour: %s", cotisationId);
|
||||
|
||||
byte[] recu = exportService.genererRecuPaiement(cotisationId);
|
||||
|
||||
return Response.ok(recu)
|
||||
.header("Content-Disposition", "attachment; filename=\"recu-" + cotisationId + ".txt\"")
|
||||
.header("Content-Type", "text/plain; charset=UTF-8")
|
||||
.build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/cotisations/recus")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces("text/plain")
|
||||
@Operation(summary = "Générer des reçus groupés")
|
||||
@APIResponse(responseCode = "200", description = "Reçus générés")
|
||||
public Response genererRecusGroupes(List<UUID> cotisationIds) {
|
||||
LOG.infof("Génération de %d reçus", cotisationIds.size());
|
||||
|
||||
byte[] recus = exportService.genererRecusGroupes(cotisationIds);
|
||||
|
||||
return Response.ok(recus)
|
||||
.header("Content-Disposition", "attachment; filename=\"recus-groupes.txt\"")
|
||||
.header("Content-Type", "text/plain; charset=UTF-8")
|
||||
.build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/rapport/mensuel")
|
||||
@Produces("text/plain")
|
||||
@Operation(summary = "Générer un rapport mensuel")
|
||||
@APIResponse(responseCode = "200", description = "Rapport généré")
|
||||
public Response genererRapportMensuel(
|
||||
@QueryParam("annee") int annee,
|
||||
@QueryParam("mois") int mois,
|
||||
@QueryParam("associationId") UUID associationId) {
|
||||
LOG.infof("Génération rapport mensuel: %d/%d", mois, annee);
|
||||
|
||||
byte[] rapport = exportService.genererRapportMensuel(annee, mois, associationId);
|
||||
|
||||
return Response.ok(rapport)
|
||||
.header("Content-Disposition",
|
||||
"attachment; filename=\"rapport-" + annee + "-" + String.format("%02d", mois) + ".txt\"")
|
||||
.header("Content-Type", "text/plain; charset=UTF-8")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,33 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.Map;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
/** Resource de santé pour UnionFlow Server */
|
||||
@Path("/api/status")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@ApplicationScoped
|
||||
@Tag(name = "Status", description = "API de statut du serveur")
|
||||
public class HealthResource {
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Vérifier le statut du serveur")
|
||||
public Response getStatus() {
|
||||
return Response.ok(
|
||||
Map.of(
|
||||
"status", "UP",
|
||||
"service", "UnionFlow Server",
|
||||
"version", "1.0.0",
|
||||
"timestamp", LocalDateTime.now().toString(),
|
||||
"message", "Serveur opérationnel"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,643 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.membre.MembreDTO;
|
||||
import dev.lions.unionflow.server.api.dto.membre.MembreSearchCriteria;
|
||||
import dev.lions.unionflow.server.api.dto.membre.MembreSearchResultDTO;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.service.MembreService;
|
||||
import io.quarkus.panache.common.Page;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.io.InputStream;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Content;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.ExampleObject;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||
import org.eclipse.microprofile.openapi.annotations.parameters.RequestBody;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||
import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/** Resource REST pour la gestion des membres */
|
||||
@Path("/api/membres")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@ApplicationScoped
|
||||
@Tag(name = "Membres", description = "API de gestion des membres")
|
||||
public class MembreResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MembreResource.class);
|
||||
|
||||
@Inject MembreService membreService;
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Lister tous les membres actifs")
|
||||
@APIResponse(responseCode = "200", description = "Liste des membres actifs")
|
||||
public Response listerMembres(
|
||||
@Parameter(description = "Numéro de page (0-based)") @QueryParam("page") @DefaultValue("0")
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page") @QueryParam("size") @DefaultValue("20")
|
||||
int size,
|
||||
@Parameter(description = "Champ de tri") @QueryParam("sort") @DefaultValue("nom")
|
||||
String sortField,
|
||||
@Parameter(description = "Direction du tri (asc/desc)")
|
||||
@QueryParam("direction")
|
||||
@DefaultValue("asc")
|
||||
String sortDirection) {
|
||||
|
||||
LOG.infof("Récupération de la liste des membres actifs - page: %d, size: %d", page, size);
|
||||
|
||||
Sort sort =
|
||||
"desc".equalsIgnoreCase(sortDirection)
|
||||
? Sort.by(sortField).descending()
|
||||
: Sort.by(sortField).ascending();
|
||||
|
||||
List<Membre> membres = membreService.listerMembresActifs(Page.of(page, size), sort);
|
||||
List<MembreDTO> membresDTO = membreService.convertToDTOList(membres);
|
||||
|
||||
return Response.ok(membresDTO).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Récupérer un membre par son ID")
|
||||
@APIResponse(responseCode = "200", description = "Membre trouvé")
|
||||
@APIResponse(responseCode = "404", description = "Membre non trouvé")
|
||||
public Response obtenirMembre(@Parameter(description = "UUID du membre") @PathParam("id") UUID id) {
|
||||
LOG.infof("Récupération du membre ID: %s", id);
|
||||
return membreService
|
||||
.trouverParId(id)
|
||||
.map(
|
||||
membre -> {
|
||||
MembreDTO membreDTO = membreService.convertToDTO(membre);
|
||||
return Response.ok(membreDTO).build();
|
||||
})
|
||||
.orElse(
|
||||
Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("message", "Membre non trouvé"))
|
||||
.build());
|
||||
}
|
||||
|
||||
@POST
|
||||
@PermitAll
|
||||
@Operation(summary = "Créer un nouveau membre")
|
||||
@APIResponse(responseCode = "201", description = "Membre créé avec succès")
|
||||
@APIResponse(responseCode = "400", description = "Données invalides")
|
||||
public Response creerMembre(@Valid MembreDTO membreDTO) {
|
||||
LOG.infof("Création d'un nouveau membre: %s", membreDTO.getEmail());
|
||||
try {
|
||||
// Conversion DTO vers entité
|
||||
Membre membre = membreService.convertFromDTO(membreDTO);
|
||||
|
||||
// Création du membre
|
||||
Membre nouveauMembre = membreService.creerMembre(membre);
|
||||
|
||||
// Conversion de retour vers DTO
|
||||
MembreDTO nouveauMembreDTO = membreService.convertToDTO(nouveauMembre);
|
||||
|
||||
return Response.status(Response.Status.CREATED).entity(nouveauMembreDTO).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Mettre à jour un membre existant")
|
||||
@APIResponse(responseCode = "200", description = "Membre mis à jour avec succès")
|
||||
@APIResponse(responseCode = "404", description = "Membre non trouvé")
|
||||
@APIResponse(responseCode = "400", description = "Données invalides")
|
||||
public Response mettreAJourMembre(
|
||||
@Parameter(description = "UUID du membre") @PathParam("id") UUID id,
|
||||
@Valid MembreDTO membreDTO) {
|
||||
LOG.infof("Mise à jour du membre ID: %s", id);
|
||||
try {
|
||||
// Conversion DTO vers entité
|
||||
Membre membre = membreService.convertFromDTO(membreDTO);
|
||||
|
||||
// Mise à jour du membre
|
||||
Membre membreMisAJour = membreService.mettreAJourMembre(id, membre);
|
||||
|
||||
// Conversion de retour vers DTO
|
||||
MembreDTO membreMisAJourDTO = membreService.convertToDTO(membreMisAJour);
|
||||
|
||||
return Response.ok(membreMisAJourDTO).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@DELETE
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Désactiver un membre")
|
||||
@APIResponse(responseCode = "204", description = "Membre désactivé avec succès")
|
||||
@APIResponse(responseCode = "404", description = "Membre non trouvé")
|
||||
public Response desactiverMembre(
|
||||
@Parameter(description = "UUID du membre") @PathParam("id") UUID id) {
|
||||
LOG.infof("Désactivation du membre ID: %s", id);
|
||||
try {
|
||||
membreService.desactiverMembre(id);
|
||||
return Response.noContent().build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/recherche")
|
||||
@Operation(summary = "Rechercher des membres par nom ou prénom")
|
||||
@APIResponse(responseCode = "200", description = "Résultats de la recherche")
|
||||
public Response rechercherMembres(
|
||||
@Parameter(description = "Terme de recherche") @QueryParam("q") String recherche,
|
||||
@Parameter(description = "Numéro de page (0-based)") @QueryParam("page") @DefaultValue("0")
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page") @QueryParam("size") @DefaultValue("20")
|
||||
int size,
|
||||
@Parameter(description = "Champ de tri") @QueryParam("sort") @DefaultValue("nom")
|
||||
String sortField,
|
||||
@Parameter(description = "Direction du tri (asc/desc)")
|
||||
@QueryParam("direction")
|
||||
@DefaultValue("asc")
|
||||
String sortDirection) {
|
||||
|
||||
LOG.infof("Recherche de membres avec le terme: %s", recherche);
|
||||
if (recherche == null || recherche.trim().isEmpty()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("message", "Le terme de recherche est requis"))
|
||||
.build();
|
||||
}
|
||||
|
||||
Sort sort =
|
||||
"desc".equalsIgnoreCase(sortDirection)
|
||||
? Sort.by(sortField).descending()
|
||||
: Sort.by(sortField).ascending();
|
||||
|
||||
List<Membre> membres =
|
||||
membreService.rechercherMembres(recherche.trim(), Page.of(page, size), sort);
|
||||
List<MembreDTO> membresDTO = membreService.convertToDTOList(membres);
|
||||
|
||||
return Response.ok(membresDTO).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/stats")
|
||||
@Operation(summary = "Obtenir les statistiques avancées des membres")
|
||||
@APIResponse(responseCode = "200", description = "Statistiques complètes des membres")
|
||||
public Response obtenirStatistiques() {
|
||||
LOG.info("Récupération des statistiques avancées des membres");
|
||||
Map<String, Object> statistiques = membreService.obtenirStatistiquesAvancees();
|
||||
return Response.ok(statistiques).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/autocomplete/villes")
|
||||
@Operation(summary = "Obtenir la liste des villes pour autocomplétion")
|
||||
@APIResponse(responseCode = "200", description = "Liste des villes distinctes")
|
||||
public Response obtenirVilles(
|
||||
@Parameter(description = "Terme de recherche (optionnel)") @QueryParam("query") String query) {
|
||||
LOG.infof("Récupération des villes pour autocomplétion - query: %s", query);
|
||||
List<String> villes = membreService.obtenirVillesDistinctes(query);
|
||||
return Response.ok(villes).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/autocomplete/professions")
|
||||
@Operation(summary = "Obtenir la liste des professions pour autocomplétion")
|
||||
@APIResponse(responseCode = "200", description = "Liste des professions distinctes")
|
||||
public Response obtenirProfessions(
|
||||
@Parameter(description = "Terme de recherche (optionnel)") @QueryParam("query") String query) {
|
||||
LOG.infof("Récupération des professions pour autocomplétion - query: %s", query);
|
||||
List<String> professions = membreService.obtenirProfessionsDistinctes(query);
|
||||
return Response.ok(professions).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/recherche-avancee")
|
||||
@Operation(summary = "Recherche avancée de membres avec filtres multiples (DEPRECATED)")
|
||||
@APIResponse(responseCode = "200", description = "Résultats de la recherche avancée")
|
||||
@Deprecated
|
||||
public Response rechercheAvancee(
|
||||
@Parameter(description = "Terme de recherche") @QueryParam("q") String recherche,
|
||||
@Parameter(description = "Statut actif (true/false)") @QueryParam("actif") Boolean actif,
|
||||
@Parameter(description = "Date d'adhésion minimum (YYYY-MM-DD)")
|
||||
@QueryParam("dateAdhesionMin")
|
||||
String dateAdhesionMin,
|
||||
@Parameter(description = "Date d'adhésion maximum (YYYY-MM-DD)")
|
||||
@QueryParam("dateAdhesionMax")
|
||||
String dateAdhesionMax,
|
||||
@Parameter(description = "Numéro de page (0-based)") @QueryParam("page") @DefaultValue("0")
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page") @QueryParam("size") @DefaultValue("20")
|
||||
int size,
|
||||
@Parameter(description = "Champ de tri") @QueryParam("sort") @DefaultValue("nom")
|
||||
String sortField,
|
||||
@Parameter(description = "Direction du tri (asc/desc)")
|
||||
@QueryParam("direction")
|
||||
@DefaultValue("asc")
|
||||
String sortDirection) {
|
||||
|
||||
LOG.infof(
|
||||
"Recherche avancée de membres (DEPRECATED) - recherche: %s, actif: %s", recherche, actif);
|
||||
|
||||
try {
|
||||
Sort sort =
|
||||
"desc".equalsIgnoreCase(sortDirection)
|
||||
? Sort.by(sortField).descending()
|
||||
: Sort.by(sortField).ascending();
|
||||
|
||||
// Conversion des dates si fournies
|
||||
java.time.LocalDate dateMin =
|
||||
dateAdhesionMin != null ? java.time.LocalDate.parse(dateAdhesionMin) : null;
|
||||
java.time.LocalDate dateMax =
|
||||
dateAdhesionMax != null ? java.time.LocalDate.parse(dateAdhesionMax) : null;
|
||||
|
||||
List<Membre> membres =
|
||||
membreService.rechercheAvancee(
|
||||
recherche, actif, dateMin, dateMax, Page.of(page, size), sort);
|
||||
List<MembreDTO> membresDTO = membreService.convertToDTOList(membres);
|
||||
|
||||
return Response.ok(membresDTO).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf("Erreur lors de la recherche avancée: %s", e.getMessage());
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("message", "Erreur dans les paramètres de recherche: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Nouvelle recherche avancée avec critères complets et résultats enrichis Réservée aux super
|
||||
* administrateurs pour des recherches sophistiquées
|
||||
*/
|
||||
@POST
|
||||
@Path("/search/advanced")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(
|
||||
summary = "Recherche avancée de membres avec critères multiples",
|
||||
description =
|
||||
"""
|
||||
Recherche sophistiquée de membres avec de nombreux critères de filtrage :
|
||||
- Recherche textuelle dans nom, prénom, email
|
||||
- Filtres par organisation, rôles, statut
|
||||
- Filtres par âge, région, profession
|
||||
- Filtres par dates d'adhésion
|
||||
- Résultats paginés avec statistiques
|
||||
|
||||
Réservée aux super administrateurs et administrateurs.
|
||||
""")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "Recherche effectuée avec succès",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = MembreSearchResultDTO.class),
|
||||
examples =
|
||||
@ExampleObject(
|
||||
name = "Exemple de résultats",
|
||||
value =
|
||||
"""
|
||||
{
|
||||
"membres": [...],
|
||||
"totalElements": 247,
|
||||
"totalPages": 13,
|
||||
"currentPage": 0,
|
||||
"pageSize": 20,
|
||||
"hasNext": true,
|
||||
"hasPrevious": false,
|
||||
"executionTimeMs": 45,
|
||||
"statistics": {
|
||||
"membresActifs": 230,
|
||||
"membresInactifs": 17,
|
||||
"ageMoyen": 34.5,
|
||||
"nombreOrganisations": 12
|
||||
}
|
||||
}
|
||||
"""))),
|
||||
@APIResponse(
|
||||
responseCode = "400",
|
||||
description = "Critères de recherche invalides",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
examples =
|
||||
@ExampleObject(
|
||||
value =
|
||||
"""
|
||||
{
|
||||
"message": "Critères de recherche invalides",
|
||||
"details": "La date minimum ne peut pas être postérieure à la date maximum"
|
||||
}
|
||||
"""))),
|
||||
@APIResponse(
|
||||
responseCode = "403",
|
||||
description = "Accès non autorisé - Rôle SUPER_ADMIN ou ADMIN requis"),
|
||||
@APIResponse(responseCode = "500", description = "Erreur interne du serveur")
|
||||
})
|
||||
@SecurityRequirement(name = "keycloak")
|
||||
public Response searchMembresAdvanced(
|
||||
@RequestBody(
|
||||
description = "Critères de recherche avancée",
|
||||
required = false,
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = MembreSearchCriteria.class),
|
||||
examples =
|
||||
@ExampleObject(
|
||||
name = "Exemple de critères",
|
||||
value =
|
||||
"""
|
||||
{
|
||||
"query": "marie",
|
||||
"statut": "ACTIF",
|
||||
"ageMin": 25,
|
||||
"ageMax": 45,
|
||||
"region": "Dakar",
|
||||
"roles": ["PRESIDENT", "SECRETAIRE"],
|
||||
"dateAdhesionMin": "2020-01-01",
|
||||
"includeInactifs": false
|
||||
}
|
||||
""")))
|
||||
MembreSearchCriteria criteria,
|
||||
@Parameter(description = "Numéro de page (0-based)", example = "0")
|
||||
@QueryParam("page")
|
||||
@DefaultValue("0")
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page", example = "20")
|
||||
@QueryParam("size")
|
||||
@DefaultValue("20")
|
||||
int size,
|
||||
@Parameter(description = "Champ de tri", example = "nom")
|
||||
@QueryParam("sort")
|
||||
@DefaultValue("nom")
|
||||
String sortField,
|
||||
@Parameter(description = "Direction du tri (asc/desc)", example = "asc")
|
||||
@QueryParam("direction")
|
||||
@DefaultValue("asc")
|
||||
String sortDirection) {
|
||||
|
||||
long startTime = System.currentTimeMillis();
|
||||
|
||||
try {
|
||||
// Validation des critères
|
||||
if (criteria == null) {
|
||||
LOG.warn("Recherche avancée de membres - critères null rejetés");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("message", "Les critères de recherche sont requis"))
|
||||
.build();
|
||||
}
|
||||
|
||||
LOG.infof(
|
||||
"Recherche avancée de membres - critères: %s, page: %d, size: %d",
|
||||
criteria.getDescription(), page, size);
|
||||
|
||||
// Nettoyage et validation des critères
|
||||
criteria.sanitize();
|
||||
|
||||
if (!criteria.hasAnyCriteria()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("message", "Au moins un critère de recherche doit être spécifié"))
|
||||
.build();
|
||||
}
|
||||
|
||||
if (!criteria.isValid()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(
|
||||
Map.of(
|
||||
"message", "Critères de recherche invalides",
|
||||
"details", "Vérifiez la cohérence des dates et des âges"))
|
||||
.build();
|
||||
}
|
||||
|
||||
// Construction du tri
|
||||
Sort sort =
|
||||
"desc".equalsIgnoreCase(sortDirection)
|
||||
? Sort.by(sortField).descending()
|
||||
: Sort.by(sortField).ascending();
|
||||
|
||||
// Exécution de la recherche
|
||||
MembreSearchResultDTO result =
|
||||
membreService.searchMembresAdvanced(criteria, Page.of(page, size), sort);
|
||||
|
||||
// Calcul du temps d'exécution
|
||||
long executionTime = System.currentTimeMillis() - startTime;
|
||||
result.setExecutionTimeMs(executionTime);
|
||||
|
||||
LOG.infof(
|
||||
"Recherche avancée terminée - %d résultats trouvés en %d ms",
|
||||
result.getTotalElements(), executionTime);
|
||||
|
||||
return Response.ok(result).build();
|
||||
|
||||
} catch (jakarta.validation.ConstraintViolationException e) {
|
||||
LOG.warnf("Erreur de validation Jakarta dans la recherche avancée: %s", e.getMessage());
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("message", "Critères de recherche invalides", "details", e.getMessage()))
|
||||
.build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warnf("Erreur de validation dans la recherche avancée: %s", e.getMessage());
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("message", "Paramètres de recherche invalides", "details", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la recherche avancée de membres");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("message", "Erreur interne lors de la recherche", "error", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/export/selection")
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Produces("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
@Operation(summary = "Exporter une sélection de membres en Excel")
|
||||
@APIResponse(responseCode = "200", description = "Fichier Excel généré")
|
||||
public Response exporterSelectionMembres(
|
||||
@Parameter(description = "Liste des IDs des membres à exporter") List<UUID> membreIds,
|
||||
@Parameter(description = "Format d'export") @QueryParam("format") @DefaultValue("EXCEL") String format) {
|
||||
LOG.infof("Export de %d membres sélectionnés", membreIds.size());
|
||||
try {
|
||||
byte[] excelData = membreService.exporterMembresSelectionnes(membreIds, format);
|
||||
return Response.ok(excelData)
|
||||
.header("Content-Disposition", "attachment; filename=\"membres_selection_" +
|
||||
java.time.LocalDate.now() + "." + (format != null ? format.toLowerCase() : "xlsx") + "\"")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'export de la sélection");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de l'export: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/import")
|
||||
@Consumes(MediaType.MULTIPART_FORM_DATA)
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Importer des membres depuis un fichier Excel ou CSV")
|
||||
@APIResponse(responseCode = "200", description = "Import terminé")
|
||||
public Response importerMembres(
|
||||
@Parameter(description = "Contenu du fichier à importer") @FormParam("file") byte[] fileContent,
|
||||
@Parameter(description = "Nom du fichier") @FormParam("fileName") String fileName,
|
||||
@Parameter(description = "ID de l'organisation (optionnel)") @FormParam("organisationId") UUID organisationId,
|
||||
@Parameter(description = "Type de membre par défaut") @FormParam("typeMembreDefaut") String typeMembreDefaut,
|
||||
@Parameter(description = "Mettre à jour les membres existants") @FormParam("mettreAJourExistants") boolean mettreAJourExistants,
|
||||
@Parameter(description = "Ignorer les erreurs") @FormParam("ignorerErreurs") boolean ignorerErreurs) {
|
||||
|
||||
try {
|
||||
if (fileContent == null || fileContent.length == 0) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Aucun fichier fourni"))
|
||||
.build();
|
||||
}
|
||||
|
||||
if (fileName == null || fileName.isEmpty()) {
|
||||
fileName = "import.xlsx";
|
||||
}
|
||||
|
||||
if (typeMembreDefaut == null || typeMembreDefaut.isEmpty()) {
|
||||
typeMembreDefaut = "ACTIF";
|
||||
}
|
||||
|
||||
InputStream fileInputStream = new java.io.ByteArrayInputStream(fileContent);
|
||||
dev.lions.unionflow.server.service.MembreImportExportService.ResultatImport resultat = membreService.importerMembres(
|
||||
fileInputStream, fileName, organisationId, typeMembreDefaut, mettreAJourExistants, ignorerErreurs);
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("totalLignes", resultat.totalLignes);
|
||||
response.put("lignesTraitees", resultat.lignesTraitees);
|
||||
response.put("lignesErreur", resultat.lignesErreur);
|
||||
response.put("erreurs", resultat.erreurs);
|
||||
response.put("membresImportes", resultat.membresImportes);
|
||||
|
||||
return Response.ok(response).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'import");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de l'import: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/export")
|
||||
@Produces("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
@Operation(summary = "Exporter des membres en Excel, CSV ou PDF")
|
||||
@APIResponse(responseCode = "200", description = "Fichier exporté")
|
||||
public Response exporterMembres(
|
||||
@Parameter(description = "Format d'export") @QueryParam("format") @DefaultValue("EXCEL") String format,
|
||||
@Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("associationId") UUID associationId,
|
||||
@Parameter(description = "Statut des membres") @QueryParam("statut") String statut,
|
||||
@Parameter(description = "Type de membre") @QueryParam("type") String type,
|
||||
@Parameter(description = "Date adhésion début") @QueryParam("dateAdhesionDebut") String dateAdhesionDebut,
|
||||
@Parameter(description = "Date adhésion fin") @QueryParam("dateAdhesionFin") String dateAdhesionFin,
|
||||
@Parameter(description = "Colonnes à exporter") @QueryParam("colonnes") List<String> colonnesExportList,
|
||||
@Parameter(description = "Inclure les en-têtes") @QueryParam("inclureHeaders") @DefaultValue("true") boolean inclureHeaders,
|
||||
@Parameter(description = "Formater les dates") @QueryParam("formaterDates") @DefaultValue("true") boolean formaterDates,
|
||||
@Parameter(description = "Inclure un onglet statistiques (Excel uniquement)") @QueryParam("inclureStatistiques") @DefaultValue("false") boolean inclureStatistiques,
|
||||
@Parameter(description = "Mot de passe pour chiffrer le fichier (optionnel)") @QueryParam("motDePasse") String motDePasse) {
|
||||
|
||||
try {
|
||||
// Récupérer les membres selon les filtres
|
||||
List<MembreDTO> membres = membreService.listerMembresPourExport(
|
||||
associationId, statut, type, dateAdhesionDebut, dateAdhesionFin);
|
||||
|
||||
byte[] exportData;
|
||||
String contentType;
|
||||
String extension;
|
||||
|
||||
List<String> colonnesExport = colonnesExportList != null ? colonnesExportList : new ArrayList<>();
|
||||
|
||||
if ("CSV".equalsIgnoreCase(format)) {
|
||||
exportData = membreService.exporterVersCSV(membres, colonnesExport, inclureHeaders, formaterDates);
|
||||
contentType = "text/csv";
|
||||
extension = "csv";
|
||||
} else {
|
||||
// Pour Excel, inclure les statistiques uniquement si demandé et si format Excel
|
||||
boolean stats = inclureStatistiques && "EXCEL".equalsIgnoreCase(format);
|
||||
exportData = membreService.exporterVersExcel(membres, colonnesExport, inclureHeaders, formaterDates, stats, motDePasse);
|
||||
contentType = "application/vnd.openxmlformats-officedocument.spreadsheetml.sheet";
|
||||
extension = "xlsx";
|
||||
}
|
||||
|
||||
return Response.ok(exportData)
|
||||
.type(contentType)
|
||||
.header("Content-Disposition", "attachment; filename=\"membres_export_" +
|
||||
java.time.LocalDate.now() + "." + extension + "\"")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'export");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de l'export: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/import/modele")
|
||||
@Produces("application/vnd.openxmlformats-officedocument.spreadsheetml.sheet")
|
||||
@Operation(summary = "Télécharger le modèle Excel pour l'import")
|
||||
@APIResponse(responseCode = "200", description = "Modèle Excel généré")
|
||||
public Response telechargerModeleImport() {
|
||||
try {
|
||||
byte[] modele = membreService.genererModeleImport();
|
||||
return Response.ok(modele)
|
||||
.header("Content-Disposition", "attachment; filename=\"modele_import_membres.xlsx\"")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la génération du modèle");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors de la génération du modèle: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/export/count")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Operation(summary = "Compter les membres selon les filtres pour l'export")
|
||||
@APIResponse(responseCode = "200", description = "Nombre de membres correspondant aux critères")
|
||||
public Response compterMembresPourExport(
|
||||
@Parameter(description = "ID de l'organisation (optionnel)") @QueryParam("associationId") UUID associationId,
|
||||
@Parameter(description = "Statut des membres") @QueryParam("statut") String statut,
|
||||
@Parameter(description = "Type de membre") @QueryParam("type") String type,
|
||||
@Parameter(description = "Date adhésion début") @QueryParam("dateAdhesionDebut") String dateAdhesionDebut,
|
||||
@Parameter(description = "Date adhésion fin") @QueryParam("dateAdhesionFin") String dateAdhesionFin) {
|
||||
|
||||
try {
|
||||
List<MembreDTO> membres = membreService.listerMembresPourExport(
|
||||
associationId, statut, type, dateAdhesionDebut, dateAdhesionFin);
|
||||
|
||||
return Response.ok(Map.of("count", membres.size())).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors du comptage des membres");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur lors du comptage: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,246 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.notification.NotificationDTO;
|
||||
import dev.lions.unionflow.server.api.dto.notification.TemplateNotificationDTO;
|
||||
import dev.lions.unionflow.server.service.NotificationService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Resource REST pour la gestion des notifications
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Path("/api/notifications")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@RolesAllowed({"ADMIN", "MEMBRE", "USER"})
|
||||
public class NotificationResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(NotificationResource.class);
|
||||
|
||||
@Inject NotificationService notificationService;
|
||||
|
||||
// ========================================
|
||||
// TEMPLATES
|
||||
// ========================================
|
||||
|
||||
/**
|
||||
* Crée un nouveau template de notification
|
||||
*
|
||||
* @param templateDTO DTO du template à créer
|
||||
* @return Template créé
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/templates")
|
||||
public Response creerTemplate(@Valid TemplateNotificationDTO templateDTO) {
|
||||
try {
|
||||
TemplateNotificationDTO result = notificationService.creerTemplate(templateDTO);
|
||||
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la création du template");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la création du template: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// NOTIFICATIONS
|
||||
// ========================================
|
||||
|
||||
/**
|
||||
* Crée une nouvelle notification
|
||||
*
|
||||
* @param notificationDTO DTO de la notification à créer
|
||||
* @return Notification créée
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
public Response creerNotification(@Valid NotificationDTO notificationDTO) {
|
||||
try {
|
||||
NotificationDTO result = notificationService.creerNotification(notificationDTO);
|
||||
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la création de la notification");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la création de la notification: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque une notification comme lue
|
||||
*
|
||||
* @param id ID de la notification
|
||||
* @return Notification mise à jour
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/{id}/marquer-lue")
|
||||
public Response marquerCommeLue(@PathParam("id") UUID id) {
|
||||
try {
|
||||
NotificationDTO result = notificationService.marquerCommeLue(id);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Notification non trouvée"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors du marquage de la notification");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors du marquage de la notification: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve une notification par son ID
|
||||
*
|
||||
* @param id ID de la notification
|
||||
* @return Notification
|
||||
*/
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
public Response trouverNotificationParId(@PathParam("id") UUID id) {
|
||||
try {
|
||||
NotificationDTO result = notificationService.trouverNotificationParId(id);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Notification non trouvée"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la recherche de la notification");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la recherche de la notification: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste toutes les notifications d'un membre
|
||||
*
|
||||
* @param membreId ID du membre
|
||||
* @return Liste des notifications
|
||||
*/
|
||||
@GET
|
||||
@Path("/membre/{membreId}")
|
||||
public Response listerNotificationsParMembre(@PathParam("membreId") UUID membreId) {
|
||||
try {
|
||||
List<NotificationDTO> result = notificationService.listerNotificationsParMembre(membreId);
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la liste des notifications");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la liste des notifications: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste les notifications non lues d'un membre
|
||||
*
|
||||
* @param membreId ID du membre
|
||||
* @return Liste des notifications non lues
|
||||
*/
|
||||
@GET
|
||||
@Path("/membre/{membreId}/non-lues")
|
||||
public Response listerNotificationsNonLuesParMembre(@PathParam("membreId") UUID membreId) {
|
||||
try {
|
||||
List<NotificationDTO> result = notificationService.listerNotificationsNonLuesParMembre(membreId);
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la liste des notifications non lues");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(
|
||||
new ErrorResponse(
|
||||
"Erreur lors de la liste des notifications non lues: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste les notifications en attente d'envoi
|
||||
*
|
||||
* @return Liste des notifications en attente
|
||||
*/
|
||||
@GET
|
||||
@Path("/en-attente-envoi")
|
||||
public Response listerNotificationsEnAttenteEnvoi() {
|
||||
try {
|
||||
List<NotificationDTO> result = notificationService.listerNotificationsEnAttenteEnvoi();
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la liste des notifications en attente");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(
|
||||
new ErrorResponse(
|
||||
"Erreur lors de la liste des notifications en attente: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie des notifications groupées à plusieurs membres (WOU/DRY)
|
||||
*
|
||||
* @param request DTO contenant les IDs des membres, sujet, corps et canaux
|
||||
* @return Nombre de notifications créées
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/groupees")
|
||||
public Response envoyerNotificationsGroupees(NotificationGroupeeRequest request) {
|
||||
try {
|
||||
int notificationsCreees =
|
||||
notificationService.envoyerNotificationsGroupees(
|
||||
request.membreIds, request.sujet, request.corps, request.canaux);
|
||||
return Response.ok(Map.of("notificationsCreees", notificationsCreees)).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'envoi des notifications groupées");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(
|
||||
new ErrorResponse(
|
||||
"Erreur lors de l'envoi des notifications groupées: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Classe interne pour les réponses d'erreur */
|
||||
public static class ErrorResponse {
|
||||
public String error;
|
||||
|
||||
public ErrorResponse(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
|
||||
/** Classe interne pour les requêtes de notifications groupées (WOU/DRY) */
|
||||
public static class NotificationGroupeeRequest {
|
||||
public List<UUID> membreIds;
|
||||
public String sujet;
|
||||
public String corps;
|
||||
public List<String> canaux;
|
||||
|
||||
public NotificationGroupeeRequest() {}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,423 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.organisation.OrganisationDTO;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
import dev.lions.unionflow.server.service.KeycloakService;
|
||||
import dev.lions.unionflow.server.service.OrganisationService;
|
||||
import io.quarkus.security.Authenticated;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.net.URI;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.enums.SchemaType;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Content;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Resource REST pour la gestion des organisations
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2025-01-15
|
||||
*/
|
||||
@Path("/api/organisations")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Organisations", description = "Gestion des organisations")
|
||||
@RolesAllowed({"ADMIN", "MEMBRE", "USER"})
|
||||
public class OrganisationResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(OrganisationResource.class);
|
||||
|
||||
@Inject OrganisationService organisationService;
|
||||
|
||||
@Inject KeycloakService keycloakService;
|
||||
|
||||
/** Crée une nouvelle organisation */
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
|
||||
@Operation(
|
||||
summary = "Créer une nouvelle organisation",
|
||||
description = "Crée une nouvelle organisation dans le système")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "201",
|
||||
description = "Organisation créée avec succès",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = OrganisationDTO.class))),
|
||||
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||
@APIResponse(responseCode = "409", description = "Organisation déjà existante"),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié"),
|
||||
@APIResponse(responseCode = "403", description = "Non autorisé")
|
||||
})
|
||||
public Response creerOrganisation(@Valid OrganisationDTO organisationDTO) {
|
||||
LOG.infof("Création d'une nouvelle organisation: %s", organisationDTO.getNom());
|
||||
|
||||
try {
|
||||
Organisation organisation = organisationService.convertFromDTO(organisationDTO);
|
||||
Organisation organisationCreee = organisationService.creerOrganisation(organisation);
|
||||
OrganisationDTO dto = organisationService.convertToDTO(organisationCreee);
|
||||
|
||||
return Response.created(URI.create("/api/organisations/" + organisationCreee.getId()))
|
||||
.entity(dto)
|
||||
.build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warnf("Erreur lors de la création de l'organisation: %s", e.getMessage());
|
||||
return Response.status(Response.Status.CONFLICT)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur inattendue lors de la création de l'organisation");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur interne du serveur"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère toutes les organisations actives */
|
||||
@GET
|
||||
@jakarta.annotation.security.PermitAll // ✅ Accès public pour inscription
|
||||
@Operation(
|
||||
summary = "Lister les organisations",
|
||||
description = "Récupère la liste des organisations actives avec pagination")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "Liste des organisations récupérée avec succès",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(type = SchemaType.ARRAY, implementation = OrganisationDTO.class))),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié"),
|
||||
@APIResponse(responseCode = "403", description = "Non autorisé")
|
||||
})
|
||||
public Response listerOrganisations(
|
||||
@Parameter(description = "Numéro de page (commence à 0)", example = "0")
|
||||
@QueryParam("page")
|
||||
@DefaultValue("0")
|
||||
int page,
|
||||
@Parameter(description = "Taille de la page", example = "20")
|
||||
@QueryParam("size")
|
||||
@DefaultValue("20")
|
||||
int size,
|
||||
@Parameter(description = "Terme de recherche (nom ou nom court)") @QueryParam("recherche")
|
||||
String recherche) {
|
||||
|
||||
LOG.infof(
|
||||
"Récupération des organisations - page: %d, size: %d, recherche: %s",
|
||||
page, size, recherche);
|
||||
|
||||
try {
|
||||
List<Organisation> organisations;
|
||||
|
||||
if (recherche != null && !recherche.trim().isEmpty()) {
|
||||
organisations = organisationService.rechercherOrganisations(recherche.trim(), page, size);
|
||||
} else {
|
||||
organisations = organisationService.listerOrganisationsActives(page, size);
|
||||
}
|
||||
|
||||
List<OrganisationDTO> dtos =
|
||||
organisations.stream()
|
||||
.map(organisationService::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Response.ok(dtos).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la récupération des organisations");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur interne du serveur"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Récupère une organisation par son ID */
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
|
||||
@Operation(
|
||||
summary = "Récupérer une organisation",
|
||||
description = "Récupère une organisation par son ID")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "Organisation trouvée",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = OrganisationDTO.class))),
|
||||
@APIResponse(responseCode = "404", description = "Organisation non trouvée"),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié"),
|
||||
@APIResponse(responseCode = "403", description = "Non autorisé")
|
||||
})
|
||||
public Response obtenirOrganisation(
|
||||
@Parameter(description = "UUID de l'organisation", required = true) @PathParam("id") UUID id) {
|
||||
|
||||
LOG.infof("Récupération de l'organisation ID: %d", id);
|
||||
|
||||
return organisationService
|
||||
.trouverParId(id)
|
||||
.map(
|
||||
organisation -> {
|
||||
OrganisationDTO dto = organisationService.convertToDTO(organisation);
|
||||
return Response.ok(dto).build();
|
||||
})
|
||||
.orElse(
|
||||
Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Organisation non trouvée"))
|
||||
.build());
|
||||
}
|
||||
|
||||
/** Met à jour une organisation */
|
||||
@PUT
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/{id}")
|
||||
|
||||
@Operation(
|
||||
summary = "Mettre à jour une organisation",
|
||||
description = "Met à jour les informations d'une organisation")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "Organisation mise à jour avec succès",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = OrganisationDTO.class))),
|
||||
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||
@APIResponse(responseCode = "404", description = "Organisation non trouvée"),
|
||||
@APIResponse(responseCode = "409", description = "Conflit de données"),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié"),
|
||||
@APIResponse(responseCode = "403", description = "Non autorisé")
|
||||
})
|
||||
public Response mettreAJourOrganisation(
|
||||
@Parameter(description = "UUID de l'organisation", required = true) @PathParam("id") UUID id,
|
||||
@Valid OrganisationDTO organisationDTO) {
|
||||
|
||||
LOG.infof("Mise à jour de l'organisation ID: %s", id);
|
||||
|
||||
try {
|
||||
Organisation organisationMiseAJour = organisationService.convertFromDTO(organisationDTO);
|
||||
Organisation organisation =
|
||||
organisationService.mettreAJourOrganisation(id, organisationMiseAJour, "system");
|
||||
OrganisationDTO dto = organisationService.convertToDTO(organisation);
|
||||
|
||||
return Response.ok(dto).build();
|
||||
} catch (NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warnf("Erreur lors de la mise à jour de l'organisation: %s", e.getMessage());
|
||||
return Response.status(Response.Status.CONFLICT)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur inattendue lors de la mise à jour de l'organisation");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur interne du serveur"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Supprime une organisation */
|
||||
@DELETE
|
||||
@RolesAllowed({"ADMIN"})
|
||||
@Path("/{id}")
|
||||
|
||||
@Operation(
|
||||
summary = "Supprimer une organisation",
|
||||
description = "Supprime une organisation (soft delete)")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "204", description = "Organisation supprimée avec succès"),
|
||||
@APIResponse(responseCode = "404", description = "Organisation non trouvée"),
|
||||
@APIResponse(responseCode = "409", description = "Impossible de supprimer l'organisation"),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié"),
|
||||
@APIResponse(responseCode = "403", description = "Non autorisé")
|
||||
})
|
||||
public Response supprimerOrganisation(
|
||||
@Parameter(description = "UUID de l'organisation", required = true) @PathParam("id") UUID id) {
|
||||
|
||||
LOG.infof("Suppression de l'organisation ID: %d", id);
|
||||
|
||||
try {
|
||||
organisationService.supprimerOrganisation(id, "system");
|
||||
return Response.noContent().build();
|
||||
} catch (NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (IllegalStateException e) {
|
||||
LOG.warnf("Erreur lors de la suppression de l'organisation: %s", e.getMessage());
|
||||
return Response.status(Response.Status.CONFLICT)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur inattendue lors de la suppression de l'organisation");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur interne du serveur"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Recherche avancée d'organisations */
|
||||
@GET
|
||||
@Path("/recherche")
|
||||
|
||||
@Operation(
|
||||
summary = "Recherche avancée",
|
||||
description = "Recherche d'organisations avec critères multiples")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "Résultats de recherche",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(type = SchemaType.ARRAY, implementation = OrganisationDTO.class))),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié"),
|
||||
@APIResponse(responseCode = "403", description = "Non autorisé")
|
||||
})
|
||||
public Response rechercheAvancee(
|
||||
@Parameter(description = "Nom de l'organisation") @QueryParam("nom") String nom,
|
||||
@Parameter(description = "Type d'organisation") @QueryParam("type") String typeOrganisation,
|
||||
@Parameter(description = "Statut") @QueryParam("statut") String statut,
|
||||
@Parameter(description = "Ville") @QueryParam("ville") String ville,
|
||||
@Parameter(description = "Région") @QueryParam("region") String region,
|
||||
@Parameter(description = "Pays") @QueryParam("pays") String pays,
|
||||
@Parameter(description = "Numéro de page") @QueryParam("page") @DefaultValue("0") int page,
|
||||
@Parameter(description = "Taille de la page") @QueryParam("size") @DefaultValue("20")
|
||||
int size) {
|
||||
|
||||
LOG.infof("Recherche avancée d'organisations avec critères multiples");
|
||||
|
||||
try {
|
||||
List<Organisation> organisations =
|
||||
organisationService.rechercheAvancee(
|
||||
nom, typeOrganisation, statut, ville, region, pays, page, size);
|
||||
|
||||
List<OrganisationDTO> dtos =
|
||||
organisations.stream()
|
||||
.map(organisationService::convertToDTO)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Response.ok(dtos).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la recherche avancée");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur interne du serveur"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Active une organisation */
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/{id}/activer")
|
||||
|
||||
@Operation(
|
||||
summary = "Activer une organisation",
|
||||
description = "Active une organisation suspendue")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Organisation activée avec succès"),
|
||||
@APIResponse(responseCode = "404", description = "Organisation non trouvée"),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié"),
|
||||
@APIResponse(responseCode = "403", description = "Non autorisé")
|
||||
})
|
||||
public Response activerOrganisation(
|
||||
@Parameter(description = "UUID de l'organisation", required = true) @PathParam("id") UUID id) {
|
||||
|
||||
LOG.infof("Activation de l'organisation ID: %d", id);
|
||||
|
||||
try {
|
||||
Organisation organisation = organisationService.activerOrganisation(id, "system");
|
||||
OrganisationDTO dto = organisationService.convertToDTO(organisation);
|
||||
return Response.ok(dto).build();
|
||||
} catch (NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'activation de l'organisation");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur interne du serveur"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Suspend une organisation */
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/{id}/suspendre")
|
||||
|
||||
@Operation(
|
||||
summary = "Suspendre une organisation",
|
||||
description = "Suspend une organisation active")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Organisation suspendue avec succès"),
|
||||
@APIResponse(responseCode = "404", description = "Organisation non trouvée"),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié"),
|
||||
@APIResponse(responseCode = "403", description = "Non autorisé")
|
||||
})
|
||||
public Response suspendreOrganisation(
|
||||
@Parameter(description = "UUID de l'organisation", required = true) @PathParam("id") UUID id) {
|
||||
|
||||
LOG.infof("Suspension de l'organisation ID: %d", id);
|
||||
|
||||
try {
|
||||
Organisation organisation = organisationService.suspendreOrganisation(id, "system");
|
||||
OrganisationDTO dto = organisationService.convertToDTO(organisation);
|
||||
return Response.ok(dto).build();
|
||||
} catch (NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la suspension de l'organisation");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur interne du serveur"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Obtient les statistiques des organisations */
|
||||
@GET
|
||||
@Path("/statistiques")
|
||||
|
||||
@Operation(
|
||||
summary = "Statistiques des organisations",
|
||||
description = "Récupère les statistiques globales des organisations")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Statistiques récupérées avec succès"),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié"),
|
||||
@APIResponse(responseCode = "403", description = "Non autorisé")
|
||||
})
|
||||
public Response obtenirStatistiques() {
|
||||
LOG.info("Récupération des statistiques des organisations");
|
||||
|
||||
try {
|
||||
Map<String, Object> statistiques = organisationService.obtenirStatistiques();
|
||||
return Response.ok(statistiques).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la récupération des statistiques");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur interne du serveur"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,213 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.paiement.PaiementDTO;
|
||||
import dev.lions.unionflow.server.service.PaiementService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Resource REST pour la gestion des paiements
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Path("/api/paiements")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@RolesAllowed({"ADMIN", "MEMBRE", "USER"})
|
||||
public class PaiementResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(PaiementResource.class);
|
||||
|
||||
@Inject PaiementService paiementService;
|
||||
|
||||
/**
|
||||
* Crée un nouveau paiement
|
||||
*
|
||||
* @param paiementDTO DTO du paiement à créer
|
||||
* @return Paiement créé
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
public Response creerPaiement(@Valid PaiementDTO paiementDTO) {
|
||||
try {
|
||||
PaiementDTO result = paiementService.creerPaiement(paiementDTO);
|
||||
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la création du paiement");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la création du paiement: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour un paiement
|
||||
*
|
||||
* @param id ID du paiement
|
||||
* @param paiementDTO DTO avec les modifications
|
||||
* @return Paiement mis à jour
|
||||
*/
|
||||
@PUT
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/{id}")
|
||||
public Response mettreAJourPaiement(@PathParam("id") UUID id, @Valid PaiementDTO paiementDTO) {
|
||||
try {
|
||||
PaiementDTO result = paiementService.mettreAJourPaiement(id, paiementDTO);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Paiement non trouvé"))
|
||||
.build();
|
||||
} catch (IllegalStateException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la mise à jour du paiement");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la mise à jour du paiement: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Valide un paiement
|
||||
*
|
||||
* @param id ID du paiement
|
||||
* @return Paiement validé
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/{id}/valider")
|
||||
public Response validerPaiement(@PathParam("id") UUID id) {
|
||||
try {
|
||||
PaiementDTO result = paiementService.validerPaiement(id);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Paiement non trouvé"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la validation du paiement");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la validation du paiement: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Annule un paiement
|
||||
*
|
||||
* @param id ID du paiement
|
||||
* @return Paiement annulé
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/{id}/annuler")
|
||||
public Response annulerPaiement(@PathParam("id") UUID id) {
|
||||
try {
|
||||
PaiementDTO result = paiementService.annulerPaiement(id);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Paiement non trouvé"))
|
||||
.build();
|
||||
} catch (IllegalStateException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de l'annulation du paiement");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de l'annulation du paiement: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve un paiement par son ID
|
||||
*
|
||||
* @param id ID du paiement
|
||||
* @return Paiement
|
||||
*/
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
public Response trouverParId(@PathParam("id") UUID id) {
|
||||
try {
|
||||
PaiementDTO result = paiementService.trouverParId(id);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Paiement non trouvé"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la recherche du paiement");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la recherche du paiement: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve un paiement par son numéro de référence
|
||||
*
|
||||
* @param numeroReference Numéro de référence
|
||||
* @return Paiement
|
||||
*/
|
||||
@GET
|
||||
@Path("/reference/{numeroReference}")
|
||||
public Response trouverParNumeroReference(@PathParam("numeroReference") String numeroReference) {
|
||||
try {
|
||||
PaiementDTO result = paiementService.trouverParNumeroReference(numeroReference);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Paiement non trouvé"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la recherche du paiement");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la recherche du paiement: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste tous les paiements d'un membre
|
||||
*
|
||||
* @param membreId ID du membre
|
||||
* @return Liste des paiements
|
||||
*/
|
||||
@GET
|
||||
@Path("/membre/{membreId}")
|
||||
public Response listerParMembre(@PathParam("membreId") UUID membreId) {
|
||||
try {
|
||||
List<PaiementDTO> result = paiementService.listerParMembre(membreId);
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la liste des paiements");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la liste des paiements: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Classe interne pour les réponses d'erreur */
|
||||
public static class ErrorResponse {
|
||||
public String error;
|
||||
|
||||
public ErrorResponse(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,75 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.service.PreferencesNotificationService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/** Resource REST pour la gestion des préférences utilisateur */
|
||||
@Path("/api/preferences")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@ApplicationScoped
|
||||
@Tag(name = "Préférences", description = "API de gestion des préférences utilisateur")
|
||||
public class PreferencesResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(PreferencesResource.class);
|
||||
|
||||
@Inject PreferencesNotificationService preferencesService;
|
||||
|
||||
@GET
|
||||
@Path("/{utilisateurId}")
|
||||
@RolesAllowed({"USER", "ADMIN", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Obtenir les préférences d'un utilisateur")
|
||||
@APIResponse(responseCode = "200", description = "Préférences récupérées avec succès")
|
||||
public Response obtenirPreferences(
|
||||
@PathParam("utilisateurId") UUID utilisateurId) {
|
||||
LOG.infof("Récupération des préférences pour l'utilisateur %s", utilisateurId);
|
||||
Map<String, Boolean> preferences = preferencesService.obtenirPreferences(utilisateurId);
|
||||
return Response.ok(preferences).build();
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{utilisateurId}")
|
||||
@RolesAllowed({"USER", "ADMIN", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Mettre à jour les préférences d'un utilisateur")
|
||||
@APIResponse(responseCode = "204", description = "Préférences mises à jour avec succès")
|
||||
public Response mettreAJourPreferences(
|
||||
@PathParam("utilisateurId") UUID utilisateurId, Map<String, Boolean> preferences) {
|
||||
LOG.infof("Mise à jour des préférences pour l'utilisateur %s", utilisateurId);
|
||||
preferencesService.mettreAJourPreferences(utilisateurId, preferences);
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/{utilisateurId}/reinitialiser")
|
||||
@RolesAllowed({"USER", "ADMIN", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Réinitialiser les préférences d'un utilisateur")
|
||||
@APIResponse(responseCode = "204", description = "Préférences réinitialisées avec succès")
|
||||
public Response reinitialiserPreferences(@PathParam("utilisateurId") UUID utilisateurId) {
|
||||
LOG.infof("Réinitialisation des préférences pour l'utilisateur %s", utilisateurId);
|
||||
preferencesService.reinitialiserPreferences(utilisateurId);
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{utilisateurId}/export")
|
||||
@RolesAllowed({"USER", "ADMIN", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Exporter les préférences d'un utilisateur")
|
||||
@APIResponse(responseCode = "200", description = "Préférences exportées avec succès")
|
||||
public Response exporterPreferences(@PathParam("utilisateurId") UUID utilisateurId) {
|
||||
LOG.infof("Export des préférences pour l'utilisateur %s", utilisateurId);
|
||||
Map<String, Object> export = preferencesService.exporterPreferences(utilisateurId);
|
||||
return Response.ok(export).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,165 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.organisation.TypeOrganisationDTO;
|
||||
import dev.lions.unionflow.server.service.TypeOrganisationService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Content;
|
||||
import org.eclipse.microprofile.openapi.annotations.media.Schema;
|
||||
import org.eclipse.microprofile.openapi.annotations.parameters.Parameter;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponses;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Ressource REST pour la gestion du catalogue des types d'organisation.
|
||||
*/
|
||||
@Path("/api/types-organisations")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Types d'organisation", description = "Catalogue des types d'organisation")
|
||||
@RolesAllowed({"ADMIN", "MEMBRE", "USER"})
|
||||
public class TypeOrganisationResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(TypeOrganisationResource.class);
|
||||
|
||||
@Inject TypeOrganisationService service;
|
||||
|
||||
/** Liste les types d'organisation. */
|
||||
@GET
|
||||
@Operation(
|
||||
summary = "Lister les types d'organisation",
|
||||
description = "Récupère la liste des types d'organisation, optionnellement seulement actifs")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "Liste des types récupérée avec succès",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = TypeOrganisationDTO.class))),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié"),
|
||||
@APIResponse(responseCode = "403", description = "Non autorisé")
|
||||
})
|
||||
public Response listTypes(
|
||||
@Parameter(description = "Limiter aux types actifs", example = "true")
|
||||
@QueryParam("onlyActifs")
|
||||
@DefaultValue("true")
|
||||
String onlyActifs) {
|
||||
// Parsing manuel pour éviter toute erreur de conversion JAX-RS (qui peut renvoyer une 400)
|
||||
boolean actifsSeulement = !"false".equalsIgnoreCase(onlyActifs);
|
||||
List<TypeOrganisationDTO> types = service.listAll(actifsSeulement);
|
||||
return Response.ok(types).build();
|
||||
}
|
||||
|
||||
/** Crée un nouveau type d'organisation (réservé à l'administration). */
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Operation(
|
||||
summary = "Créer un type d'organisation",
|
||||
description = "Crée un nouveau type dans le catalogue (code doit exister dans l'enum)")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "201",
|
||||
description = "Type créé avec succès",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = TypeOrganisationDTO.class))),
|
||||
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié"),
|
||||
@APIResponse(responseCode = "403", description = "Non autorisé")
|
||||
})
|
||||
public Response create(TypeOrganisationDTO dto) {
|
||||
try {
|
||||
TypeOrganisationDTO created = service.create(dto);
|
||||
return Response.status(Response.Status.CREATED).entity(created).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warnf("Erreur lors de la création du type d'organisation: %s", e.getMessage());
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur inattendue lors de la création du type d'organisation");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur interne du serveur"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Met à jour un type. */
|
||||
@PUT
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/{id}")
|
||||
@Operation(
|
||||
summary = "Mettre à jour un type d'organisation",
|
||||
description = "Met à jour un type existant (libellé, description, ordre, actif, code)")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "Type mis à jour avec succès",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = TypeOrganisationDTO.class))),
|
||||
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||
@APIResponse(responseCode = "404", description = "Type non trouvé"),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié"),
|
||||
@APIResponse(responseCode = "403", description = "Non autorisé")
|
||||
})
|
||||
public Response update(@PathParam("id") UUID id, TypeOrganisationDTO dto) {
|
||||
try {
|
||||
TypeOrganisationDTO updated = service.update(id, dto);
|
||||
return Response.ok(updated).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warnf("Erreur lors de la mise à jour du type d'organisation: %s", e.getMessage());
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur inattendue lors de la mise à jour du type d'organisation");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur interne du serveur"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Désactive un type (soft delete). */
|
||||
@DELETE
|
||||
@RolesAllowed({"ADMIN"})
|
||||
@Path("/{id}")
|
||||
@Operation(
|
||||
summary = "Désactiver un type d'organisation",
|
||||
description = "Désactive un type dans le catalogue (soft delete)")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "204", description = "Type désactivé avec succès"),
|
||||
@APIResponse(responseCode = "404", description = "Type non trouvé"),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié"),
|
||||
@APIResponse(responseCode = "403", description = "Non autorisé")
|
||||
})
|
||||
public Response disable(@PathParam("id") UUID id) {
|
||||
try {
|
||||
service.disable(id);
|
||||
return Response.noContent().build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur inattendue lors de la désactivation du type d'organisation");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur interne du serveur"))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,269 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.wave.CompteWaveDTO;
|
||||
import dev.lions.unionflow.server.api.dto.wave.TransactionWaveDTO;
|
||||
import dev.lions.unionflow.server.api.enums.wave.StatutTransactionWave;
|
||||
import dev.lions.unionflow.server.service.WaveService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Resource REST pour l'intégration Wave Mobile Money
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2025-01-29
|
||||
*/
|
||||
@Path("/api/wave")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@RolesAllowed({"ADMIN", "MEMBRE", "USER"})
|
||||
public class WaveResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(WaveResource.class);
|
||||
|
||||
@Inject WaveService waveService;
|
||||
|
||||
// ========================================
|
||||
// COMPTES WAVE
|
||||
// ========================================
|
||||
|
||||
/**
|
||||
* Crée un nouveau compte Wave
|
||||
*
|
||||
* @param compteWaveDTO DTO du compte à créer
|
||||
* @return Compte créé
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/comptes")
|
||||
public Response creerCompteWave(@Valid CompteWaveDTO compteWaveDTO) {
|
||||
try {
|
||||
CompteWaveDTO result = waveService.creerCompteWave(compteWaveDTO);
|
||||
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse(e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la création du compte Wave");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la création du compte Wave: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour un compte Wave
|
||||
*
|
||||
* @param id ID du compte
|
||||
* @param compteWaveDTO DTO avec les modifications
|
||||
* @return Compte mis à jour
|
||||
*/
|
||||
@PUT
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/comptes/{id}")
|
||||
public Response mettreAJourCompteWave(@PathParam("id") UUID id, @Valid CompteWaveDTO compteWaveDTO) {
|
||||
try {
|
||||
CompteWaveDTO result = waveService.mettreAJourCompteWave(id, compteWaveDTO);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Compte Wave non trouvé"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la mise à jour du compte Wave");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la mise à jour du compte Wave: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie un compte Wave
|
||||
*
|
||||
* @param id ID du compte
|
||||
* @return Compte vérifié
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/comptes/{id}/verifier")
|
||||
public Response verifierCompteWave(@PathParam("id") UUID id) {
|
||||
try {
|
||||
CompteWaveDTO result = waveService.verifierCompteWave(id);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Compte Wave non trouvé"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la vérification du compte Wave");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la vérification du compte Wave: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve un compte Wave par son ID
|
||||
*
|
||||
* @param id ID du compte
|
||||
* @return Compte Wave
|
||||
*/
|
||||
@GET
|
||||
@Path("/comptes/{id}")
|
||||
public Response trouverCompteWaveParId(@PathParam("id") UUID id) {
|
||||
try {
|
||||
CompteWaveDTO result = waveService.trouverCompteWaveParId(id);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Compte Wave non trouvé"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la recherche du compte Wave");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la recherche du compte Wave: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve un compte Wave par numéro de téléphone
|
||||
*
|
||||
* @param numeroTelephone Numéro de téléphone
|
||||
* @return Compte Wave ou null
|
||||
*/
|
||||
@GET
|
||||
@Path("/comptes/telephone/{numeroTelephone}")
|
||||
public Response trouverCompteWaveParTelephone(@PathParam("numeroTelephone") String numeroTelephone) {
|
||||
try {
|
||||
CompteWaveDTO result = waveService.trouverCompteWaveParTelephone(numeroTelephone);
|
||||
if (result == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Compte Wave non trouvé"))
|
||||
.build();
|
||||
}
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la recherche du compte Wave");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la recherche du compte Wave: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste tous les comptes Wave d'une organisation
|
||||
*
|
||||
* @param organisationId ID de l'organisation
|
||||
* @return Liste des comptes Wave
|
||||
*/
|
||||
@GET
|
||||
@Path("/comptes/organisation/{organisationId}")
|
||||
public Response listerComptesWaveParOrganisation(@PathParam("organisationId") UUID organisationId) {
|
||||
try {
|
||||
List<CompteWaveDTO> result = waveService.listerComptesWaveParOrganisation(organisationId);
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la liste des comptes Wave");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la liste des comptes Wave: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
// ========================================
|
||||
// TRANSACTIONS WAVE
|
||||
// ========================================
|
||||
|
||||
/**
|
||||
* Crée une nouvelle transaction Wave
|
||||
*
|
||||
* @param transactionWaveDTO DTO de la transaction à créer
|
||||
* @return Transaction créée
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/transactions")
|
||||
public Response creerTransactionWave(@Valid TransactionWaveDTO transactionWaveDTO) {
|
||||
try {
|
||||
TransactionWaveDTO result = waveService.creerTransactionWave(transactionWaveDTO);
|
||||
return Response.status(Response.Status.CREATED).entity(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la création de la transaction Wave");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la création de la transaction Wave: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le statut d'une transaction Wave
|
||||
*
|
||||
* @param waveTransactionId Identifiant Wave de la transaction
|
||||
* @param statut Nouveau statut
|
||||
* @return Transaction mise à jour
|
||||
*/
|
||||
@PUT
|
||||
@RolesAllowed({"ADMIN", "MEMBRE"})
|
||||
@Path("/transactions/{waveTransactionId}/statut")
|
||||
public Response mettreAJourStatutTransaction(
|
||||
@PathParam("waveTransactionId") String waveTransactionId, StatutTransactionWave statut) {
|
||||
try {
|
||||
TransactionWaveDTO result = waveService.mettreAJourStatutTransaction(waveTransactionId, statut);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Transaction Wave non trouvée"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la mise à jour du statut de la transaction Wave");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(
|
||||
new ErrorResponse(
|
||||
"Erreur lors de la mise à jour du statut de la transaction Wave: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve une transaction Wave par son identifiant Wave
|
||||
*
|
||||
* @param waveTransactionId Identifiant Wave
|
||||
* @return Transaction Wave
|
||||
*/
|
||||
@GET
|
||||
@Path("/transactions/{waveTransactionId}")
|
||||
public Response trouverTransactionWaveParId(@PathParam("waveTransactionId") String waveTransactionId) {
|
||||
try {
|
||||
TransactionWaveDTO result = waveService.trouverTransactionWaveParId(waveTransactionId);
|
||||
return Response.ok(result).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(new ErrorResponse("Transaction Wave non trouvée"))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur lors de la recherche de la transaction Wave");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(new ErrorResponse("Erreur lors de la recherche de la transaction Wave: " + e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Classe interne pour les réponses d'erreur */
|
||||
public static class ErrorResponse {
|
||||
public String error;
|
||||
|
||||
public ErrorResponse(String error) {
|
||||
this.error = error;
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user