feat(backend): Ajout complet des fonctionnalités Chat, Social, Story et Notifications

Implémentation complète de toutes les fonctionnalités backend :

## Nouvelles Fonctionnalités

### Chat (Messagerie Instantanée)
- Entities : Conversation, Message
- DTOs : ConversationResponseDTO, MessageResponseDTO, SendMessageRequestDTO
- Resources : MessageResource (endpoints REST)
- Services : MessageService (logique métier)
- Repositories : ConversationRepository, MessageRepository
- WebSocket : ChatWebSocket (temps réel)

### Social (Publications Sociales)
- Entities : SocialPost, SocialComment, SocialLike
- DTOs : SocialPostResponseDTO, CreateSocialPostRequestDTO
- Resources : SocialPostResource
- Services : SocialPostService
- Repositories : SocialPostRepository

### Story (Stories temporaires)
- Entities : Story, StoryView
- DTOs : StoryResponseDTO, CreateStoryRequestDTO
- Resources : StoryResource
- Services : StoryService
- Repositories : StoryRepository

### Notifications (Temps Réel)
- Entities : Notification
- DTOs : NotificationResponseDTO
- Resources : NotificationResource
- Services : NotificationService, PresenceService
- Repositories : NotificationRepository
- WebSocket : NotificationWebSocket (temps réel)

## Améliorations

### Users & Friendship
- Mise à jour UserResponseDTO avec nouveaux champs
- Amélioration FriendshipResource avec séparation demandes envoyées/reçues
- FriendSuggestionResponseDTO pour suggestions d'amis
- Optimisations dans UsersService et FriendshipService

### Events
- Améliorations EventsResource et EventService
- Optimisations EventsRepository

### Configuration
- Mise à jour application.properties
- Configuration docker-compose.yml
- Dockerfile pour développement

## Fichiers Modifiés
- .dockerignore, .gitignore
- README.md
- docker-compose.yml
- Configuration Maven wrapper
This commit is contained in:
dahoud
2026-01-10 10:39:58 +00:00
parent fd67140961
commit 093d04c224
60 changed files with 14652 additions and 220 deletions

View File

@@ -0,0 +1,339 @@
package com.lions.dev.service;
import com.lions.dev.entity.chat.Conversation;
import com.lions.dev.entity.chat.Message;
import com.lions.dev.entity.users.Users;
import com.lions.dev.exception.UserNotFoundException;
import com.lions.dev.repository.ConversationRepository;
import com.lions.dev.repository.MessageRepository;
import com.lions.dev.repository.UsersRepository;
import com.lions.dev.websocket.ChatWebSocket;
import jakarta.enterprise.context.ApplicationScoped;
import jakarta.inject.Inject;
import jakarta.transaction.Transactional;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
/**
* Service de gestion des messages et conversations.
*
* Ce service contient la logique métier pour l'envoi de messages,
* la récupération de conversations, et la gestion des messages non lus.
*
* Tous les logs nécessaires pour la traçabilité sont intégrés.
*/
@ApplicationScoped
public class MessageService {
@Inject
MessageRepository messageRepository;
@Inject
ConversationRepository conversationRepository;
@Inject
UsersRepository usersRepository;
@Inject
NotificationService notificationService;
/**
* Envoie un message d'un utilisateur à un autre.
*
* @param senderId L'ID de l'expéditeur
* @param recipientId L'ID du destinataire
* @param content Le contenu du message
* @param messageType Le type de message (text, image, etc.)
* @param mediaUrl L'URL du média (optionnel)
* @return Le message créé
* @throws UserNotFoundException Si l'un des utilisateurs n'existe pas
*/
@Transactional
public Message sendMessage(
UUID senderId,
UUID recipientId,
String content,
String messageType,
String mediaUrl) {
System.out.println("[LOG] Envoi de message de " + senderId + " à " + recipientId);
// Récupérer les utilisateurs
Users sender = usersRepository.findById(senderId);
Users recipient = usersRepository.findById(recipientId);
if (sender == null) {
throw new UserNotFoundException("Expéditeur non trouvé avec l'ID : " + senderId);
}
if (recipient == null) {
throw new UserNotFoundException("Destinataire non trouvé avec l'ID : " + recipientId);
}
// Trouver ou créer la conversation
Conversation conversation = conversationRepository.findOrCreate(sender, recipient);
// Créer le message
Message message = new Message(conversation, sender, content);
message.setMessageType(messageType != null ? messageType : "text");
if (mediaUrl != null && !mediaUrl.isEmpty()) {
message.setMediaUrl(mediaUrl);
}
message.markAsDelivered();
// Persister le message
messageRepository.persist(message);
System.out.println("[LOG] Message créé avec l'ID : " + message.getId());
// Mettre à jour la conversation
conversation.updateLastMessage(message);
conversationRepository.persist(conversation);
// Créer une notification pour le destinataire
try {
String senderName = sender.getPrenoms() + " " + sender.getNom();
String notificationMessage = content.length() > 50
? content.substring(0, 50) + "..."
: content;
notificationService.createNotification(
"Nouveau message de " + senderName,
notificationMessage,
"message",
recipientId,
null
);
System.out.println("[LOG] Notification créée pour le destinataire");
} catch (Exception e) {
System.out.println("[ERROR] Erreur lors de la création de la notification : " + e.getMessage());
}
// TEMPS RÉEL : Envoyer le message via WebSocket au destinataire
try {
Map<String, Object> messageData = new HashMap<>();
messageData.put("id", message.getId().toString());
messageData.put("conversationId", conversation.getId().toString());
messageData.put("senderId", senderId.toString());
messageData.put("senderFirstName", sender.getPrenoms());
messageData.put("senderLastName", sender.getNom());
messageData.put("senderProfileImageUrl", sender.getProfileImageUrl() != null ? sender.getProfileImageUrl() : "");
messageData.put("content", content);
messageData.put("timestamp", message.getCreatedAt().toString());
messageData.put("isRead", message.isRead());
messageData.put("attachmentUrl", mediaUrl != null ? mediaUrl : "");
messageData.put("attachmentType", messageType != null ? messageType : "text");
// Envoyer au destinataire via ChatWebSocket
ChatWebSocket.sendMessageToUser(recipientId, messageData);
System.out.println("[LOG] Message envoyé via WebSocket au destinataire : " + recipientId);
// Envoyer confirmation de délivrance à l'expéditeur
try {
Map<String, Object> deliveryConfirmation = new HashMap<>();
deliveryConfirmation.put("messageId", message.getId().toString());
deliveryConfirmation.put("isDelivered", true);
deliveryConfirmation.put("timestamp", System.currentTimeMillis());
ChatWebSocket.sendDeliveryConfirmation(senderId, deliveryConfirmation);
System.out.println("[LOG] Confirmation de délivrance envoyée à l'expéditeur : " + senderId);
} catch (Exception deliveryEx) {
System.out.println("[ERROR] Erreur envoi confirmation délivrance : " + deliveryEx.getMessage());
// Ne pas bloquer si la confirmation échoue
}
} catch (Exception e) {
System.out.println("[ERROR] Erreur lors de l'envoi du message via WebSocket : " + e.getMessage());
// Ne pas bloquer l'envoi du message si WebSocket échoue
}
return message;
}
/**
* Récupère toutes les conversations d'un utilisateur.
*
* @param userId L'ID de l'utilisateur
* @return Liste des conversations
* @throws UserNotFoundException Si l'utilisateur n'existe pas
*/
public List<Conversation> getUserConversations(UUID userId) {
System.out.println("[LOG] Récupération des conversations pour l'utilisateur ID : " + userId);
Users user = usersRepository.findById(userId);
if (user == null) {
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
}
return conversationRepository.findByUser(user);
}
/**
* Récupère tous les messages d'une conversation avec pagination.
*
* @param conversationId L'ID de la conversation
* @param page Le numéro de la page
* @param size La taille de la page
* @return Liste paginée des messages
*/
public List<Message> getConversationMessages(UUID conversationId, int page, int size) {
System.out.println("[LOG] Récupération des messages pour la conversation ID : " + conversationId);
return messageRepository.findByConversationId(conversationId, page, size);
}
/**
* Récupère une conversation spécifique.
*
* @param conversationId L'ID de la conversation
* @return La conversation
*/
public Conversation getConversation(UUID conversationId) {
System.out.println("[LOG] Récupération de la conversation ID : " + conversationId);
return conversationRepository.findById(conversationId);
}
/**
* Récupère une conversation entre deux utilisateurs.
*
* @param user1Id L'ID du premier utilisateur
* @param user2Id L'ID du deuxième utilisateur
* @return La conversation ou null si elle n'existe pas
* @throws UserNotFoundException Si l'un des utilisateurs n'existe pas
*/
public Conversation getConversationBetweenUsers(UUID user1Id, UUID user2Id) {
System.out.println("[LOG] Recherche de conversation entre " + user1Id + " et " + user2Id);
Users user1 = usersRepository.findById(user1Id);
Users user2 = usersRepository.findById(user2Id);
if (user1 == null || user2 == null) {
throw new UserNotFoundException("Un ou plusieurs utilisateurs non trouvés");
}
return conversationRepository.findBetweenUsers(user1, user2);
}
/**
* Marque un message comme lu.
*
* @param messageId L'ID du message
* @return Le message mis à jour
*/
@Transactional
public Message markMessageAsRead(UUID messageId) {
System.out.println("[LOG] Marquage du message comme lu : " + messageId);
Message message = messageRepository.findById(messageId);
if (message == null) {
throw new IllegalArgumentException("Message non trouvé avec l'ID : " + messageId);
}
message.markAsRead();
messageRepository.persist(message);
// Envoyer confirmation de lecture à l'expéditeur via WebSocket
try {
// Récupérer le destinataire (l'autre utilisateur de la conversation)
Conversation conversation = message.getConversation();
UUID recipientId = conversation.getUser1().getId().equals(message.getSender().getId())
? conversation.getUser2().getId()
: conversation.getUser1().getId();
Map<String, Object> readConfirmation = new HashMap<>();
readConfirmation.put("messageId", message.getId().toString());
readConfirmation.put("userId", recipientId.toString());
readConfirmation.put("timestamp", java.time.LocalDateTime.now().toString());
// Envoyer via ChatWebSocket avec type "read"
com.lions.dev.websocket.ChatWebSocket.sendReadConfirmation(
message.getSender().getId(),
readConfirmation
);
System.out.println("[LOG] Confirmation de lecture envoyée à l'expéditeur : " + message.getSender().getId());
} catch (Exception e) {
System.out.println("[ERROR] Erreur envoi confirmation lecture : " + e.getMessage());
}
return message;
}
/**
* Marque tous les messages d'une conversation comme lus pour un utilisateur.
*
* @param conversationId L'ID de la conversation
* @param userId L'ID de l'utilisateur
* @return Le nombre de messages marqués comme lus
*/
@Transactional
public int markAllMessagesAsRead(UUID conversationId, UUID userId) {
System.out.println("[LOG] Marquage de tous les messages comme lus pour la conversation " + conversationId);
Conversation conversation = conversationRepository.findById(conversationId);
if (conversation == null) {
throw new IllegalArgumentException("Conversation non trouvée");
}
Users user = usersRepository.findById(userId);
if (user == null) {
throw new UserNotFoundException("Utilisateur non trouvé");
}
// Marquer les messages comme lus
int count = messageRepository.markAllAsRead(conversationId, userId);
// Mettre à jour le compteur de la conversation
conversation.markAllAsReadForUser(user);
conversationRepository.persist(conversation);
return count;
}
/**
* Compte le nombre total de messages non lus pour un utilisateur.
*
* @param userId L'ID de l'utilisateur
* @return Le nombre de messages non lus
*/
public long getTotalUnreadCount(UUID userId) {
System.out.println("[LOG] Récupération du nombre total de messages non lus pour l'utilisateur " + userId);
return conversationRepository.countTotalUnreadMessages(userId);
}
/**
* Supprime un message.
*
* @param messageId L'ID du message
* @return true si le message a été supprimé
*/
@Transactional
public boolean deleteMessage(UUID messageId) {
System.out.println("[LOG] Suppression du message ID : " + messageId);
Message message = messageRepository.findById(messageId);
if (message == null) {
return false;
}
messageRepository.delete(message);
return true;
}
/**
* Supprime une conversation et tous ses messages.
*
* @param conversationId L'ID de la conversation
* @return true si la conversation a été supprimée
*/
@Transactional
public boolean deleteConversation(UUID conversationId) {
System.out.println("[LOG] Suppression de la conversation ID : " + conversationId);
// Supprimer d'abord tous les messages
messageRepository.deleteByConversationId(conversationId);
// Puis supprimer la conversation
return conversationRepository.deleteConversation(conversationId);
}
}