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:
dahoud
2026-03-15 02:31:35 +00:00
parent a1e30b51fb
commit eb729bdc56
2 changed files with 284 additions and 0 deletions

View File

@@ -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();
}
}