feat(backend): Phase 3 - Service et Resource LCB-FT (Spec 001)
Implémentation backend pour conformité anti-blanchiment : 1. ParametresLcbFtService - getParametres() : récupération params par org/devise - getSeuilJustification() : seuil rapide (avec cache) - saveOrUpdateParametres() : CRUD admin - Cache Quarkus pour performance - Fallback 500k XOF par défaut 2. ParametresLcbFtResource - GET /api/parametres-lcb-ft : params complets (@PermitAll) - GET /api/parametres-lcb-ft/seuil-justification : seuil léger - POST /api/parametres-lcb-ft : CRUD admin (@RolesAllowed) - OpenAPI/Swagger documentation complète 3. Validation existante confirmée - TransactionEpargneService.validerLcbFtSiSeuilAtteint() - Audit LCB-FT via AuditService.logLcbFtSeuilAtteint() Phase 3 : 67% complété (4/6 tâches, 2 optionnelles skip) - T012 ✅ Service paramètres - T013 ✅ Validation seuils (existante) - T014 ✅ Audit opérations (existant) - T017 ✅ Endpoint REST mobile - T015 ⏩ Optionnel (KYC crédit) - T016 ⏩ Optionnel (alertes) Spec : specs/001-mutuelles-anti-blanchiment/spec.md Branche : 001-mutuelles-anti-blanchiment Signed-off-by: lions dev Team
This commit is contained in:
@@ -0,0 +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
|
||||
) {}
|
||||
}
|
||||
@@ -0,0 +1,154 @@
|
||||
package dev.lions.unionflow.server.service;
|
||||
|
||||
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.entity.Organisation;
|
||||
import dev.lions.unionflow.server.entity.ParametresLcbFt;
|
||||
import dev.lions.unionflow.server.repository.OrganisationRepository;
|
||||
import dev.lions.unionflow.server.repository.ParametresLcbFtRepository;
|
||||
import io.quarkus.cache.CacheResult;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.ws.rs.NotFoundException;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.math.BigDecimal;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Service métier pour la gestion des paramètres LCB-FT (seuils anti-blanchiment).
|
||||
*
|
||||
* @author lions dev Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-13
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class ParametresLcbFtService {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(ParametresLcbFtService.java);
|
||||
private static final String CODE_DEVISE_DEFAULT = "XOF";
|
||||
|
||||
@Inject
|
||||
ParametresLcbFtRepository parametresRepository;
|
||||
|
||||
@Inject
|
||||
OrganisationRepository organisationRepository;
|
||||
|
||||
/**
|
||||
* Récupère les paramètres LCB-FT pour une organisation et une devise.
|
||||
* Avec cache pour performance (les seuils changent rarement).
|
||||
*
|
||||
* @param organisationId ID de l'organisation (null pour paramètres plateforme)
|
||||
* @param codeDevise Code devise ISO 4217 (XOF par défaut)
|
||||
* @return Paramètres LCB-FT ou paramètres plateforme par défaut
|
||||
*/
|
||||
@CacheResult(cacheName = "parametres-lcb-ft")
|
||||
public ParametresLcbFtResponse getParametres(UUID organisationId, String codeDevise) {
|
||||
if (codeDevise == null || codeDevise.isBlank()) {
|
||||
codeDevise = CODE_DEVISE_DEFAULT;
|
||||
}
|
||||
|
||||
LOG.infof("Récupération paramètres LCB-FT pour organisation=%s, devise=%s",
|
||||
organisationId, codeDevise);
|
||||
|
||||
Optional<ParametresLcbFt> params = parametresRepository
|
||||
.findByOrganisationAndDevise(organisationId, codeDevise);
|
||||
|
||||
if (params.isEmpty()) {
|
||||
LOG.warnf("Aucun paramètre LCB-FT trouvé pour organisation=%s, devise=%s",
|
||||
organisationId, codeDevise);
|
||||
throw new NotFoundException(
|
||||
"Paramètres LCB-FT non configurés pour cette organisation/devise");
|
||||
}
|
||||
|
||||
return toDTO(params.get());
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère uniquement le seuil de justification (pour validation rapide).
|
||||
*
|
||||
* @param organisationId ID de l'organisation
|
||||
* @param codeDevise Code devise (XOF par défaut)
|
||||
* @return Montant seuil au-dessus duquel origine des fonds est obligatoire
|
||||
*/
|
||||
@CacheResult(cacheName = "seuil-justification-lcb-ft")
|
||||
public BigDecimal getSeuilJustification(UUID organisationId, String codeDevise) {
|
||||
if (codeDevise == null || codeDevise.isBlank()) {
|
||||
codeDevise = CODE_DEVISE_DEFAULT;
|
||||
}
|
||||
|
||||
LOG.debugf("Récupération seuil justification LCB-FT org=%s, devise=%s",
|
||||
organisationId, codeDevise);
|
||||
|
||||
return parametresRepository.getSeuilJustification(organisationId, codeDevise)
|
||||
.orElse(BigDecimal.valueOf(500000)); // Fallback 500k XOF
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée ou met à jour les paramètres LCB-FT pour une organisation.
|
||||
*/
|
||||
@Transactional
|
||||
public ParametresLcbFtResponse saveOrUpdateParametres(ParametresLcbFtRequest request) {
|
||||
LOG.infof("Sauvegarde paramètres LCB-FT pour organisation=%s",
|
||||
request.getOrganisationId());
|
||||
|
||||
Organisation organisation = null;
|
||||
if (request.getOrganisationId() != null) {
|
||||
organisation = organisationRepository.findByIdOptional(
|
||||
UUID.fromString(request.getOrganisationId()))
|
||||
.orElseThrow(() -> new NotFoundException(
|
||||
"Organisation non trouvée: " + request.getOrganisationId()));
|
||||
}
|
||||
|
||||
// Chercher paramètre existant
|
||||
String codeDevise = request.getCodeDevise() != null ?
|
||||
request.getCodeDevise() : CODE_DEVISE_DEFAULT;
|
||||
|
||||
UUID orgId = organisation != null ? organisation.getId() : null;
|
||||
Optional<ParametresLcbFt> existing = parametresRepository
|
||||
.findByOrganisationAndDevise(orgId, codeDevise);
|
||||
|
||||
ParametresLcbFt params;
|
||||
if (existing.isPresent()) {
|
||||
// Mise à jour
|
||||
params = existing.get();
|
||||
params.setMontantSeuilJustification(request.getMontantSeuilJustification());
|
||||
params.setMontantSeuilValidationManuelle(request.getMontantSeuilValidationManuelle());
|
||||
LOG.infof("Paramètres LCB-FT mis à jour : %s", params.getId());
|
||||
} else {
|
||||
// Création
|
||||
params = ParametresLcbFt.builder()
|
||||
.organisation(organisation)
|
||||
.codeDevise(codeDevise)
|
||||
.montantSeuilJustification(request.getMontantSeuilJustification())
|
||||
.montantSeuilValidationManuelle(request.getMontantSeuilValidationManuelle())
|
||||
.build();
|
||||
parametresRepository.persist(params);
|
||||
LOG.infof("Nouveaux paramètres LCB-FT créés : %s", params.getId());
|
||||
}
|
||||
|
||||
return toDTO(params);
|
||||
}
|
||||
|
||||
/**
|
||||
* Convertit l'entité en DTO de réponse.
|
||||
*/
|
||||
private ParametresLcbFtResponse toDTO(ParametresLcbFt params) {
|
||||
return ParametresLcbFtResponse.builder()
|
||||
.id(params.getId().toString())
|
||||
.organisationId(params.getOrganisation() != null ?
|
||||
params.getOrganisation().getId().toString() : null)
|
||||
.organisationNom(params.getOrganisation() != null ?
|
||||
params.getOrganisation().getNom() : null)
|
||||
.montantSeuilJustification(params.getMontantSeuilJustification())
|
||||
.montantSeuilValidationManuelle(params.getMontantSeuilValidationManuelle())
|
||||
.codeDevise(params.getCodeDevise())
|
||||
.estParametrePlateforme(params.getOrganisation() == null)
|
||||
.dateCreation(params.getDateCreation())
|
||||
.dateModification(params.getDateModification())
|
||||
.actif(params.getActif())
|
||||
.build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user