Refactoring - Bonne version améliorée

This commit is contained in:
dahoud
2026-02-05 14:14:45 +00:00
parent a515963a4a
commit dd4dbe111e
56 changed files with 4274 additions and 2142 deletions

View File

@@ -0,0 +1,423 @@
package com.lions.dev.resource;
import com.lions.dev.core.security.JwtAuthFilter;
import com.lions.dev.core.security.RequiresAuth;
import com.lions.dev.dto.request.promotion.PromotionCreateRequestDTO;
import com.lions.dev.dto.request.promotion.PromotionUpdateRequestDTO;
import com.lions.dev.dto.response.promotion.PromotionResponseDTO;
import com.lions.dev.entity.promotion.Promotion;
import com.lions.dev.service.PromotionService;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import jakarta.ws.rs.*;
import jakarta.ws.rs.container.ContainerRequestContext;
import jakarta.ws.rs.core.Context;
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.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.security.SecurityRequirement;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
/**
* Ressource REST pour la gestion des promotions dans le système AfterWork.
*
* Cette classe expose des endpoints pour créer, récupérer, mettre à jour
* et supprimer des promotions d'établissements.
*/
@Path("/promotions")
@Produces(MediaType.APPLICATION_JSON)
@Consumes(MediaType.APPLICATION_JSON)
@Tag(name = "Promotions", description = "Opérations liées à la gestion des promotions")
public class PromotionResource {
@Inject
PromotionService promotionService;
private static final Logger LOG = Logger.getLogger(PromotionResource.class);
/**
* Extrait l'ID de l'utilisateur authentifié du contexte de la requête.
*/
private UUID getAuthenticatedUserId(ContainerRequestContext requestContext) {
return (UUID) requestContext.getProperty(JwtAuthFilter.AUTHENTICATED_USER_ID);
}
// =====================================================================
// ENDPOINTS PUBLICS (LECTURE)
// =====================================================================
/**
* Récupère toutes les promotions actives et valides.
*
* @param page Le numéro de la page (0-indexé)
* @param size La taille de la page
* @return Liste paginée des promotions actives
*/
@GET
@Operation(
summary = "Récupérer toutes les promotions actives",
description = "Retourne une liste paginée de toutes les promotions actives et valides")
@APIResponse(responseCode = "200", description = "Liste des promotions")
public Response getAllActivePromotions(
@QueryParam("page") @DefaultValue("0") int page,
@QueryParam("size") @DefaultValue("10") int size) {
LOG.info("[LOG] Récupération de toutes les promotions actives (page: " + page + ", size: " + size + ")");
try {
List<Promotion> promotions = promotionService.getAllActivePromotions(page, size);
List<PromotionResponseDTO> responseDTOs = promotions.stream()
.map(PromotionResponseDTO::new)
.collect(Collectors.toList());
return Response.ok(responseDTOs).build();
} catch (Exception e) {
LOG.error("[ERROR] Erreur lors de la récupération des promotions : " + e.getMessage(), e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("{\"message\": \"Erreur lors de la récupération des promotions.\"}")
.build();
}
}
/**
* Récupère une promotion par son ID.
*
* @param promotionId L'ID de la promotion
* @return La promotion trouvée
*/
@GET
@Path("/{id}")
@Operation(
summary = "Récupérer une promotion par ID",
description = "Retourne les détails d'une promotion spécifique")
@APIResponse(responseCode = "200", description = "Promotion trouvée")
@APIResponse(responseCode = "404", description = "Promotion non trouvée")
public Response getPromotionById(@PathParam("id") UUID promotionId) {
LOG.info("[LOG] Récupération de la promotion ID : " + promotionId);
try {
Promotion promotion = promotionService.getPromotionById(promotionId);
PromotionResponseDTO responseDTO = new PromotionResponseDTO(promotion);
return Response.ok(responseDTO).build();
} catch (IllegalArgumentException e) {
LOG.warn("[WARN] Promotion non trouvée : " + e.getMessage());
return Response.status(Response.Status.NOT_FOUND)
.entity("{\"message\": \"Promotion non trouvée.\"}")
.build();
} catch (Exception e) {
LOG.error("[ERROR] Erreur lors de la récupération de la promotion : " + e.getMessage(), e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("{\"message\": \"Erreur lors de la récupération de la promotion.\"}")
.build();
}
}
/**
* Recherche une promotion par code promo.
*
* @param code Le code promo à rechercher
* @return La promotion trouvée
*/
@GET
@Path("/code/{code}")
@Operation(
summary = "Rechercher une promotion par code promo",
description = "Retourne la promotion correspondant au code promo")
@APIResponse(responseCode = "200", description = "Promotion trouvée")
@APIResponse(responseCode = "404", description = "Code promo non trouvé")
public Response getPromotionByCode(@PathParam("code") String code) {
LOG.info("[LOG] Recherche de la promotion avec le code : " + code);
try {
Optional<Promotion> promotionOpt = promotionService.getPromotionByCode(code);
if (promotionOpt.isEmpty()) {
return Response.status(Response.Status.NOT_FOUND)
.entity("{\"message\": \"Code promo non trouvé.\"}")
.build();
}
PromotionResponseDTO responseDTO = new PromotionResponseDTO(promotionOpt.get());
return Response.ok(responseDTO).build();
} catch (Exception e) {
LOG.error("[ERROR] Erreur lors de la recherche du code promo : " + e.getMessage(), e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("{\"message\": \"Erreur lors de la recherche du code promo.\"}")
.build();
}
}
/**
* Récupère les promotions d'un établissement.
*
* @param establishmentId L'ID de l'établissement
* @param activeOnly Si true, retourne uniquement les promotions actives et valides
* @param page Le numéro de la page
* @param size La taille de la page
* @return Liste des promotions
*/
@GET
@Path("/establishment/{establishmentId}")
@Operation(
summary = "Récupérer les promotions d'un établissement",
description = "Retourne les promotions d'un établissement spécifique")
@APIResponse(responseCode = "200", description = "Liste des promotions")
public Response getPromotionsByEstablishment(
@PathParam("establishmentId") UUID establishmentId,
@QueryParam("activeOnly") @DefaultValue("false") boolean activeOnly,
@QueryParam("page") @DefaultValue("0") int page,
@QueryParam("size") @DefaultValue("10") int size) {
LOG.info("[LOG] Récupération des promotions pour l'établissement : " + establishmentId);
try {
List<Promotion> promotions;
if (activeOnly) {
promotions = promotionService.getActivePromotionsByEstablishment(establishmentId);
} else {
promotions = promotionService.getPromotionsByEstablishment(establishmentId, page, size);
}
List<PromotionResponseDTO> responseDTOs = promotions.stream()
.map(PromotionResponseDTO::new)
.collect(Collectors.toList());
return Response.ok(responseDTOs).build();
} catch (Exception e) {
LOG.error("[ERROR] Erreur lors de la récupération des promotions : " + e.getMessage(), e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("{\"message\": \"Erreur lors de la récupération des promotions.\"}")
.build();
}
}
// =====================================================================
// ENDPOINTS PROTÉGÉS (CRÉATION, MODIFICATION, SUPPRESSION)
// =====================================================================
/**
* Crée une nouvelle promotion.
* Requiert une authentification JWT.
* Seul le responsable de l'établissement peut créer des promotions.
*
* @param requestContext Le contexte de la requête (injecté)
* @param requestDTO Le DTO de création
* @return La promotion créée
*/
@POST
@Transactional
@RequiresAuth
@Operation(
summary = "Créer une promotion",
description = "Crée une nouvelle promotion pour un établissement. Seul le responsable peut créer.")
@SecurityRequirement(name = "bearerAuth")
@APIResponse(responseCode = "201", description = "Promotion créée avec succès")
@APIResponse(responseCode = "400", description = "Données invalides")
@APIResponse(responseCode = "401", description = "Non authentifié")
@APIResponse(responseCode = "403", description = "Non autorisé à créer des promotions pour cet établissement")
public Response createPromotion(
@Context ContainerRequestContext requestContext,
@Valid PromotionCreateRequestDTO requestDTO) {
UUID authenticatedUserId = getAuthenticatedUserId(requestContext);
LOG.info("[LOG] Création d'une promotion pour l'établissement : " + requestDTO.getEstablishmentId() +
" par l'utilisateur : " + authenticatedUserId);
try {
Promotion promotion = promotionService.createPromotion(requestDTO);
// Vérifier que l'utilisateur est bien le responsable
if (!promotionService.canModifyPromotion(promotion, authenticatedUserId)) {
promotionService.deletePromotion(promotion.getId()); // Rollback
LOG.warn("[WARN] Utilisateur " + authenticatedUserId + " non autorisé à créer des promotions pour cet établissement");
return Response.status(Response.Status.FORBIDDEN)
.entity("{\"message\": \"Vous n'êtes pas autorisé à créer des promotions pour cet établissement.\"}")
.build();
}
PromotionResponseDTO responseDTO = new PromotionResponseDTO(promotion);
return Response.status(Response.Status.CREATED).entity(responseDTO).build();
} catch (IllegalArgumentException e) {
LOG.warn("[WARN] " + e.getMessage());
return Response.status(Response.Status.BAD_REQUEST)
.entity("{\"message\": \"" + e.getMessage() + "\"}")
.build();
} catch (Exception e) {
LOG.error("[ERROR] Erreur lors de la création de la promotion : " + e.getMessage(), e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("{\"message\": \"Erreur lors de la création de la promotion.\"}")
.build();
}
}
/**
* Met à jour une promotion.
* Requiert une authentification JWT.
* Seul le responsable de l'établissement peut modifier.
*
* @param requestContext Le contexte de la requête (injecté)
* @param promotionId L'ID de la promotion
* @param requestDTO Le DTO de mise à jour
* @return La promotion mise à jour
*/
@PUT
@Path("/{id}")
@Transactional
@RequiresAuth
@Operation(
summary = "Mettre à jour une promotion",
description = "Met à jour une promotion existante. Seul le responsable peut modifier.")
@SecurityRequirement(name = "bearerAuth")
@APIResponse(responseCode = "200", description = "Promotion mise à jour")
@APIResponse(responseCode = "401", description = "Non authentifié")
@APIResponse(responseCode = "403", description = "Non autorisé à modifier cette promotion")
@APIResponse(responseCode = "404", description = "Promotion non trouvée")
public Response updatePromotion(
@Context ContainerRequestContext requestContext,
@PathParam("id") UUID promotionId,
@Valid PromotionUpdateRequestDTO requestDTO) {
UUID authenticatedUserId = getAuthenticatedUserId(requestContext);
LOG.info("[LOG] Mise à jour de la promotion : " + promotionId + " par l'utilisateur : " + authenticatedUserId);
try {
// Vérifier les permissions
Promotion existingPromotion = promotionService.getPromotionById(promotionId);
if (!promotionService.canModifyPromotion(existingPromotion, authenticatedUserId)) {
LOG.warn("[WARN] Utilisateur " + authenticatedUserId + " non autorisé à modifier la promotion " + promotionId);
return Response.status(Response.Status.FORBIDDEN)
.entity("{\"message\": \"Vous n'êtes pas autorisé à modifier cette promotion.\"}")
.build();
}
Promotion promotion = promotionService.updatePromotion(promotionId, requestDTO);
PromotionResponseDTO responseDTO = new PromotionResponseDTO(promotion);
return Response.ok(responseDTO).build();
} catch (IllegalArgumentException e) {
LOG.warn("[WARN] " + e.getMessage());
return Response.status(Response.Status.NOT_FOUND)
.entity("{\"message\": \"" + e.getMessage() + "\"}")
.build();
} catch (Exception e) {
LOG.error("[ERROR] Erreur lors de la mise à jour de la promotion : " + e.getMessage(), e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("{\"message\": \"Erreur lors de la mise à jour de la promotion.\"}")
.build();
}
}
/**
* Supprime une promotion.
* Requiert une authentification JWT.
* Seul le responsable de l'établissement peut supprimer.
*
* @param requestContext Le contexte de la requête (injecté)
* @param promotionId L'ID de la promotion
* @return Confirmation de suppression
*/
@DELETE
@Path("/{id}")
@Transactional
@RequiresAuth
@Operation(
summary = "Supprimer une promotion",
description = "Supprime une promotion. Seul le responsable peut supprimer.")
@SecurityRequirement(name = "bearerAuth")
@APIResponse(responseCode = "204", description = "Promotion supprimée")
@APIResponse(responseCode = "401", description = "Non authentifié")
@APIResponse(responseCode = "403", description = "Non autorisé à supprimer cette promotion")
@APIResponse(responseCode = "404", description = "Promotion non trouvée")
public Response deletePromotion(
@Context ContainerRequestContext requestContext,
@PathParam("id") UUID promotionId) {
UUID authenticatedUserId = getAuthenticatedUserId(requestContext);
LOG.info("[LOG] Suppression de la promotion : " + promotionId + " par l'utilisateur : " + authenticatedUserId);
try {
// Vérifier les permissions
Promotion existingPromotion = promotionService.getPromotionById(promotionId);
if (!promotionService.canModifyPromotion(existingPromotion, authenticatedUserId)) {
LOG.warn("[WARN] Utilisateur " + authenticatedUserId + " non autorisé à supprimer la promotion " + promotionId);
return Response.status(Response.Status.FORBIDDEN)
.entity("{\"message\": \"Vous n'êtes pas autorisé à supprimer cette promotion.\"}")
.build();
}
boolean deleted = promotionService.deletePromotion(promotionId);
if (deleted) {
return Response.noContent().build();
} else {
return Response.status(Response.Status.NOT_FOUND)
.entity("{\"message\": \"Promotion non trouvée.\"}")
.build();
}
} catch (IllegalArgumentException e) {
LOG.warn("[WARN] " + e.getMessage());
return Response.status(Response.Status.NOT_FOUND)
.entity("{\"message\": \"Promotion non trouvée.\"}")
.build();
} catch (Exception e) {
LOG.error("[ERROR] Erreur lors de la suppression de la promotion : " + e.getMessage(), e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("{\"message\": \"Erreur lors de la suppression de la promotion.\"}")
.build();
}
}
/**
* Active ou désactive une promotion.
* Requiert une authentification JWT.
*
* @param requestContext Le contexte de la requête (injecté)
* @param promotionId L'ID de la promotion
* @param isActive L'état à appliquer
* @return La promotion mise à jour
*/
@PATCH
@Path("/{id}/active")
@Transactional
@RequiresAuth
@Operation(
summary = "Activer/Désactiver une promotion",
description = "Change l'état actif d'une promotion. Seul le responsable peut modifier.")
@SecurityRequirement(name = "bearerAuth")
@APIResponse(responseCode = "200", description = "État mis à jour")
@APIResponse(responseCode = "401", description = "Non authentifié")
@APIResponse(responseCode = "403", description = "Non autorisé")
@APIResponse(responseCode = "404", description = "Promotion non trouvée")
public Response setPromotionActive(
@Context ContainerRequestContext requestContext,
@PathParam("id") UUID promotionId,
@QueryParam("active") @DefaultValue("true") boolean isActive) {
UUID authenticatedUserId = getAuthenticatedUserId(requestContext);
LOG.info("[LOG] Changement d'état de la promotion " + promotionId + " à " + isActive);
try {
// Vérifier les permissions
Promotion existingPromotion = promotionService.getPromotionById(promotionId);
if (!promotionService.canModifyPromotion(existingPromotion, authenticatedUserId)) {
LOG.warn("[WARN] Utilisateur " + authenticatedUserId + " non autorisé");
return Response.status(Response.Status.FORBIDDEN)
.entity("{\"message\": \"Vous n'êtes pas autorisé à modifier cette promotion.\"}")
.build();
}
Promotion promotion = promotionService.setPromotionActive(promotionId, isActive);
PromotionResponseDTO responseDTO = new PromotionResponseDTO(promotion);
return Response.ok(responseDTO).build();
} catch (IllegalArgumentException e) {
LOG.warn("[WARN] " + e.getMessage());
return Response.status(Response.Status.NOT_FOUND)
.entity("{\"message\": \"Promotion non trouvée.\"}")
.build();
} catch (Exception e) {
LOG.error("[ERROR] Erreur : " + e.getMessage(), e);
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
.entity("{\"message\": \"Erreur lors de la mise à jour.\"}")
.build();
}
}
}