Versione OK Pour l'onglet événements.

This commit is contained in:
DahoudG
2025-09-15 20:15:34 +00:00
parent 8a619ee1bf
commit 12d514d866
73 changed files with 11508 additions and 674 deletions

View File

@@ -39,6 +39,62 @@ 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 {
System.out.println("GET /api/cotisations/public - page: " + page + ", size: " + size);
// Données de test pour l'application mobile
List<Map<String, Object>> cotisations = List.of(
Map.of(
"id", "1",
"nom", "Cotisation Mensuelle Janvier 2025",
"description", "Cotisation mensuelle pour le mois de janvier",
"montant", 25000.0,
"devise", "XOF",
"dateEcheance", "2025-01-31T23:59:59",
"statut", "ACTIVE",
"type", "MENSUELLE"
),
Map.of(
"id", "2",
"nom", "Cotisation Spéciale Projet",
"description", "Cotisation pour le financement du projet communautaire",
"montant", 50000.0,
"devise", "XOF",
"dateEcheance", "2025-03-15T23:59:59",
"statut", "ACTIVE",
"type", "SPECIALE"
)
);
Map<String, Object> response = Map.of(
"content", cotisations,
"totalElements", cotisations.size(),
"totalPages", 1,
"size", size,
"number", page
);
return Response.ok(response).build();
} catch (Exception e) {
System.err.println("Erreur lors de la récupération des cotisations publiques: " + e.getMessage());
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
*/

View File

@@ -19,6 +19,8 @@ import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
import org.jboss.logging.Logger;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Optional;
@@ -44,6 +46,93 @@ public class EvenementResource {
@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 temporaire pour les événements à venir (sans authentification)
*/
@GET
@Path("/a-venir-public")
@Operation(summary = "Événements à venir (public)", description = "Liste des événements à venir sans authentification")
@APIResponse(responseCode = "200", description = "Liste des événements")
public Response getEvenementsAVenirPublic(
@QueryParam("page") @DefaultValue("0") @Min(0) int page,
@QueryParam("size") @DefaultValue("10") @Min(1) int size) {
try {
LOG.infof("GET /api/evenements/a-venir-public - page: %d, size: %d", page, size);
// Créer des données de test pour l'application mobile (format List direct)
List<Map<String, Object>> evenements = new ArrayList<>();
Map<String, Object> event1 = new HashMap<>();
event1.put("id", "1");
event1.put("titre", "Assemblée Générale 2025");
event1.put("description", "Assemblée générale annuelle de l'union");
event1.put("dateDebut", "2025-02-15T09:00:00");
event1.put("dateFin", "2025-02-15T17:00:00");
event1.put("lieu", "Salle de conférence principale");
event1.put("statut", "PLANIFIE");
event1.put("typeEvenement", "ASSEMBLEE_GENERALE");
event1.put("inscriptionRequise", false);
event1.put("visiblePublic", true);
event1.put("actif", true);
evenements.add(event1);
Map<String, Object> event2 = new HashMap<>();
event2.put("id", "2");
event2.put("titre", "Formation Gestion Financière");
event2.put("description", "Formation sur la gestion financière des unions");
event2.put("dateDebut", "2025-02-20T14:00:00");
event2.put("dateFin", "2025-02-20T18:00:00");
event2.put("lieu", "Centre de formation");
event2.put("statut", "PLANIFIE");
event2.put("typeEvenement", "FORMATION");
event2.put("inscriptionRequise", true);
event2.put("visiblePublic", true);
event2.put("actif", true);
evenements.add(event2);
Map<String, Object> event3 = new HashMap<>();
event3.put("id", "3");
event3.put("titre", "Réunion Mensuelle");
event3.put("description", "Réunion mensuelle des membres");
event3.put("dateDebut", "2025-02-25T19:00:00");
event3.put("dateFin", "2025-02-25T21:00:00");
event3.put("lieu", "Siège de l'union");
event3.put("statut", "PLANIFIE");
event3.put("typeEvenement", "REUNION");
event3.put("inscriptionRequise", false);
event3.put("visiblePublic", true);
event3.put("actif", true);
evenements.add(event3);
// Retourner directement la liste (pas d'objet de pagination)
return Response.ok(evenements).build();
} catch (Exception e) {
LOG.errorf("Erreur lors de la récupération des é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 des événements"))
.build();
}
}
/**
* Liste tous les événements actifs avec pagination
*/

View File

@@ -0,0 +1,345 @@
package dev.lions.unionflow.server.security;
import io.quarkus.security.identity.SecurityIdentity;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import org.eclipse.microprofile.jwt.JsonWebToken;
import org.jboss.logging.Logger;
import java.util.Set;
import java.util.stream.Collectors;
/**
* Service pour l'intégration avec Keycloak et la gestion de la sécurité
* Fournit des méthodes utilitaires pour accéder aux informations de l'utilisateur connecté
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-15
*/
@ApplicationScoped
public class KeycloakService {
private static final Logger LOG = Logger.getLogger(KeycloakService.class);
@Inject
SecurityIdentity securityIdentity;
@Inject
JsonWebToken jwt;
/**
* Récupère l'email de l'utilisateur actuellement connecté
*
* @return l'email de l'utilisateur ou null si non connecté
*/
public String getCurrentUserEmail() {
if (securityIdentity == null || securityIdentity.isAnonymous()) {
LOG.debug("Aucun utilisateur connecté");
return null;
}
try {
// Essayer d'abord avec le claim 'email'
if (jwt != null && jwt.containsClaim("email")) {
String email = jwt.getClaim("email");
LOG.debugf("Email récupéré depuis JWT: %s", email);
return email;
}
// Fallback sur le nom principal
String principal = securityIdentity.getPrincipal().getName();
LOG.debugf("Email récupéré depuis principal: %s", principal);
return principal;
} catch (Exception e) {
LOG.warnf("Erreur lors de la récupération de l'email utilisateur: %s", e.getMessage());
return null;
}
}
/**
* Récupère l'ID utilisateur Keycloak de l'utilisateur actuellement connecté
*
* @return l'ID utilisateur Keycloak ou null si non connecté
*/
public String getCurrentUserId() {
if (securityIdentity == null || securityIdentity.isAnonymous()) {
return null;
}
try {
if (jwt != null && jwt.containsClaim("sub")) {
String userId = jwt.getClaim("sub");
LOG.debugf("ID utilisateur récupéré: %s", userId);
return userId;
}
} catch (Exception e) {
LOG.warnf("Erreur lors de la récupération de l'ID utilisateur: %s", e.getMessage());
}
return null;
}
/**
* Récupère le nom complet de l'utilisateur actuellement connecté
*
* @return le nom complet ou null si non disponible
*/
public String getCurrentUserFullName() {
if (securityIdentity == null || securityIdentity.isAnonymous()) {
return null;
}
try {
if (jwt != null) {
// Essayer le claim 'name' en premier
if (jwt.containsClaim("name")) {
return jwt.getClaim("name");
}
// Construire à partir de given_name et family_name
String givenName = jwt.containsClaim("given_name") ? jwt.getClaim("given_name") : "";
String familyName = jwt.containsClaim("family_name") ? jwt.getClaim("family_name") : "";
if (!givenName.isEmpty() || !familyName.isEmpty()) {
return (givenName + " " + familyName).trim();
}
// Fallback sur preferred_username
if (jwt.containsClaim("preferred_username")) {
return jwt.getClaim("preferred_username");
}
}
} catch (Exception e) {
LOG.warnf("Erreur lors de la récupération du nom complet: %s", e.getMessage());
}
return getCurrentUserEmail(); // Fallback sur l'email
}
/**
* Récupère le prénom de l'utilisateur actuellement connecté
*
* @return le prénom ou null si non disponible
*/
public String getCurrentUserFirstName() {
if (securityIdentity == null || securityIdentity.isAnonymous()) {
return null;
}
try {
if (jwt != null && jwt.containsClaim("given_name")) {
return jwt.getClaim("given_name");
}
} catch (Exception e) {
LOG.warnf("Erreur lors de la récupération du prénom: %s", e.getMessage());
}
return null;
}
/**
* Récupère le nom de famille de l'utilisateur actuellement connecté
*
* @return le nom de famille ou null si non disponible
*/
public String getCurrentUserLastName() {
if (securityIdentity == null || securityIdentity.isAnonymous()) {
return null;
}
try {
if (jwt != null && jwt.containsClaim("family_name")) {
return jwt.getClaim("family_name");
}
} catch (Exception e) {
LOG.warnf("Erreur lors de la récupération du nom de famille: %s", e.getMessage());
}
return null;
}
/**
* Vérifie si l'utilisateur actuel possède un rôle spécifique
*
* @param role le nom du rôle à vérifier
* @return true si l'utilisateur possède le rôle
*/
public boolean hasRole(String role) {
if (securityIdentity == null || securityIdentity.isAnonymous()) {
return false;
}
try {
boolean hasRole = securityIdentity.hasRole(role);
LOG.debugf("Vérification du rôle '%s' pour l'utilisateur: %s", role, hasRole);
return hasRole;
} catch (Exception e) {
LOG.warnf("Erreur lors de la vérification du rôle '%s': %s", role, e.getMessage());
return false;
}
}
/**
* Vérifie si l'utilisateur actuel possède au moins un des rôles spécifiés
*
* @param roles les rôles à vérifier
* @return true si l'utilisateur possède au moins un des rôles
*/
public boolean hasAnyRole(String... roles) {
if (roles == null || roles.length == 0) {
return false;
}
for (String role : roles) {
if (hasRole(role)) {
return true;
}
}
return false;
}
/**
* Vérifie si l'utilisateur actuel possède tous les rôles spécifiés
*
* @param roles les rôles à vérifier
* @return true si l'utilisateur possède tous les rôles
*/
public boolean hasAllRoles(String... roles) {
if (roles == null || roles.length == 0) {
return true;
}
for (String role : roles) {
if (!hasRole(role)) {
return false;
}
}
return true;
}
/**
* Récupère tous les rôles de l'utilisateur actuel
*
* @return ensemble des rôles de l'utilisateur
*/
public Set<String> getCurrentUserRoles() {
if (securityIdentity == null || securityIdentity.isAnonymous()) {
return Set.of();
}
try {
Set<String> roles = securityIdentity.getRoles();
LOG.debugf("Rôles de l'utilisateur actuel: %s", roles);
return roles;
} catch (Exception e) {
LOG.warnf("Erreur lors de la récupération des rôles: %s", e.getMessage());
return Set.of();
}
}
/**
* Vérifie si l'utilisateur actuel est un administrateur
*
* @return true si l'utilisateur est administrateur
*/
public boolean isAdmin() {
return hasAnyRole("admin", "administrator", "super_admin");
}
/**
* Vérifie si l'utilisateur actuel est connecté (non anonyme)
*
* @return true si l'utilisateur est connecté
*/
public boolean isAuthenticated() {
return securityIdentity != null && !securityIdentity.isAnonymous();
}
/**
* Récupère une claim spécifique du JWT
*
* @param claimName nom de la claim
* @return valeur de la claim ou null si non trouvée
*/
public <T> T getClaim(String claimName, Class<T> claimType) {
if (jwt == null || !jwt.containsClaim(claimName)) {
return null;
}
try {
return jwt.getClaim(claimName);
} catch (Exception e) {
LOG.warnf("Erreur lors de la récupération de la claim '%s': %s", claimName, e.getMessage());
return null;
}
}
/**
* Récupère les groupes de l'utilisateur depuis le JWT
*
* @return ensemble des groupes de l'utilisateur
*/
public Set<String> getCurrentUserGroups() {
if (jwt == null) {
return Set.of();
}
try {
if (jwt.containsClaim("groups")) {
Object groups = jwt.getClaim("groups");
if (groups instanceof Set) {
return ((Set<?>) groups).stream()
.map(Object::toString)
.collect(Collectors.toSet());
}
}
} catch (Exception e) {
LOG.warnf("Erreur lors de la récupération des groupes: %s", e.getMessage());
}
return Set.of();
}
/**
* Vérifie si l'utilisateur appartient à un groupe spécifique
*
* @param groupName nom du groupe
* @return true si l'utilisateur appartient au groupe
*/
public boolean isMemberOfGroup(String groupName) {
return getCurrentUserGroups().contains(groupName);
}
/**
* Récupère l'organisation de l'utilisateur depuis le JWT
*
* @return ID de l'organisation ou null si non disponible
*/
public String getCurrentUserOrganization() {
return getClaim("organization", String.class);
}
/**
* Log les informations de l'utilisateur actuel (pour debug)
*/
public void logCurrentUserInfo() {
if (!LOG.isDebugEnabled()) {
return;
}
LOG.debugf("=== Informations utilisateur actuel ===");
LOG.debugf("Email: %s", getCurrentUserEmail());
LOG.debugf("ID: %s", getCurrentUserId());
LOG.debugf("Nom complet: %s", getCurrentUserFullName());
LOG.debugf("Rôles: %s", getCurrentUserRoles());
LOG.debugf("Groupes: %s", getCurrentUserGroups());
LOG.debugf("Organisation: %s", getCurrentUserOrganization());
LOG.debugf("Authentifié: %s", isAuthenticated());
LOG.debugf("Admin: %s", isAdmin());
LOG.debugf("=====================================");
}
}

View File

@@ -0,0 +1,176 @@
package dev.lions.unionflow.server.service;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.transaction.Transactional;
import jakarta.validation.Valid;
import jakarta.validation.constraints.NotNull;
import org.jboss.logging.Logger;
import java.math.BigDecimal;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* Service métier pour la gestion des paiements Mobile Money
* Intègre Wave Money, Orange Money, et Moov Money
*
* @author UnionFlow Team
* @version 1.0
* @since 2025-01-15
*/
@ApplicationScoped
public class PaiementService {
private static final Logger LOG = Logger.getLogger(PaiementService.class);
/**
* Initie un paiement Mobile Money
*
* @param paymentData données du paiement
* @return informations du paiement initié
*/
@Transactional
public Map<String, Object> initiatePayment(@Valid Map<String, Object> paymentData) {
LOG.infof("Initiation d'un paiement");
try {
String operateur = (String) paymentData.get("operateur");
BigDecimal montant = new BigDecimal(paymentData.get("montant").toString());
String numeroTelephone = (String) paymentData.get("numeroTelephone");
String cotisationId = (String) paymentData.get("cotisationId");
// Générer un ID unique pour le paiement
String paymentId = UUID.randomUUID().toString();
String numeroReference = "PAY-" + System.currentTimeMillis();
Map<String, Object> response = new HashMap<>();
response.put("id", paymentId);
response.put("cotisationId", cotisationId);
response.put("numeroReference", numeroReference);
response.put("montant", montant);
response.put("codeDevise", "XOF");
response.put("methodePaiement", operateur != null ? operateur.toUpperCase() : "WAVE");
response.put("statut", "PENDING");
response.put("dateTransaction", LocalDateTime.now().toString());
response.put("numeroTransaction", numeroReference);
response.put("operateurMobileMoney", operateur != null ? operateur.toUpperCase() : "WAVE");
response.put("numeroTelephone", numeroTelephone);
response.put("dateCreation", LocalDateTime.now().toString());
// Métadonnées
Map<String, Object> metadonnees = new HashMap<>();
metadonnees.put("source", "unionflow_mobile");
metadonnees.put("operateur", operateur);
metadonnees.put("numero_telephone", numeroTelephone);
metadonnees.put("cotisation_id", cotisationId);
response.put("metadonnees", metadonnees);
return response;
} catch (Exception e) {
LOG.errorf("Erreur lors de l'initiation du paiement: %s", e.getMessage());
throw new RuntimeException("Erreur lors de l'initiation du paiement: " + e.getMessage());
}
}
/**
* Récupère le statut d'un paiement
*
* @param paymentId ID du paiement
* @return statut du paiement
*/
public Map<String, Object> getPaymentStatus(@NotNull String paymentId) {
LOG.infof("Récupération du statut du paiement: %s", paymentId);
// Simulation du statut
Map<String, Object> status = new HashMap<>();
status.put("id", paymentId);
status.put("statut", "COMPLETED"); // Simulation d'un paiement réussi
status.put("dateModification", LocalDateTime.now().toString());
status.put("message", "Paiement traité avec succès");
return status;
}
/**
* Annule un paiement
*
* @param paymentId ID du paiement
* @param cotisationId ID de la cotisation
* @return résultat de l'annulation
*/
@Transactional
public Map<String, Object> cancelPayment(@NotNull String paymentId, @NotNull String cotisationId) {
LOG.infof("Annulation du paiement: %s pour cotisation: %s", paymentId, cotisationId);
Map<String, Object> result = new HashMap<>();
result.put("id", paymentId);
result.put("cotisationId", cotisationId);
result.put("statut", "CANCELLED");
result.put("dateAnnulation", LocalDateTime.now().toString());
result.put("message", "Paiement annulé avec succès");
return result;
}
/**
* Récupère l'historique des paiements
*
* @param filters filtres de recherche
* @return liste des paiements
*/
public List<Map<String, Object>> getPaymentHistory(Map<String, Object> filters) {
LOG.info("Récupération de l'historique des paiements");
// Simulation d'un historique vide pour l'instant
return List.of();
}
/**
* Vérifie le statut d'un service de paiement
*
* @param serviceType type de service (WAVE, ORANGE_MONEY, MOOV_MONEY)
* @return statut du service
*/
public Map<String, Object> checkServiceStatus(@NotNull String serviceType) {
LOG.infof("Vérification du statut du service: %s", serviceType);
Map<String, Object> status = new HashMap<>();
status.put("service", serviceType);
status.put("statut", "OPERATIONAL");
status.put("disponible", true);
status.put("derniereMiseAJour", LocalDateTime.now().toString());
return status;
}
/**
* Récupère les statistiques de paiement
*
* @param filters filtres pour les statistiques
* @return statistiques des paiements
*/
public Map<String, Object> getPaymentStatistics(Map<String, Object> filters) {
LOG.info("Récupération des statistiques de paiement");
Map<String, Object> stats = new HashMap<>();
stats.put("totalPaiements", 0);
stats.put("montantTotal", BigDecimal.ZERO);
stats.put("paiementsReussis", 0);
stats.put("paiementsEchoues", 0);
stats.put("paiementsEnAttente", 0);
stats.put("operateurs", Map.of(
"WAVE", 0,
"ORANGE_MONEY", 0,
"MOOV_MONEY", 0
));
return stats;
}
}

View File

@@ -32,7 +32,7 @@ quarkus.flyway.baseline-on-migrate=true
quarkus.flyway.baseline-version=1.0.0
# Configuration Keycloak OIDC
quarkus.oidc.auth-server-url=http://192.168.1.11:8180/realms/unionflow
quarkus.oidc.auth-server-url=http://192.168.1.145:8180/realms/unionflow
quarkus.oidc.client-id=unionflow-server
quarkus.oidc.credentials.secret=unionflow-secret-2025
quarkus.oidc.tls.verification=none
@@ -83,9 +83,9 @@ quarkus.log.category."io.quarkus".level=INFO
%dev.quarkus.log.category."dev.lions.unionflow".level=DEBUG
%dev.quarkus.log.category."org.hibernate.SQL".level=DEBUG
# Configuration Keycloak pour développement
%dev.quarkus.oidc.tenant-enabled=true
%dev.quarkus.oidc.auth-server-url=http://192.168.1.11:8180/realms/unionflow
# Configuration Keycloak pour développement (temporairement désactivé)
%dev.quarkus.oidc.tenant-enabled=false
%dev.quarkus.oidc.auth-server-url=http://192.168.1.145:8180/realms/unionflow
%dev.quarkus.oidc.client-id=unionflow-server
%dev.quarkus.oidc.credentials.secret=unionflow-secret-2025
%dev.quarkus.oidc.tls.verification=none
@@ -114,7 +114,7 @@ quarkus.log.category."io.quarkus".level=INFO
%prod.quarkus.log.category.root.level=WARN
# Configuration Keycloak pour production
%prod.quarkus.oidc.auth-server-url=${KEYCLOAK_SERVER_URL:http://192.168.1.11:8180/realms/unionflow}
%prod.quarkus.oidc.auth-server-url=${KEYCLOAK_SERVER_URL:http://192.168.1.145:8180/realms/unionflow}
%prod.quarkus.oidc.client-id=${KEYCLOAK_CLIENT_ID:unionflow-server}
%prod.quarkus.oidc.credentials.secret=${KEYCLOAK_CLIENT_SECRET}
%prod.quarkus.oidc.tls.verification=required