Server-Api OK

This commit is contained in:
DahoudG
2025-09-10 22:02:16 +00:00
parent b2a23bdf89
commit bf79fa4e04
36 changed files with 8934 additions and 0 deletions

View File

@@ -0,0 +1,96 @@
package dev.lions.unionflow.server.entity;
import io.quarkus.hibernate.orm.panache.PanacheEntity;
import jakarta.persistence.*;
import jakarta.validation.constraints.Email;
import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;
import lombok.EqualsAndHashCode;
import lombok.NoArgsConstructor;
import java.time.LocalDate;
import java.time.LocalDateTime;
/**
* Entité Membre avec Lombok
*/
@Entity
@Table(name = "membres", indexes = {
@Index(name = "idx_membre_email", columnList = "email", unique = true),
@Index(name = "idx_membre_numero", columnList = "numeroMembre", unique = true),
@Index(name = "idx_membre_actif", columnList = "actif")
})
@Data
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class Membre extends PanacheEntity {
@NotBlank
@Column(name = "numero_membre", unique = true, nullable = false, length = 20)
private String numeroMembre;
@NotBlank
@Column(name = "prenom", nullable = false, length = 100)
private String prenom;
@NotBlank
@Column(name = "nom", nullable = false, length = 100)
private String nom;
@Email
@NotBlank
@Column(name = "email", unique = true, nullable = false, length = 255)
private String email;
@Column(name = "telephone", length = 20)
private String telephone;
@NotNull
@Column(name = "date_naissance", nullable = false)
private LocalDate dateNaissance;
@NotNull
@Column(name = "date_adhesion", nullable = false)
private LocalDate dateAdhesion;
@Builder.Default
@Column(name = "actif", nullable = false)
private Boolean actif = true;
@Builder.Default
@Column(name = "date_creation", nullable = false)
private LocalDateTime dateCreation = LocalDateTime.now();
@Column(name = "date_modification")
private LocalDateTime dateModification;
/**
* Méthode métier pour obtenir le nom complet
*/
public String getNomComplet() {
return prenom + " " + nom;
}
/**
* Méthode métier pour vérifier si le membre est majeur
*/
public boolean isMajeur() {
return dateNaissance.isBefore(LocalDate.now().minusYears(18));
}
/**
* Méthode métier pour calculer l'âge
*/
public int getAge() {
return LocalDate.now().getYear() - dateNaissance.getYear();
}
@PreUpdate
public void preUpdate() {
this.dateModification = LocalDateTime.now();
}
}

View File

@@ -0,0 +1,51 @@
package dev.lions.unionflow.server.repository;
import dev.lions.unionflow.server.entity.Membre;
import io.quarkus.hibernate.orm.panache.PanacheRepository;
import jakarta.enterprise.context.ApplicationScoped;
import java.util.List;
import java.util.Optional;
/**
* Repository pour l'entité Membre
*/
@ApplicationScoped
public class MembreRepository implements PanacheRepository<Membre> {
/**
* Trouve un membre par son email
*/
public Optional<Membre> findByEmail(String email) {
return find("email", email).firstResultOptional();
}
/**
* Trouve un membre par son numéro
*/
public Optional<Membre> findByNumeroMembre(String numeroMembre) {
return find("numeroMembre", numeroMembre).firstResultOptional();
}
/**
* Trouve tous les membres actifs
*/
public List<Membre> findAllActifs() {
return find("actif", true).list();
}
/**
* Compte le nombre de membres actifs
*/
public long countActifs() {
return count("actif", true);
}
/**
* Trouve les membres par nom ou prénom (recherche partielle)
*/
public List<Membre> findByNomOrPrenom(String recherche) {
return find("lower(nom) like ?1 or lower(prenom) like ?1",
"%" + recherche.toLowerCase() + "%").list();
}
}

View File

@@ -0,0 +1,132 @@
package dev.lions.unionflow.server.resource;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.service.MembreService;
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 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;
import java.util.List;
import java.util.Map;
/**
* 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() {
LOG.info("Récupération de la liste des membres actifs");
List<Membre> membres = membreService.listerMembresActifs();
return Response.ok(membres).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 = "ID du membre") @PathParam("id") Long id) {
LOG.infof("Récupération du membre ID: %d", id);
return membreService.trouverParId(id)
.map(membre -> Response.ok(membre).build())
.orElse(Response.status(Response.Status.NOT_FOUND)
.entity(Map.of("message", "Membre non trouvé")).build());
}
@POST
@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 Membre membre) {
LOG.infof("Création d'un nouveau membre: %s", membre.getEmail());
try {
Membre nouveauMembre = membreService.creerMembre(membre);
return Response.status(Response.Status.CREATED).entity(nouveauMembre).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 = "ID du membre") @PathParam("id") Long id,
@Valid Membre membre) {
LOG.infof("Mise à jour du membre ID: %d", id);
try {
Membre membreMisAJour = membreService.mettreAJourMembre(id, membre);
return Response.ok(membreMisAJour).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 = "ID du membre") @PathParam("id") Long id) {
LOG.infof("Désactivation du membre ID: %d", 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) {
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();
}
List<Membre> membres = membreService.rechercherMembres(recherche.trim());
return Response.ok(membres).build();
}
@GET
@Path("/stats")
@Operation(summary = "Obtenir les statistiques des membres")
@APIResponse(responseCode = "200", description = "Statistiques des membres")
public Response obtenirStatistiques() {
LOG.info("Récupération des statistiques des membres");
long nombreMembresActifs = membreService.compterMembresActifs();
return Response.ok(Map.of(
"nombreMembresActifs", nombreMembresActifs,
"timestamp", java.time.LocalDateTime.now()
)).build();
}
}

View File

@@ -0,0 +1,143 @@
package dev.lions.unionflow.server.service;
import dev.lions.unionflow.server.entity.Membre;
import dev.lions.unionflow.server.repository.MembreRepository;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import org.jboss.logging.Logger;
import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
/**
* Service métier pour les membres
*/
@ApplicationScoped
public class MembreService {
private static final Logger LOG = Logger.getLogger(MembreService.class);
@Inject
MembreRepository membreRepository;
/**
* Crée un nouveau membre
*/
@Transactional
public Membre creerMembre(Membre membre) {
LOG.infof("Création d'un nouveau membre: %s", membre.getEmail());
// Générer un numéro de membre unique
if (membre.getNumeroMembre() == null || membre.getNumeroMembre().isEmpty()) {
membre.setNumeroMembre(genererNumeroMembre());
}
// Vérifier l'unicité de l'email
if (membreRepository.findByEmail(membre.getEmail()).isPresent()) {
throw new IllegalArgumentException("Un membre avec cet email existe déjà");
}
// Vérifier l'unicité du numéro de membre
if (membreRepository.findByNumeroMembre(membre.getNumeroMembre()).isPresent()) {
throw new IllegalArgumentException("Un membre avec ce numéro existe déjà");
}
membreRepository.persist(membre);
LOG.infof("Membre créé avec succès: %s (ID: %d)", membre.getNomComplet(), membre.id);
return membre;
}
/**
* Met à jour un membre existant
*/
@Transactional
public Membre mettreAJourMembre(Long id, Membre membreModifie) {
LOG.infof("Mise à jour du membre ID: %d", id);
Membre membre = membreRepository.findById(id);
if (membre == null) {
throw new IllegalArgumentException("Membre non trouvé avec l'ID: " + id);
}
// Vérifier l'unicité de l'email si modifié
if (!membre.getEmail().equals(membreModifie.getEmail())) {
if (membreRepository.findByEmail(membreModifie.getEmail()).isPresent()) {
throw new IllegalArgumentException("Un membre avec cet email existe déjà");
}
}
// Mettre à jour les champs
membre.setPrenom(membreModifie.getPrenom());
membre.setNom(membreModifie.getNom());
membre.setEmail(membreModifie.getEmail());
membre.setTelephone(membreModifie.getTelephone());
membre.setDateNaissance(membreModifie.getDateNaissance());
membre.setActif(membreModifie.getActif());
LOG.infof("Membre mis à jour avec succès: %s", membre.getNomComplet());
return membre;
}
/**
* Trouve un membre par son ID
*/
public Optional<Membre> trouverParId(Long id) {
return Optional.ofNullable(membreRepository.findById(id));
}
/**
* Trouve un membre par son email
*/
public Optional<Membre> trouverParEmail(String email) {
return membreRepository.findByEmail(email);
}
/**
* Liste tous les membres actifs
*/
public List<Membre> listerMembresActifs() {
return membreRepository.findAllActifs();
}
/**
* Recherche des membres par nom ou prénom
*/
public List<Membre> rechercherMembres(String recherche) {
return membreRepository.findByNomOrPrenom(recherche);
}
/**
* Désactive un membre
*/
@Transactional
public void desactiverMembre(Long id) {
LOG.infof("Désactivation du membre ID: %d", id);
Membre membre = membreRepository.findById(id);
if (membre == null) {
throw new IllegalArgumentException("Membre non trouvé avec l'ID: " + id);
}
membre.setActif(false);
LOG.infof("Membre désactivé: %s", membre.getNomComplet());
}
/**
* Génère un numéro de membre unique
*/
private String genererNumeroMembre() {
String prefix = "UF" + LocalDate.now().getYear();
String suffix = UUID.randomUUID().toString().substring(0, 8).toUpperCase();
return prefix + "-" + suffix;
}
/**
* Compte le nombre total de membres actifs
*/
public long compterMembresActifs() {
return membreRepository.countActifs();
}
}

View File

@@ -0,0 +1,24 @@
# Configuration de base pour UnionFlow Server
quarkus.application.name=unionflow-server
quarkus.http.port=8080
# Configuration de développement
%dev.quarkus.log.level=INFO
%dev.quarkus.log.console.enable=true
# Configuration de base de données (PostgreSQL)
quarkus.datasource.db-kind=postgresql
quarkus.datasource.username=unionflow
quarkus.datasource.password=unionflow
quarkus.datasource.jdbc.url=jdbc:postgresql://localhost:5432/unionflow_dev
quarkus.hibernate-orm.database.generation=drop-and-create
quarkus.hibernate-orm.log.sql=false
# Configuration pour le développement sans base de données externe
%dev.quarkus.datasource.db-kind=h2
%dev.quarkus.datasource.jdbc.url=jdbc:h2:mem:unionflow;DB_CLOSE_DELAY=-1
%dev.quarkus.hibernate-orm.database.generation=drop-and-create
# Configuration Swagger UI
quarkus.swagger-ui.always-include=true
quarkus.swagger-ui.path=/swagger-ui