chore(docker): add root Dockerfile pinning ubi8/openjdk-21:1.21 + UID 1001 for lionsctl pipeline
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 4m2s
Some checks failed
CI/CD Pipeline / pipeline (push) Failing after 4m2s
This commit is contained in:
@@ -1,88 +1,88 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.service.OrganisationService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
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.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* API réservée au SUPER_ADMIN pour associer un utilisateur (par email) à une organisation.
|
||||
* Permet à un admin d'organisation de voir « Mes organisations » après connexion.
|
||||
*/
|
||||
@Path("/api/admin/associer-organisation")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Admin - Association", description = "Associer un utilisateur à une organisation (SUPER_ADMIN)")
|
||||
@RolesAllowed("SUPER_ADMIN")
|
||||
public class AdminAssocierOrganisationResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(AdminAssocierOrganisationResource.class);
|
||||
|
||||
@Inject
|
||||
OrganisationService organisationService;
|
||||
|
||||
/**
|
||||
* Associe l'utilisateur ayant l'email donné à l'organisation indiquée.
|
||||
* Crée un Membre minimal si nécessaire, puis le lien MembreOrganisation (idempotent).
|
||||
*/
|
||||
@POST
|
||||
@Operation(
|
||||
summary = "Associer un compte à une organisation",
|
||||
description = "En tant que super admin, associe l'utilisateur (email) à une organisation. "
|
||||
+ "Si aucun Membre n'existe pour cet email, une fiche minimale est créée. "
|
||||
+ "L'utilisateur pourra alors voir cette organisation dans « Mes organisations »."
|
||||
)
|
||||
public Response associerOrganisation(AssocierOrganisationRequest request) {
|
||||
if (request == null || request.email() == null || request.email().isBlank()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "L'email est obligatoire"))
|
||||
.build();
|
||||
}
|
||||
if (request.organisationId() == null) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "L'organisation (organisationId) est obligatoire"))
|
||||
.build();
|
||||
}
|
||||
try {
|
||||
organisationService.associerUtilisateurAOrganisation(request.email().trim(), request.organisationId());
|
||||
LOG.infof("Association réussie: %s -> organisation %s", request.email(), request.organisationId());
|
||||
return Response.ok(Map.of(
|
||||
"success", true,
|
||||
"message", "Utilisateur associé à l'organisation avec succès.",
|
||||
"email", request.email(),
|
||||
"organisationId", request.organisationId().toString()
|
||||
)).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Non trouvé", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Requête invalide", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur association organisation: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Corps de la requête pour associer un utilisateur à une organisation.
|
||||
*/
|
||||
public record AssocierOrganisationRequest(
|
||||
@NotBlank(message = "L'email est obligatoire") String email,
|
||||
@NotNull(message = "L'organisation est obligatoire") UUID organisationId
|
||||
) {}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.service.OrganisationService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
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.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* API réservée au SUPER_ADMIN pour associer un utilisateur (par email) à une organisation.
|
||||
* Permet à un admin d'organisation de voir « Mes organisations » après connexion.
|
||||
*/
|
||||
@Path("/api/admin/associer-organisation")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Admin - Association", description = "Associer un utilisateur à une organisation (SUPER_ADMIN)")
|
||||
@RolesAllowed("SUPER_ADMIN")
|
||||
public class AdminAssocierOrganisationResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(AdminAssocierOrganisationResource.class);
|
||||
|
||||
@Inject
|
||||
OrganisationService organisationService;
|
||||
|
||||
/**
|
||||
* Associe l'utilisateur ayant l'email donné à l'organisation indiquée.
|
||||
* Crée un Membre minimal si nécessaire, puis le lien MembreOrganisation (idempotent).
|
||||
*/
|
||||
@POST
|
||||
@Operation(
|
||||
summary = "Associer un compte à une organisation",
|
||||
description = "En tant que super admin, associe l'utilisateur (email) à une organisation. "
|
||||
+ "Si aucun Membre n'existe pour cet email, une fiche minimale est créée. "
|
||||
+ "L'utilisateur pourra alors voir cette organisation dans « Mes organisations »."
|
||||
)
|
||||
public Response associerOrganisation(AssocierOrganisationRequest request) {
|
||||
if (request == null || request.email() == null || request.email().isBlank()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "L'email est obligatoire"))
|
||||
.build();
|
||||
}
|
||||
if (request.organisationId() == null) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "L'organisation (organisationId) est obligatoire"))
|
||||
.build();
|
||||
}
|
||||
try {
|
||||
organisationService.associerUtilisateurAOrganisation(request.email().trim(), request.organisationId());
|
||||
LOG.infof("Association réussie: %s -> organisation %s", request.email(), request.organisationId());
|
||||
return Response.ok(Map.of(
|
||||
"success", true,
|
||||
"message", "Utilisateur associé à l'organisation avec succès.",
|
||||
"email", request.email(),
|
||||
"organisationId", request.organisationId().toString()
|
||||
)).build();
|
||||
} catch (jakarta.ws.rs.NotFoundException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Non trouvé", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Requête invalide", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur association organisation: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Corps de la requête pour associer un utilisateur à une organisation.
|
||||
*/
|
||||
public record AssocierOrganisationRequest(
|
||||
@NotBlank(message = "L'email est obligatoire") String email,
|
||||
@NotNull(message = "L'organisation est obligatoire") UUID organisationId
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -1,64 +0,0 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.service.MigrerOrganisationsVersKeycloakService;
|
||||
import dev.lions.unionflow.server.service.MigrerOrganisationsVersKeycloakService.MigrationReport;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* Endpoints d'administration Keycloak 26 Organizations.
|
||||
*
|
||||
* <p>Réservés aux SUPER_ADMIN. Opérations à déclencher manuellement lors de la
|
||||
* migration Keycloak 23 → 26.
|
||||
*/
|
||||
@Slf4j
|
||||
@Path("/api/admin/keycloak")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@RolesAllowed("SUPER_ADMIN")
|
||||
public class AdminKeycloakOrganisationResource {
|
||||
|
||||
@Inject
|
||||
MigrerOrganisationsVersKeycloakService migrationService;
|
||||
|
||||
/**
|
||||
* Lance la migration one-shot des organisations UnionFlow vers Keycloak 26 Organizations.
|
||||
*
|
||||
* <p>Idempotent : les organisations déjà migrées (keycloak_org_id non null) sont ignorées.
|
||||
*
|
||||
* @return rapport de migration (total, créés, ignorés, erreurs)
|
||||
*/
|
||||
@POST
|
||||
@Path("/migrer-organisations")
|
||||
public Response migrerOrganisations() {
|
||||
log.info("Déclenchement migration organisations → Keycloak 26 Organizations");
|
||||
try {
|
||||
MigrationReport report = migrationService.migrerToutesLesOrganisations();
|
||||
log.info("Migration terminée : {}", report);
|
||||
|
||||
return Response
|
||||
.status(report.success() ? Response.Status.OK.getStatusCode() : 207)
|
||||
.entity(Map.of(
|
||||
"total", report.total(),
|
||||
"crees", report.crees(),
|
||||
"ignores", report.ignores(),
|
||||
"erreurs", report.erreurs(),
|
||||
"succes", report.success()
|
||||
))
|
||||
.build();
|
||||
|
||||
} catch (Exception e) {
|
||||
log.error("Erreur critique lors de la migration : {}", e.getMessage(), e);
|
||||
return Response.serverError()
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,162 +1,162 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.service.AdminUserService;
|
||||
import dev.lions.user.manager.dto.role.RoleDTO;
|
||||
import dev.lions.user.manager.dto.user.UserDTO;
|
||||
import dev.lions.user.manager.dto.user.UserSearchResultDTO;
|
||||
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 org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* API admin pour la gestion des utilisateurs Keycloak (proxy vers lions-user-manager).
|
||||
* Réservé au rôle SUPER_ADMIN — la vérification est faite par @RolesAllowed au niveau classe.
|
||||
*/
|
||||
@Path("/api/admin/users")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Admin - Utilisateurs", description = "Gestion des utilisateurs Keycloak (SUPER_ADMIN)")
|
||||
@RolesAllowed("SUPER_ADMIN")
|
||||
public class AdminUserResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(AdminUserResource.class);
|
||||
|
||||
@Inject
|
||||
AdminUserService adminUserService;
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Lister les utilisateurs", description = "Liste paginée des utilisateurs du realm")
|
||||
public Response list(
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size,
|
||||
@QueryParam("search") String search
|
||||
) {
|
||||
try {
|
||||
UserSearchResultDTO result = adminUserService.searchUsers(page, size, search);
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur liste utilisateurs: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Détail utilisateur")
|
||||
public Response getById(@PathParam("id") String id) {
|
||||
try {
|
||||
UserDTO user = adminUserService.getUserById(id);
|
||||
if (user == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND).build();
|
||||
}
|
||||
return Response.ok(user).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur détail utilisateur %s: %s", id, e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/roles")
|
||||
@Operation(summary = "Liste des rôles realm")
|
||||
public Response listRoles() {
|
||||
try {
|
||||
List<RoleDTO> roles = adminUserService.getRealmRoles();
|
||||
return Response.ok(roles).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur liste rôles: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}/roles")
|
||||
@Operation(summary = "Rôles d'un utilisateur")
|
||||
public Response getUserRoles(@PathParam("id") String id) {
|
||||
try {
|
||||
List<RoleDTO> roles = adminUserService.getUserRoles(id);
|
||||
return Response.ok(roles).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur rôles utilisateur %s: %s", id, e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{id}/roles")
|
||||
@Operation(summary = "Mettre à jour les rôles d'un utilisateur")
|
||||
public Response setUserRoles(@PathParam("id") String id, List<String> roleNames) {
|
||||
try {
|
||||
adminUserService.setUserRoles(id, roleNames);
|
||||
return Response.ok(Map.of("success", true, "userId", id)).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur mise à jour rôles %s: %s", id, e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Operation(summary = "Créer un utilisateur", description = "Crée un nouvel utilisateur Keycloak (proxy lions-user-manager)")
|
||||
public Response createUser(UserDTO user) {
|
||||
try {
|
||||
UserDTO created = adminUserService.createUser(user);
|
||||
return Response.status(Response.Status.CREATED).entity(created).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warnf("Création utilisateur refusée: %s", e.getMessage());
|
||||
return Response.status(Response.Status.CONFLICT)
|
||||
.entity(Map.of("error", "Conflit", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur création utilisateur: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Mettre à jour un utilisateur", description = "Met à jour un utilisateur (au minimum enabled)")
|
||||
public Response updateUser(@PathParam("id") String id, UserDTO user) {
|
||||
try {
|
||||
if (user.getEnabled() != null) {
|
||||
UserDTO updated = adminUserService.updateUserEnabled(id, user.getEnabled());
|
||||
return Response.ok(updated).build();
|
||||
}
|
||||
UserDTO updated = adminUserService.updateUser(id, user);
|
||||
return Response.ok(updated).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (e.getMessage() != null && e.getMessage().contains("non trouvé")) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Non trouvé", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Requête invalide", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur mise à jour utilisateur %s: %s", id, e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.service.AdminUserService;
|
||||
import dev.lions.user.manager.dto.role.RoleDTO;
|
||||
import dev.lions.user.manager.dto.user.UserDTO;
|
||||
import dev.lions.user.manager.dto.user.UserSearchResultDTO;
|
||||
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 org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* API admin pour la gestion des utilisateurs Keycloak (proxy vers lions-user-manager).
|
||||
* Réservé au rôle SUPER_ADMIN — la vérification est faite par @RolesAllowed au niveau classe.
|
||||
*/
|
||||
@Path("/api/admin/users")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Admin - Utilisateurs", description = "Gestion des utilisateurs Keycloak (SUPER_ADMIN)")
|
||||
@RolesAllowed("SUPER_ADMIN")
|
||||
public class AdminUserResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(AdminUserResource.class);
|
||||
|
||||
@Inject
|
||||
AdminUserService adminUserService;
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Lister les utilisateurs", description = "Liste paginée des utilisateurs du realm")
|
||||
public Response list(
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size,
|
||||
@QueryParam("search") String search
|
||||
) {
|
||||
try {
|
||||
UserSearchResultDTO result = adminUserService.searchUsers(page, size, search);
|
||||
return Response.ok(result).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur liste utilisateurs: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Détail utilisateur")
|
||||
public Response getById(@PathParam("id") String id) {
|
||||
try {
|
||||
UserDTO user = adminUserService.getUserById(id);
|
||||
if (user == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND).build();
|
||||
}
|
||||
return Response.ok(user).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur détail utilisateur %s: %s", id, e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/roles")
|
||||
@Operation(summary = "Liste des rôles realm")
|
||||
public Response listRoles() {
|
||||
try {
|
||||
List<RoleDTO> roles = adminUserService.getRealmRoles();
|
||||
return Response.ok(roles).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur liste rôles: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}/roles")
|
||||
@Operation(summary = "Rôles d'un utilisateur")
|
||||
public Response getUserRoles(@PathParam("id") String id) {
|
||||
try {
|
||||
List<RoleDTO> roles = adminUserService.getUserRoles(id);
|
||||
return Response.ok(roles).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur rôles utilisateur %s: %s", id, e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{id}/roles")
|
||||
@Operation(summary = "Mettre à jour les rôles d'un utilisateur")
|
||||
public Response setUserRoles(@PathParam("id") String id, List<String> roleNames) {
|
||||
try {
|
||||
adminUserService.setUserRoles(id, roleNames);
|
||||
return Response.ok(Map.of("success", true, "userId", id)).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur mise à jour rôles %s: %s", id, e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@POST
|
||||
@Operation(summary = "Créer un utilisateur", description = "Crée un nouvel utilisateur Keycloak (proxy lions-user-manager)")
|
||||
public Response createUser(UserDTO user) {
|
||||
try {
|
||||
UserDTO created = adminUserService.createUser(user);
|
||||
return Response.status(Response.Status.CREATED).entity(created).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warnf("Création utilisateur refusée: %s", e.getMessage());
|
||||
return Response.status(Response.Status.CONFLICT)
|
||||
.entity(Map.of("error", "Conflit", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur création utilisateur: %s", e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Mettre à jour un utilisateur", description = "Met à jour un utilisateur (au minimum enabled)")
|
||||
public Response updateUser(@PathParam("id") String id, UserDTO user) {
|
||||
try {
|
||||
if (user.getEnabled() != null) {
|
||||
UserDTO updated = adminUserService.updateUserEnabled(id, user.getEnabled());
|
||||
return Response.ok(updated).build();
|
||||
}
|
||||
UserDTO updated = adminUserService.updateUser(id, user);
|
||||
return Response.ok(updated).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
if (e.getMessage() != null && e.getMessage().contains("non trouvé")) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Non trouvé", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Requête invalide", "message", e.getMessage()))
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur mise à jour utilisateur %s: %s", id, e.getMessage());
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", "Erreur serveur", "message", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,168 +1,168 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.lcbft.AlerteLcbFtResponse;
|
||||
import dev.lions.unionflow.server.entity.AlerteLcbFt;
|
||||
import dev.lions.unionflow.server.repository.AlerteLcbFtRepository;
|
||||
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 org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* API REST pour la gestion des alertes LCB-FT.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-15
|
||||
*/
|
||||
@Path("/api/alertes-lcb-ft")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Alertes LCB-FT", description = "Gestion des alertes Lutte Contre le Blanchiment")
|
||||
public class AlerteLcbFtResource {
|
||||
|
||||
@Inject
|
||||
AlerteLcbFtRepository alerteLcbFtRepository;
|
||||
|
||||
/**
|
||||
* Récupère les alertes LCB-FT avec filtres et pagination.
|
||||
*/
|
||||
@GET
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Liste des alertes LCB-FT", description = "Récupère les alertes avec filtrage et pagination")
|
||||
public Response getAlertes(
|
||||
@QueryParam("organisationId") String organisationId,
|
||||
@QueryParam("typeAlerte") String typeAlerte,
|
||||
@QueryParam("traitee") Boolean traitee,
|
||||
@QueryParam("dateDebut") String dateDebut,
|
||||
@QueryParam("dateFin") String dateFin,
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size
|
||||
) {
|
||||
UUID orgId = organisationId != null && !organisationId.isBlank() ? UUID.fromString(organisationId) : null;
|
||||
LocalDateTime debut = dateDebut != null && !dateDebut.isBlank() ? LocalDateTime.parse(dateDebut) : null;
|
||||
LocalDateTime fin = dateFin != null && !dateFin.isBlank() ? LocalDateTime.parse(dateFin) : null;
|
||||
|
||||
List<AlerteLcbFt> alertes = alerteLcbFtRepository.search(
|
||||
orgId,
|
||||
typeAlerte,
|
||||
traitee,
|
||||
debut,
|
||||
fin,
|
||||
page,
|
||||
size
|
||||
);
|
||||
|
||||
long total = alerteLcbFtRepository.count(orgId, typeAlerte, traitee, debut, fin);
|
||||
|
||||
List<AlerteLcbFtResponse> responses = alertes.stream()
|
||||
.map(this::mapToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("content", responses);
|
||||
result.put("totalElements", total);
|
||||
result.put("totalPages", (int) Math.ceil((double) total / size));
|
||||
result.put("currentPage", page);
|
||||
result.put("pageSize", size);
|
||||
|
||||
return Response.ok(result).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère une alerte par son ID.
|
||||
*/
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Détails d'une alerte", description = "Récupère une alerte par son ID")
|
||||
public Response getAlerteById(@PathParam("id") String id) {
|
||||
AlerteLcbFt alerte = alerteLcbFtRepository.findById(UUID.fromString(id));
|
||||
if (alerte == null) {
|
||||
throw new NotFoundException("Alerte non trouvée");
|
||||
}
|
||||
|
||||
return Response.ok(mapToResponse(alerte)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque une alerte comme traitée.
|
||||
*/
|
||||
@POST
|
||||
@Path("/{id}/traiter")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Traiter une alerte", description = "Marque une alerte comme traitée avec un commentaire")
|
||||
public Response traiterAlerte(
|
||||
@PathParam("id") String id,
|
||||
Map<String, String> body
|
||||
) {
|
||||
AlerteLcbFt alerte = alerteLcbFtRepository.findById(UUID.fromString(id));
|
||||
if (alerte == null) {
|
||||
throw new NotFoundException("Alerte non trouvée");
|
||||
}
|
||||
|
||||
alerte.setTraitee(true);
|
||||
alerte.setDateTraitement(LocalDateTime.now());
|
||||
String traiteParStr = body.get("traitePar");
|
||||
if (traiteParStr != null && !traiteParStr.isBlank()) {
|
||||
try {
|
||||
alerte.setTraitePar(UUID.fromString(traiteParStr));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new BadRequestException("traitePar doit être un UUID valide");
|
||||
}
|
||||
}
|
||||
alerte.setCommentaireTraitement(body.get("commentaire"));
|
||||
|
||||
alerteLcbFtRepository.persist(alerte);
|
||||
|
||||
return Response.ok(mapToResponse(alerte)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte les alertes non traitées.
|
||||
*/
|
||||
@GET
|
||||
@Path("/stats/non-traitees")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Statistiques alertes", description = "Nombre d'alertes non traitées")
|
||||
public Response getStatsNonTraitees(@QueryParam("organisationId") String organisationId) {
|
||||
UUID orgId = organisationId != null && !organisationId.isBlank() ? UUID.fromString(organisationId) : null;
|
||||
long count = alerteLcbFtRepository.countNonTraitees(orgId);
|
||||
|
||||
return Response.ok(Map.of("count", count)).build();
|
||||
}
|
||||
|
||||
private AlerteLcbFtResponse mapToResponse(AlerteLcbFt alerte) {
|
||||
return AlerteLcbFtResponse.builder()
|
||||
.id(alerte.getId().toString())
|
||||
.organisationId(alerte.getOrganisation() != null ? alerte.getOrganisation().getId().toString() : null)
|
||||
.organisationNom(alerte.getOrganisation() != null ? alerte.getOrganisation().getNom() : null)
|
||||
.membreId(alerte.getMembre() != null ? alerte.getMembre().getId().toString() : null)
|
||||
.membreNomComplet(alerte.getMembre() != null ?
|
||||
alerte.getMembre().getPrenom() + " " + alerte.getMembre().getNom() : null)
|
||||
.typeAlerte(alerte.getTypeAlerte())
|
||||
.dateAlerte(alerte.getDateAlerte())
|
||||
.description(alerte.getDescription())
|
||||
.details(alerte.getDetails())
|
||||
.montant(alerte.getMontant())
|
||||
.seuil(alerte.getSeuil())
|
||||
.typeOperation(alerte.getTypeOperation())
|
||||
.transactionRef(alerte.getTransactionRef())
|
||||
.severite(alerte.getSeverite())
|
||||
.traitee(alerte.getTraitee())
|
||||
.dateTraitement(alerte.getDateTraitement())
|
||||
.traitePar(alerte.getTraitePar() != null ? alerte.getTraitePar().toString() : null)
|
||||
.commentaireTraitement(alerte.getCommentaireTraitement())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.lcbft.AlerteLcbFtResponse;
|
||||
import dev.lions.unionflow.server.entity.AlerteLcbFt;
|
||||
import dev.lions.unionflow.server.repository.AlerteLcbFtRepository;
|
||||
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 org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashMap;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* API REST pour la gestion des alertes LCB-FT.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-15
|
||||
*/
|
||||
@Path("/api/alertes-lcb-ft")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Alertes LCB-FT", description = "Gestion des alertes Lutte Contre le Blanchiment")
|
||||
public class AlerteLcbFtResource {
|
||||
|
||||
@Inject
|
||||
AlerteLcbFtRepository alerteLcbFtRepository;
|
||||
|
||||
/**
|
||||
* Récupère les alertes LCB-FT avec filtres et pagination.
|
||||
*/
|
||||
@GET
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Liste des alertes LCB-FT", description = "Récupère les alertes avec filtrage et pagination")
|
||||
public Response getAlertes(
|
||||
@QueryParam("organisationId") String organisationId,
|
||||
@QueryParam("typeAlerte") String typeAlerte,
|
||||
@QueryParam("traitee") Boolean traitee,
|
||||
@QueryParam("dateDebut") String dateDebut,
|
||||
@QueryParam("dateFin") String dateFin,
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size
|
||||
) {
|
||||
UUID orgId = organisationId != null && !organisationId.isBlank() ? UUID.fromString(organisationId) : null;
|
||||
LocalDateTime debut = dateDebut != null && !dateDebut.isBlank() ? LocalDateTime.parse(dateDebut) : null;
|
||||
LocalDateTime fin = dateFin != null && !dateFin.isBlank() ? LocalDateTime.parse(dateFin) : null;
|
||||
|
||||
List<AlerteLcbFt> alertes = alerteLcbFtRepository.search(
|
||||
orgId,
|
||||
typeAlerte,
|
||||
traitee,
|
||||
debut,
|
||||
fin,
|
||||
page,
|
||||
size
|
||||
);
|
||||
|
||||
long total = alerteLcbFtRepository.count(orgId, typeAlerte, traitee, debut, fin);
|
||||
|
||||
List<AlerteLcbFtResponse> responses = alertes.stream()
|
||||
.map(this::mapToResponse)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
Map<String, Object> result = new HashMap<>();
|
||||
result.put("content", responses);
|
||||
result.put("totalElements", total);
|
||||
result.put("totalPages", (int) Math.ceil((double) total / size));
|
||||
result.put("currentPage", page);
|
||||
result.put("pageSize", size);
|
||||
|
||||
return Response.ok(result).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère une alerte par son ID.
|
||||
*/
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Détails d'une alerte", description = "Récupère une alerte par son ID")
|
||||
public Response getAlerteById(@PathParam("id") String id) {
|
||||
AlerteLcbFt alerte = alerteLcbFtRepository.findById(UUID.fromString(id));
|
||||
if (alerte == null) {
|
||||
throw new NotFoundException("Alerte non trouvée");
|
||||
}
|
||||
|
||||
return Response.ok(mapToResponse(alerte)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque une alerte comme traitée.
|
||||
*/
|
||||
@POST
|
||||
@Path("/{id}/traiter")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Traiter une alerte", description = "Marque une alerte comme traitée avec un commentaire")
|
||||
public Response traiterAlerte(
|
||||
@PathParam("id") String id,
|
||||
Map<String, String> body
|
||||
) {
|
||||
AlerteLcbFt alerte = alerteLcbFtRepository.findById(UUID.fromString(id));
|
||||
if (alerte == null) {
|
||||
throw new NotFoundException("Alerte non trouvée");
|
||||
}
|
||||
|
||||
alerte.setTraitee(true);
|
||||
alerte.setDateTraitement(LocalDateTime.now());
|
||||
String traiteParStr = body.get("traitePar");
|
||||
if (traiteParStr != null && !traiteParStr.isBlank()) {
|
||||
try {
|
||||
alerte.setTraitePar(UUID.fromString(traiteParStr));
|
||||
} catch (IllegalArgumentException e) {
|
||||
throw new BadRequestException("traitePar doit être un UUID valide");
|
||||
}
|
||||
}
|
||||
alerte.setCommentaireTraitement(body.get("commentaire"));
|
||||
|
||||
alerteLcbFtRepository.persist(alerte);
|
||||
|
||||
return Response.ok(mapToResponse(alerte)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte les alertes non traitées.
|
||||
*/
|
||||
@GET
|
||||
@Path("/stats/non-traitees")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Statistiques alertes", description = "Nombre d'alertes non traitées")
|
||||
public Response getStatsNonTraitees(@QueryParam("organisationId") String organisationId) {
|
||||
UUID orgId = organisationId != null && !organisationId.isBlank() ? UUID.fromString(organisationId) : null;
|
||||
long count = alerteLcbFtRepository.countNonTraitees(orgId);
|
||||
|
||||
return Response.ok(Map.of("count", count)).build();
|
||||
}
|
||||
|
||||
private AlerteLcbFtResponse mapToResponse(AlerteLcbFt alerte) {
|
||||
return AlerteLcbFtResponse.builder()
|
||||
.id(alerte.getId().toString())
|
||||
.organisationId(alerte.getOrganisation() != null ? alerte.getOrganisation().getId().toString() : null)
|
||||
.organisationNom(alerte.getOrganisation() != null ? alerte.getOrganisation().getNom() : null)
|
||||
.membreId(alerte.getMembre() != null ? alerte.getMembre().getId().toString() : null)
|
||||
.membreNomComplet(alerte.getMembre() != null ?
|
||||
alerte.getMembre().getPrenom() + " " + alerte.getMembre().getNom() : null)
|
||||
.typeAlerte(alerte.getTypeAlerte())
|
||||
.dateAlerte(alerte.getDateAlerte())
|
||||
.description(alerte.getDescription())
|
||||
.details(alerte.getDetails())
|
||||
.montant(alerte.getMontant())
|
||||
.seuil(alerte.getSeuil())
|
||||
.typeOperation(alerte.getTypeOperation())
|
||||
.transactionRef(alerte.getTransactionRef())
|
||||
.severite(alerte.getSeverite())
|
||||
.traitee(alerte.getTraitee())
|
||||
.dateTraitement(alerte.getDateTraitement())
|
||||
.traitePar(alerte.getTraitePar() != null ? alerte.getTraitePar().toString() : null)
|
||||
.commentaireTraitement(alerte.getCommentaireTraitement())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,314 +1,314 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.analytics.AnalyticsDataResponse;
|
||||
import dev.lions.unionflow.server.api.dto.analytics.DashboardWidgetResponse;
|
||||
import dev.lions.unionflow.server.api.dto.analytics.KPITrendResponse;
|
||||
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", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
@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);
|
||||
|
||||
AnalyticsDataResponse 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", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
@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);
|
||||
|
||||
KPITrendResponse 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", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
@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", "ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@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", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
@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", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
@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<DashboardWidgetResponse> 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", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
@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() {
|
||||
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();
|
||||
}
|
||||
|
||||
/** Obtient les périodes d'analyse disponibles */
|
||||
@GET
|
||||
@Path("/periodes-analyse")
|
||||
@RolesAllowed({"ADMIN", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
@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() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.analytics.AnalyticsDataResponse;
|
||||
import dev.lions.unionflow.server.api.dto.analytics.DashboardWidgetResponse;
|
||||
import dev.lions.unionflow.server.api.dto.analytics.KPITrendResponse;
|
||||
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", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
@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);
|
||||
|
||||
AnalyticsDataResponse 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", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
@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);
|
||||
|
||||
KPITrendResponse 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", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
@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", "ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@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", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
@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", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
@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<DashboardWidgetResponse> 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", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
@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() {
|
||||
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();
|
||||
}
|
||||
|
||||
/** Obtient les périodes d'analyse disponibles */
|
||||
@GET
|
||||
@Path("/periodes-analyse")
|
||||
@RolesAllowed({"ADMIN", "ADMIN_ORGANISATION", "MEMBRE"})
|
||||
@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() {
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,132 +1,132 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.backup.request.CreateBackupRequest;
|
||||
import dev.lions.unionflow.server.api.dto.backup.request.RestoreBackupRequest;
|
||||
import dev.lions.unionflow.server.api.dto.backup.request.UpdateBackupConfigRequest;
|
||||
import dev.lions.unionflow.server.api.dto.backup.response.BackupConfigResponse;
|
||||
import dev.lions.unionflow.server.api.dto.backup.response.BackupResponse;
|
||||
import dev.lions.unionflow.server.service.BackupService;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* REST Resource pour la gestion des sauvegardes système
|
||||
*/
|
||||
@Slf4j
|
||||
@Path("/api/backups")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Sauvegardes", description = "Gestion des sauvegardes et restaurations")
|
||||
public class BackupResource {
|
||||
|
||||
@Inject
|
||||
BackupService backupService;
|
||||
|
||||
/**
|
||||
* Lister toutes les sauvegardes
|
||||
*/
|
||||
@GET
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Lister toutes les sauvegardes disponibles")
|
||||
public List<BackupResponse> getAllBackups() {
|
||||
log.info("GET /api/backups");
|
||||
return backupService.getAllBackups();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer une sauvegarde par ID
|
||||
*/
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Récupérer une sauvegarde par ID")
|
||||
public BackupResponse getBackupById(@PathParam("id") UUID id) {
|
||||
log.info("GET /api/backups/{}", id);
|
||||
return backupService.getBackupById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Créer une nouvelle sauvegarde
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Créer une nouvelle sauvegarde")
|
||||
public Response createBackup(@Valid CreateBackupRequest request) {
|
||||
log.info("POST /api/backups - {}", request.getName());
|
||||
BackupResponse backup = backupService.createBackup(request);
|
||||
return Response.status(Response.Status.CREATED).entity(backup).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restaurer une sauvegarde
|
||||
*/
|
||||
@POST
|
||||
@Path("/restore")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Restaurer une sauvegarde")
|
||||
public Response restoreBackup(@Valid RestoreBackupRequest request) {
|
||||
log.info("POST /api/backups/restore - backupId={}", request.getBackupId());
|
||||
backupService.restoreBackup(request);
|
||||
return Response.ok().entity(java.util.Map.of("message", "Restauration en cours")).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprimer une sauvegarde
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/{id}")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Supprimer une sauvegarde")
|
||||
public Response deleteBackup(@PathParam("id") UUID id) {
|
||||
log.info("DELETE /api/backups/{}", id);
|
||||
backupService.deleteBackup(id);
|
||||
return Response.ok().entity(java.util.Map.of("message", "Sauvegarde supprimée avec succès")).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer la configuration des sauvegardes automatiques
|
||||
*/
|
||||
@GET
|
||||
@Path("/config")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Récupérer la configuration des sauvegardes automatiques")
|
||||
public BackupConfigResponse getBackupConfig() {
|
||||
log.info("GET /api/backups/config");
|
||||
return backupService.getBackupConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mettre à jour la configuration des sauvegardes automatiques
|
||||
*/
|
||||
@PUT
|
||||
@Path("/config")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Mettre à jour la configuration des sauvegardes automatiques")
|
||||
public BackupConfigResponse updateBackupConfig(@Valid UpdateBackupConfigRequest request) {
|
||||
log.info("PUT /api/backups/config");
|
||||
return backupService.updateBackupConfig(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Créer un point de restauration
|
||||
*/
|
||||
@POST
|
||||
@Path("/restore-point")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Créer un point de restauration")
|
||||
public Response createRestorePoint() {
|
||||
log.info("POST /api/backups/restore-point");
|
||||
BackupResponse backup = backupService.createRestorePoint();
|
||||
return Response.status(Response.Status.CREATED).entity(backup).build();
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.backup.request.CreateBackupRequest;
|
||||
import dev.lions.unionflow.server.api.dto.backup.request.RestoreBackupRequest;
|
||||
import dev.lions.unionflow.server.api.dto.backup.request.UpdateBackupConfigRequest;
|
||||
import dev.lions.unionflow.server.api.dto.backup.response.BackupConfigResponse;
|
||||
import dev.lions.unionflow.server.api.dto.backup.response.BackupResponse;
|
||||
import dev.lions.unionflow.server.service.BackupService;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* REST Resource pour la gestion des sauvegardes système
|
||||
*/
|
||||
@Slf4j
|
||||
@Path("/api/backups")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Sauvegardes", description = "Gestion des sauvegardes et restaurations")
|
||||
public class BackupResource {
|
||||
|
||||
@Inject
|
||||
BackupService backupService;
|
||||
|
||||
/**
|
||||
* Lister toutes les sauvegardes
|
||||
*/
|
||||
@GET
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Lister toutes les sauvegardes disponibles")
|
||||
public List<BackupResponse> getAllBackups() {
|
||||
log.info("GET /api/backups");
|
||||
return backupService.getAllBackups();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer une sauvegarde par ID
|
||||
*/
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Récupérer une sauvegarde par ID")
|
||||
public BackupResponse getBackupById(@PathParam("id") UUID id) {
|
||||
log.info("GET /api/backups/{}", id);
|
||||
return backupService.getBackupById(id);
|
||||
}
|
||||
|
||||
/**
|
||||
* Créer une nouvelle sauvegarde
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Créer une nouvelle sauvegarde")
|
||||
public Response createBackup(@Valid CreateBackupRequest request) {
|
||||
log.info("POST /api/backups - {}", request.getName());
|
||||
BackupResponse backup = backupService.createBackup(request);
|
||||
return Response.status(Response.Status.CREATED).entity(backup).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Restaurer une sauvegarde
|
||||
*/
|
||||
@POST
|
||||
@Path("/restore")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Restaurer une sauvegarde")
|
||||
public Response restoreBackup(@Valid RestoreBackupRequest request) {
|
||||
log.info("POST /api/backups/restore - backupId={}", request.getBackupId());
|
||||
backupService.restoreBackup(request);
|
||||
return Response.ok().entity(java.util.Map.of("message", "Restauration en cours")).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprimer une sauvegarde
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/{id}")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Supprimer une sauvegarde")
|
||||
public Response deleteBackup(@PathParam("id") UUID id) {
|
||||
log.info("DELETE /api/backups/{}", id);
|
||||
backupService.deleteBackup(id);
|
||||
return Response.ok().entity(java.util.Map.of("message", "Sauvegarde supprimée avec succès")).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer la configuration des sauvegardes automatiques
|
||||
*/
|
||||
@GET
|
||||
@Path("/config")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Récupérer la configuration des sauvegardes automatiques")
|
||||
public BackupConfigResponse getBackupConfig() {
|
||||
log.info("GET /api/backups/config");
|
||||
return backupService.getBackupConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mettre à jour la configuration des sauvegardes automatiques
|
||||
*/
|
||||
@PUT
|
||||
@Path("/config")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Mettre à jour la configuration des sauvegardes automatiques")
|
||||
public BackupConfigResponse updateBackupConfig(@Valid UpdateBackupConfigRequest request) {
|
||||
log.info("PUT /api/backups/config");
|
||||
return backupService.updateBackupConfig(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Créer un point de restauration
|
||||
*/
|
||||
@POST
|
||||
@Path("/restore-point")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Créer un point de restauration")
|
||||
public Response createRestorePoint() {
|
||||
log.info("POST /api/backups/restore-point");
|
||||
BackupResponse backup = backupService.createRestorePoint();
|
||||
return Response.status(Response.Status.CREATED).entity(backup).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,98 +0,0 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.service.ComptabilitePdfService;
|
||||
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.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
/**
|
||||
* Endpoints de téléchargement des rapports comptables PDF SYSCOHADA révisé.
|
||||
*/
|
||||
@Path("/api/comptabilite/pdf")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "TRESORIER", "COMMISSAIRE_COMPTES", "SUPER_ADMIN"})
|
||||
@Tag(name = "Comptabilité PDF", description = "Rapports comptables SYSCOHADA : balance, compte de résultat, grand livre")
|
||||
public class ComptabilitePdfResource {
|
||||
|
||||
@Inject
|
||||
ComptabilitePdfService comptabilitePdfService;
|
||||
|
||||
@GET
|
||||
@Path("/organisations/{organisationId}/balance")
|
||||
@Produces("application/pdf")
|
||||
@Operation(summary = "Balance générale SYSCOHADA",
|
||||
description = "Génère la balance générale (cumul débit/crédit/solde) pour la période.")
|
||||
public Response balance(
|
||||
@PathParam("organisationId") UUID organisationId,
|
||||
@QueryParam("dateDebut") @DefaultValue("") String dateDebutStr,
|
||||
@QueryParam("dateFin") @DefaultValue("") String dateFinStr) {
|
||||
|
||||
LocalDate dateDebut = parseDateOrStartOfYear(dateDebutStr);
|
||||
LocalDate dateFin = parseDateOrToday(dateFinStr);
|
||||
|
||||
byte[] pdf = comptabilitePdfService.genererBalance(organisationId, dateDebut, dateFin);
|
||||
return buildPdfResponse(pdf, "balance_" + organisationId + ".pdf");
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/organisations/{organisationId}/compte-de-resultat")
|
||||
@Produces("application/pdf")
|
||||
@Operation(summary = "Compte de résultat SYSCOHADA",
|
||||
description = "Génère le compte de résultat (produits classes 7/8 − charges classes 6/8).")
|
||||
public Response compteDeResultat(
|
||||
@PathParam("organisationId") UUID organisationId,
|
||||
@QueryParam("dateDebut") @DefaultValue("") String dateDebutStr,
|
||||
@QueryParam("dateFin") @DefaultValue("") String dateFinStr) {
|
||||
|
||||
LocalDate dateDebut = parseDateOrStartOfYear(dateDebutStr);
|
||||
LocalDate dateFin = parseDateOrToday(dateFinStr);
|
||||
|
||||
byte[] pdf = comptabilitePdfService.genererCompteResultat(organisationId, dateDebut, dateFin);
|
||||
return buildPdfResponse(pdf, "compte_resultat_" + organisationId + ".pdf");
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/organisations/{organisationId}/grand-livre/{numeroCompte}")
|
||||
@Produces("application/pdf")
|
||||
@Operation(summary = "Grand livre d'un compte SYSCOHADA",
|
||||
description = "Génère le grand livre (détail chronologique) pour un compte comptable donné.")
|
||||
public Response grandLivre(
|
||||
@PathParam("organisationId") UUID organisationId,
|
||||
@PathParam("numeroCompte") String numeroCompte,
|
||||
@QueryParam("dateDebut") @DefaultValue("") String dateDebutStr,
|
||||
@QueryParam("dateFin") @DefaultValue("") String dateFinStr) {
|
||||
|
||||
LocalDate dateDebut = parseDateOrStartOfYear(dateDebutStr);
|
||||
LocalDate dateFin = parseDateOrToday(dateFinStr);
|
||||
|
||||
byte[] pdf = comptabilitePdfService.genererGrandLivre(organisationId, numeroCompte, dateDebut, dateFin);
|
||||
return buildPdfResponse(pdf, "grand_livre_" + numeroCompte + ".pdf");
|
||||
}
|
||||
|
||||
private static Response buildPdfResponse(byte[] pdf, String filename) {
|
||||
return Response.ok(pdf)
|
||||
.header("Content-Disposition", "attachment; filename=\"" + filename + "\"")
|
||||
.header("Content-Length", pdf.length)
|
||||
.build();
|
||||
}
|
||||
|
||||
private static LocalDate parseDateOrStartOfYear(String s) {
|
||||
if (s == null || s.isBlank()) return LocalDate.of(LocalDate.now().getYear(), 1, 1);
|
||||
try { return LocalDate.parse(s); } catch (Exception e) {
|
||||
throw new BadRequestException("Format de date invalide (attendu : YYYY-MM-DD) : " + s);
|
||||
}
|
||||
}
|
||||
|
||||
private static LocalDate parseDateOrToday(String s) {
|
||||
if (s == null || s.isBlank()) return LocalDate.now();
|
||||
try { return LocalDate.parse(s); } catch (Exception e) {
|
||||
throw new BadRequestException("Format de date invalide (attendu : YYYY-MM-DD) : " + s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,7 +1,6 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.membre.CompteAdherentResponse;
|
||||
import dev.lions.unionflow.server.service.FirebasePushService;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.entity.MembreOrganisation;
|
||||
import dev.lions.unionflow.server.entity.SouscriptionOrganisation;
|
||||
@@ -68,59 +67,6 @@ public class CompteAdherentResource {
|
||||
@Inject
|
||||
MembreService membreService;
|
||||
|
||||
@Inject
|
||||
FirebasePushService firebasePushService;
|
||||
|
||||
/**
|
||||
* Enregistre ou met à jour le token FCM du membre connecté pour les notifications push.
|
||||
* Appelé par l'application mobile au démarrage ou quand Firebase renouvelle le token.
|
||||
*/
|
||||
@PUT
|
||||
@Path("/mon-compte/fcm-token")
|
||||
@Authenticated
|
||||
@Operation(summary = "Enregistrer le token FCM pour les notifications push")
|
||||
@jakarta.transaction.Transactional
|
||||
public Response enregistrerFcmToken(Map<String, String> body) {
|
||||
String email = securiteHelper.resolveEmail();
|
||||
if (email == null) return Response.status(Response.Status.UNAUTHORIZED).build();
|
||||
|
||||
String token = body != null ? body.get("token") : null;
|
||||
if (token == null || token.isBlank()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("message", "Le champ 'token' est requis.")).build();
|
||||
}
|
||||
|
||||
return membreRepository.findByEmail(email)
|
||||
.map(membre -> {
|
||||
membre.setFcmToken(token.trim());
|
||||
membreRepository.persist(membre);
|
||||
return Response.ok(Map.of("message", "Token FCM enregistré.")).build();
|
||||
})
|
||||
.orElse(Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("message", "Membre introuvable.")).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime le token FCM (désabonnement des notifications push).
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/mon-compte/fcm-token")
|
||||
@Authenticated
|
||||
@Operation(summary = "Désactiver les notifications push")
|
||||
@jakarta.transaction.Transactional
|
||||
public Response supprimerFcmToken() {
|
||||
String email = securiteHelper.resolveEmail();
|
||||
if (email == null) return Response.status(Response.Status.UNAUTHORIZED).build();
|
||||
|
||||
return membreRepository.findByEmail(email)
|
||||
.map(membre -> {
|
||||
membre.setFcmToken(null);
|
||||
membreRepository.persist(membre);
|
||||
return Response.ok(Map.of("message", "Notifications push désactivées.")).build();
|
||||
})
|
||||
.orElse(Response.status(Response.Status.NOT_FOUND).build());
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne le compte adhérent complet du membre connecté :
|
||||
* numéro de membre, soldes (cotisations + épargne), capacité d'emprunt, taux d'engagement.
|
||||
@@ -192,17 +138,15 @@ public class CompteAdherentResource {
|
||||
}
|
||||
}
|
||||
|
||||
// Fallback : auto-activer si EN_ATTENTE_VALIDATION et org a reçu un paiement.
|
||||
// Couvre le cas PAIEMENT_CONFIRME (admin a payé mais super admin n'a pas encore validé)
|
||||
// et ACTIVE/VALIDEE (chemin nominal). L'admin ne doit pas bloquer sur l'AwaitingValidationPage
|
||||
// dès lors que le paiement est confirmé côté Wave.
|
||||
// Fallback : auto-activer si EN_ATTENTE_VALIDATION et org a souscription active
|
||||
// (membres sans premiereConnexion=true ou créés avant cette logique)
|
||||
if ("EN_ATTENTE_VALIDATION".equals(statutCompte) && membreOpt.isPresent()) {
|
||||
Membre m = membreOpt.get();
|
||||
UUID orgId = membreOrganisationRepo.findFirstByMembreId(m.getId())
|
||||
.map(mo -> mo.getOrganisation().getId())
|
||||
.orElse(null);
|
||||
if (membreService.orgHasPaidSubscription(orgId)) {
|
||||
LOG.infof("Auto-activation au login de %s (org %s a souscription payée)", m.getEmail(), orgId);
|
||||
if (membreService.orgHasActiveSubscription(orgId)) {
|
||||
LOG.infof("Auto-activation au login de %s (org %s a souscription active)", m.getEmail(), orgId);
|
||||
membreService.activerMembre(m.getId());
|
||||
try {
|
||||
membreKeycloakSyncService.activerMembreDansKeycloak(m.getId());
|
||||
|
||||
@@ -1,64 +1,64 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.config.request.UpdateConfigurationRequest;
|
||||
import dev.lions.unionflow.server.api.dto.config.response.ConfigurationResponse;
|
||||
import dev.lions.unionflow.server.service.ConfigurationService;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Resource REST pour la gestion de la configuration système
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
*/
|
||||
@Path("/api/configuration")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Configuration", description = "Gestion de la configuration système")
|
||||
@Slf4j
|
||||
@RolesAllowed({ "ADMIN", "SUPER_ADMIN" })
|
||||
public class ConfigurationResource {
|
||||
|
||||
@Inject
|
||||
ConfigurationService configurationService;
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Lister toutes les configurations")
|
||||
@APIResponse(responseCode = "200", description = "Liste des configurations récupérée avec succès")
|
||||
public Response listerConfigurations() {
|
||||
log.info("GET /api/configuration");
|
||||
List<ConfigurationResponse> configurations = configurationService.listerConfigurations();
|
||||
return Response.ok(configurations).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{cle}")
|
||||
@Operation(summary = "Récupérer une configuration par clé")
|
||||
@APIResponse(responseCode = "200", description = "Configuration trouvée")
|
||||
public Response obtenirConfiguration(@PathParam("cle") String cle) {
|
||||
log.info("GET /api/configuration/{}", cle);
|
||||
ConfigurationResponse config = configurationService.obtenirConfiguration(cle);
|
||||
return Response.ok(config).build();
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{cle}")
|
||||
@Operation(summary = "Mettre à jour une configuration")
|
||||
@APIResponse(responseCode = "200", description = "Configuration mise à jour avec succès")
|
||||
public Response mettreAJourConfiguration(@PathParam("cle") String cle, @Valid UpdateConfigurationRequest request) {
|
||||
log.info("PUT /api/configuration/{}", cle);
|
||||
ConfigurationResponse updated = configurationService.mettreAJourConfiguration(cle, request);
|
||||
return Response.ok(updated).build();
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.config.request.UpdateConfigurationRequest;
|
||||
import dev.lions.unionflow.server.api.dto.config.response.ConfigurationResponse;
|
||||
import dev.lions.unionflow.server.service.ConfigurationService;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Resource REST pour la gestion de la configuration système
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
*/
|
||||
@Path("/api/configuration")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Configuration", description = "Gestion de la configuration système")
|
||||
@Slf4j
|
||||
@RolesAllowed({ "ADMIN", "SUPER_ADMIN" })
|
||||
public class ConfigurationResource {
|
||||
|
||||
@Inject
|
||||
ConfigurationService configurationService;
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Lister toutes les configurations")
|
||||
@APIResponse(responseCode = "200", description = "Liste des configurations récupérée avec succès")
|
||||
public Response listerConfigurations() {
|
||||
log.info("GET /api/configuration");
|
||||
List<ConfigurationResponse> configurations = configurationService.listerConfigurations();
|
||||
return Response.ok(configurations).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{cle}")
|
||||
@Operation(summary = "Récupérer une configuration par clé")
|
||||
@APIResponse(responseCode = "200", description = "Configuration trouvée")
|
||||
public Response obtenirConfiguration(@PathParam("cle") String cle) {
|
||||
log.info("GET /api/configuration/{}", cle);
|
||||
ConfigurationResponse config = configurationService.obtenirConfiguration(cle);
|
||||
return Response.ok(config).build();
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{cle}")
|
||||
@Operation(summary = "Mettre à jour une configuration")
|
||||
@APIResponse(responseCode = "200", description = "Configuration mise à jour avec succès")
|
||||
public Response mettreAJourConfiguration(@PathParam("cle") String cle, @Valid UpdateConfigurationRequest request) {
|
||||
log.info("PUT /api/configuration/{}", cle);
|
||||
ConfigurationResponse updated = configurationService.mettreAJourConfiguration(cle, request);
|
||||
return Response.ok(updated).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,140 +1,140 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.solidarite.request.CreateDemandeAideRequest;
|
||||
import dev.lions.unionflow.server.api.dto.solidarite.request.UpdateDemandeAideRequest;
|
||||
import dev.lions.unionflow.server.api.dto.solidarite.response.DemandeAideResponse;
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.PrioriteAide;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.service.DemandeAideService;
|
||||
import dev.lions.unionflow.server.repository.MembreRepository;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.UUID;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import io.quarkus.security.identity.SecurityIdentity;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Resource REST pour les demandes d'aide.
|
||||
* Expose l'API attendue par le client (DemandeAideService REST client).
|
||||
*/
|
||||
@Path("/api/demandes-aide")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Demandes d'aide", description = "Gestion des demandes d'aide solidarité")
|
||||
@RolesAllowed({ "USER", "MEMBRE", "ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION" })
|
||||
public class DemandeAideResource {
|
||||
|
||||
@Inject
|
||||
DemandeAideService demandeAideService;
|
||||
|
||||
@Inject
|
||||
MembreRepository membreRepository;
|
||||
|
||||
@Inject
|
||||
SecurityIdentity securityIdentity;
|
||||
|
||||
@GET
|
||||
@Path("/mes")
|
||||
@Operation(summary = "Mes demandes d'aide", description = "Liste les demandes du membre connecté")
|
||||
public List<DemandeAideResponse> mesDemandes(
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("50") int size) {
|
||||
String email = securityIdentity.getPrincipal().getName();
|
||||
Membre membre = membreRepository.findByEmail(email).orElse(null);
|
||||
if (membre == null) {
|
||||
return List.of();
|
||||
}
|
||||
List<DemandeAideResponse> all = demandeAideService.rechercherAvecFiltres(
|
||||
java.util.Map.of("demandeurId", membre.getId()));
|
||||
int from = Math.min(page * size, all.size());
|
||||
int to = Math.min(from + size, all.size());
|
||||
return from < to ? all.subList(from, to) : List.of();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Liste les demandes d'aide avec pagination")
|
||||
public List<DemandeAideResponse> listerToutes(
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size) {
|
||||
List<DemandeAideResponse> all = demandeAideService.rechercherAvecFiltres(Collections.emptyMap());
|
||||
int from = Math.min(page * size, all.size());
|
||||
int to = Math.min(from + size, all.size());
|
||||
return from < to ? all.subList(from, to) : List.of();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/search")
|
||||
@Operation(summary = "Recherche les demandes d'aide avec filtres (statut, type, urgence)")
|
||||
public List<DemandeAideResponse> rechercher(
|
||||
@QueryParam("statut") String statut,
|
||||
@QueryParam("type") String type,
|
||||
@QueryParam("urgence") String urgence,
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size) {
|
||||
|
||||
java.util.Map<String, Object> filtres = new java.util.HashMap<>();
|
||||
if (statut != null && !statut.isEmpty()) {
|
||||
try { filtres.put("statut", StatutAide.valueOf(statut)); } catch (IllegalArgumentException e) {}
|
||||
}
|
||||
if (type != null && !type.isEmpty()) {
|
||||
try { filtres.put("typeAide", TypeAide.valueOf(type)); } catch (IllegalArgumentException e) {}
|
||||
}
|
||||
if (urgence != null && !urgence.isEmpty()) {
|
||||
try { filtres.put("priorite", PrioriteAide.valueOf(urgence)); } catch (IllegalArgumentException e) {}
|
||||
}
|
||||
|
||||
List<DemandeAideResponse> all = demandeAideService.rechercherAvecFiltres(filtres);
|
||||
int from = Math.min(page * size, all.size());
|
||||
int to = Math.min(from + size, all.size());
|
||||
return from < to ? all.subList(from, to) : List.of();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Récupère une demande d'aide par son ID")
|
||||
public DemandeAideResponse obtenirParId(@PathParam("id") UUID id) {
|
||||
DemandeAideResponse response = demandeAideService.obtenirParId(id);
|
||||
if (response == null) {
|
||||
throw new NotFoundException("Demande d'aide non trouvée : " + id);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Operation(summary = "Crée une nouvelle demande d'aide")
|
||||
public Response creer(@Valid CreateDemandeAideRequest request) {
|
||||
DemandeAideResponse response = demandeAideService.creerDemande(request);
|
||||
return Response.status(Response.Status.CREATED).entity(response).build();
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Met à jour une demande d'aide")
|
||||
public DemandeAideResponse mettreAJour(@PathParam("id") UUID id, @Valid UpdateDemandeAideRequest request) {
|
||||
return demandeAideService.mettreAJour(id, request);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{id}/approuver")
|
||||
@Operation(summary = "Approuver une demande d'aide")
|
||||
public DemandeAideResponse approuver(@PathParam("id") UUID id, @QueryParam("motif") String motif) {
|
||||
return demandeAideService.changerStatut(id, StatutAide.APPROUVEE, motif);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{id}/rejeter")
|
||||
@Operation(summary = "Rejeter une demande d'aide")
|
||||
public DemandeAideResponse rejeter(@PathParam("id") UUID id, @QueryParam("motif") String motif) {
|
||||
return demandeAideService.changerStatut(id, StatutAide.REJETEE, motif);
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.solidarite.request.CreateDemandeAideRequest;
|
||||
import dev.lions.unionflow.server.api.dto.solidarite.request.UpdateDemandeAideRequest;
|
||||
import dev.lions.unionflow.server.api.dto.solidarite.response.DemandeAideResponse;
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.StatutAide;
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.TypeAide;
|
||||
import dev.lions.unionflow.server.api.enums.solidarite.PrioriteAide;
|
||||
import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.service.DemandeAideService;
|
||||
import dev.lions.unionflow.server.repository.MembreRepository;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.UUID;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import io.quarkus.security.identity.SecurityIdentity;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Resource REST pour les demandes d'aide.
|
||||
* Expose l'API attendue par le client (DemandeAideService REST client).
|
||||
*/
|
||||
@Path("/api/demandes-aide")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Demandes d'aide", description = "Gestion des demandes d'aide solidarité")
|
||||
@RolesAllowed({ "USER", "MEMBRE", "ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION" })
|
||||
public class DemandeAideResource {
|
||||
|
||||
@Inject
|
||||
DemandeAideService demandeAideService;
|
||||
|
||||
@Inject
|
||||
MembreRepository membreRepository;
|
||||
|
||||
@Inject
|
||||
SecurityIdentity securityIdentity;
|
||||
|
||||
@GET
|
||||
@Path("/mes")
|
||||
@Operation(summary = "Mes demandes d'aide", description = "Liste les demandes du membre connecté")
|
||||
public List<DemandeAideResponse> mesDemandes(
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("50") int size) {
|
||||
String email = securityIdentity.getPrincipal().getName();
|
||||
Membre membre = membreRepository.findByEmail(email).orElse(null);
|
||||
if (membre == null) {
|
||||
return List.of();
|
||||
}
|
||||
List<DemandeAideResponse> all = demandeAideService.rechercherAvecFiltres(
|
||||
java.util.Map.of("demandeurId", membre.getId()));
|
||||
int from = Math.min(page * size, all.size());
|
||||
int to = Math.min(from + size, all.size());
|
||||
return from < to ? all.subList(from, to) : List.of();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Liste les demandes d'aide avec pagination")
|
||||
public List<DemandeAideResponse> listerToutes(
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size) {
|
||||
List<DemandeAideResponse> all = demandeAideService.rechercherAvecFiltres(Collections.emptyMap());
|
||||
int from = Math.min(page * size, all.size());
|
||||
int to = Math.min(from + size, all.size());
|
||||
return from < to ? all.subList(from, to) : List.of();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/search")
|
||||
@Operation(summary = "Recherche les demandes d'aide avec filtres (statut, type, urgence)")
|
||||
public List<DemandeAideResponse> rechercher(
|
||||
@QueryParam("statut") String statut,
|
||||
@QueryParam("type") String type,
|
||||
@QueryParam("urgence") String urgence,
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size) {
|
||||
|
||||
java.util.Map<String, Object> filtres = new java.util.HashMap<>();
|
||||
if (statut != null && !statut.isEmpty()) {
|
||||
try { filtres.put("statut", StatutAide.valueOf(statut)); } catch (IllegalArgumentException e) {}
|
||||
}
|
||||
if (type != null && !type.isEmpty()) {
|
||||
try { filtres.put("typeAide", TypeAide.valueOf(type)); } catch (IllegalArgumentException e) {}
|
||||
}
|
||||
if (urgence != null && !urgence.isEmpty()) {
|
||||
try { filtres.put("priorite", PrioriteAide.valueOf(urgence)); } catch (IllegalArgumentException e) {}
|
||||
}
|
||||
|
||||
List<DemandeAideResponse> all = demandeAideService.rechercherAvecFiltres(filtres);
|
||||
int from = Math.min(page * size, all.size());
|
||||
int to = Math.min(from + size, all.size());
|
||||
return from < to ? all.subList(from, to) : List.of();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Récupère une demande d'aide par son ID")
|
||||
public DemandeAideResponse obtenirParId(@PathParam("id") UUID id) {
|
||||
DemandeAideResponse response = demandeAideService.obtenirParId(id);
|
||||
if (response == null) {
|
||||
throw new NotFoundException("Demande d'aide non trouvée : " + id);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Operation(summary = "Crée une nouvelle demande d'aide")
|
||||
public Response creer(@Valid CreateDemandeAideRequest request) {
|
||||
DemandeAideResponse response = demandeAideService.creerDemande(request);
|
||||
return Response.status(Response.Status.CREATED).entity(response).build();
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Met à jour une demande d'aide")
|
||||
public DemandeAideResponse mettreAJour(@PathParam("id") UUID id, @Valid UpdateDemandeAideRequest request) {
|
||||
return demandeAideService.mettreAJour(id, request);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{id}/approuver")
|
||||
@Operation(summary = "Approuver une demande d'aide")
|
||||
public DemandeAideResponse approuver(@PathParam("id") UUID id, @QueryParam("motif") String motif) {
|
||||
return demandeAideService.changerStatut(id, StatutAide.APPROUVEE, motif);
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{id}/rejeter")
|
||||
@Operation(summary = "Rejeter une demande d'aide")
|
||||
public DemandeAideResponse rejeter(@PathParam("id") UUID id, @QueryParam("motif") String motif) {
|
||||
return demandeAideService.changerStatut(id, StatutAide.REJETEE, motif);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,114 +1,114 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
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.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Resource REST pour les workflows financiers (stats et audits)
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-16
|
||||
*/
|
||||
@Path("/api/finance")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Finance - Workflow", description = "Statistiques et audits des workflows financiers")
|
||||
public class FinanceWorkflowResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(FinanceWorkflowResource.class);
|
||||
|
||||
@GET
|
||||
@Path("/stats")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Statistiques du workflow financier",
|
||||
description = "Retourne les statistiques globales du workflow financier")
|
||||
public Response getWorkflowStats(
|
||||
@QueryParam("organizationId") String organizationId,
|
||||
@QueryParam("startDate") String startDate,
|
||||
@QueryParam("endDate") String endDate) {
|
||||
LOG.infof("GET /api/finance/stats?organizationId=%s", organizationId);
|
||||
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
stats.put("totalApprovals", 0);
|
||||
stats.put("pendingApprovals", 0);
|
||||
stats.put("approvedCount", 0);
|
||||
stats.put("rejectedCount", 0);
|
||||
stats.put("totalBudgets", 0);
|
||||
stats.put("activeBudgets", 0);
|
||||
stats.put("averageApprovalTime", "0 hours");
|
||||
stats.put("period", Map.of(
|
||||
"startDate", startDate != null ? startDate : LocalDateTime.now().minusMonths(1).toString(),
|
||||
"endDate", endDate != null ? endDate : LocalDateTime.now().toString()
|
||||
));
|
||||
return Response.ok(stats).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/audit-logs")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Récupère les logs d'audit financier",
|
||||
description = "Liste les logs d'audit avec filtres optionnels")
|
||||
public Response getAuditLogs(
|
||||
@QueryParam("organizationId") String organizationId,
|
||||
@QueryParam("startDate") String startDate,
|
||||
@QueryParam("endDate") String endDate,
|
||||
@QueryParam("operation") String operation,
|
||||
@QueryParam("entityType") String entityType,
|
||||
@QueryParam("severity") String severity,
|
||||
@QueryParam("limit") @DefaultValue("100") int limit) {
|
||||
LOG.infof("GET /api/finance/audit-logs?organizationId=%s&limit=%d", organizationId, limit);
|
||||
|
||||
// Retourne une liste vide pour l'instant - à implémenter plus tard avec vraie persistence
|
||||
return Response.ok(new ArrayList<>()).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/audit-logs/anomalies")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Récupère les anomalies financières détectées",
|
||||
description = "Liste les anomalies et transactions suspectes")
|
||||
public Response getAnomalies(
|
||||
@QueryParam("organizationId") String organizationId,
|
||||
@QueryParam("startDate") String startDate,
|
||||
@QueryParam("endDate") String endDate) {
|
||||
LOG.infof("GET /api/finance/audit-logs/anomalies?organizationId=%s", organizationId);
|
||||
|
||||
// Retourne une liste vide pour l'instant - à implémenter plus tard
|
||||
return Response.ok(new ArrayList<>()).build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/audit-logs/export")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Exporte les logs d'audit",
|
||||
description = "Génère un export des logs d'audit au format spécifié (CSV/PDF)")
|
||||
public Response exportAuditLogs(Map<String, Object> request) {
|
||||
String organizationId = (String) request.get("organizationId");
|
||||
String format = (String) request.getOrDefault("format", "csv");
|
||||
|
||||
LOG.infof("POST /api/finance/audit-logs/export - format: %s", format);
|
||||
|
||||
// Pour l'instant, retourne un URL fictif - à implémenter plus tard
|
||||
String exportUrl = "/api/finance/exports/" + UUID.randomUUID() + "." + format;
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("exportUrl", exportUrl);
|
||||
response.put("format", format);
|
||||
response.put("status", "generated");
|
||||
response.put("expiresAt", LocalDateTime.now().plusHours(24).toString());
|
||||
|
||||
return Response.ok(response).build();
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
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.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Resource REST pour les workflows financiers (stats et audits)
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-16
|
||||
*/
|
||||
@Path("/api/finance")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Finance - Workflow", description = "Statistiques et audits des workflows financiers")
|
||||
public class FinanceWorkflowResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(FinanceWorkflowResource.class);
|
||||
|
||||
@GET
|
||||
@Path("/stats")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Statistiques du workflow financier",
|
||||
description = "Retourne les statistiques globales du workflow financier")
|
||||
public Response getWorkflowStats(
|
||||
@QueryParam("organizationId") String organizationId,
|
||||
@QueryParam("startDate") String startDate,
|
||||
@QueryParam("endDate") String endDate) {
|
||||
LOG.infof("GET /api/finance/stats?organizationId=%s", organizationId);
|
||||
|
||||
Map<String, Object> stats = new HashMap<>();
|
||||
stats.put("totalApprovals", 0);
|
||||
stats.put("pendingApprovals", 0);
|
||||
stats.put("approvedCount", 0);
|
||||
stats.put("rejectedCount", 0);
|
||||
stats.put("totalBudgets", 0);
|
||||
stats.put("activeBudgets", 0);
|
||||
stats.put("averageApprovalTime", "0 hours");
|
||||
stats.put("period", Map.of(
|
||||
"startDate", startDate != null ? startDate : LocalDateTime.now().minusMonths(1).toString(),
|
||||
"endDate", endDate != null ? endDate : LocalDateTime.now().toString()
|
||||
));
|
||||
return Response.ok(stats).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/audit-logs")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Récupère les logs d'audit financier",
|
||||
description = "Liste les logs d'audit avec filtres optionnels")
|
||||
public Response getAuditLogs(
|
||||
@QueryParam("organizationId") String organizationId,
|
||||
@QueryParam("startDate") String startDate,
|
||||
@QueryParam("endDate") String endDate,
|
||||
@QueryParam("operation") String operation,
|
||||
@QueryParam("entityType") String entityType,
|
||||
@QueryParam("severity") String severity,
|
||||
@QueryParam("limit") @DefaultValue("100") int limit) {
|
||||
LOG.infof("GET /api/finance/audit-logs?organizationId=%s&limit=%d", organizationId, limit);
|
||||
|
||||
// Retourne une liste vide pour l'instant - à implémenter plus tard avec vraie persistence
|
||||
return Response.ok(new ArrayList<>()).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/audit-logs/anomalies")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Récupère les anomalies financières détectées",
|
||||
description = "Liste les anomalies et transactions suspectes")
|
||||
public Response getAnomalies(
|
||||
@QueryParam("organizationId") String organizationId,
|
||||
@QueryParam("startDate") String startDate,
|
||||
@QueryParam("endDate") String endDate) {
|
||||
LOG.infof("GET /api/finance/audit-logs/anomalies?organizationId=%s", organizationId);
|
||||
|
||||
// Retourne une liste vide pour l'instant - à implémenter plus tard
|
||||
return Response.ok(new ArrayList<>()).build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/audit-logs/export")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
@Operation(summary = "Exporte les logs d'audit",
|
||||
description = "Génère un export des logs d'audit au format spécifié (CSV/PDF)")
|
||||
public Response exportAuditLogs(Map<String, Object> request) {
|
||||
String organizationId = (String) request.get("organizationId");
|
||||
String format = (String) request.getOrDefault("format", "csv");
|
||||
|
||||
LOG.infof("POST /api/finance/audit-logs/export - format: %s", format);
|
||||
|
||||
// Pour l'instant, retourne un URL fictif - à implémenter plus tard
|
||||
String exportUrl = "/api/finance/exports/" + UUID.randomUUID() + "." + format;
|
||||
|
||||
Map<String, Object> response = new HashMap<>();
|
||||
response.put("exportUrl", exportUrl);
|
||||
response.put("format", format);
|
||||
response.put("status", "generated");
|
||||
response.put("expiresAt", LocalDateTime.now().plusHours(24).toString());
|
||||
|
||||
return Response.ok(response).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,35 +1,35 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
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
|
||||
@PermitAll
|
||||
@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();
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
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
|
||||
@PermitAll
|
||||
@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();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,111 +0,0 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.kyc.KycDossierRequest;
|
||||
import dev.lions.unionflow.server.api.dto.kyc.KycDossierResponse;
|
||||
import dev.lions.unionflow.server.service.KycAmlService;
|
||||
import io.quarkus.security.identity.SecurityIdentity;
|
||||
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;
|
||||
|
||||
/**
|
||||
* Endpoints KYC/AML — gestion des dossiers d'identification et évaluation risque LCB-FT.
|
||||
*/
|
||||
@Path("/api/kyc")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public class KycResource {
|
||||
|
||||
@Inject
|
||||
KycAmlService kycAmlService;
|
||||
|
||||
@Inject
|
||||
SecurityIdentity identity;
|
||||
|
||||
/** Soumet ou met à jour un dossier KYC pour un membre. */
|
||||
@POST
|
||||
@Path("/dossiers")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "TRESORIER", "SUPER_ADMIN"})
|
||||
public Response soumettre(@Valid KycDossierRequest request) {
|
||||
KycDossierResponse response = kycAmlService.soumettreOuMettreAJour(request, identity.getPrincipal().getName());
|
||||
return Response.status(Response.Status.CREATED).entity(response).build();
|
||||
}
|
||||
|
||||
/** Récupère le dossier KYC actif d'un membre. */
|
||||
@GET
|
||||
@Path("/membres/{membreId}")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "TRESORIER", "SUPER_ADMIN"})
|
||||
public Response getDossierActif(@PathParam("membreId") UUID membreId) {
|
||||
return kycAmlService.getDossierActif(membreId)
|
||||
.map(d -> Response.ok(d).build())
|
||||
.orElse(Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Aucun dossier KYC actif pour ce membre."))
|
||||
.build());
|
||||
}
|
||||
|
||||
/** Évalue le score de risque LCB-FT du membre. */
|
||||
@POST
|
||||
@Path("/membres/{membreId}/evaluer-risque")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "TRESORIER", "SUPER_ADMIN"})
|
||||
public Response evaluerRisque(@PathParam("membreId") UUID membreId) {
|
||||
KycDossierResponse response = kycAmlService.evaluerRisque(membreId);
|
||||
return Response.ok(response).build();
|
||||
}
|
||||
|
||||
/** Valide manuellement un dossier KYC (agent habilité). */
|
||||
@POST
|
||||
@Path("/dossiers/{dossierId}/valider")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN_ORGANISATION"})
|
||||
public Response valider(
|
||||
@PathParam("dossierId") UUID dossierId,
|
||||
@QueryParam("validateurId") UUID validateurId,
|
||||
@QueryParam("notes") String notes) {
|
||||
KycDossierResponse response = kycAmlService.valider(
|
||||
dossierId, validateurId, notes, identity.getPrincipal().getName());
|
||||
return Response.ok(response).build();
|
||||
}
|
||||
|
||||
/** Refuse un dossier KYC avec motif. */
|
||||
@POST
|
||||
@Path("/dossiers/{dossierId}/refuser")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN_ORGANISATION"})
|
||||
public Response refuser(
|
||||
@PathParam("dossierId") UUID dossierId,
|
||||
@QueryParam("validateurId") UUID validateurId,
|
||||
@QueryParam("motif") String motif) {
|
||||
KycDossierResponse response = kycAmlService.refuser(
|
||||
dossierId, validateurId, motif, identity.getPrincipal().getName());
|
||||
return Response.ok(response).build();
|
||||
}
|
||||
|
||||
/** Liste les dossiers KYC en attente de validation. */
|
||||
@GET
|
||||
@Path("/dossiers/en-attente")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN_ORGANISATION"})
|
||||
public List<KycDossierResponse> getDossiersEnAttente() {
|
||||
return kycAmlService.getDossiersEnAttente();
|
||||
}
|
||||
|
||||
/** Liste les membres PEP (Personnes Exposées Politiquement). */
|
||||
@GET
|
||||
@Path("/pep")
|
||||
@RolesAllowed({"SUPER_ADMIN"})
|
||||
public List<KycDossierResponse> getPep() {
|
||||
return kycAmlService.getDossiersPep();
|
||||
}
|
||||
|
||||
/** Pièces d'identité expirant dans les 30 jours. */
|
||||
@GET
|
||||
@Path("/pieces-expirant-bientot")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN_ORGANISATION", "TRESORIER"})
|
||||
public List<KycDossierResponse> getPiecesExpirant() {
|
||||
return kycAmlService.getPiecesExpirantDansLes30Jours();
|
||||
}
|
||||
}
|
||||
@@ -1,148 +1,148 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.logs.request.LogSearchRequest;
|
||||
import dev.lions.unionflow.server.api.dto.logs.request.UpdateAlertConfigRequest;
|
||||
import dev.lions.unionflow.server.api.dto.logs.response.AlertConfigResponse;
|
||||
import dev.lions.unionflow.server.api.dto.logs.response.SystemAlertResponse;
|
||||
import dev.lions.unionflow.server.api.dto.logs.response.SystemLogResponse;
|
||||
import dev.lions.unionflow.server.api.dto.logs.response.SystemMetricsResponse;
|
||||
import dev.lions.unionflow.server.service.LogsMonitoringService;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* REST Resource pour la gestion des logs et du monitoring système
|
||||
*/
|
||||
@Slf4j
|
||||
@Path("/api")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Logs & Monitoring", description = "Gestion des logs système et monitoring")
|
||||
public class LogsMonitoringResource {
|
||||
|
||||
@Inject
|
||||
LogsMonitoringService logsMonitoringService;
|
||||
|
||||
/**
|
||||
* Rechercher dans les logs système
|
||||
*/
|
||||
@POST
|
||||
@Path("/logs/search")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Rechercher dans les logs système", description = "Recherche avec filtres (niveau, source, texte, dates)")
|
||||
public List<SystemLogResponse> searchLogs(@Valid LogSearchRequest request) {
|
||||
log.info("POST /api/logs/search - level={}, source={}", request.getLevel(), request.getSource());
|
||||
return logsMonitoringService.searchLogs(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exporter les logs (simplifié)
|
||||
*/
|
||||
@GET
|
||||
@Path("/logs/export")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Produces("text/csv")
|
||||
@Operation(summary = "Exporter les logs en CSV")
|
||||
public Response exportLogs(
|
||||
@QueryParam("level") String level,
|
||||
@QueryParam("source") String source,
|
||||
@QueryParam("timeRange") String timeRange
|
||||
) {
|
||||
log.info("GET /api/logs/export");
|
||||
|
||||
// Dans une vraie implémentation, on générerait un vrai CSV
|
||||
LogSearchRequest request = LogSearchRequest.builder()
|
||||
.level(level)
|
||||
.source(source)
|
||||
.timeRange(timeRange)
|
||||
.build();
|
||||
|
||||
List<SystemLogResponse> logs = logsMonitoringService.searchLogs(request);
|
||||
|
||||
// Génération simplifiée du CSV
|
||||
StringBuilder csv = new StringBuilder();
|
||||
csv.append("Timestamp,Level,Source,Message,Details\n");
|
||||
logs.forEach(log -> csv.append(String.format("%s,%s,%s,\"%s\",\"%s\"\n",
|
||||
log.getTimestamp(),
|
||||
log.getLevel(),
|
||||
log.getSource(),
|
||||
log.getMessage().replace("\"", "\"\""),
|
||||
log.getDetails() != null ? log.getDetails().replace("\"", "\"\"") : ""
|
||||
)));
|
||||
|
||||
return Response.ok(csv.toString())
|
||||
.header("Content-Disposition", "attachment; filename=\"logs-export.csv\"")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer les métriques système en temps réel
|
||||
*/
|
||||
@GET
|
||||
@Path("/monitoring/metrics")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Récupérer les métriques système en temps réel")
|
||||
public SystemMetricsResponse getSystemMetrics() {
|
||||
log.debug("GET /api/monitoring/metrics");
|
||||
return logsMonitoringService.getSystemMetrics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer toutes les alertes actives
|
||||
*/
|
||||
@GET
|
||||
@Path("/alerts")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Récupérer toutes les alertes actives")
|
||||
public List<SystemAlertResponse> getActiveAlerts() {
|
||||
log.info("GET /api/alerts");
|
||||
return logsMonitoringService.getActiveAlerts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquitter une alerte
|
||||
*/
|
||||
@POST
|
||||
@Path("/alerts/{id}/acknowledge")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Acquitter une alerte")
|
||||
public Response acknowledgeAlert(@PathParam("id") UUID id) {
|
||||
log.info("POST /api/alerts/{}/acknowledge", id);
|
||||
logsMonitoringService.acknowledgeAlert(id);
|
||||
return Response.ok().entity(java.util.Map.of("message", "Alerte acquittée avec succès")).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer la configuration des alertes
|
||||
*/
|
||||
@GET
|
||||
@Path("/alerts/config")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Récupérer la configuration des alertes système")
|
||||
public AlertConfigResponse getAlertConfig() {
|
||||
log.info("GET /api/alerts/config");
|
||||
return logsMonitoringService.getAlertConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mettre à jour la configuration des alertes
|
||||
*/
|
||||
@PUT
|
||||
@Path("/alerts/config")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Mettre à jour la configuration des alertes système")
|
||||
public AlertConfigResponse updateAlertConfig(@Valid UpdateAlertConfigRequest request) {
|
||||
log.info("PUT /api/alerts/config");
|
||||
return logsMonitoringService.updateAlertConfig(request);
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.logs.request.LogSearchRequest;
|
||||
import dev.lions.unionflow.server.api.dto.logs.request.UpdateAlertConfigRequest;
|
||||
import dev.lions.unionflow.server.api.dto.logs.response.AlertConfigResponse;
|
||||
import dev.lions.unionflow.server.api.dto.logs.response.SystemAlertResponse;
|
||||
import dev.lions.unionflow.server.api.dto.logs.response.SystemLogResponse;
|
||||
import dev.lions.unionflow.server.api.dto.logs.response.SystemMetricsResponse;
|
||||
import dev.lions.unionflow.server.service.LogsMonitoringService;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* REST Resource pour la gestion des logs et du monitoring système
|
||||
*/
|
||||
@Slf4j
|
||||
@Path("/api")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Logs & Monitoring", description = "Gestion des logs système et monitoring")
|
||||
public class LogsMonitoringResource {
|
||||
|
||||
@Inject
|
||||
LogsMonitoringService logsMonitoringService;
|
||||
|
||||
/**
|
||||
* Rechercher dans les logs système
|
||||
*/
|
||||
@POST
|
||||
@Path("/logs/search")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Rechercher dans les logs système", description = "Recherche avec filtres (niveau, source, texte, dates)")
|
||||
public List<SystemLogResponse> searchLogs(@Valid LogSearchRequest request) {
|
||||
log.info("POST /api/logs/search - level={}, source={}", request.getLevel(), request.getSource());
|
||||
return logsMonitoringService.searchLogs(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Exporter les logs (simplifié)
|
||||
*/
|
||||
@GET
|
||||
@Path("/logs/export")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Produces("text/csv")
|
||||
@Operation(summary = "Exporter les logs en CSV")
|
||||
public Response exportLogs(
|
||||
@QueryParam("level") String level,
|
||||
@QueryParam("source") String source,
|
||||
@QueryParam("timeRange") String timeRange
|
||||
) {
|
||||
log.info("GET /api/logs/export");
|
||||
|
||||
// Dans une vraie implémentation, on générerait un vrai CSV
|
||||
LogSearchRequest request = LogSearchRequest.builder()
|
||||
.level(level)
|
||||
.source(source)
|
||||
.timeRange(timeRange)
|
||||
.build();
|
||||
|
||||
List<SystemLogResponse> logs = logsMonitoringService.searchLogs(request);
|
||||
|
||||
// Génération simplifiée du CSV
|
||||
StringBuilder csv = new StringBuilder();
|
||||
csv.append("Timestamp,Level,Source,Message,Details\n");
|
||||
logs.forEach(log -> csv.append(String.format("%s,%s,%s,\"%s\",\"%s\"\n",
|
||||
log.getTimestamp(),
|
||||
log.getLevel(),
|
||||
log.getSource(),
|
||||
log.getMessage().replace("\"", "\"\""),
|
||||
log.getDetails() != null ? log.getDetails().replace("\"", "\"\"") : ""
|
||||
)));
|
||||
|
||||
return Response.ok(csv.toString())
|
||||
.header("Content-Disposition", "attachment; filename=\"logs-export.csv\"")
|
||||
.build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer les métriques système en temps réel
|
||||
*/
|
||||
@GET
|
||||
@Path("/monitoring/metrics")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Récupérer les métriques système en temps réel")
|
||||
public SystemMetricsResponse getSystemMetrics() {
|
||||
log.debug("GET /api/monitoring/metrics");
|
||||
return logsMonitoringService.getSystemMetrics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer toutes les alertes actives
|
||||
*/
|
||||
@GET
|
||||
@Path("/alerts")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Récupérer toutes les alertes actives")
|
||||
public List<SystemAlertResponse> getActiveAlerts() {
|
||||
log.info("GET /api/alerts");
|
||||
return logsMonitoringService.getActiveAlerts();
|
||||
}
|
||||
|
||||
/**
|
||||
* Acquitter une alerte
|
||||
*/
|
||||
@POST
|
||||
@Path("/alerts/{id}/acknowledge")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Acquitter une alerte")
|
||||
public Response acknowledgeAlert(@PathParam("id") UUID id) {
|
||||
log.info("POST /api/alerts/{}/acknowledge", id);
|
||||
logsMonitoringService.acknowledgeAlert(id);
|
||||
return Response.ok().entity(java.util.Map.of("message", "Alerte acquittée avec succès")).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer la configuration des alertes
|
||||
*/
|
||||
@GET
|
||||
@Path("/alerts/config")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Récupérer la configuration des alertes système")
|
||||
public AlertConfigResponse getAlertConfig() {
|
||||
log.info("GET /api/alerts/config");
|
||||
return logsMonitoringService.getAlertConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mettre à jour la configuration des alertes
|
||||
*/
|
||||
@PUT
|
||||
@Path("/alerts/config")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Mettre à jour la configuration des alertes système")
|
||||
public AlertConfigResponse updateAlertConfig(@Valid UpdateAlertConfigRequest request) {
|
||||
log.info("PUT /api/alerts/config");
|
||||
return logsMonitoringService.updateAlertConfig(request);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,7 +11,6 @@ import dev.lions.unionflow.server.entity.Membre;
|
||||
import dev.lions.unionflow.server.entity.Organisation;
|
||||
import dev.lions.unionflow.server.entity.MembreOrganisation;
|
||||
import dev.lions.unionflow.server.repository.MembreOrganisationRepository;
|
||||
import dev.lions.unionflow.server.repository.MembreRepository;
|
||||
import dev.lions.unionflow.server.repository.MembreRoleRepository;
|
||||
import dev.lions.unionflow.server.service.MemberLifecycleService;
|
||||
import dev.lions.unionflow.server.service.MembreKeycloakSyncService;
|
||||
@@ -79,9 +78,6 @@ public class MembreResource {
|
||||
@Inject
|
||||
MembreOrganisationRepository membreOrgRepository;
|
||||
|
||||
@Inject
|
||||
MembreRepository membreRepository;
|
||||
|
||||
@Inject
|
||||
MembreRoleRepository membreRoleRepository;
|
||||
|
||||
@@ -451,40 +447,6 @@ public class MembreResource {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste TOUS les membres (y compris EN_ATTENTE_VALIDATION) — réservé SUPER_ADMIN.
|
||||
* Utile pour les imports de données historiques et la gestion admin.
|
||||
*/
|
||||
@GET
|
||||
@Path("/admin/tous")
|
||||
@RolesAllowed({ "SUPER_ADMIN" })
|
||||
@Operation(summary = "Tous les membres (admin)", description = "Liste tous les membres quelque soit leur statut, réservé SUPER_ADMIN")
|
||||
@APIResponse(responseCode = "200", description = "Liste complète des membres")
|
||||
public Response getTousMembres(
|
||||
@Parameter(description = "Numéro de page (0-based)") @QueryParam("page") @DefaultValue("0") int page,
|
||||
@Parameter(description = "Taille de la page") @QueryParam("size") @DefaultValue("100") int size) {
|
||||
try {
|
||||
LOG.infof("GET /api/membres/admin/tous - page=%d size=%d", page, size);
|
||||
List<Membre> membres = membreRepository.findAll(
|
||||
io.quarkus.panache.common.Sort.by("nom").ascending())
|
||||
.page(io.quarkus.panache.common.Page.of(page, size))
|
||||
.list();
|
||||
List<MembreResponse> membresDTO = membreService.convertToResponseList(membres);
|
||||
long total = membreRepository.count();
|
||||
return Response.ok(Map.of(
|
||||
"data", membresDTO,
|
||||
"totalElements", total,
|
||||
"page", page,
|
||||
"size", size,
|
||||
"totalPages", (int) Math.ceil((double) total / size)
|
||||
)).build();
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Erreur récupération tous membres");
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity(Map.of("error", e.getMessage())).build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste les membres d'une organisation spécifique (statut ACTIF dans l'organisation).
|
||||
* Utilisé pour la création de campagnes ciblées.
|
||||
@@ -626,7 +588,7 @@ public class MembreResource {
|
||||
@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": [],
|
||||
"membres": [...],
|
||||
"totalElements": 247,
|
||||
"totalPages": 13,
|
||||
"currentPage": 0,
|
||||
|
||||
@@ -1,139 +0,0 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.payment.*;
|
||||
import dev.lions.unionflow.server.entity.SouscriptionOrganisation;
|
||||
import dev.lions.unionflow.server.payment.orchestration.PaymentOrchestrator;
|
||||
import dev.lions.unionflow.server.payment.orchestration.PaymentProviderRegistry;
|
||||
import dev.lions.unionflow.server.repository.SouscriptionOrganisationRepository;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.Context;
|
||||
import jakarta.ws.rs.core.HttpHeaders;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import lombok.extern.slf4j.Slf4j;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Endpoints de paiement unifiés — abstraction multi-provider.
|
||||
* Remplace à terme les endpoints Wave-spécifiques.
|
||||
*/
|
||||
@Slf4j
|
||||
@Path("/api/paiements")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
public class PaiementUnifieResource {
|
||||
|
||||
@Inject
|
||||
PaymentOrchestrator orchestrator;
|
||||
|
||||
@Inject
|
||||
PaymentProviderRegistry registry;
|
||||
|
||||
@Inject
|
||||
SouscriptionOrganisationRepository souscriptionRepository;
|
||||
|
||||
/**
|
||||
* Initie un paiement via le provider demandé (ou le provider par défaut).
|
||||
*
|
||||
* <p>Exemple : {@code POST /api/paiements/initier?provider=WAVE}
|
||||
*/
|
||||
@POST
|
||||
@Path("/initier")
|
||||
@RolesAllowed({"MEMBRE_ACTIF", "ADMIN_ORGANISATION", "TRESORIER", "SUPER_ADMIN"})
|
||||
public Response initier(
|
||||
@QueryParam("provider") String provider,
|
||||
PaiementInitierRequest req) {
|
||||
try {
|
||||
// Si une souscription est fournie, utiliser le providerDefaut de sa formule
|
||||
String resolvedProvider = provider;
|
||||
if (req.souscriptionId() != null) {
|
||||
resolvedProvider = souscriptionRepository.findByIdOptional(req.souscriptionId())
|
||||
.map(SouscriptionOrganisation::getFormule)
|
||||
.map(f -> f.getProviderDefaut())
|
||||
.filter(p -> p != null && !p.isBlank())
|
||||
.orElse(provider);
|
||||
}
|
||||
|
||||
CheckoutRequest checkoutRequest = new CheckoutRequest(
|
||||
req.montant(),
|
||||
req.devise() != null ? req.devise() : "XOF",
|
||||
req.telephone(),
|
||||
req.email(),
|
||||
req.reference(),
|
||||
req.successUrl(),
|
||||
req.cancelUrl(),
|
||||
Map.of()
|
||||
);
|
||||
CheckoutSession session = orchestrator.initierPaiement(checkoutRequest, resolvedProvider);
|
||||
return Response.ok(session).build();
|
||||
} catch (PaymentException e) {
|
||||
return Response.status(e.getHttpStatus())
|
||||
.entity(Map.of("error", e.getMessage(), "provider", e.getProviderCode()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Webhook entrant d'un provider. Vérifie la signature et met à jour le statut.
|
||||
* Route : {@code POST /api/paiements/webhook/{provider}}
|
||||
*/
|
||||
@POST
|
||||
@Path("/webhook/{provider}")
|
||||
@PermitAll
|
||||
@Consumes(MediaType.WILDCARD)
|
||||
public Response webhook(
|
||||
@PathParam("provider") String providerCode,
|
||||
String rawBody,
|
||||
@Context HttpHeaders httpHeaders) {
|
||||
try {
|
||||
PaymentProvider provider = registry.get(providerCode.toUpperCase());
|
||||
Map<String, String> headers = httpHeaders.getRequestHeaders().entrySet().stream()
|
||||
.collect(Collectors.toMap(
|
||||
Map.Entry::getKey,
|
||||
e -> e.getValue().isEmpty() ? "" : e.getValue().get(0)
|
||||
));
|
||||
|
||||
PaymentEvent event = provider.processWebhook(rawBody, headers);
|
||||
orchestrator.handleEvent(event);
|
||||
return Response.ok().build();
|
||||
|
||||
} catch (UnsupportedOperationException e) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", "Provider inconnu : " + providerCode))
|
||||
.build();
|
||||
} catch (PaymentException e) {
|
||||
log.error("Webhook {} rejeté : {}", providerCode, e.getMessage());
|
||||
return Response.status(e.getHttpStatus())
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/** Retourne les providers de paiement disponibles. */
|
||||
@GET
|
||||
@Path("/providers")
|
||||
@RolesAllowed({"ADMIN_ORGANISATION", "SUPER_ADMIN"})
|
||||
public List<String> getProviders() {
|
||||
return registry.getAvailableCodes();
|
||||
}
|
||||
|
||||
public record PaiementInitierRequest(
|
||||
BigDecimal montant,
|
||||
String devise,
|
||||
String telephone,
|
||||
String email,
|
||||
String reference,
|
||||
String successUrl,
|
||||
String cancelUrl,
|
||||
/** Optionnel — si fourni, le providerDefaut de la formule prend le dessus sur le query param. */
|
||||
UUID souscriptionId
|
||||
) {}
|
||||
}
|
||||
@@ -1,130 +1,130 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.config.request.ParametresLcbFtRequest;
|
||||
import dev.lions.unionflow.server.api.dto.config.response.ParametresLcbFtResponse;
|
||||
import dev.lions.unionflow.server.service.ParametresLcbFtService;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
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 java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Resource REST pour les paramètres LCB-FT (seuils anti-blanchiment).
|
||||
*
|
||||
* @author lions dev Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-13
|
||||
*/
|
||||
@Path("/api/parametres-lcb-ft")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Paramètres LCB-FT", description = "Gestion des seuils anti-blanchiment (LCB-FT)")
|
||||
@Slf4j
|
||||
public class ParametresLcbFtResource {
|
||||
|
||||
@Inject
|
||||
ParametresLcbFtService parametresService;
|
||||
|
||||
/**
|
||||
* Récupère les paramètres LCB-FT pour une organisation et une devise.
|
||||
* Endpoint utilisé par le mobile pour valider côté client.
|
||||
*
|
||||
* @param organisationId ID de l'organisation (optionnel, null pour paramètres plateforme)
|
||||
* @param codeDevise Code devise ISO 4217 (XOF par défaut)
|
||||
* @return Paramètres LCB-FT complets
|
||||
*/
|
||||
@GET
|
||||
@PermitAll
|
||||
@Operation(
|
||||
summary = "Récupérer les paramètres LCB-FT",
|
||||
description = "Retourne les seuils anti-blanchiment pour une organisation ou la plateforme"
|
||||
)
|
||||
@APIResponse(responseCode = "200", description = "Paramètres récupérés")
|
||||
@APIResponse(responseCode = "404", description = "Paramètres non configurés")
|
||||
public Response getParametres(
|
||||
@QueryParam("organisationId")
|
||||
@Parameter(description = "ID de l'organisation (optionnel)")
|
||||
String organisationId,
|
||||
|
||||
@QueryParam("codeDevise")
|
||||
@DefaultValue("XOF")
|
||||
@Parameter(description = "Code devise (XOF par défaut)")
|
||||
String codeDevise) {
|
||||
|
||||
log.info("GET /api/parametres-lcb-ft?organisationId={}&codeDevise={}",
|
||||
organisationId, codeDevise);
|
||||
|
||||
UUID orgId = organisationId != null && !organisationId.isBlank() ?
|
||||
UUID.fromString(organisationId) : null;
|
||||
|
||||
ParametresLcbFtResponse params = parametresService.getParametres(orgId, codeDevise);
|
||||
return Response.ok(params).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère uniquement le seuil de justification (endpoint léger pour mobile).
|
||||
*
|
||||
* @param organisationId ID de l'organisation
|
||||
* @param codeDevise Code devise (XOF par défaut)
|
||||
* @return Montant seuil
|
||||
*/
|
||||
@GET
|
||||
@Path("/seuil-justification")
|
||||
@PermitAll
|
||||
@Operation(
|
||||
summary = "Récupérer le seuil de justification uniquement",
|
||||
description = "Endpoint léger pour récupérer juste le montant seuil (utilisé par mobile)"
|
||||
)
|
||||
@APIResponse(responseCode = "200", description = "Seuil récupéré")
|
||||
public Response getSeuilJustification(
|
||||
@QueryParam("organisationId") String organisationId,
|
||||
@QueryParam("codeDevise") @DefaultValue("XOF") String codeDevise) {
|
||||
|
||||
log.debug("GET /api/parametres-lcb-ft/seuil-justification");
|
||||
|
||||
UUID orgId = organisationId != null && !organisationId.isBlank() ?
|
||||
UUID.fromString(organisationId) : null;
|
||||
|
||||
BigDecimal seuil = parametresService.getSeuilJustification(orgId, codeDevise);
|
||||
return Response.ok().entity(new SeuilResponse(seuil, codeDevise)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée ou met à jour les paramètres LCB-FT (admin uniquement).
|
||||
*
|
||||
* @param request Paramètres à créer/mettre à jour
|
||||
* @return Paramètres créés/mis à jour
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({ "ADMIN", "SUPER_ADMIN" })
|
||||
@Operation(
|
||||
summary = "Créer ou mettre à jour les paramètres LCB-FT",
|
||||
description = "Admin uniquement - Configure les seuils pour une organisation ou la plateforme"
|
||||
)
|
||||
@APIResponse(responseCode = "200", description = "Paramètres sauvegardés")
|
||||
public Response saveOrUpdateParametres(@Valid ParametresLcbFtRequest request) {
|
||||
log.info("POST /api/parametres-lcb-ft - org={}", request.getOrganisationId());
|
||||
|
||||
ParametresLcbFtResponse saved = parametresService.saveOrUpdateParametres(request);
|
||||
return Response.ok(saved).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO léger pour retourner uniquement le seuil.
|
||||
*/
|
||||
public record SeuilResponse(
|
||||
BigDecimal montantSeuil,
|
||||
String codeDevise
|
||||
) {}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.config.request.ParametresLcbFtRequest;
|
||||
import dev.lions.unionflow.server.api.dto.config.response.ParametresLcbFtResponse;
|
||||
import dev.lions.unionflow.server.service.ParametresLcbFtService;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
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 java.math.BigDecimal;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Resource REST pour les paramètres LCB-FT (seuils anti-blanchiment).
|
||||
*
|
||||
* @author lions dev Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-13
|
||||
*/
|
||||
@Path("/api/parametres-lcb-ft")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Paramètres LCB-FT", description = "Gestion des seuils anti-blanchiment (LCB-FT)")
|
||||
@Slf4j
|
||||
public class ParametresLcbFtResource {
|
||||
|
||||
@Inject
|
||||
ParametresLcbFtService parametresService;
|
||||
|
||||
/**
|
||||
* Récupère les paramètres LCB-FT pour une organisation et une devise.
|
||||
* Endpoint utilisé par le mobile pour valider côté client.
|
||||
*
|
||||
* @param organisationId ID de l'organisation (optionnel, null pour paramètres plateforme)
|
||||
* @param codeDevise Code devise ISO 4217 (XOF par défaut)
|
||||
* @return Paramètres LCB-FT complets
|
||||
*/
|
||||
@GET
|
||||
@PermitAll
|
||||
@Operation(
|
||||
summary = "Récupérer les paramètres LCB-FT",
|
||||
description = "Retourne les seuils anti-blanchiment pour une organisation ou la plateforme"
|
||||
)
|
||||
@APIResponse(responseCode = "200", description = "Paramètres récupérés")
|
||||
@APIResponse(responseCode = "404", description = "Paramètres non configurés")
|
||||
public Response getParametres(
|
||||
@QueryParam("organisationId")
|
||||
@Parameter(description = "ID de l'organisation (optionnel)")
|
||||
String organisationId,
|
||||
|
||||
@QueryParam("codeDevise")
|
||||
@DefaultValue("XOF")
|
||||
@Parameter(description = "Code devise (XOF par défaut)")
|
||||
String codeDevise) {
|
||||
|
||||
log.info("GET /api/parametres-lcb-ft?organisationId={}&codeDevise={}",
|
||||
organisationId, codeDevise);
|
||||
|
||||
UUID orgId = organisationId != null && !organisationId.isBlank() ?
|
||||
UUID.fromString(organisationId) : null;
|
||||
|
||||
ParametresLcbFtResponse params = parametresService.getParametres(orgId, codeDevise);
|
||||
return Response.ok(params).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère uniquement le seuil de justification (endpoint léger pour mobile).
|
||||
*
|
||||
* @param organisationId ID de l'organisation
|
||||
* @param codeDevise Code devise (XOF par défaut)
|
||||
* @return Montant seuil
|
||||
*/
|
||||
@GET
|
||||
@Path("/seuil-justification")
|
||||
@PermitAll
|
||||
@Operation(
|
||||
summary = "Récupérer le seuil de justification uniquement",
|
||||
description = "Endpoint léger pour récupérer juste le montant seuil (utilisé par mobile)"
|
||||
)
|
||||
@APIResponse(responseCode = "200", description = "Seuil récupéré")
|
||||
public Response getSeuilJustification(
|
||||
@QueryParam("organisationId") String organisationId,
|
||||
@QueryParam("codeDevise") @DefaultValue("XOF") String codeDevise) {
|
||||
|
||||
log.debug("GET /api/parametres-lcb-ft/seuil-justification");
|
||||
|
||||
UUID orgId = organisationId != null && !organisationId.isBlank() ?
|
||||
UUID.fromString(organisationId) : null;
|
||||
|
||||
BigDecimal seuil = parametresService.getSeuilJustification(orgId, codeDevise);
|
||||
return Response.ok().entity(new SeuilResponse(seuil, codeDevise)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée ou met à jour les paramètres LCB-FT (admin uniquement).
|
||||
*
|
||||
* @param request Paramètres à créer/mettre à jour
|
||||
* @return Paramètres créés/mis à jour
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({ "ADMIN", "SUPER_ADMIN" })
|
||||
@Operation(
|
||||
summary = "Créer ou mettre à jour les paramètres LCB-FT",
|
||||
description = "Admin uniquement - Configure les seuils pour une organisation ou la plateforme"
|
||||
)
|
||||
@APIResponse(responseCode = "200", description = "Paramètres sauvegardés")
|
||||
public Response saveOrUpdateParametres(@Valid ParametresLcbFtRequest request) {
|
||||
log.info("POST /api/parametres-lcb-ft - org={}", request.getOrganisationId());
|
||||
|
||||
ParametresLcbFtResponse saved = parametresService.saveOrUpdateParametres(request);
|
||||
return Response.ok(saved).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* DTO léger pour retourner uniquement le seuil.
|
||||
*/
|
||||
public record SeuilResponse(
|
||||
BigDecimal montantSeuil,
|
||||
String codeDevise
|
||||
) {}
|
||||
}
|
||||
|
||||
@@ -1,75 +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();
|
||||
}
|
||||
}
|
||||
|
||||
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();
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -1,75 +1,75 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.solidarite.request.CreatePropositionAideRequest;
|
||||
import dev.lions.unionflow.server.api.dto.solidarite.request.UpdatePropositionAideRequest;
|
||||
import dev.lions.unionflow.server.api.dto.solidarite.response.PropositionAideResponse;
|
||||
import dev.lions.unionflow.server.service.PropositionAideService;
|
||||
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 org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Resource REST pour les propositions d'aide.
|
||||
*/
|
||||
@Path("/api/propositions-aide")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "MEMBRE", "USER"})
|
||||
@Tag(name = "Propositions d'aide", description = "Gestion des propositions d'aide solidarité")
|
||||
public class PropositionAideResource {
|
||||
|
||||
@Inject
|
||||
PropositionAideService propositionAideService;
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Liste les propositions d'aide avec pagination")
|
||||
public List<PropositionAideResponse> listerToutes(
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size) {
|
||||
List<PropositionAideResponse> all = propositionAideService.rechercherAvecFiltres(Collections.emptyMap());
|
||||
int from = Math.min(page * size, all.size());
|
||||
int to = Math.min(from + size, all.size());
|
||||
return from < to ? all.subList(from, to) : List.of();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Récupère une proposition d'aide par son ID")
|
||||
public PropositionAideResponse obtenirParId(@PathParam("id") String id) {
|
||||
PropositionAideResponse response = propositionAideService.obtenirParId(id);
|
||||
if (response == null) {
|
||||
throw new NotFoundException("Proposition d'aide non trouvée : " + id);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Operation(summary = "Crée une nouvelle proposition d'aide")
|
||||
public Response creer(@Valid CreatePropositionAideRequest request) {
|
||||
PropositionAideResponse response = propositionAideService.creerProposition(request);
|
||||
return Response.status(Response.Status.CREATED).entity(response).build();
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Met à jour une proposition d'aide")
|
||||
public PropositionAideResponse mettreAJour(@PathParam("id") String id,
|
||||
@Valid UpdatePropositionAideRequest request) {
|
||||
return propositionAideService.mettreAJour(id, request);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/meilleures")
|
||||
@Operation(summary = "Récupère les meilleures propositions")
|
||||
public List<PropositionAideResponse> obtenirMeilleures(@QueryParam("limite") @DefaultValue("5") int limite) {
|
||||
return propositionAideService.obtenirMeilleuresPropositions(limite);
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.solidarite.request.CreatePropositionAideRequest;
|
||||
import dev.lions.unionflow.server.api.dto.solidarite.request.UpdatePropositionAideRequest;
|
||||
import dev.lions.unionflow.server.api.dto.solidarite.response.PropositionAideResponse;
|
||||
import dev.lions.unionflow.server.service.PropositionAideService;
|
||||
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 org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.util.Collections;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Resource REST pour les propositions d'aide.
|
||||
*/
|
||||
@Path("/api/propositions-aide")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "MEMBRE", "USER"})
|
||||
@Tag(name = "Propositions d'aide", description = "Gestion des propositions d'aide solidarité")
|
||||
public class PropositionAideResource {
|
||||
|
||||
@Inject
|
||||
PropositionAideService propositionAideService;
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Liste les propositions d'aide avec pagination")
|
||||
public List<PropositionAideResponse> listerToutes(
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size) {
|
||||
List<PropositionAideResponse> all = propositionAideService.rechercherAvecFiltres(Collections.emptyMap());
|
||||
int from = Math.min(page * size, all.size());
|
||||
int to = Math.min(from + size, all.size());
|
||||
return from < to ? all.subList(from, to) : List.of();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Récupère une proposition d'aide par son ID")
|
||||
public PropositionAideResponse obtenirParId(@PathParam("id") String id) {
|
||||
PropositionAideResponse response = propositionAideService.obtenirParId(id);
|
||||
if (response == null) {
|
||||
throw new NotFoundException("Proposition d'aide non trouvée : " + id);
|
||||
}
|
||||
return response;
|
||||
}
|
||||
|
||||
@POST
|
||||
@Operation(summary = "Crée une nouvelle proposition d'aide")
|
||||
public Response creer(@Valid CreatePropositionAideRequest request) {
|
||||
PropositionAideResponse response = propositionAideService.creerProposition(request);
|
||||
return Response.status(Response.Status.CREATED).entity(response).build();
|
||||
}
|
||||
|
||||
@PUT
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Met à jour une proposition d'aide")
|
||||
public PropositionAideResponse mettreAJour(@PathParam("id") String id,
|
||||
@Valid UpdatePropositionAideRequest request) {
|
||||
return propositionAideService.mettreAJour(id, request);
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/meilleures")
|
||||
@Operation(summary = "Récupère les meilleures propositions")
|
||||
public List<PropositionAideResponse> obtenirMeilleures(@QueryParam("limite") @DefaultValue("5") int limite) {
|
||||
return propositionAideService.obtenirMeilleuresPropositions(limite);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,55 +1,55 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.role.response.RoleResponse;
|
||||
import dev.lions.unionflow.server.entity.Role;
|
||||
import dev.lions.unionflow.server.service.RoleService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Resource REST pour les rôles.
|
||||
*/
|
||||
@Path("/api/roles")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION"})
|
||||
@Tag(name = "Rôles", description = "Gestion des rôles et permissions")
|
||||
public class RoleResource {
|
||||
|
||||
@Inject
|
||||
RoleService roleService;
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Liste tous les rôles actifs")
|
||||
public List<RoleResponse> listerTous() {
|
||||
return roleService.listerTousActifs().stream()
|
||||
.map(this::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private RoleResponse toDTO(Role entity) {
|
||||
RoleResponse dto = new RoleResponse();
|
||||
dto.setId(entity.getId());
|
||||
dto.setCode(entity.getCode());
|
||||
dto.setLibelle(entity.getLibelle());
|
||||
dto.setDescription(entity.getDescription());
|
||||
dto.setTypeRole(entity.getTypeRole());
|
||||
dto.setNiveauHierarchique(entity.getNiveauHierarchique());
|
||||
if (entity.getOrganisation() != null) {
|
||||
dto.setOrganisationId(entity.getOrganisation().getId());
|
||||
dto.setNomOrganisation(entity.getOrganisation().getNom());
|
||||
}
|
||||
dto.setActif(entity.getActif());
|
||||
dto.setDateCreation(entity.getDateCreation());
|
||||
dto.setDateModification(entity.getDateModification());
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.role.response.RoleResponse;
|
||||
import dev.lions.unionflow.server.entity.Role;
|
||||
import dev.lions.unionflow.server.service.RoleService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Resource REST pour les rôles.
|
||||
*/
|
||||
@Path("/api/roles")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION"})
|
||||
@Tag(name = "Rôles", description = "Gestion des rôles et permissions")
|
||||
public class RoleResource {
|
||||
|
||||
@Inject
|
||||
RoleService roleService;
|
||||
|
||||
@GET
|
||||
@Operation(summary = "Liste tous les rôles actifs")
|
||||
public List<RoleResponse> listerTous() {
|
||||
return roleService.listerTousActifs().stream()
|
||||
.map(this::toDTO)
|
||||
.collect(Collectors.toList());
|
||||
}
|
||||
|
||||
private RoleResponse toDTO(Role entity) {
|
||||
RoleResponse dto = new RoleResponse();
|
||||
dto.setId(entity.getId());
|
||||
dto.setCode(entity.getCode());
|
||||
dto.setLibelle(entity.getLibelle());
|
||||
dto.setDescription(entity.getDescription());
|
||||
dto.setTypeRole(entity.getTypeRole());
|
||||
dto.setNiveauHierarchique(entity.getNiveauHierarchique());
|
||||
if (entity.getOrganisation() != null) {
|
||||
dto.setOrganisationId(entity.getOrganisation().getId());
|
||||
dto.setNomOrganisation(entity.getOrganisation().getNom());
|
||||
}
|
||||
dto.setActif(entity.getActif());
|
||||
dto.setDateCreation(entity.getDateCreation());
|
||||
dto.setDateModification(entity.getDateModification());
|
||||
return dto;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,293 +1,293 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.system.request.UpdateSystemConfigRequest;
|
||||
import dev.lions.unionflow.server.api.dto.system.response.CacheStatsResponse;
|
||||
import dev.lions.unionflow.server.api.dto.system.response.SystemConfigResponse;
|
||||
import dev.lions.unionflow.server.api.dto.logs.response.SystemMetricsResponse;
|
||||
import dev.lions.unionflow.server.api.dto.system.response.SystemTestResultResponse;
|
||||
import dev.lions.unionflow.server.service.SystemConfigService;
|
||||
import dev.lions.unionflow.server.service.SystemMetricsService;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* REST Resource pour la gestion de la configuration système
|
||||
*/
|
||||
@Slf4j
|
||||
@Path("/api/system")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Système", description = "Gestion de la configuration système")
|
||||
public class SystemResource {
|
||||
|
||||
@Inject
|
||||
SystemConfigService systemConfigService;
|
||||
|
||||
@Inject
|
||||
SystemMetricsService systemMetricsService;
|
||||
|
||||
/**
|
||||
* Récupérer la configuration système
|
||||
*/
|
||||
@GET
|
||||
@Path("/config")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Récupérer la configuration système", description = "Retourne la configuration système complète")
|
||||
public SystemConfigResponse getSystemConfig() {
|
||||
log.info("GET /api/system/config");
|
||||
return systemConfigService.getSystemConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mettre à jour la configuration système
|
||||
*/
|
||||
@PUT
|
||||
@Path("/config")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Mettre à jour la configuration système")
|
||||
public SystemConfigResponse updateSystemConfig(@Valid UpdateSystemConfigRequest request) {
|
||||
log.info("PUT /api/system/config");
|
||||
return systemConfigService.updateSystemConfig(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer les statistiques du cache
|
||||
*/
|
||||
@GET
|
||||
@Path("/cache/stats")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Récupérer les statistiques du cache système")
|
||||
public CacheStatsResponse getCacheStats() {
|
||||
log.info("GET /api/system/cache/stats");
|
||||
return systemConfigService.getCacheStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vider le cache système
|
||||
*/
|
||||
@POST
|
||||
@Path("/cache/clear")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Vider le cache système")
|
||||
public Response clearCache() {
|
||||
log.info("POST /api/system/cache/clear");
|
||||
systemConfigService.clearCache();
|
||||
return Response.ok().entity(java.util.Map.of("message", "Cache vidé avec succès")).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tester la connexion à la base de données
|
||||
*/
|
||||
@POST
|
||||
@Path("/test/database")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Tester la connexion à la base de données")
|
||||
public SystemTestResultResponse testDatabaseConnection() {
|
||||
log.info("POST /api/system/test/database");
|
||||
return systemConfigService.testDatabaseConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tester la configuration email
|
||||
*/
|
||||
@POST
|
||||
@Path("/test/email")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Tester la configuration email")
|
||||
public SystemTestResultResponse testEmailConfiguration() {
|
||||
log.info("POST /api/system/test/email");
|
||||
return systemConfigService.testEmailConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer les métriques système en temps réel
|
||||
*/
|
||||
@GET
|
||||
@Path("/metrics")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(
|
||||
summary = "Récupérer les métriques système en temps réel",
|
||||
description = "Retourne toutes les métriques système (CPU, RAM, disque, utilisateurs actifs, etc.)"
|
||||
)
|
||||
public SystemMetricsResponse getSystemMetrics() {
|
||||
log.info("GET /api/system/metrics");
|
||||
return systemMetricsService.getSystemMetrics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimiser la base de données
|
||||
*/
|
||||
@POST
|
||||
@Path("/database/optimize")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Optimiser la base de données (VACUUM ANALYZE)")
|
||||
public Response optimizeDatabase() {
|
||||
log.info("POST /api/system/database/optimize");
|
||||
return Response.ok(systemConfigService.optimizeDatabase()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcer la déconnexion globale
|
||||
*/
|
||||
@POST
|
||||
@Path("/auth/logout-all")
|
||||
@RolesAllowed({"SUPER_ADMIN"})
|
||||
@Operation(summary = "Forcer la déconnexion de tous les utilisateurs")
|
||||
public Response forceGlobalLogout() {
|
||||
log.info("POST /api/system/auth/logout-all");
|
||||
return Response.ok(systemConfigService.forceGlobalLogout()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Nettoyer les sessions expirées
|
||||
*/
|
||||
@POST
|
||||
@Path("/sessions/cleanup")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Nettoyer les sessions expirées")
|
||||
public Response cleanupSessions() {
|
||||
log.info("POST /api/system/sessions/cleanup");
|
||||
return Response.ok(systemConfigService.cleanupSessions()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Nettoyer les anciens logs
|
||||
*/
|
||||
@POST
|
||||
@Path("/logs/cleanup")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Nettoyer les anciens logs selon la politique de rétention")
|
||||
public Response cleanOldLogs() {
|
||||
log.info("POST /api/system/logs/cleanup");
|
||||
return Response.ok(systemConfigService.cleanOldLogs()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Purger les données expirées
|
||||
*/
|
||||
@POST
|
||||
@Path("/data/purge")
|
||||
@RolesAllowed({"SUPER_ADMIN"})
|
||||
@Operation(summary = "Purger les données expirées (RGPD)")
|
||||
public Response purgeExpiredData() {
|
||||
log.info("POST /api/system/data/purge");
|
||||
return Response.ok(systemConfigService.purgeExpiredData()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyser les performances de la base de données
|
||||
*/
|
||||
@POST
|
||||
@Path("/performance/analyze")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Analyser les performances du système")
|
||||
public Response analyzePerformance() {
|
||||
log.info("POST /api/system/performance/analyze");
|
||||
return Response.ok(systemConfigService.analyzePerformance()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Créer une sauvegarde
|
||||
*/
|
||||
@POST
|
||||
@Path("/backup/create")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Créer une sauvegarde du système")
|
||||
public Response createBackup() {
|
||||
log.info("POST /api/system/backup/create");
|
||||
return Response.ok(systemConfigService.createBackup()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Planifier une maintenance
|
||||
*/
|
||||
@POST
|
||||
@Path("/maintenance/schedule")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Planifier une maintenance")
|
||||
public Response scheduleMaintenance(@QueryParam("scheduledAt") String scheduledAt, @QueryParam("reason") String reason) {
|
||||
log.info("POST /api/system/maintenance/schedule");
|
||||
return Response.ok(systemConfigService.scheduleMaintenance(scheduledAt, reason)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Activer la maintenance d'urgence
|
||||
*/
|
||||
@POST
|
||||
@Path("/maintenance/emergency")
|
||||
@RolesAllowed({"SUPER_ADMIN"})
|
||||
@Operation(summary = "Activer le mode maintenance d'urgence")
|
||||
public Response emergencyMaintenance() {
|
||||
log.info("POST /api/system/maintenance/emergency");
|
||||
return Response.ok(systemConfigService.emergencyMaintenance()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifier les mises à jour
|
||||
*/
|
||||
@GET
|
||||
@Path("/updates/check")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Vérifier les mises à jour disponibles")
|
||||
public Response checkUpdates() {
|
||||
log.info("GET /api/system/updates/check");
|
||||
return Response.ok(systemConfigService.checkUpdates()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exporter les logs récents
|
||||
*/
|
||||
@GET
|
||||
@Path("/logs/export")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Exporter les logs des dernières 24h")
|
||||
public Response exportLogs() {
|
||||
log.info("GET /api/system/logs/export");
|
||||
return Response.ok(systemConfigService.exportLogs()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Générer un rapport d'utilisation
|
||||
*/
|
||||
@GET
|
||||
@Path("/reports/usage")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Générer un rapport d'utilisation du système")
|
||||
public Response generateUsageReport() {
|
||||
log.info("GET /api/system/reports/usage");
|
||||
return Response.ok(systemConfigService.generateUsageReport()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Générer un rapport d'audit
|
||||
*/
|
||||
@GET
|
||||
@Path("/audit/report")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Générer un rapport d'audit")
|
||||
public Response generateAuditReport() {
|
||||
log.info("GET /api/system/audit/report");
|
||||
return Response.ok(systemConfigService.generateAuditReport()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Export RGPD
|
||||
*/
|
||||
@POST
|
||||
@Path("/gdpr/export")
|
||||
@RolesAllowed({"SUPER_ADMIN"})
|
||||
@Operation(summary = "Initier un export RGPD des données utilisateurs")
|
||||
public Response exportGDPRData() {
|
||||
log.info("POST /api/system/gdpr/export");
|
||||
return Response.ok(systemConfigService.exportGDPRData()).build();
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.system.request.UpdateSystemConfigRequest;
|
||||
import dev.lions.unionflow.server.api.dto.system.response.CacheStatsResponse;
|
||||
import dev.lions.unionflow.server.api.dto.system.response.SystemConfigResponse;
|
||||
import dev.lions.unionflow.server.api.dto.logs.response.SystemMetricsResponse;
|
||||
import dev.lions.unionflow.server.api.dto.system.response.SystemTestResultResponse;
|
||||
import dev.lions.unionflow.server.service.SystemConfigService;
|
||||
import dev.lions.unionflow.server.service.SystemMetricsService;
|
||||
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 lombok.extern.slf4j.Slf4j;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
|
||||
import java.util.Map;
|
||||
|
||||
/**
|
||||
* REST Resource pour la gestion de la configuration système
|
||||
*/
|
||||
@Slf4j
|
||||
@Path("/api/system")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Système", description = "Gestion de la configuration système")
|
||||
public class SystemResource {
|
||||
|
||||
@Inject
|
||||
SystemConfigService systemConfigService;
|
||||
|
||||
@Inject
|
||||
SystemMetricsService systemMetricsService;
|
||||
|
||||
/**
|
||||
* Récupérer la configuration système
|
||||
*/
|
||||
@GET
|
||||
@Path("/config")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Récupérer la configuration système", description = "Retourne la configuration système complète")
|
||||
public SystemConfigResponse getSystemConfig() {
|
||||
log.info("GET /api/system/config");
|
||||
return systemConfigService.getSystemConfig();
|
||||
}
|
||||
|
||||
/**
|
||||
* Mettre à jour la configuration système
|
||||
*/
|
||||
@PUT
|
||||
@Path("/config")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Mettre à jour la configuration système")
|
||||
public SystemConfigResponse updateSystemConfig(@Valid UpdateSystemConfigRequest request) {
|
||||
log.info("PUT /api/system/config");
|
||||
return systemConfigService.updateSystemConfig(request);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer les statistiques du cache
|
||||
*/
|
||||
@GET
|
||||
@Path("/cache/stats")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Récupérer les statistiques du cache système")
|
||||
public CacheStatsResponse getCacheStats() {
|
||||
log.info("GET /api/system/cache/stats");
|
||||
return systemConfigService.getCacheStats();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vider le cache système
|
||||
*/
|
||||
@POST
|
||||
@Path("/cache/clear")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Vider le cache système")
|
||||
public Response clearCache() {
|
||||
log.info("POST /api/system/cache/clear");
|
||||
systemConfigService.clearCache();
|
||||
return Response.ok().entity(java.util.Map.of("message", "Cache vidé avec succès")).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tester la connexion à la base de données
|
||||
*/
|
||||
@POST
|
||||
@Path("/test/database")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Tester la connexion à la base de données")
|
||||
public SystemTestResultResponse testDatabaseConnection() {
|
||||
log.info("POST /api/system/test/database");
|
||||
return systemConfigService.testDatabaseConnection();
|
||||
}
|
||||
|
||||
/**
|
||||
* Tester la configuration email
|
||||
*/
|
||||
@POST
|
||||
@Path("/test/email")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Tester la configuration email")
|
||||
public SystemTestResultResponse testEmailConfiguration() {
|
||||
log.info("POST /api/system/test/email");
|
||||
return systemConfigService.testEmailConfiguration();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer les métriques système en temps réel
|
||||
*/
|
||||
@GET
|
||||
@Path("/metrics")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(
|
||||
summary = "Récupérer les métriques système en temps réel",
|
||||
description = "Retourne toutes les métriques système (CPU, RAM, disque, utilisateurs actifs, etc.)"
|
||||
)
|
||||
public SystemMetricsResponse getSystemMetrics() {
|
||||
log.info("GET /api/system/metrics");
|
||||
return systemMetricsService.getSystemMetrics();
|
||||
}
|
||||
|
||||
/**
|
||||
* Optimiser la base de données
|
||||
*/
|
||||
@POST
|
||||
@Path("/database/optimize")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Optimiser la base de données (VACUUM ANALYZE)")
|
||||
public Response optimizeDatabase() {
|
||||
log.info("POST /api/system/database/optimize");
|
||||
return Response.ok(systemConfigService.optimizeDatabase()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Forcer la déconnexion globale
|
||||
*/
|
||||
@POST
|
||||
@Path("/auth/logout-all")
|
||||
@RolesAllowed({"SUPER_ADMIN"})
|
||||
@Operation(summary = "Forcer la déconnexion de tous les utilisateurs")
|
||||
public Response forceGlobalLogout() {
|
||||
log.info("POST /api/system/auth/logout-all");
|
||||
return Response.ok(systemConfigService.forceGlobalLogout()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Nettoyer les sessions expirées
|
||||
*/
|
||||
@POST
|
||||
@Path("/sessions/cleanup")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Nettoyer les sessions expirées")
|
||||
public Response cleanupSessions() {
|
||||
log.info("POST /api/system/sessions/cleanup");
|
||||
return Response.ok(systemConfigService.cleanupSessions()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Nettoyer les anciens logs
|
||||
*/
|
||||
@POST
|
||||
@Path("/logs/cleanup")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Nettoyer les anciens logs selon la politique de rétention")
|
||||
public Response cleanOldLogs() {
|
||||
log.info("POST /api/system/logs/cleanup");
|
||||
return Response.ok(systemConfigService.cleanOldLogs()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Purger les données expirées
|
||||
*/
|
||||
@POST
|
||||
@Path("/data/purge")
|
||||
@RolesAllowed({"SUPER_ADMIN"})
|
||||
@Operation(summary = "Purger les données expirées (RGPD)")
|
||||
public Response purgeExpiredData() {
|
||||
log.info("POST /api/system/data/purge");
|
||||
return Response.ok(systemConfigService.purgeExpiredData()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Analyser les performances de la base de données
|
||||
*/
|
||||
@POST
|
||||
@Path("/performance/analyze")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Analyser les performances du système")
|
||||
public Response analyzePerformance() {
|
||||
log.info("POST /api/system/performance/analyze");
|
||||
return Response.ok(systemConfigService.analyzePerformance()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Créer une sauvegarde
|
||||
*/
|
||||
@POST
|
||||
@Path("/backup/create")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Créer une sauvegarde du système")
|
||||
public Response createBackup() {
|
||||
log.info("POST /api/system/backup/create");
|
||||
return Response.ok(systemConfigService.createBackup()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Planifier une maintenance
|
||||
*/
|
||||
@POST
|
||||
@Path("/maintenance/schedule")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Planifier une maintenance")
|
||||
public Response scheduleMaintenance(@QueryParam("scheduledAt") String scheduledAt, @QueryParam("reason") String reason) {
|
||||
log.info("POST /api/system/maintenance/schedule");
|
||||
return Response.ok(systemConfigService.scheduleMaintenance(scheduledAt, reason)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Activer la maintenance d'urgence
|
||||
*/
|
||||
@POST
|
||||
@Path("/maintenance/emergency")
|
||||
@RolesAllowed({"SUPER_ADMIN"})
|
||||
@Operation(summary = "Activer le mode maintenance d'urgence")
|
||||
public Response emergencyMaintenance() {
|
||||
log.info("POST /api/system/maintenance/emergency");
|
||||
return Response.ok(systemConfigService.emergencyMaintenance()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifier les mises à jour
|
||||
*/
|
||||
@GET
|
||||
@Path("/updates/check")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Vérifier les mises à jour disponibles")
|
||||
public Response checkUpdates() {
|
||||
log.info("GET /api/system/updates/check");
|
||||
return Response.ok(systemConfigService.checkUpdates()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Exporter les logs récents
|
||||
*/
|
||||
@GET
|
||||
@Path("/logs/export")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Exporter les logs des dernières 24h")
|
||||
public Response exportLogs() {
|
||||
log.info("GET /api/system/logs/export");
|
||||
return Response.ok(systemConfigService.exportLogs()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Générer un rapport d'utilisation
|
||||
*/
|
||||
@GET
|
||||
@Path("/reports/usage")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN"})
|
||||
@Operation(summary = "Générer un rapport d'utilisation du système")
|
||||
public Response generateUsageReport() {
|
||||
log.info("GET /api/system/reports/usage");
|
||||
return Response.ok(systemConfigService.generateUsageReport()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Générer un rapport d'audit
|
||||
*/
|
||||
@GET
|
||||
@Path("/audit/report")
|
||||
@RolesAllowed({"SUPER_ADMIN", "ADMIN", "MODERATEUR"})
|
||||
@Operation(summary = "Générer un rapport d'audit")
|
||||
public Response generateAuditReport() {
|
||||
log.info("GET /api/system/audit/report");
|
||||
return Response.ok(systemConfigService.generateAuditReport()).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Export RGPD
|
||||
*/
|
||||
@POST
|
||||
@Path("/gdpr/export")
|
||||
@RolesAllowed({"SUPER_ADMIN"})
|
||||
@Operation(summary = "Initier un export RGPD des données utilisateurs")
|
||||
public Response exportGDPRData() {
|
||||
log.info("POST /api/system/gdpr/export");
|
||||
return Response.ok(systemConfigService.exportGDPRData()).build();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,276 +1,276 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.reference.request.CreateTypeReferenceRequest;
|
||||
import dev.lions.unionflow.server.api.dto.reference.request.UpdateTypeReferenceRequest;
|
||||
import dev.lions.unionflow.server.api.dto.reference.response.TypeReferenceResponse;
|
||||
import dev.lions.unionflow.server.service.TypeReferenceService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
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 le CRUD des données
|
||||
* de référence.
|
||||
*
|
||||
* <p>
|
||||
* Expose les endpoints permettant de gérer
|
||||
* dynamiquement toutes les valeurs catégorielles
|
||||
* de l'application (statuts, types, devises,
|
||||
* priorités, etc.) via la table
|
||||
* {@code types_reference}.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2026-02-21
|
||||
*/
|
||||
@Path("/api/v1/types-reference")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Types de référence", description = "Gestion des données de référence"
|
||||
+ " paramétrables")
|
||||
@RolesAllowed({
|
||||
"SUPER_ADMIN",
|
||||
"ADMIN", "MEMBRE", "USER"
|
||||
})
|
||||
public class TypeReferenceResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(TypeReferenceResource.class);
|
||||
|
||||
@Inject
|
||||
TypeReferenceService service;
|
||||
|
||||
/**
|
||||
* Liste les références actives d'un domaine.
|
||||
*
|
||||
* @param domaine le domaine fonctionnel
|
||||
* @param organisationId l'UUID de l'organisation
|
||||
* @return liste triée par ordre d'affichage
|
||||
*/
|
||||
@GET
|
||||
@Operation(summary = "Lister par domaine", description = "Récupère les valeurs actives"
|
||||
+ " d'un domaine donné")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste récupérée", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = TypeReferenceResponse.class))),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié")
|
||||
})
|
||||
public Response listerParDomaine(
|
||||
@Parameter(description = "Domaine fonctionnel", example = "STATUT_ORGANISATION", required = true) @QueryParam("domaine") String domaine,
|
||||
@Parameter(description = "UUID de l'organisation", example = "550e8400-e29b-41d4-a716-4466"
|
||||
+ "55440000") @QueryParam("organisationId") UUID organisationId) {
|
||||
if (domaine == null || domaine.isBlank()) {
|
||||
return Response
|
||||
.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of(
|
||||
"error",
|
||||
"Le paramètre 'domaine' est"
|
||||
+ " obligatoire"))
|
||||
.build();
|
||||
}
|
||||
List<TypeReferenceResponse> result = service.listerParDomaine(
|
||||
domaine, organisationId);
|
||||
return Response.ok(result).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne un type de référence par son ID.
|
||||
*
|
||||
* @param id l'UUID du type de référence
|
||||
* @return le détail complet
|
||||
*/
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Détail d'une référence", description = "Récupère une référence par"
|
||||
+ " son identifiant")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Référence trouvée"),
|
||||
@APIResponse(responseCode = "404", description = "Référence non trouvée")
|
||||
})
|
||||
public Response trouverParId(
|
||||
@PathParam("id") UUID id) {
|
||||
try {
|
||||
TypeReferenceResponse response = service.trouverParId(id);
|
||||
return Response.ok(response).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste les domaines disponibles.
|
||||
*
|
||||
* @return noms de domaines distincts
|
||||
*/
|
||||
@GET
|
||||
@Path("/domaines")
|
||||
@Operation(summary = "Lister les domaines", description = "Récupère la liste des domaines"
|
||||
+ " disponibles")
|
||||
public Response listerDomaines() {
|
||||
List<String> domaines = service.listerDomaines();
|
||||
return Response.ok(domaines).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la valeur par défaut d'un domaine.
|
||||
*
|
||||
* @param domaine le domaine fonctionnel
|
||||
* @param organisationId l'UUID de l'organisation
|
||||
* @return la valeur par défaut
|
||||
*/
|
||||
@GET
|
||||
@Path("/defaut")
|
||||
@Operation(summary = "Valeur par défaut", description = "Récupère la valeur par défaut"
|
||||
+ " d'un domaine")
|
||||
public Response trouverDefaut(
|
||||
@Parameter(description = "Domaine fonctionnel", required = true) @QueryParam("domaine") String domaine,
|
||||
@Parameter(description = "UUID de l'organisation") @QueryParam("organisationId") UUID organisationId) {
|
||||
if (domaine == null || domaine.isBlank()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Le paramètre 'domaine' est obligatoire"))
|
||||
.build();
|
||||
}
|
||||
try {
|
||||
TypeReferenceResponse response = service.trouverDefaut(
|
||||
domaine, organisationId);
|
||||
return Response.ok(response).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une nouvelle donnée de référence.
|
||||
*
|
||||
* @param request la requête de création validée
|
||||
* @return la référence créée (HTTP 201)
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({
|
||||
"SUPER_ADMIN", "ADMIN"
|
||||
})
|
||||
@Operation(summary = "Créer une référence", description = "Ajoute une nouvelle valeur dans"
|
||||
+ " un domaine")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "201", description = "Référence créée"),
|
||||
@APIResponse(responseCode = "400", description = "Données invalides ou"
|
||||
+ " code dupliqué")
|
||||
})
|
||||
public Response creer(
|
||||
@Valid CreateTypeReferenceRequest request) {
|
||||
try {
|
||||
TypeReferenceResponse created = service.creer(request);
|
||||
return Response
|
||||
.status(Response.Status.CREATED)
|
||||
.entity(created)
|
||||
.build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warnf(
|
||||
"Erreur création référence: %s",
|
||||
e.getMessage());
|
||||
return Response
|
||||
.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour une donnée de référence.
|
||||
*
|
||||
* @param id l'UUID de la référence
|
||||
* @param request la requête de mise à jour
|
||||
* @return la référence mise à jour
|
||||
*/
|
||||
@PUT
|
||||
@Path("/{id}")
|
||||
@RolesAllowed({
|
||||
"SUPER_ADMIN", "ADMIN"
|
||||
})
|
||||
@Operation(summary = "Modifier une référence", description = "Met à jour une valeur"
|
||||
+ " existante")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Référence modifiée"),
|
||||
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||
@APIResponse(responseCode = "404", description = "Référence non trouvée")
|
||||
})
|
||||
public Response modifier(
|
||||
@PathParam("id") UUID id,
|
||||
@Valid UpdateTypeReferenceRequest request) {
|
||||
try {
|
||||
TypeReferenceResponse updated = service.modifier(id, request);
|
||||
return Response.ok(updated).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warnf(
|
||||
"Erreur modification référence: %s",
|
||||
e.getMessage());
|
||||
return Response
|
||||
.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime une donnée de référence.
|
||||
*
|
||||
* <p>
|
||||
* Les valeurs système ne peuvent pas être
|
||||
* supprimées.
|
||||
*
|
||||
* @param id l'UUID de la référence
|
||||
* @return HTTP 204 si succès
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/{id}")
|
||||
@RolesAllowed({
|
||||
"SUPER_ADMIN"
|
||||
})
|
||||
@Operation(summary = "Supprimer une référence", description = "Supprime une valeur non"
|
||||
+ " système")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "204", description = "Référence supprimée"),
|
||||
@APIResponse(responseCode = "400", description = "Valeur système non"
|
||||
+ " supprimable"),
|
||||
@APIResponse(responseCode = "404", description = "Référence non trouvée")
|
||||
})
|
||||
public Response supprimer(
|
||||
@PathParam("id") UUID id) {
|
||||
try {
|
||||
service.supprimer(id);
|
||||
return Response.noContent().build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response
|
||||
.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.reference.request.CreateTypeReferenceRequest;
|
||||
import dev.lions.unionflow.server.api.dto.reference.request.UpdateTypeReferenceRequest;
|
||||
import dev.lions.unionflow.server.api.dto.reference.response.TypeReferenceResponse;
|
||||
import dev.lions.unionflow.server.service.TypeReferenceService;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.Consumes;
|
||||
import jakarta.ws.rs.DELETE;
|
||||
import jakarta.ws.rs.DefaultValue;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.POST;
|
||||
import jakarta.ws.rs.PUT;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.PathParam;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
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 le CRUD des données
|
||||
* de référence.
|
||||
*
|
||||
* <p>
|
||||
* Expose les endpoints permettant de gérer
|
||||
* dynamiquement toutes les valeurs catégorielles
|
||||
* de l'application (statuts, types, devises,
|
||||
* priorités, etc.) via la table
|
||||
* {@code types_reference}.
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 3.0
|
||||
* @since 2026-02-21
|
||||
*/
|
||||
@Path("/api/v1/types-reference")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Types de référence", description = "Gestion des données de référence"
|
||||
+ " paramétrables")
|
||||
@RolesAllowed({
|
||||
"SUPER_ADMIN",
|
||||
"ADMIN", "MEMBRE", "USER"
|
||||
})
|
||||
public class TypeReferenceResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(TypeReferenceResource.class);
|
||||
|
||||
@Inject
|
||||
TypeReferenceService service;
|
||||
|
||||
/**
|
||||
* Liste les références actives d'un domaine.
|
||||
*
|
||||
* @param domaine le domaine fonctionnel
|
||||
* @param organisationId l'UUID de l'organisation
|
||||
* @return liste triée par ordre d'affichage
|
||||
*/
|
||||
@GET
|
||||
@Operation(summary = "Lister par domaine", description = "Récupère les valeurs actives"
|
||||
+ " d'un domaine donné")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Liste récupérée", content = @Content(mediaType = MediaType.APPLICATION_JSON, schema = @Schema(implementation = TypeReferenceResponse.class))),
|
||||
@APIResponse(responseCode = "401", description = "Non authentifié")
|
||||
})
|
||||
public Response listerParDomaine(
|
||||
@Parameter(description = "Domaine fonctionnel", example = "STATUT_ORGANISATION", required = true) @QueryParam("domaine") String domaine,
|
||||
@Parameter(description = "UUID de l'organisation", example = "550e8400-e29b-41d4-a716-4466"
|
||||
+ "55440000") @QueryParam("organisationId") UUID organisationId) {
|
||||
if (domaine == null || domaine.isBlank()) {
|
||||
return Response
|
||||
.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of(
|
||||
"error",
|
||||
"Le paramètre 'domaine' est"
|
||||
+ " obligatoire"))
|
||||
.build();
|
||||
}
|
||||
List<TypeReferenceResponse> result = service.listerParDomaine(
|
||||
domaine, organisationId);
|
||||
return Response.ok(result).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne un type de référence par son ID.
|
||||
*
|
||||
* @param id l'UUID du type de référence
|
||||
* @return le détail complet
|
||||
*/
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Détail d'une référence", description = "Récupère une référence par"
|
||||
+ " son identifiant")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Référence trouvée"),
|
||||
@APIResponse(responseCode = "404", description = "Référence non trouvée")
|
||||
})
|
||||
public Response trouverParId(
|
||||
@PathParam("id") UUID id) {
|
||||
try {
|
||||
TypeReferenceResponse response = service.trouverParId(id);
|
||||
return Response.ok(response).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Liste les domaines disponibles.
|
||||
*
|
||||
* @return noms de domaines distincts
|
||||
*/
|
||||
@GET
|
||||
@Path("/domaines")
|
||||
@Operation(summary = "Lister les domaines", description = "Récupère la liste des domaines"
|
||||
+ " disponibles")
|
||||
public Response listerDomaines() {
|
||||
List<String> domaines = service.listerDomaines();
|
||||
return Response.ok(domaines).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Retourne la valeur par défaut d'un domaine.
|
||||
*
|
||||
* @param domaine le domaine fonctionnel
|
||||
* @param organisationId l'UUID de l'organisation
|
||||
* @return la valeur par défaut
|
||||
*/
|
||||
@GET
|
||||
@Path("/defaut")
|
||||
@Operation(summary = "Valeur par défaut", description = "Récupère la valeur par défaut"
|
||||
+ " d'un domaine")
|
||||
public Response trouverDefaut(
|
||||
@Parameter(description = "Domaine fonctionnel", required = true) @QueryParam("domaine") String domaine,
|
||||
@Parameter(description = "UUID de l'organisation") @QueryParam("organisationId") UUID organisationId) {
|
||||
if (domaine == null || domaine.isBlank()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", "Le paramètre 'domaine' est obligatoire"))
|
||||
.build();
|
||||
}
|
||||
try {
|
||||
TypeReferenceResponse response = service.trouverDefaut(
|
||||
domaine, organisationId);
|
||||
return Response.ok(response).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response
|
||||
.status(Response.Status.NOT_FOUND)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une nouvelle donnée de référence.
|
||||
*
|
||||
* @param request la requête de création validée
|
||||
* @return la référence créée (HTTP 201)
|
||||
*/
|
||||
@POST
|
||||
@RolesAllowed({
|
||||
"SUPER_ADMIN", "ADMIN"
|
||||
})
|
||||
@Operation(summary = "Créer une référence", description = "Ajoute une nouvelle valeur dans"
|
||||
+ " un domaine")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "201", description = "Référence créée"),
|
||||
@APIResponse(responseCode = "400", description = "Données invalides ou"
|
||||
+ " code dupliqué")
|
||||
})
|
||||
public Response creer(
|
||||
@Valid CreateTypeReferenceRequest request) {
|
||||
try {
|
||||
TypeReferenceResponse created = service.creer(request);
|
||||
return Response
|
||||
.status(Response.Status.CREATED)
|
||||
.entity(created)
|
||||
.build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warnf(
|
||||
"Erreur création référence: %s",
|
||||
e.getMessage());
|
||||
return Response
|
||||
.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour une donnée de référence.
|
||||
*
|
||||
* @param id l'UUID de la référence
|
||||
* @param request la requête de mise à jour
|
||||
* @return la référence mise à jour
|
||||
*/
|
||||
@PUT
|
||||
@Path("/{id}")
|
||||
@RolesAllowed({
|
||||
"SUPER_ADMIN", "ADMIN"
|
||||
})
|
||||
@Operation(summary = "Modifier une référence", description = "Met à jour une valeur"
|
||||
+ " existante")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "200", description = "Référence modifiée"),
|
||||
@APIResponse(responseCode = "400", description = "Données invalides"),
|
||||
@APIResponse(responseCode = "404", description = "Référence non trouvée")
|
||||
})
|
||||
public Response modifier(
|
||||
@PathParam("id") UUID id,
|
||||
@Valid UpdateTypeReferenceRequest request) {
|
||||
try {
|
||||
TypeReferenceResponse updated = service.modifier(id, request);
|
||||
return Response.ok(updated).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warnf(
|
||||
"Erreur modification référence: %s",
|
||||
e.getMessage());
|
||||
return Response
|
||||
.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime une donnée de référence.
|
||||
*
|
||||
* <p>
|
||||
* Les valeurs système ne peuvent pas être
|
||||
* supprimées.
|
||||
*
|
||||
* @param id l'UUID de la référence
|
||||
* @return HTTP 204 si succès
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/{id}")
|
||||
@RolesAllowed({
|
||||
"SUPER_ADMIN"
|
||||
})
|
||||
@Operation(summary = "Supprimer une référence", description = "Supprime une valeur non"
|
||||
+ " système")
|
||||
@APIResponses({
|
||||
@APIResponse(responseCode = "204", description = "Référence supprimée"),
|
||||
@APIResponse(responseCode = "400", description = "Valeur système non"
|
||||
+ " supprimable"),
|
||||
@APIResponse(responseCode = "404", description = "Référence non trouvée")
|
||||
})
|
||||
public Response supprimer(
|
||||
@PathParam("id") UUID id) {
|
||||
try {
|
||||
service.supprimer(id);
|
||||
return Response.noContent().build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
return Response
|
||||
.status(Response.Status.BAD_REQUEST)
|
||||
.entity(Map.of("error", e.getMessage()))
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,191 +1,191 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import dev.lions.unionflow.server.api.dto.mutuelle.epargne.TransactionEpargneRequest;
|
||||
import dev.lions.unionflow.server.api.enums.mutuelle.epargne.TypeTransactionEpargne;
|
||||
import dev.lions.unionflow.server.entity.IntentionPaiement;
|
||||
import dev.lions.unionflow.server.repository.IntentionPaiementRepository;
|
||||
import dev.lions.unionflow.server.service.VersementService;
|
||||
import dev.lions.unionflow.server.service.mutuelle.epargne.TransactionEpargneService;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URI;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Redirection après paiement Wave (spec Checkout API).
|
||||
* Wave redirige le client vers success_url ou error_url (https).
|
||||
* On renvoie une 302 vers le deep link de l'app (unionflow://payment?result=...&ref=...).
|
||||
* En mode mock : GET /success exécute aussi la validation simulée (intention COMPLETEE, cotisations PAYEE)
|
||||
* pour que le flux "Ouvrir Wave" → retour app soit entièrement mocké.
|
||||
*/
|
||||
@Path("/api/wave-redirect")
|
||||
@PermitAll
|
||||
public class WaveRedirectResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(WaveRedirectResource.class);
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
@ConfigProperty(name = "wave.deep.link.scheme", defaultValue = "unionflow")
|
||||
String deepLinkScheme;
|
||||
|
||||
@ConfigProperty(name = "wave.mock.enabled", defaultValue = "false")
|
||||
boolean mockEnabled;
|
||||
|
||||
@Inject
|
||||
IntentionPaiementRepository intentionPaiementRepository;
|
||||
|
||||
@Inject
|
||||
TransactionEpargneService transactionEpargneService;
|
||||
|
||||
@Inject
|
||||
VersementService versementService;
|
||||
|
||||
@GET
|
||||
@Path("/success")
|
||||
@Transactional
|
||||
public Response success(@QueryParam("ref") String ref) {
|
||||
LOG.infof("Wave redirect success (mobile), ref=%s", ref);
|
||||
if (ref != null && !ref.isBlank()) {
|
||||
applyCompletion(ref);
|
||||
}
|
||||
String location = buildDeepLink("success", ref);
|
||||
return Response.seeOther(URI.create(location)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint de redirection Wave pour le flux web QR code.
|
||||
* Appelé par Wave sur le téléphone du membre après paiement confirmé.
|
||||
* Marque la cotisation PAYEE et affiche une page HTML de confirmation.
|
||||
*/
|
||||
@GET
|
||||
@Path("/web-success")
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
@Transactional
|
||||
public Response webSuccess(@QueryParam("ref") String ref) {
|
||||
LOG.infof("Wave redirect web-success, ref=%s", ref);
|
||||
if (ref != null && !ref.isBlank()) {
|
||||
applyCompletion(ref);
|
||||
}
|
||||
String html = """
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Paiement confirmé</title>
|
||||
<style>
|
||||
body{font-family:sans-serif;display:flex;flex-direction:column;align-items:center;
|
||||
justify-content:center;min-height:100vh;margin:0;background:#f0fdf4;}
|
||||
.card{background:#fff;border-radius:16px;padding:2rem 2.5rem;text-align:center;
|
||||
box-shadow:0 4px 24px #0001;max-width:360px;}
|
||||
.icon{font-size:3.5rem;margin-bottom:1rem;}
|
||||
h2{color:#16a34a;margin:.5rem 0;}
|
||||
p{color:#555;margin:.5rem 0;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="icon">✅</div>
|
||||
<h2>Paiement confirmé !</h2>
|
||||
<p>Votre cotisation a été enregistrée avec succès.</p>
|
||||
<p style="font-size:.85rem;color:#888;margin-top:1rem;">
|
||||
Vous pouvez fermer cette page et revenir sur UnionFlow.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
return Response.ok(html).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/error")
|
||||
public Response error(@QueryParam("ref") String ref) {
|
||||
LOG.infof("Wave redirect error, ref=%s", ref);
|
||||
String location = buildDeepLink("error", ref);
|
||||
return Response.seeOther(URI.create(location)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test uniquement (wave.mock.enabled=true) : simule la validation Wave puis redirige.
|
||||
* Appelle la même logique que /success en mock (applyMockCompletion).
|
||||
*/
|
||||
@GET
|
||||
@Path("/mock-complete")
|
||||
@Transactional
|
||||
public Response mockComplete(@QueryParam("ref") String ref) {
|
||||
if (!mockEnabled) {
|
||||
LOG.warn("mock-complete ignoré (wave.mock.enabled=false)");
|
||||
return Response.seeOther(URI.create(buildDeepLink("error", ref))).build();
|
||||
}
|
||||
if (ref == null || ref.isBlank()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST).entity("ref requis").build();
|
||||
}
|
||||
applyCompletion(ref);
|
||||
return Response.seeOther(URI.create(buildDeepLink("success", ref))).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque l'intention comme complétée et réconcilie les cotisations/dépôts liés.
|
||||
* Délègue au PaiementService pour les cotisations ; gère les dépôts épargne localement.
|
||||
*/
|
||||
private void applyCompletion(String ref) {
|
||||
try {
|
||||
UUID intentionId = UUID.fromString(ref.trim());
|
||||
IntentionPaiement intention = intentionPaiementRepository.findById(intentionId);
|
||||
if (intention == null) {
|
||||
LOG.warnf("Intention non trouvée: %s", ref);
|
||||
return;
|
||||
}
|
||||
|
||||
// Gérer les dépôts épargne (non couverts par PaiementService)
|
||||
String objetsCibles = intention.getObjetsCibles();
|
||||
if (objetsCibles != null && !objetsCibles.isBlank()) {
|
||||
JsonNode arr = OBJECT_MAPPER.readTree(objetsCibles);
|
||||
if (arr.isArray()) {
|
||||
for (JsonNode node : arr) {
|
||||
if ("DEPOT_EPARGNE".equals(node.path("type").asText())
|
||||
&& node.has("compteId") && node.has("montant")) {
|
||||
String compteId = node.get("compteId").asText();
|
||||
BigDecimal montant = new BigDecimal(node.get("montant").asText());
|
||||
TransactionEpargneRequest req = TransactionEpargneRequest.builder()
|
||||
.compteId(compteId)
|
||||
.typeTransaction(TypeTransactionEpargne.DEPOT)
|
||||
.montant(montant)
|
||||
.motif("Dépôt via Wave (mobile money)")
|
||||
.build();
|
||||
transactionEpargneService.executerTransaction(req);
|
||||
LOG.infof("Wave: dépôt épargne %s XOF sur compte %s", montant, compteId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Déléguer la confirmation cotisations au service
|
||||
versementService.confirmerVersementWave(intention, null);
|
||||
LOG.infof("Wave: intention %s complétée", ref);
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Wave: erreur applyCompletion ref=%s", ref);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildDeepLink(String result, String ref) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(deepLinkScheme).append("://payment?result=").append(result);
|
||||
if (ref != null && !ref.isBlank()) {
|
||||
sb.append("&ref=").append(ref);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
import com.fasterxml.jackson.databind.JsonNode;
|
||||
import com.fasterxml.jackson.databind.ObjectMapper;
|
||||
import dev.lions.unionflow.server.api.dto.mutuelle.epargne.TransactionEpargneRequest;
|
||||
import dev.lions.unionflow.server.api.enums.mutuelle.epargne.TypeTransactionEpargne;
|
||||
import dev.lions.unionflow.server.entity.IntentionPaiement;
|
||||
import dev.lions.unionflow.server.repository.IntentionPaiementRepository;
|
||||
import dev.lions.unionflow.server.service.VersementService;
|
||||
import dev.lions.unionflow.server.service.mutuelle.epargne.TransactionEpargneService;
|
||||
import jakarta.annotation.security.PermitAll;
|
||||
import jakarta.ws.rs.Produces;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.ws.rs.GET;
|
||||
import jakarta.ws.rs.Path;
|
||||
import jakarta.ws.rs.QueryParam;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.eclipse.microprofile.config.inject.ConfigProperty;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.net.URI;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Redirection après paiement Wave (spec Checkout API).
|
||||
* Wave redirige le client vers success_url ou error_url (https).
|
||||
* On renvoie une 302 vers le deep link de l'app (unionflow://payment?result=...&ref=...).
|
||||
* En mode mock : GET /success exécute aussi la validation simulée (intention COMPLETEE, cotisations PAYEE)
|
||||
* pour que le flux "Ouvrir Wave" → retour app soit entièrement mocké.
|
||||
*/
|
||||
@Path("/api/wave-redirect")
|
||||
@PermitAll
|
||||
public class WaveRedirectResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(WaveRedirectResource.class);
|
||||
private static final ObjectMapper OBJECT_MAPPER = new ObjectMapper();
|
||||
|
||||
@ConfigProperty(name = "wave.deep.link.scheme", defaultValue = "unionflow")
|
||||
String deepLinkScheme;
|
||||
|
||||
@ConfigProperty(name = "wave.mock.enabled", defaultValue = "false")
|
||||
boolean mockEnabled;
|
||||
|
||||
@Inject
|
||||
IntentionPaiementRepository intentionPaiementRepository;
|
||||
|
||||
@Inject
|
||||
TransactionEpargneService transactionEpargneService;
|
||||
|
||||
@Inject
|
||||
VersementService versementService;
|
||||
|
||||
@GET
|
||||
@Path("/success")
|
||||
@Transactional
|
||||
public Response success(@QueryParam("ref") String ref) {
|
||||
LOG.infof("Wave redirect success (mobile), ref=%s", ref);
|
||||
if (ref != null && !ref.isBlank()) {
|
||||
applyCompletion(ref);
|
||||
}
|
||||
String location = buildDeepLink("success", ref);
|
||||
return Response.seeOther(URI.create(location)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint de redirection Wave pour le flux web QR code.
|
||||
* Appelé par Wave sur le téléphone du membre après paiement confirmé.
|
||||
* Marque la cotisation PAYEE et affiche une page HTML de confirmation.
|
||||
*/
|
||||
@GET
|
||||
@Path("/web-success")
|
||||
@Produces(MediaType.TEXT_HTML)
|
||||
@Transactional
|
||||
public Response webSuccess(@QueryParam("ref") String ref) {
|
||||
LOG.infof("Wave redirect web-success, ref=%s", ref);
|
||||
if (ref != null && !ref.isBlank()) {
|
||||
applyCompletion(ref);
|
||||
}
|
||||
String html = """
|
||||
<!DOCTYPE html>
|
||||
<html lang="fr">
|
||||
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width,initial-scale=1">
|
||||
<title>Paiement confirmé</title>
|
||||
<style>
|
||||
body{font-family:sans-serif;display:flex;flex-direction:column;align-items:center;
|
||||
justify-content:center;min-height:100vh;margin:0;background:#f0fdf4;}
|
||||
.card{background:#fff;border-radius:16px;padding:2rem 2.5rem;text-align:center;
|
||||
box-shadow:0 4px 24px #0001;max-width:360px;}
|
||||
.icon{font-size:3.5rem;margin-bottom:1rem;}
|
||||
h2{color:#16a34a;margin:.5rem 0;}
|
||||
p{color:#555;margin:.5rem 0;}
|
||||
</style>
|
||||
</head>
|
||||
<body>
|
||||
<div class="card">
|
||||
<div class="icon">✅</div>
|
||||
<h2>Paiement confirmé !</h2>
|
||||
<p>Votre cotisation a été enregistrée avec succès.</p>
|
||||
<p style="font-size:.85rem;color:#888;margin-top:1rem;">
|
||||
Vous pouvez fermer cette page et revenir sur UnionFlow.
|
||||
</p>
|
||||
</div>
|
||||
</body>
|
||||
</html>
|
||||
""";
|
||||
return Response.ok(html).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/error")
|
||||
public Response error(@QueryParam("ref") String ref) {
|
||||
LOG.infof("Wave redirect error, ref=%s", ref);
|
||||
String location = buildDeepLink("error", ref);
|
||||
return Response.seeOther(URI.create(location)).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Test uniquement (wave.mock.enabled=true) : simule la validation Wave puis redirige.
|
||||
* Appelle la même logique que /success en mock (applyMockCompletion).
|
||||
*/
|
||||
@GET
|
||||
@Path("/mock-complete")
|
||||
@Transactional
|
||||
public Response mockComplete(@QueryParam("ref") String ref) {
|
||||
if (!mockEnabled) {
|
||||
LOG.warn("mock-complete ignoré (wave.mock.enabled=false)");
|
||||
return Response.seeOther(URI.create(buildDeepLink("error", ref))).build();
|
||||
}
|
||||
if (ref == null || ref.isBlank()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST).entity("ref requis").build();
|
||||
}
|
||||
applyCompletion(ref);
|
||||
return Response.seeOther(URI.create(buildDeepLink("success", ref))).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque l'intention comme complétée et réconcilie les cotisations/dépôts liés.
|
||||
* Délègue au PaiementService pour les cotisations ; gère les dépôts épargne localement.
|
||||
*/
|
||||
private void applyCompletion(String ref) {
|
||||
try {
|
||||
UUID intentionId = UUID.fromString(ref.trim());
|
||||
IntentionPaiement intention = intentionPaiementRepository.findById(intentionId);
|
||||
if (intention == null) {
|
||||
LOG.warnf("Intention non trouvée: %s", ref);
|
||||
return;
|
||||
}
|
||||
|
||||
// Gérer les dépôts épargne (non couverts par PaiementService)
|
||||
String objetsCibles = intention.getObjetsCibles();
|
||||
if (objetsCibles != null && !objetsCibles.isBlank()) {
|
||||
JsonNode arr = OBJECT_MAPPER.readTree(objetsCibles);
|
||||
if (arr.isArray()) {
|
||||
for (JsonNode node : arr) {
|
||||
if ("DEPOT_EPARGNE".equals(node.path("type").asText())
|
||||
&& node.has("compteId") && node.has("montant")) {
|
||||
String compteId = node.get("compteId").asText();
|
||||
BigDecimal montant = new BigDecimal(node.get("montant").asText());
|
||||
TransactionEpargneRequest req = TransactionEpargneRequest.builder()
|
||||
.compteId(compteId)
|
||||
.typeTransaction(TypeTransactionEpargne.DEPOT)
|
||||
.montant(montant)
|
||||
.motif("Dépôt via Wave (mobile money)")
|
||||
.build();
|
||||
transactionEpargneService.executerTransaction(req);
|
||||
LOG.infof("Wave: dépôt épargne %s XOF sur compte %s", montant, compteId);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Déléguer la confirmation cotisations au service
|
||||
versementService.confirmerVersementWave(intention, null);
|
||||
LOG.infof("Wave: intention %s complétée", ref);
|
||||
} catch (Exception e) {
|
||||
LOG.errorf(e, "Wave: erreur applyCompletion ref=%s", ref);
|
||||
}
|
||||
}
|
||||
|
||||
private String buildDeepLink(String result, String ref) {
|
||||
StringBuilder sb = new StringBuilder();
|
||||
sb.append(deepLinkScheme).append("://payment?result=").append(result);
|
||||
if (ref != null && !ref.isBlank()) {
|
||||
sb.append("&ref=").append(ref);
|
||||
}
|
||||
return sb.toString();
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,52 +0,0 @@
|
||||
package dev.lions.unionflow.server.resource.mutuelle;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.mutuelle.financier.ParametresFinanciersMutuellRequest;
|
||||
import dev.lions.unionflow.server.api.dto.mutuelle.financier.ParametresFinanciersMutuellResponse;
|
||||
import dev.lions.unionflow.server.security.RequiresModule;
|
||||
import dev.lions.unionflow.server.service.mutuelle.InteretsEpargneService;
|
||||
import dev.lions.unionflow.server.service.mutuelle.ParametresFinanciersService;
|
||||
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.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
@Path("/api/v1/mutuelle/parametres-financiers")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@RequiresModule("EPARGNE")
|
||||
public class ParametresFinanciersResource {
|
||||
|
||||
@Inject ParametresFinanciersService parametresService;
|
||||
@Inject InteretsEpargneService interetsService;
|
||||
|
||||
@GET
|
||||
@Path("/{orgId}")
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP"})
|
||||
public Response getByOrganisation(@PathParam("orgId") UUID orgId) {
|
||||
return Response.ok(parametresService.getByOrganisation(orgId)).build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION"})
|
||||
public Response creerOuMettrAJour(@Valid ParametresFinanciersMutuellRequest request) {
|
||||
ParametresFinanciersMutuellResponse resp = parametresService.creerOuMettrAJour(request);
|
||||
return Response.ok(resp).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Déclenche manuellement le calcul des intérêts / dividendes pour une organisation.
|
||||
* Utile pour régularisation ou test.
|
||||
*/
|
||||
@POST
|
||||
@Path("/{orgId}/calculer-interets")
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION"})
|
||||
public Response calculerInterets(@PathParam("orgId") UUID orgId) {
|
||||
Map<String, Object> result = interetsService.calculerManuellement(orgId);
|
||||
return Response.ok(result).build();
|
||||
}
|
||||
}
|
||||
@@ -1,74 +0,0 @@
|
||||
package dev.lions.unionflow.server.resource.mutuelle;
|
||||
|
||||
import dev.lions.unionflow.server.security.RequiresModule;
|
||||
import dev.lions.unionflow.server.service.mutuelle.ReleveComptePdfService;
|
||||
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 jakarta.ws.rs.core.Response.ResponseBuilder;
|
||||
|
||||
import java.time.LocalDate;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Relevés de compte en PDF.
|
||||
* - GET /api/v1/releves/epargne/{compteId} → relevé épargne
|
||||
* - GET /api/v1/releves/parts-sociales/{compteId} → relevé parts sociales
|
||||
*/
|
||||
@Path("/api/v1/releves")
|
||||
@RequiresModule("EPARGNE")
|
||||
public class ReleveCompteResource {
|
||||
|
||||
@Inject ReleveComptePdfService releveService;
|
||||
|
||||
@GET
|
||||
@Path("/epargne/{compteId}")
|
||||
@Produces("application/pdf")
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP", "MEMBRE", "USER"})
|
||||
public Response releveEpargne(
|
||||
@PathParam("compteId") UUID compteId,
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr) {
|
||||
|
||||
LocalDate dateDebut = parseDate(dateDebutStr);
|
||||
LocalDate dateFin = parseDate(dateFinStr);
|
||||
byte[] pdf = releveService.genererReleveEpargne(compteId, dateDebut, dateFin);
|
||||
|
||||
ResponseBuilder rb = Response.ok(pdf);
|
||||
rb.header("Content-Disposition",
|
||||
"attachment; filename=\"releve-epargne-" + compteId + ".pdf\"");
|
||||
rb.header("Content-Type", MediaType.valueOf("application/pdf"));
|
||||
return rb.build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/parts-sociales/{compteId}")
|
||||
@Produces("application/pdf")
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP", "MEMBRE", "USER"})
|
||||
public Response releveParts(
|
||||
@PathParam("compteId") UUID compteId,
|
||||
@QueryParam("dateDebut") String dateDebutStr,
|
||||
@QueryParam("dateFin") String dateFinStr) {
|
||||
|
||||
LocalDate dateDebut = parseDate(dateDebutStr);
|
||||
LocalDate dateFin = parseDate(dateFinStr);
|
||||
byte[] pdf = releveService.genererReleveParts(compteId, dateDebut, dateFin);
|
||||
|
||||
ResponseBuilder rb = Response.ok(pdf);
|
||||
rb.header("Content-Disposition",
|
||||
"attachment; filename=\"releve-parts-" + compteId + ".pdf\"");
|
||||
rb.header("Content-Type", MediaType.valueOf("application/pdf"));
|
||||
return rb.build();
|
||||
}
|
||||
|
||||
private LocalDate parseDate(String s) {
|
||||
if (s == null || s.isBlank()) return null;
|
||||
try {
|
||||
return LocalDate.parse(s);
|
||||
} catch (Exception e) {
|
||||
throw new IllegalArgumentException("Format de date invalide. Utilisez YYYY-MM-DD. Valeur reçue: " + s);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -11,7 +11,6 @@ import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import io.quarkus.security.identity.SecurityIdentity;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
@@ -25,16 +24,10 @@ public class TransactionEpargneResource {
|
||||
@Inject
|
||||
TransactionEpargneService transactionEpargneService;
|
||||
|
||||
@Inject
|
||||
SecurityIdentity securityIdentity;
|
||||
|
||||
@POST
|
||||
@RolesAllowed({ "ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP", "MEMBRE", "USER" })
|
||||
public Response executerTransaction(
|
||||
@Valid TransactionEpargneRequest request,
|
||||
@QueryParam("historique") @DefaultValue("false") boolean historique) {
|
||||
boolean bypassSolde = historique && securityIdentity.hasRole("SUPER_ADMIN");
|
||||
TransactionEpargneResponse transaction = transactionEpargneService.executerTransaction(request, bypassSolde);
|
||||
public Response executerTransaction(@Valid TransactionEpargneRequest request) {
|
||||
TransactionEpargneResponse transaction = transactionEpargneService.executerTransaction(request);
|
||||
return Response.status(Response.Status.CREATED).entity(transaction).build();
|
||||
}
|
||||
|
||||
|
||||
@@ -1,73 +0,0 @@
|
||||
package dev.lions.unionflow.server.resource.mutuelle.parts;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.mutuelle.parts.ComptePartsSocialesRequest;
|
||||
import dev.lions.unionflow.server.api.dto.mutuelle.parts.ComptePartsSocialesResponse;
|
||||
import dev.lions.unionflow.server.api.dto.mutuelle.parts.TransactionPartsSocialesRequest;
|
||||
import dev.lions.unionflow.server.api.dto.mutuelle.parts.TransactionPartsSocialesResponse;
|
||||
import dev.lions.unionflow.server.security.RequiresModule;
|
||||
import dev.lions.unionflow.server.service.mutuelle.parts.ComptePartsSocialesService;
|
||||
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;
|
||||
|
||||
@Path("/api/v1/parts-sociales/comptes")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@RequiresModule("EPARGNE")
|
||||
public class ComptePartsSocialesResource {
|
||||
|
||||
@Inject
|
||||
ComptePartsSocialesService service;
|
||||
|
||||
@POST
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP"})
|
||||
public Response ouvrirCompte(@Valid ComptePartsSocialesRequest request) {
|
||||
ComptePartsSocialesResponse resp = service.ouvrirCompte(request);
|
||||
return Response.status(Response.Status.CREATED).entity(resp).build();
|
||||
}
|
||||
|
||||
@POST
|
||||
@Path("/transactions")
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP"})
|
||||
public Response enregistrerTransaction(@Valid TransactionPartsSocialesRequest request) {
|
||||
TransactionPartsSocialesResponse resp = service.enregistrerSouscription(request);
|
||||
return Response.status(Response.Status.CREATED).entity(resp).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP", "MEMBRE", "USER"})
|
||||
public Response getById(@PathParam("id") UUID id) {
|
||||
return Response.ok(service.getById(id)).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/membre/{membreId}")
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP", "MEMBRE", "USER"})
|
||||
public Response getByMembre(@PathParam("membreId") UUID membreId) {
|
||||
List<ComptePartsSocialesResponse> list = service.getByMembre(membreId);
|
||||
return Response.ok(list).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/organisation/{orgId}")
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP"})
|
||||
public Response getByOrganisation(@PathParam("orgId") UUID orgId) {
|
||||
List<ComptePartsSocialesResponse> list = service.getByOrganisation(orgId);
|
||||
return Response.ok(list).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}/transactions")
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "MUTUELLE_RESP", "MEMBRE", "USER"})
|
||||
public Response getTransactions(@PathParam("id") UUID id) {
|
||||
List<TransactionPartsSocialesResponse> list = service.getTransactions(id);
|
||||
return Response.ok(list).build();
|
||||
}
|
||||
}
|
||||
@@ -1,61 +1,61 @@
|
||||
package dev.lions.unionflow.server.resource.tontine;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.tontine.TontineRequest;
|
||||
import dev.lions.unionflow.server.api.dto.tontine.TontineResponse;
|
||||
import dev.lions.unionflow.server.api.enums.tontine.StatutTontine;
|
||||
import dev.lions.unionflow.server.service.tontine.TontineService;
|
||||
|
||||
import dev.lions.unionflow.server.security.RequiresModule;
|
||||
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;
|
||||
|
||||
@Path("/api/v1/tontines")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@RequiresModule("TONTINE")
|
||||
public class TontineResource {
|
||||
|
||||
@Inject
|
||||
TontineService tontineService;
|
||||
|
||||
@POST
|
||||
@RolesAllowed({ "ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "TONTINE_RESP" })
|
||||
public Response creerTontine(@Valid TontineRequest request) {
|
||||
TontineResponse response = tontineService.creerTontine(request);
|
||||
return Response.status(Response.Status.CREATED).entity(response).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@RolesAllowed({ "ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "TONTINE_RESP", "MEMBRE", "USER" })
|
||||
public Response getTontineById(@PathParam("id") UUID id) {
|
||||
TontineResponse response = tontineService.getTontineById(id);
|
||||
return Response.ok(response).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/organisation/{organisationId}")
|
||||
@RolesAllowed({ "ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "TONTINE_RESP" })
|
||||
public Response getTontinesByOrganisation(@PathParam("organisationId") UUID organisationId) {
|
||||
List<TontineResponse> response = tontineService.getTontinesByOrganisation(organisationId);
|
||||
return Response.ok(response).build();
|
||||
}
|
||||
|
||||
@PATCH
|
||||
@Path("/{id}/statut")
|
||||
@RolesAllowed({ "ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "TONTINE_RESP" })
|
||||
public Response changerStatut(@PathParam("id") UUID id, @QueryParam("statut") StatutTontine statut) {
|
||||
if (statut == null) {
|
||||
return Response.status(Response.Status.BAD_REQUEST).entity("Le statut est requis").build();
|
||||
}
|
||||
TontineResponse response = tontineService.changerStatut(id, statut);
|
||||
return Response.ok(response).build();
|
||||
}
|
||||
}
|
||||
package dev.lions.unionflow.server.resource.tontine;
|
||||
|
||||
import dev.lions.unionflow.server.api.dto.tontine.TontineRequest;
|
||||
import dev.lions.unionflow.server.api.dto.tontine.TontineResponse;
|
||||
import dev.lions.unionflow.server.api.enums.tontine.StatutTontine;
|
||||
import dev.lions.unionflow.server.service.tontine.TontineService;
|
||||
|
||||
import dev.lions.unionflow.server.security.RequiresModule;
|
||||
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;
|
||||
|
||||
@Path("/api/v1/tontines")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@RequiresModule("TONTINE")
|
||||
public class TontineResource {
|
||||
|
||||
@Inject
|
||||
TontineService tontineService;
|
||||
|
||||
@POST
|
||||
@RolesAllowed({ "ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "TONTINE_RESP" })
|
||||
public Response creerTontine(@Valid TontineRequest request) {
|
||||
TontineResponse response = tontineService.creerTontine(request);
|
||||
return Response.status(Response.Status.CREATED).entity(response).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@RolesAllowed({ "ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "TONTINE_RESP", "MEMBRE", "USER" })
|
||||
public Response getTontineById(@PathParam("id") UUID id) {
|
||||
TontineResponse response = tontineService.getTontineById(id);
|
||||
return Response.ok(response).build();
|
||||
}
|
||||
|
||||
@GET
|
||||
@Path("/organisation/{organisationId}")
|
||||
@RolesAllowed({ "ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "TONTINE_RESP" })
|
||||
public Response getTontinesByOrganisation(@PathParam("organisationId") UUID organisationId) {
|
||||
List<TontineResponse> response = tontineService.getTontinesByOrganisation(organisationId);
|
||||
return Response.ok(response).build();
|
||||
}
|
||||
|
||||
@PATCH
|
||||
@Path("/{id}/statut")
|
||||
@RolesAllowed({ "ADMIN", "SUPER_ADMIN", "ADMIN_ORGANISATION", "TONTINE_RESP" })
|
||||
public Response changerStatut(@PathParam("id") UUID id, @QueryParam("statut") StatutTontine statut) {
|
||||
if (statut == null) {
|
||||
return Response.status(Response.Status.BAD_REQUEST).entity("Le statut est requis").build();
|
||||
}
|
||||
TontineResponse response = tontineService.changerStatut(id, statut);
|
||||
return Response.ok(response).build();
|
||||
}
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user