617 lines
22 KiB
Java
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());
|
|
}
|
|
}
|