426 lines
18 KiB
Java
426 lines
18 KiB
Java
package com.lions.dev.resource;
|
|
|
|
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.security.Permission;
|
|
import com.lions.dev.security.RequiresPermission;
|
|
import com.lions.dev.service.PromotionService;
|
|
import com.lions.dev.service.SecurityService;
|
|
import com.lions.dev.util.UserRoles;
|
|
import jakarta.annotation.security.PermitAll;
|
|
import jakarta.annotation.security.RolesAllowed;
|
|
import jakarta.inject.Inject;
|
|
import jakarta.transaction.Transactional;
|
|
import jakarta.validation.Valid;
|
|
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.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.
|
|
*
|
|
* SÉCURITÉ : Les lectures sont publiques, les écritures requièrent une authentification.
|
|
* Seul le responsable de l'établissement peut créer/modifier/supprimer des promotions.
|
|
*
|
|
* @since 2.0 - Sécurité JWT + RBAC production-ready
|
|
*/
|
|
@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;
|
|
|
|
@Inject
|
|
SecurityService securityService;
|
|
|
|
private static final Logger LOG = Logger.getLogger(PromotionResource.class);
|
|
|
|
// =====================================================================
|
|
// 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
|
|
@PermitAll
|
|
@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}")
|
|
@PermitAll
|
|
@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}")
|
|
@PermitAll
|
|
@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}")
|
|
@PermitAll
|
|
@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
|
|
@RolesAllowed({UserRoles.OWNER, UserRoles.MANAGER, UserRoles.ADMIN, UserRoles.SUPER_ADMIN})
|
|
@RequiresPermission(Permission.PROMOTIONS_CREATE)
|
|
@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(@Valid PromotionCreateRequestDTO requestDTO) {
|
|
UUID authenticatedUserId = securityService.getCurrentUserId();
|
|
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
|
|
@RolesAllowed({UserRoles.OWNER, UserRoles.MANAGER, UserRoles.ADMIN, UserRoles.SUPER_ADMIN})
|
|
@RequiresPermission(Permission.PROMOTIONS_UPDATE_OWN)
|
|
@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(
|
|
@PathParam("id") UUID promotionId,
|
|
@Valid PromotionUpdateRequestDTO requestDTO) {
|
|
UUID authenticatedUserId = securityService.getCurrentUserId();
|
|
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
|
|
@RolesAllowed({UserRoles.OWNER, UserRoles.MANAGER, UserRoles.ADMIN, UserRoles.SUPER_ADMIN})
|
|
@RequiresPermission(Permission.PROMOTIONS_DELETE_OWN)
|
|
@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(@PathParam("id") UUID promotionId) {
|
|
UUID authenticatedUserId = securityService.getCurrentUserId();
|
|
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
|
|
@RolesAllowed({UserRoles.OWNER, UserRoles.MANAGER, UserRoles.ADMIN, UserRoles.SUPER_ADMIN})
|
|
@RequiresPermission(Permission.PROMOTIONS_UPDATE_OWN)
|
|
@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(
|
|
@PathParam("id") UUID promotionId,
|
|
@QueryParam("active") @DefaultValue("true") boolean isActive) {
|
|
UUID authenticatedUserId = securityService.getCurrentUserId();
|
|
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();
|
|
}
|
|
}
|
|
}
|