248 lines
9.5 KiB
Java
248 lines
9.5 KiB
Java
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<String, List<AuditQuestion>> getQuestionsByCategory() {
|
|
List<AuditQuestion> 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<String, List<AuditQuestion>> questionsByCategory = getQuestionsByCategory();
|
|
Map<String, Integer> categoryScores = new HashMap<>();
|
|
Map<String, Integer> categoryMaxScores = new HashMap<>();
|
|
|
|
int totalScore = 0;
|
|
int maxPossibleScore = 0;
|
|
|
|
// Calcul des scores par catégorie
|
|
for (Map.Entry<String, List<AuditQuestion>> entry : questionsByCategory.entrySet()) {
|
|
String category = entry.getKey();
|
|
List<AuditQuestion> 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<String, Integer> categoryScores,
|
|
Map<String, Integer> categoryMaxScores) {
|
|
|
|
StringBuilder recommendations = new StringBuilder();
|
|
List<String> priorities = new ArrayList<>();
|
|
|
|
// Analyse par catégorie
|
|
for (Map.Entry<String, Integer> 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<String, Integer> categoryScores) {
|
|
double budgetMin = 0;
|
|
double budgetMax = 0;
|
|
|
|
// Tarification par module selon le niveau de maturité
|
|
Map<String, Double[]> 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<String, Integer> 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<AuditResponse> 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<String, Object> getAuditStatistics() {
|
|
Map<String, Object> 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<Object[]> sectorStats = em.createQuery(
|
|
"SELECT a.sector, COUNT(a) FROM AuditResponse a GROUP BY a.sector", Object[].class)
|
|
.getResultList();
|
|
Map<String, Long> 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;
|
|
};
|
|
}
|
|
}
|