Architecture modifiée pour Frontend-Centric Authentication: 1. **Suppression des dépendances OIDC** - quarkus-oidc → quarkus-smallrye-jwt - quarkus-keycloak-authorization → quarkus-smallrye-jwt-build - Le backend ne gère plus l'authentification OAuth 2. **Configuration JWT simple** - Validation des tokens JWT envoyés par le frontend - mp.jwt.verify.publickey.location (JWKS de Keycloak) - mp.jwt.verify.issuer (Keycloak realm) - Authentification via Authorization: Bearer header 3. **Suppression configurations OIDC** - application.properties: Suppression %dev.quarkus.oidc.* - application.properties: Suppression %prod.quarkus.oidc.* - application-prod.properties: Remplacement par mp.jwt.* - Logging: io.quarkus.oidc → io.quarkus.smallrye.jwt 4. **Sécurité simplifiée** - quarkus.security.auth.proactive=false - @Authenticated sur les endpoints - CORS configuré pour le frontend - Endpoints publics: /q/*, /openapi, /swagger-ui/* Flux d'authentification: 1️⃣ Frontend → Keycloak (OAuth login) 2️⃣ Frontend ← Keycloak (access_token) 3️⃣ Frontend → Backend (Authorization: Bearer token) 4️⃣ Backend valide le token JWT (signature + issuer) 5️⃣ Backend → Frontend (données API) Avantages: ✅ Pas de secret backend à gérer ✅ Pas de client btpxpress-backend dans Keycloak ✅ Séparation claire frontend/backend ✅ Backend devient une API REST stateless ✅ Tokens gérés par le frontend (localStorage/sessionStorage) 🤖 Generated with [Claude Code](https://claude.com/claude-code) Co-Authored-By: Claude <noreply@anthropic.com>
520 lines
19 KiB
Java
520 lines
19 KiB
Java
package dev.lions.btpxpress.adapter.http;
|
|
|
|
import dev.lions.btpxpress.application.service.ComparaisonFournisseurService;
|
|
import dev.lions.btpxpress.domain.core.entity.*;
|
|
import jakarta.inject.Inject;
|
|
import jakarta.validation.Valid;
|
|
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.time.LocalDate;
|
|
import java.util.Arrays;
|
|
import java.util.HashMap;
|
|
import java.util.List;
|
|
import java.util.Map;
|
|
import java.util.UUID;
|
|
import java.util.stream.Collectors;
|
|
import org.slf4j.Logger;
|
|
import org.slf4j.LoggerFactory;
|
|
|
|
/**
|
|
* API REST pour la comparaison des fournisseurs EXPOSITION: Endpoints pour l'aide à la décision et
|
|
* l'optimisation des achats BTP
|
|
*/
|
|
@Path("/api/v1/comparaisons-fournisseurs")
|
|
@Produces(MediaType.APPLICATION_JSON)
|
|
@Consumes(MediaType.APPLICATION_JSON)
|
|
public class ComparaisonFournisseurResource {
|
|
|
|
private static final Logger logger =
|
|
LoggerFactory.getLogger(ComparaisonFournisseurResource.class);
|
|
|
|
@Inject ComparaisonFournisseurService comparaisonService;
|
|
|
|
// === ENDPOINTS DE CONSULTATION ===
|
|
|
|
@GET
|
|
@Path("/")
|
|
public Response findAll(
|
|
@QueryParam("page") @DefaultValue("0") int page,
|
|
@QueryParam("size") @DefaultValue("50") int size) {
|
|
try {
|
|
logger.debug("GET /api/comparaisons-fournisseurs/ - page: {}, size: {}", page, size);
|
|
|
|
List<ComparaisonFournisseur> comparaisons;
|
|
if (page > 0 || size < 1000) {
|
|
comparaisons = comparaisonService.findAll(page, size);
|
|
} else {
|
|
comparaisons = comparaisonService.findAll();
|
|
}
|
|
|
|
return Response.ok(comparaisons).build();
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors de la récupération des comparaisons", e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de la récupération des comparaisons: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/{id}")
|
|
public Response findById(@PathParam("id") UUID id) {
|
|
try {
|
|
logger.debug("GET /api/comparaisons-fournisseurs/{}", id);
|
|
|
|
ComparaisonFournisseur comparaison = comparaisonService.findByIdRequired(id);
|
|
return Response.ok(comparaison).build();
|
|
|
|
} catch (NotFoundException e) {
|
|
return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build();
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors de la récupération de la comparaison: " + id, e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de la récupération de la comparaison: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/materiel/{materielId}")
|
|
public Response findByMateriel(@PathParam("materielId") UUID materielId) {
|
|
try {
|
|
logger.debug("GET /api/comparaisons-fournisseurs/materiel/{}", materielId);
|
|
|
|
List<ComparaisonFournisseur> comparaisons = comparaisonService.findByMateriel(materielId);
|
|
return Response.ok(comparaisons).build();
|
|
|
|
} catch (Exception e) {
|
|
logger.error(
|
|
"Erreur lors de la récupération des comparaisons pour matériel: " + materielId, e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de la récupération des comparaisons: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/fournisseur/{fournisseurId}")
|
|
public Response findByFournisseur(@PathParam("fournisseurId") UUID fournisseurId) {
|
|
try {
|
|
logger.debug("GET /api/comparaisons-fournisseurs/fournisseur/{}", fournisseurId);
|
|
|
|
List<ComparaisonFournisseur> comparaisons =
|
|
comparaisonService.findByFournisseur(fournisseurId);
|
|
return Response.ok(comparaisons).build();
|
|
|
|
} catch (Exception e) {
|
|
logger.error(
|
|
"Erreur lors de la récupération des comparaisons pour fournisseur: " + fournisseurId, e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de la récupération des comparaisons: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/session/{sessionId}")
|
|
public Response findBySession(@PathParam("sessionId") String sessionId) {
|
|
try {
|
|
logger.debug("GET /api/comparaisons-fournisseurs/session/{}", sessionId);
|
|
|
|
List<ComparaisonFournisseur> comparaisons = comparaisonService.findBySession(sessionId);
|
|
return Response.ok(comparaisons).build();
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors de la récupération des comparaisons pour session: " + sessionId, e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de la récupération des comparaisons: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/search")
|
|
public Response search(@QueryParam("terme") String terme) {
|
|
try {
|
|
logger.debug("GET /api/comparaisons-fournisseurs/search?terme={}", terme);
|
|
|
|
List<ComparaisonFournisseur> resultats = comparaisonService.search(terme);
|
|
return Response.ok(resultats).build();
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors de la recherche avec terme: " + terme, e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de la recherche: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
// === ENDPOINTS MÉTIER SPÉCIALISÉS ===
|
|
|
|
@GET
|
|
@Path("/meilleures-offres/{materielId}")
|
|
public Response findMeilleuresOffres(
|
|
@PathParam("materielId") UUID materielId,
|
|
@QueryParam("limite") @DefaultValue("5") int limite) {
|
|
try {
|
|
logger.debug("GET /api/comparaisons-fournisseurs/meilleures-offres/{}", materielId);
|
|
|
|
List<ComparaisonFournisseur> meilleures =
|
|
comparaisonService.findMeilleuresOffres(materielId, limite);
|
|
return Response.ok(meilleures).build();
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors de la récupération des meilleures offres: " + materielId, e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de la récupération des meilleures offres: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/recommandees")
|
|
public Response findOffresRecommandees() {
|
|
try {
|
|
logger.debug("GET /api/comparaisons-fournisseurs/recommandees");
|
|
|
|
List<ComparaisonFournisseur> recommandees = comparaisonService.findOffresRecommandees();
|
|
return Response.ok(recommandees).build();
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors de la récupération des offres recommandées", e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de la récupération des offres recommandées: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/gamme-prix")
|
|
public Response findByGammePrix(
|
|
@QueryParam("prixMin") @NotNull BigDecimal prixMin,
|
|
@QueryParam("prixMax") @NotNull BigDecimal prixMax) {
|
|
try {
|
|
logger.debug(
|
|
"GET /api/comparaisons-fournisseurs/gamme-prix?prixMin={}&prixMax={}", prixMin, prixMax);
|
|
|
|
List<ComparaisonFournisseur> comparaisons =
|
|
comparaisonService.findByGammePrix(prixMin, prixMax);
|
|
return Response.ok(comparaisons).build();
|
|
|
|
} catch (BadRequestException e) {
|
|
return Response.status(Response.Status.BAD_REQUEST).entity(e.getMessage()).build();
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors de la recherche par gamme de prix", e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de la recherche par gamme de prix: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/disponibles-delai")
|
|
public Response findDisponiblesDansDelai(
|
|
@QueryParam("maxJours") @DefaultValue("30") int maxJours) {
|
|
try {
|
|
logger.debug("GET /api/comparaisons-fournisseurs/disponibles-delai?maxJours={}", maxJours);
|
|
|
|
List<ComparaisonFournisseur> disponibles =
|
|
comparaisonService.findDisponiblesDansDelai(maxJours);
|
|
return Response.ok(disponibles).build();
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors de la recherche par délai", e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de la recherche par délai: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
// === ENDPOINTS DE CRÉATION ET GESTION ===
|
|
|
|
@POST
|
|
@Path("/lancer-comparaison")
|
|
public Response lancerComparaison(@Valid LancerComparaisonRequest request) {
|
|
try {
|
|
logger.info("POST /api/comparaisons-fournisseurs/lancer-comparaison");
|
|
|
|
String sessionId =
|
|
comparaisonService.lancerComparaison(
|
|
request.materielId,
|
|
request.quantiteDemandee,
|
|
request.uniteDemandee,
|
|
request.dateDebutSouhaitee,
|
|
request.dateFinSouhaitee,
|
|
request.lieuLivraison,
|
|
request.evaluateur);
|
|
|
|
return Response.status(Response.Status.CREATED)
|
|
.entity(Map.of("sessionId", sessionId))
|
|
.build();
|
|
|
|
} catch (NotFoundException e) {
|
|
return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build();
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors du lancement de la comparaison", e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors du lancement de la comparaison: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@PUT
|
|
@Path("/{id}")
|
|
public Response updateComparaison(
|
|
@PathParam("id") UUID id, @Valid UpdateComparaisonRequest request) {
|
|
try {
|
|
logger.info("PUT /api/comparaisons-fournisseurs/{}", id);
|
|
|
|
ComparaisonFournisseurService.ComparaisonUpdateRequest updateRequest =
|
|
mapToServiceRequest(request);
|
|
|
|
ComparaisonFournisseur comparaison = comparaisonService.updateComparaison(id, updateRequest);
|
|
return Response.ok(comparaison).build();
|
|
|
|
} catch (NotFoundException e) {
|
|
return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build();
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors de la mise à jour de la comparaison: " + id, e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de la mise à jour de la comparaison: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@PUT
|
|
@Path("/{id}/calculer-scores")
|
|
public Response calculerScores(@PathParam("id") UUID id, @Valid CalculerScoresRequest request) {
|
|
try {
|
|
logger.info("PUT /api/comparaisons-fournisseurs/{}/calculer-scores", id);
|
|
|
|
ComparaisonFournisseur comparaison = comparaisonService.findByIdRequired(id);
|
|
comparaisonService.calculerScores(comparaison, request.poidsCriteres);
|
|
|
|
return Response.ok(comparaison).build();
|
|
|
|
} catch (NotFoundException e) {
|
|
return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build();
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors du calcul des scores: " + id, e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors du calcul des scores: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@PUT
|
|
@Path("/session/{sessionId}/classer")
|
|
public Response classerComparaisons(@PathParam("sessionId") String sessionId) {
|
|
try {
|
|
logger.info("PUT /api/comparaisons-fournisseurs/session/{}/classer", sessionId);
|
|
|
|
comparaisonService.classerComparaisons(sessionId);
|
|
|
|
List<ComparaisonFournisseur> comparaisons = comparaisonService.findBySession(sessionId);
|
|
return Response.ok(
|
|
Map.of("message", "Classement effectué avec succès", "comparaisons", comparaisons))
|
|
.build();
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors du classement des comparaisons: " + sessionId, e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors du classement des comparaisons: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
// === ENDPOINTS D'ANALYSE ET RAPPORTS ===
|
|
|
|
@GET
|
|
@Path("/statistiques")
|
|
public Response getStatistiques() {
|
|
try {
|
|
logger.debug("GET /api/comparaisons-fournisseurs/statistiques");
|
|
|
|
Map<String, Object> statistiques = comparaisonService.getStatistiques();
|
|
return Response.ok(statistiques).build();
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors de la génération des statistiques", e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de la génération des statistiques: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/evolution-prix/{materielId}")
|
|
public Response analyserEvolutionPrix(
|
|
@PathParam("materielId") UUID materielId,
|
|
@QueryParam("dateDebut") @NotNull LocalDate dateDebut,
|
|
@QueryParam("dateFin") @NotNull LocalDate dateFin) {
|
|
try {
|
|
logger.debug("GET /api/comparaisons-fournisseurs/evolution-prix/{}", materielId);
|
|
|
|
List<Object> evolution =
|
|
comparaisonService.analyserEvolutionPrix(materielId, dateDebut, dateFin);
|
|
return Response.ok(evolution).build();
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors de l'analyse d'évolution des prix: " + materielId, e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de l'analyse d'évolution des prix: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/delais-fournisseurs")
|
|
public Response analyserDelaisFournisseurs() {
|
|
try {
|
|
logger.debug("GET /api/comparaisons-fournisseurs/delais-fournisseurs");
|
|
|
|
List<Object> delais = comparaisonService.analyserDelaisFournisseurs();
|
|
return Response.ok(delais).build();
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors de l'analyse des délais fournisseurs", e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de l'analyse des délais fournisseurs: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
@GET
|
|
@Path("/rapport/{sessionId}")
|
|
public Response genererRapportComparaison(@PathParam("sessionId") String sessionId) {
|
|
try {
|
|
logger.debug("GET /api/comparaisons-fournisseurs/rapport/{}", sessionId);
|
|
|
|
Map<String, Object> rapport = comparaisonService.genererRapportComparaison(sessionId);
|
|
return Response.ok(rapport).build();
|
|
|
|
} catch (NotFoundException e) {
|
|
return Response.status(Response.Status.NOT_FOUND).entity(e.getMessage()).build();
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors de la génération du rapport: " + sessionId, e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de la génération du rapport: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
// === ENDPOINTS UTILITAIRES ===
|
|
|
|
@GET
|
|
@Path("/criteres-comparaison")
|
|
public Response getCriteresComparaison() {
|
|
try {
|
|
CritereComparaison[] criteres = CritereComparaison.values();
|
|
|
|
List<Map<String, Object>> criteresInfo =
|
|
Arrays.stream(criteres)
|
|
.map(
|
|
critere -> {
|
|
Map<String, Object> map = new HashMap<>();
|
|
map.put("code", critere.name());
|
|
map.put("libelle", critere.getLibelle());
|
|
map.put("description", critere.getDescription());
|
|
map.put("poidsDefaut", critere.getPoidsDefaut());
|
|
map.put("uniteMesure", critere.getUniteMesure());
|
|
map.put("icone", critere.getIcone());
|
|
map.put("couleur", critere.getCouleur());
|
|
return map;
|
|
})
|
|
.collect(Collectors.toList());
|
|
|
|
return Response.ok(criteresInfo).build();
|
|
|
|
} catch (Exception e) {
|
|
logger.error("Erreur lors de la récupération des critères", e);
|
|
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
|
.entity("Erreur lors de la récupération des critères: " + e.getMessage())
|
|
.build();
|
|
}
|
|
}
|
|
|
|
// === MÉTHODES UTILITAIRES ===
|
|
|
|
private ComparaisonFournisseurService.ComparaisonUpdateRequest mapToServiceRequest(
|
|
UpdateComparaisonRequest request) {
|
|
ComparaisonFournisseurService.ComparaisonUpdateRequest serviceRequest =
|
|
new ComparaisonFournisseurService.ComparaisonUpdateRequest();
|
|
|
|
serviceRequest.disponible = request.disponible;
|
|
serviceRequest.quantiteDisponible = request.quantiteDisponible;
|
|
serviceRequest.dateDisponibilite = request.dateDisponibilite;
|
|
serviceRequest.delaiLivraisonJours = request.delaiLivraisonJours;
|
|
serviceRequest.prixUnitaireHT = request.prixUnitaireHT;
|
|
serviceRequest.fraisLivraison = request.fraisLivraison;
|
|
serviceRequest.fraisInstallation = request.fraisInstallation;
|
|
serviceRequest.fraisMaintenance = request.fraisMaintenance;
|
|
serviceRequest.cautionDemandee = request.cautionDemandee;
|
|
serviceRequest.remiseAppliquee = request.remiseAppliquee;
|
|
serviceRequest.dureeValiditeOffre = request.dureeValiditeOffre;
|
|
serviceRequest.delaiPaiement = request.delaiPaiement;
|
|
serviceRequest.garantieMois = request.garantieMois;
|
|
serviceRequest.maintenanceIncluse = request.maintenanceIncluse;
|
|
serviceRequest.formationIncluse = request.formationIncluse;
|
|
serviceRequest.noteQualite = request.noteQualite;
|
|
serviceRequest.noteFiabilite = request.noteFiabilite;
|
|
serviceRequest.distanceKm = request.distanceKm;
|
|
serviceRequest.conditionsParticulieres = request.conditionsParticulieres;
|
|
serviceRequest.avantages = request.avantages;
|
|
serviceRequest.inconvenients = request.inconvenients;
|
|
serviceRequest.commentairesEvaluateur = request.commentairesEvaluateur;
|
|
serviceRequest.recommandations = request.recommandations;
|
|
serviceRequest.poidsCriteres = request.poidsCriteres;
|
|
|
|
return serviceRequest;
|
|
}
|
|
|
|
// === CLASSES DE REQUÊTE ===
|
|
|
|
public static class LancerComparaisonRequest {
|
|
@NotNull public UUID materielId;
|
|
|
|
@NotNull public BigDecimal quantiteDemandee;
|
|
|
|
public String uniteDemandee;
|
|
public LocalDate dateDebutSouhaitee;
|
|
public LocalDate dateFinSouhaitee;
|
|
public String lieuLivraison;
|
|
public String evaluateur;
|
|
}
|
|
|
|
public static class UpdateComparaisonRequest {
|
|
public Boolean disponible;
|
|
public BigDecimal quantiteDisponible;
|
|
public LocalDate dateDisponibilite;
|
|
public Integer delaiLivraisonJours;
|
|
public BigDecimal prixUnitaireHT;
|
|
public BigDecimal fraisLivraison;
|
|
public BigDecimal fraisInstallation;
|
|
public BigDecimal fraisMaintenance;
|
|
public BigDecimal cautionDemandee;
|
|
public BigDecimal remiseAppliquee;
|
|
public Integer dureeValiditeOffre;
|
|
public Integer delaiPaiement;
|
|
public Integer garantieMois;
|
|
public Boolean maintenanceIncluse;
|
|
public Boolean formationIncluse;
|
|
public BigDecimal noteQualite;
|
|
public BigDecimal noteFiabilite;
|
|
public BigDecimal distanceKm;
|
|
public String conditionsParticulieres;
|
|
public String avantages;
|
|
public String inconvenients;
|
|
public String commentairesEvaluateur;
|
|
public String recommandations;
|
|
public Map<CritereComparaison, Integer> poidsCriteres;
|
|
}
|
|
|
|
public static class CalculerScoresRequest {
|
|
public Map<CritereComparaison, Integer> poidsCriteres;
|
|
}
|
|
}
|