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
340 lines
12 KiB
Java
340 lines
12 KiB
Java
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);
|
|
}
|
|
}
|