Files
btpxpress-backend/src/main/java/dev/lions/btpxpress/application/service/NotificationService.java
2025-10-01 01:37:34 +00:00

617 lines
22 KiB
Java

package dev.lions.btpxpress.application.service;
import dev.lions.btpxpress.domain.core.entity.*;
import dev.lions.btpxpress.domain.infrastructure.repository.*;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import jakarta.ws.rs.BadRequestException;
import jakarta.ws.rs.NotFoundException;
import java.time.LocalDate;
import java.time.LocalDateTime;
import java.util.ArrayList;
import java.util.List;
import java.util.Optional;
import java.util.UUID;
import java.util.stream.Collectors;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
/**
* Service de gestion des notifications - Architecture 2025 COMMUNICATION: Logique métier complète
* pour les notifications BTP
*/
@ApplicationScoped
public class NotificationService {
private static final Logger logger = LoggerFactory.getLogger(NotificationService.class);
@Inject NotificationRepository notificationRepository;
@Inject UserRepository userRepository;
@Inject ChantierRepository chantierRepository;
@Inject MaterielRepository materielRepository;
@Inject MaintenanceService maintenanceService;
@Inject ChantierService chantierService;
// === MÉTHODES DE CONSULTATION ===
public List<Notification> findAll() {
logger.debug("Recherche de toutes les notifications");
return notificationRepository.findActives();
}
public List<Notification> findAll(int page, int size) {
logger.debug("Recherche des notifications - page: {}, taille: {}", page, size);
return notificationRepository.findActives(page, size);
}
public Optional<Notification> findById(UUID id) {
logger.debug("Recherche de la notification avec l'ID: {}", id);
return notificationRepository.findByIdOptional(id);
}
public Notification findByIdRequired(UUID id) {
return findById(id)
.orElseThrow(() -> new NotFoundException("Notification non trouvée avec l'ID: " + id));
}
public List<Notification> findByUser(UUID userId) {
logger.debug("Recherche des notifications pour l'utilisateur: {}", userId);
return notificationRepository.findByUser(userId);
}
public List<Notification> findByType(TypeNotification type) {
logger.debug("Recherche des notifications par type: {}", type);
return notificationRepository.findByType(type);
}
public List<Notification> findByPriorite(PrioriteNotification priorite) {
logger.debug("Recherche des notifications par priorité: {}", priorite);
return notificationRepository.findByPriorite(priorite);
}
public List<Notification> findNonLues() {
logger.debug("Recherche des notifications non lues");
return notificationRepository.findNonLues();
}
public List<Notification> findNonLuesByUser(UUID userId) {
logger.debug("Recherche des notifications non lues pour l'utilisateur: {}", userId);
return notificationRepository.findNonLuesByUser(userId);
}
public List<Notification> findRecentes(int limite) {
logger.debug("Recherche des {} notifications les plus récentes", limite);
return notificationRepository.findRecentes(limite);
}
public List<Notification> findRecentsByUser(UUID userId, int limite) {
logger.debug(
"Recherche des {} notifications les plus récentes pour l'utilisateur: {}", limite, userId);
return notificationRepository.findRecentsByUser(userId, limite);
}
// === CRÉATION DE NOTIFICATIONS ===
@Transactional
public Notification createNotification(
String titre,
String message,
String typeStr,
String prioriteStr,
UUID userId,
UUID chantierId,
String lienAction,
String donnees) {
logger.info("Création d'une notification: {} pour l'utilisateur: {}", titre, userId);
// Validation des données
validateNotificationData(titre, message, typeStr, userId);
TypeNotification type = parseTypeRequired(typeStr);
PrioriteNotification priorite = parsePriorite(prioriteStr, PrioriteNotification.NORMALE);
// Récupération des entités liées
User user = getUserById(userId);
Chantier chantier = chantierId != null ? getChantierById(chantierId) : null;
// Création de la notification
Notification notification =
Notification.builder()
.titre(titre)
.message(message)
.type(type)
.priorite(priorite)
.user(user)
.chantier(chantier)
.lienAction(lienAction)
.donnees(donnees)
.actif(true)
.build();
notificationRepository.persist(notification);
logger.info(
"Notification créée avec succès: {} (ID: {})",
notification.getTitre(),
notification.getId());
return notification;
}
@Transactional
public List<Notification> broadcastNotification(
String titre,
String message,
String typeStr,
String prioriteStr,
List<UUID> userIds,
String roleTarget,
String lienAction,
String donnees) {
logger.info("Diffusion d'une notification: {}", titre);
// Validation des données
validateNotificationData(titre, message, typeStr, null);
TypeNotification type = parseTypeRequired(typeStr);
PrioriteNotification priorite = parsePriorite(prioriteStr, PrioriteNotification.NORMALE);
// Détermination des destinataires
List<User> destinataires;
if (userIds != null && !userIds.isEmpty()) {
destinataires = userIds.stream().map(this::getUserById).collect(Collectors.toList());
} else if (roleTarget != null) {
destinataires = getUsersByRole(roleTarget);
} else {
throw new BadRequestException("Aucun destinataire spécifié pour la diffusion");
}
// Création des notifications pour chaque destinataire
List<Notification> notifications =
destinataires.stream()
.map(
user -> {
Notification notification =
Notification.builder()
.titre(titre)
.message(message)
.type(type)
.priorite(priorite)
.user(user)
.lienAction(lienAction)
.donnees(donnees)
.actif(true)
.build();
notificationRepository.persist(notification);
return notification;
})
.collect(Collectors.toList());
logger.info("Notification diffusée à {} utilisateurs", notifications.size());
return notifications;
}
@Transactional
public List<Notification> generateMaintenanceNotifications() {
logger.info("Génération des notifications de maintenance automatiques");
List<MaintenanceMateriel> maintenancesEnRetard = maintenanceService.findEnRetard();
List<MaintenanceMateriel> prochainesMaintenances =
maintenanceService.findProchainesMaintenances(7);
List<Notification> notifications = new ArrayList<>();
// Notifications pour maintenances en retard
for (MaintenanceMateriel maintenance : maintenancesEnRetard) {
String titre = "⚠️ Maintenance en retard: " + maintenance.getMateriel().getNom();
String message =
String.format(
"La maintenance %s du matériel %s était prévue le %s et est maintenant en retard.",
maintenance.getType().toString(),
maintenance.getMateriel().getNom(),
maintenance.getDatePrevue());
// Notification pour le technicien responsable et les superviseurs
List<User> destinataires = getUsersForMaintenance(maintenance);
for (User user : destinataires) {
Notification notification =
Notification.builder()
.titre(titre)
.message(message)
.type(TypeNotification.MAINTENANCE)
.priorite(PrioriteNotification.CRITIQUE)
.user(user)
.materiel(maintenance.getMateriel())
.maintenance(maintenance)
.lienAction("/maintenance/" + maintenance.getId())
.actif(true)
.build();
notificationRepository.persist(notification);
notifications.add(notification);
}
}
// Notifications pour prochaines maintenances
for (MaintenanceMateriel maintenance : prochainesMaintenances) {
String titre = "📅 Maintenance programmée: " + maintenance.getMateriel().getNom();
String message =
String.format(
"La maintenance %s du matériel %s est programmée pour le %s.",
maintenance.getType().toString(),
maintenance.getMateriel().getNom(),
maintenance.getDatePrevue());
List<User> destinataires = getUsersForMaintenance(maintenance);
for (User user : destinataires) {
Notification notification =
Notification.builder()
.titre(titre)
.message(message)
.type(TypeNotification.MAINTENANCE)
.priorite(PrioriteNotification.HAUTE)
.user(user)
.materiel(maintenance.getMateriel())
.maintenance(maintenance)
.lienAction("/maintenance/" + maintenance.getId())
.actif(true)
.build();
notificationRepository.persist(notification);
notifications.add(notification);
}
}
logger.info("Générées {} notifications de maintenance", notifications.size());
return notifications;
}
@Transactional
public List<Notification> generateChantierNotifications() {
logger.info("Génération des notifications de chantiers automatiques");
List<Chantier> chantiersEnRetard =
chantierService.findByStatut(StatutChantier.EN_COURS).stream()
.filter(
c -> c.getDateFinPrevue() != null && c.getDateFinPrevue().isBefore(LocalDate.now()))
.collect(Collectors.toList());
List<Notification> notifications = new ArrayList<>();
for (Chantier chantier : chantiersEnRetard) {
String titre = "🚧 Chantier en retard: " + chantier.getNom();
String message =
String.format(
"Le chantier %s devait se terminer le %s et accuse maintenant un retard.",
chantier.getNom(), chantier.getDateFinPrevue());
// Notification pour le client et les responsables
List<User> destinataires = getUsersForChantier(chantier);
for (User user : destinataires) {
Notification notification =
Notification.builder()
.titre(titre)
.message(message)
.type(TypeNotification.CHANTIER)
.priorite(PrioriteNotification.HAUTE)
.user(user)
.chantier(chantier)
.lienAction("/chantiers/" + chantier.getId())
.actif(true)
.build();
notificationRepository.persist(notification);
notifications.add(notification);
}
}
logger.info("Générées {} notifications de chantiers", notifications.size());
return notifications;
}
// === GESTION DES NOTIFICATIONS ===
@Transactional
public Notification marquerCommeLue(UUID id) {
logger.info("Marquage de la notification comme lue: {}", id);
Notification notification = findByIdRequired(id);
notification.marquerCommeLue();
notificationRepository.persist(notification);
return notification;
}
@Transactional
public Notification marquerCommeNonLue(UUID id) {
logger.info("Marquage de la notification comme non lue: {}", id);
Notification notification = findByIdRequired(id);
notification.marquerCommeNonLue();
notificationRepository.persist(notification);
return notification;
}
@Transactional
public int marquerToutesCommeLues(UUID userId) {
logger.info("Marquage de toutes les notifications comme lues pour l'utilisateur: {}", userId);
return notificationRepository.marquerToutesCommeLues(userId);
}
@Transactional
public void deleteNotification(UUID id) {
logger.info("Suppression de la notification: {}", id);
Notification notification = findByIdRequired(id);
notificationRepository.softDelete(id);
logger.info("Notification supprimée avec succès: {}", notification.getTitre());
}
@Transactional
public int deleteAnciennesNotifications(UUID userId, int jours) {
logger.info(
"Suppression des anciennes notifications (plus de {} jours) pour l'utilisateur: {}",
jours,
userId);
return notificationRepository.deleteAnciennesByUser(userId, jours);
}
// === STATISTIQUES ===
public Object getStatistiques() {
logger.debug("Génération des statistiques globales des notifications");
return new Object() {
public final long totalNotifications = notificationRepository.count("actif = true");
public final long notificationsNonLues = notificationRepository.countNonLues();
public final long notificationsCritiques = notificationRepository.countCritiques();
public final long notificationsRecentes = notificationRepository.countRecentes(24);
public final List<Object> parType = notificationRepository.getStatsByType();
public final List<Object> parPriorite = notificationRepository.getStatsByPriorite();
public final LocalDateTime genereA = LocalDateTime.now();
};
}
public Object getStatistiquesUser(UUID userId) {
logger.debug("Génération des statistiques des notifications pour l'utilisateur: {}", userId);
return new Object() {
public final long totalNotifications = notificationRepository.countByUser(userId);
public final long notificationsNonLues = notificationRepository.countNonLuesByUser(userId);
public final long notificationsCritiques =
notificationRepository.countCritiquesByUser(userId);
public final List<Notification> dernieresNonLues =
notificationRepository.findNonLuesByUser(userId).stream()
.limit(5)
.collect(Collectors.toList());
public final LocalDateTime genereA = LocalDateTime.now();
};
}
public Object getTableauBordGlobal() {
logger.debug("Génération du tableau de bord global des notifications");
List<Notification> alertesCritiques = notificationRepository.findCritiques();
List<Notification> notificationsRecentes = notificationRepository.findRecentes(10);
return new Object() {
public final String titre = "Tableau de Bord Global des Notifications";
public final Object resume =
new Object() {
public final long total = notificationRepository.count("actif = true");
public final long nonLues = notificationRepository.countNonLues();
public final long critiques = alertesCritiques.size();
public final boolean alerteCritique = !alertesCritiques.isEmpty();
};
public final List<Object> alertesCritiquesDetail =
alertesCritiques.stream()
.limit(5)
.map(
notification ->
new Object() {
public final String titre = notification.getTitre();
public final String type = notification.getType().toString();
public final String destinataire = notification.getUser().getEmail();
public final LocalDateTime dateCreation = notification.getDateCreation();
})
.collect(Collectors.toList());
public final List<Object> activiteRecente =
notificationsRecentes.stream()
.map(
notif ->
new Object() {
public final String titre = notif.getTitre();
public final String type = notif.getType().toString();
public final String priorite = notif.getPriorite().toString();
public final LocalDateTime dateCreation = notif.getDateCreation();
})
.collect(Collectors.toList());
public final LocalDateTime genereA = LocalDateTime.now();
};
}
public Object getTableauBordUser(UUID userId) {
logger.debug("Génération du tableau de bord des notifications pour l'utilisateur: {}", userId);
List<Notification> notificationsNonLues = notificationRepository.findNonLuesByUser(userId);
List<Notification> notificationsRecentes = notificationRepository.findRecentsByUser(userId, 5);
final UUID userIdFinal = userId;
return new Object() {
public final String titre = "Mes Notifications";
public final UUID userId = userIdFinal;
public final Object resume =
new Object() {
public final long total = notificationRepository.countByUser(userIdFinal);
public final long nonLues = notificationsNonLues.size();
public final long critiques =
notificationsNonLues.stream().filter(Notification::estCritique).count();
public final boolean alerteCritique =
notificationsNonLues.stream().anyMatch(Notification::estCritique);
};
public final List<Object> nonLues =
notificationsNonLues.stream()
.limit(10)
.map(
n ->
new Object() {
public final UUID id = n.getId();
public final String titre = n.getTitre();
public final String message = n.getMessage();
public final String type = n.getType().toString();
public final String priorite = n.getPriorite().toString();
public final LocalDateTime dateCreation = n.getDateCreation();
public final String lienAction = n.getLienAction();
})
.collect(Collectors.toList());
public final List<Object> recentes =
notificationsRecentes.stream()
.map(
n ->
new Object() {
public final UUID id = n.getId();
public final String titre = n.getTitre();
public final String type = n.getType().toString();
public final String priorite = n.getPriorite().toString();
public final boolean lue = n.getLue();
public final LocalDateTime dateCreation = n.getDateCreation();
})
.collect(Collectors.toList());
public final LocalDateTime genereA = LocalDateTime.now();
};
}
// === MÉTHODES PRIVÉES ===
private void validateNotificationData(String titre, String message, String type, UUID userId) {
if (titre == null || titre.trim().isEmpty()) {
throw new BadRequestException("Le titre de la notification est obligatoire");
}
if (message == null || message.trim().isEmpty()) {
throw new BadRequestException("Le message de la notification est obligatoire");
}
if (type == null || type.trim().isEmpty()) {
throw new BadRequestException("Le type de notification est obligatoire");
}
if (userId != null && userRepository.findByIdOptional(userId).isEmpty()) {
throw new BadRequestException("Utilisateur non trouvé: " + userId);
}
}
private TypeNotification parseTypeRequired(String typeStr) {
try {
return TypeNotification.valueOf(typeStr.toUpperCase());
} catch (IllegalArgumentException e) {
throw new BadRequestException("Type de notification invalide: " + typeStr);
}
}
private PrioriteNotification parsePriorite(
String prioriteStr, PrioriteNotification defaultValue) {
if (prioriteStr == null || prioriteStr.trim().isEmpty()) {
return defaultValue;
}
try {
return PrioriteNotification.valueOf(prioriteStr.toUpperCase());
} catch (IllegalArgumentException e) {
logger.warn(
"Priorité de notification invalide: {}, utilisation de la valeur par défaut",
prioriteStr);
return defaultValue;
}
}
private User getUserById(UUID userId) {
return userRepository
.findByIdOptional(userId)
.orElseThrow(() -> new BadRequestException("Utilisateur non trouvé: " + userId));
}
private Chantier getChantierById(UUID chantierId) {
return chantierRepository
.findByIdOptional(chantierId)
.orElseThrow(() -> new BadRequestException("Chantier non trouvé: " + chantierId));
}
private List<User> getUsersByRole(String role) {
logger.debug("Recherche des utilisateurs par rôle: {}", role);
try {
UserRole roleEnum = UserRole.valueOf(role.toUpperCase());
return userRepository.findByRole(roleEnum);
} catch (IllegalArgumentException e) {
logger.warn("Rôle invalide: {}, retour de liste vide", role);
return List.of();
}
}
private List<User> getUsersForMaintenance(MaintenanceMateriel maintenance) {
logger.debug(
"Récupération des utilisateurs concernés par la maintenance: {}", maintenance.getId());
List<User> users = new ArrayList<>();
// Récupérer les techniciens de maintenance
List<User> techniciens = userRepository.findByRole(UserRole.OUVRIER);
users.addAll(techniciens);
// Récupérer les chefs de chantier et responsables
List<User> responsables = userRepository.findByRole(UserRole.CHEF_CHANTIER);
users.addAll(responsables);
// Récupérer les managers
List<User> managers = userRepository.findByRole(UserRole.MANAGER);
users.addAll(managers);
return users.stream().distinct().collect(Collectors.toList());
}
private List<User> getUsersForChantier(Chantier chantier) {
logger.debug("Récupération des utilisateurs concernés par le chantier: {}", chantier.getId());
List<User> users = new ArrayList<>();
// Ajouter le client du chantier si disponible
if (chantier.getClient() != null && chantier.getClient().getCompteUtilisateur() != null) {
users.add(chantier.getClient().getCompteUtilisateur());
}
// Ajouter le chef de chantier assigné
if (chantier.getChefChantier() != null) {
users.add(chantier.getChefChantier());
}
// Ajouter les gestionnaires de projet
List<User> gestionnaires = userRepository.findByRole(UserRole.GESTIONNAIRE_PROJET);
users.addAll(gestionnaires);
// Ajouter les managers
List<User> managers = userRepository.findByRole(UserRole.MANAGER);
users.addAll(managers);
return users.stream().distinct().collect(Collectors.toList());
}
}