package dev.lions.audit; import jakarta.enterprise.context.ApplicationScoped; import jakarta.inject.Inject; import jakarta.persistence.EntityManager; import jakarta.transaction.Transactional; import java.util.*; import java.util.stream.Collectors; /** * Service de gestion des audits de maturité digitale */ @ApplicationScoped public class AuditService { @Inject EntityManager em; /** * Récupère toutes les questions d'audit actives par catégorie */ public Map> getQuestionsByCategory() { List questions = em.createQuery( "SELECT q FROM AuditQuestion q WHERE q.active = true ORDER BY q.displayOrder", AuditQuestion.class ).getResultList(); return questions.stream() .collect(Collectors.groupingBy(AuditQuestion::getCategory)); } /** * Calcule le score d'un audit et génère les recommandations */ @Transactional public AuditResponse processAuditResponse(AuditResponse response) { Map> questionsByCategory = getQuestionsByCategory(); Map categoryScores = new HashMap<>(); Map categoryMaxScores = new HashMap<>(); int totalScore = 0; int maxPossibleScore = 0; // Calcul des scores par catégorie for (Map.Entry> entry : questionsByCategory.entrySet()) { String category = entry.getKey(); List questions = entry.getValue(); int categoryScore = 0; int categoryMaxScore = 0; for (AuditQuestion question : questions) { Integer answerIndex = response.getAnswers().get(question.getId()); if (answerIndex != null && answerIndex < question.getScores().size()) { int questionScore = question.getScores().get(answerIndex) * question.getWeight(); categoryScore += questionScore; totalScore += questionScore; } int maxQuestionScore = Collections.max(question.getScores()) * question.getWeight(); categoryMaxScore += maxQuestionScore; maxPossibleScore += maxQuestionScore; } categoryScores.put(category, categoryScore); categoryMaxScores.put(category, categoryMaxScore); } // Mise à jour des scores response.setTotalScore(totalScore); response.setMaxPossibleScore(maxPossibleScore); response.setMaturityPercentage((double) totalScore / maxPossibleScore * 100); response.setCategoryScores(categoryScores); // Génération des recommandations generateRecommendations(response, categoryScores, categoryMaxScores); // Estimation budgétaire estimateBudget(response, categoryScores); // Sauvegarde em.persist(response); return response; } /** * Génère les recommandations personnalisées */ private void generateRecommendations(AuditResponse response, Map categoryScores, Map categoryMaxScores) { StringBuilder recommendations = new StringBuilder(); List priorities = new ArrayList<>(); // Analyse par catégorie for (Map.Entry entry : categoryScores.entrySet()) { String category = entry.getKey(); int score = entry.getValue(); int maxScore = categoryMaxScores.get(category); double percentage = (double) score / maxScore * 100; String categoryName = getCategoryDisplayName(category); if (percentage < 30) { recommendations.append("🚨 ").append(categoryName).append(" : Niveau critique - Digitalisation urgente nécessaire\n"); priorities.add("Digitaliser " + categoryName.toLowerCase()); } else if (percentage < 60) { recommendations.append("⚠️ ").append(categoryName).append(" : Niveau faible - Améliorations importantes recommandées\n"); priorities.add("Améliorer " + categoryName.toLowerCase()); } else if (percentage < 80) { recommendations.append("✅ ").append(categoryName).append(" : Niveau correct - Optimisations possibles\n"); } else { recommendations.append("🏆 ").append(categoryName).append(" : Excellent niveau de digitalisation\n"); } } response.setRecommendations(recommendations.toString()); response.setPriorityActions(String.join(", ", priorities)); } /** * Estime le budget nécessaire selon les scores */ private void estimateBudget(AuditResponse response, Map categoryScores) { double budgetMin = 0; double budgetMax = 0; // Tarification par module selon le niveau de maturité Map modulePricing = Map.of( "commercial", new Double[]{150000.0, 300000.0}, // CRM "stock", new Double[]{100000.0, 250000.0}, // Gestion stocks "comptabilite", new Double[]{200000.0, 400000.0}, // Comptabilité "rh", new Double[]{80000.0, 200000.0}, // RH "infrastructure", new Double[]{100000.0, 300000.0} // IT ); for (Map.Entry entry : categoryScores.entrySet()) { String category = entry.getKey(); int score = entry.getValue(); if (score < 50 && modulePricing.containsKey(category)) { // Besoin d'amélioration Double[] pricing = modulePricing.get(category); budgetMin += pricing[0]; budgetMax += pricing[1]; } } response.setEstimatedBudgetMin(budgetMin); response.setEstimatedBudgetMax(budgetMax); } /** * Récupère les audits non contactés pour le suivi commercial */ public List getUncontactedAudits() { return em.createQuery( "SELECT a FROM AuditResponse a WHERE a.contacted = false ORDER BY a.submittedAt DESC", AuditResponse.class ).getResultList(); } /** * Marque un audit comme contacté */ @Transactional public void markAsContacted(Long auditId, String notes) { AuditResponse audit = em.find(AuditResponse.class, auditId); if (audit != null) { audit.setContacted(true); audit.setContactedAt(java.time.LocalDateTime.now()); audit.setSalesNotes(notes); } } /** * Récupère un audit par ID */ public AuditResponse getAuditById(Long auditId) { return em.find(AuditResponse.class, auditId); } /** * Récupère les statistiques d'audit */ public Map getAuditStatistics() { Map stats = new HashMap<>(); // Nombre total d'audits Long totalAudits = em.createQuery("SELECT COUNT(a) FROM AuditResponse a", Long.class) .getSingleResult(); stats.put("totalAudits", totalAudits); // Audits cette semaine Long weeklyAudits = em.createQuery( "SELECT COUNT(a) FROM AuditResponse a WHERE a.submittedAt >= :weekStart", Long.class) .setParameter("weekStart", java.time.LocalDateTime.now().minusDays(7)) .getSingleResult(); stats.put("weeklyAudits", weeklyAudits); // Score moyen Double avgScore = em.createQuery( "SELECT AVG(a.maturityPercentage) FROM AuditResponse a", Double.class) .getSingleResult(); stats.put("averageScore", avgScore != null ? avgScore : 0.0); // Répartition par secteur List sectorStats = em.createQuery( "SELECT a.sector, COUNT(a) FROM AuditResponse a GROUP BY a.sector", Object[].class) .getResultList(); Map sectorDistribution = new HashMap<>(); for (Object[] row : sectorStats) { sectorDistribution.put((String) row[0], (Long) row[1]); } stats.put("sectorDistribution", sectorDistribution); return stats; } /** * Traite une demande de rendez-vous */ @Transactional public void processMeetingRequest(MeetingRequestDTO request) { // Ici on pourrait créer une entité MeetingRequest // Pour l'instant, on met à jour les notes de l'audit AuditResponse audit = em.find(AuditResponse.class, request.getAuditId()); if (audit != null) { String meetingNote = String.format("RDV demandé: %s à %s (%s) - %s", request.getPreferredDate(), request.getPreferredTime(), request.getMeetingType(), request.getMessage()); audit.setSalesNotes(meetingNote); } } private String getCategoryDisplayName(String category) { return switch (category) { case "commercial" -> "Gestion Commerciale"; case "stock" -> "Gestion des Stocks"; case "comptabilite" -> "Comptabilité"; case "rh" -> "Ressources Humaines"; case "infrastructure" -> "Infrastructure IT"; default -> category; }; } }