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:
@@ -0,0 +1,34 @@
|
||||
package com.lions.dev.dto.request.chat;
|
||||
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* DTO pour l'envoi d'un message.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class SendMessageRequestDTO {
|
||||
|
||||
private UUID senderId; // L'ID de l'expéditeur
|
||||
private UUID recipientId; // L'ID du destinataire
|
||||
private String content; // Le contenu du message
|
||||
private String messageType; // Le type de message (text, image, video, file)
|
||||
private String mediaUrl; // L'URL du média (optionnel)
|
||||
|
||||
/**
|
||||
* Valide les données du DTO.
|
||||
*
|
||||
* @return true si les données sont valides, false sinon
|
||||
*/
|
||||
public boolean isValid() {
|
||||
return senderId != null
|
||||
&& recipientId != null
|
||||
&& content != null
|
||||
&& !content.trim().isEmpty();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,35 @@
|
||||
package com.lions.dev.dto.request.social;
|
||||
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la création d'un post social.
|
||||
*
|
||||
* Ce DTO est utilisé dans les requêtes de création de posts sociaux,
|
||||
* envoyant les informations nécessaires comme le contenu, l'utilisateur
|
||||
* créateur, et optionnellement une image.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class SocialPostCreateRequestDTO {
|
||||
|
||||
@NotBlank(message = "Le contenu du post est obligatoire.")
|
||||
@Size(max = 2000, message = "Le contenu ne peut pas dépasser 2000 caractères.")
|
||||
private String content; // Le contenu textuel du post
|
||||
|
||||
@NotNull(message = "L'identifiant de l'utilisateur est obligatoire.")
|
||||
private UUID userId; // L'ID de l'utilisateur créateur
|
||||
|
||||
@Size(max = 500, message = "L'URL de l'image ne peut pas dépasser 500 caractères.")
|
||||
private String imageUrl; // URL de l'image (optionnel)
|
||||
|
||||
public SocialPostCreateRequestDTO() {
|
||||
System.out.println("[LOG] DTO de requête de création de post social initialisé.");
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,39 @@
|
||||
package com.lions.dev.dto.request.story;
|
||||
|
||||
import com.lions.dev.entity.story.MediaType;
|
||||
import jakarta.validation.constraints.NotBlank;
|
||||
import jakarta.validation.constraints.NotNull;
|
||||
import jakarta.validation.constraints.Size;
|
||||
import java.util.UUID;
|
||||
import lombok.Getter;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO pour la création d'une story.
|
||||
*
|
||||
* Ce DTO est utilisé dans les requêtes de création de stories,
|
||||
* envoyant les informations nécessaires comme le média, le type et l'utilisateur.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
public class StoryCreateRequestDTO {
|
||||
|
||||
@NotNull(message = "L'identifiant de l'utilisateur est obligatoire.")
|
||||
private UUID userId; // L'ID de l'utilisateur créateur
|
||||
|
||||
@NotNull(message = "Le type de média est obligatoire.")
|
||||
private MediaType mediaType; // Type de média (IMAGE ou VIDEO)
|
||||
|
||||
@NotBlank(message = "L'URL du média est obligatoire.")
|
||||
@Size(max = 500, message = "L'URL du média ne peut pas dépasser 500 caractères.")
|
||||
private String mediaUrl; // URL du média
|
||||
|
||||
@Size(max = 500, message = "L'URL du thumbnail ne peut pas dépasser 500 caractères.")
|
||||
private String thumbnailUrl; // URL du thumbnail (optionnel, pour les vidéos)
|
||||
|
||||
private Integer durationSeconds; // Durée en secondes (optionnel, pour les vidéos)
|
||||
|
||||
public StoryCreateRequestDTO() {
|
||||
System.out.println("[LOG] DTO de requête de création de story initialisé.");
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,53 @@
|
||||
package com.lions.dev.dto.response.chat;
|
||||
|
||||
import com.lions.dev.entity.chat.Conversation;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* DTO de réponse pour une conversation.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class ConversationResponseDTO {
|
||||
|
||||
private UUID id;
|
||||
private UUID participantId;
|
||||
private String participantFirstName;
|
||||
private String participantLastName;
|
||||
private String participantProfileImageUrl;
|
||||
private String lastMessage;
|
||||
private LocalDateTime lastMessageTimestamp;
|
||||
private int unreadCount;
|
||||
private boolean isTyping;
|
||||
|
||||
/**
|
||||
* Constructeur depuis une entité Conversation.
|
||||
*
|
||||
* @param conversation La conversation
|
||||
* @param currentUser L'utilisateur actuel (pour déterminer l'autre utilisateur)
|
||||
*/
|
||||
public ConversationResponseDTO(Conversation conversation, Users currentUser) {
|
||||
this.id = conversation.getId();
|
||||
|
||||
// Déterminer l'autre utilisateur
|
||||
Users otherUser = conversation.getOtherUser(currentUser);
|
||||
if (otherUser != null) {
|
||||
this.participantId = otherUser.getId();
|
||||
this.participantFirstName = otherUser.getPrenoms();
|
||||
this.participantLastName = otherUser.getNom();
|
||||
this.participantProfileImageUrl = otherUser.getProfileImageUrl();
|
||||
}
|
||||
|
||||
this.lastMessage = conversation.getLastMessageContent();
|
||||
this.lastMessageTimestamp = conversation.getLastMessageTimestamp();
|
||||
this.unreadCount = conversation.getUnreadCountForUser(currentUser);
|
||||
this.isTyping = false; // Par défaut, pas en train de taper
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,49 @@
|
||||
package com.lions.dev.dto.response.chat;
|
||||
|
||||
import com.lions.dev.entity.chat.Message;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* DTO de réponse pour un message.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
public class MessageResponseDTO {
|
||||
|
||||
private UUID id;
|
||||
private UUID conversationId;
|
||||
private UUID senderId;
|
||||
private String senderFirstName;
|
||||
private String senderLastName;
|
||||
private String senderProfileImageUrl;
|
||||
private String content;
|
||||
private String attachmentType;
|
||||
private String attachmentUrl;
|
||||
private boolean isRead;
|
||||
private boolean isDelivered;
|
||||
private LocalDateTime timestamp;
|
||||
|
||||
/**
|
||||
* Constructeur depuis une entité Message.
|
||||
*/
|
||||
public MessageResponseDTO(Message message) {
|
||||
this.id = message.getId();
|
||||
this.conversationId = message.getConversation().getId();
|
||||
this.senderId = message.getSender().getId();
|
||||
this.senderFirstName = message.getSender().getPrenoms();
|
||||
this.senderLastName = message.getSender().getNom();
|
||||
this.senderProfileImageUrl = message.getSender().getProfileImageUrl();
|
||||
this.content = message.getContent();
|
||||
this.attachmentType = message.getMessageType();
|
||||
this.attachmentUrl = message.getMediaUrl();
|
||||
this.isRead = message.isRead();
|
||||
this.isDelivered = message.isDelivered();
|
||||
this.timestamp = message.getCreatedAt();
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,52 @@
|
||||
package com.lions.dev.dto.response.notifications;
|
||||
|
||||
import com.lions.dev.entity.notification.Notification;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO (Data Transfer Object) pour la réponse d'une notification.
|
||||
*
|
||||
* Cette classe sert de représentation simplifiée d'une notification
|
||||
* pour la réponse de l'API.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class NotificationResponseDTO {
|
||||
|
||||
private UUID id;
|
||||
private String title;
|
||||
private String message;
|
||||
private String type;
|
||||
private boolean isRead;
|
||||
private LocalDateTime timestamp;
|
||||
private UUID userId;
|
||||
private UUID eventId; // Optionnel
|
||||
private String metadata; // Optionnel
|
||||
|
||||
/**
|
||||
* Constructeur à partir d'une entité Notification.
|
||||
*
|
||||
* @param notification L'entité Notification
|
||||
*/
|
||||
public NotificationResponseDTO(Notification notification) {
|
||||
if (notification != null) {
|
||||
this.id = notification.getId();
|
||||
this.title = notification.getTitle();
|
||||
this.message = notification.getMessage();
|
||||
this.type = notification.getType();
|
||||
this.isRead = notification.isRead();
|
||||
this.timestamp = notification.getCreatedAt();
|
||||
this.userId = notification.getUser() != null ? notification.getUser().getId() : null;
|
||||
this.eventId = notification.getEvent() != null ? notification.getEvent().getId() : null;
|
||||
this.metadata = notification.getMetadata();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,56 @@
|
||||
package com.lions.dev.dto.response.social;
|
||||
|
||||
import com.lions.dev.entity.social.SocialPost;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO (Data Transfer Object) pour la réponse d'un post social.
|
||||
*
|
||||
* Cette classe sert de représentation simplifiée d'un post social
|
||||
* pour la réponse de l'API.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class SocialPostResponseDTO {
|
||||
|
||||
private UUID id;
|
||||
private String content;
|
||||
private UUID userId;
|
||||
private String userFirstName;
|
||||
private String userLastName;
|
||||
private String userProfileImageUrl;
|
||||
private LocalDateTime timestamp;
|
||||
private String imageUrl; // Optionnel
|
||||
private int likesCount;
|
||||
private int commentsCount;
|
||||
private int sharesCount;
|
||||
|
||||
/**
|
||||
* Constructeur à partir d'une entité SocialPost.
|
||||
*
|
||||
* @param post L'entité SocialPost
|
||||
*/
|
||||
public SocialPostResponseDTO(SocialPost post) {
|
||||
if (post != null) {
|
||||
this.id = post.getId();
|
||||
this.content = post.getContent();
|
||||
this.userId = post.getUser() != null ? post.getUser().getId() : null;
|
||||
this.userFirstName = post.getUser() != null ? post.getUser().getPrenoms() : null;
|
||||
this.userLastName = post.getUser() != null ? post.getUser().getNom() : null;
|
||||
this.userProfileImageUrl = post.getUser() != null ? post.getUser().getProfileImageUrl() : null;
|
||||
this.timestamp = post.getCreatedAt();
|
||||
this.imageUrl = post.getImageUrl();
|
||||
this.likesCount = post.getLikesCount();
|
||||
this.commentsCount = post.getCommentsCount();
|
||||
this.sharesCount = post.getSharesCount();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,77 @@
|
||||
package com.lions.dev.dto.response.story;
|
||||
|
||||
import com.lions.dev.entity.story.MediaType;
|
||||
import com.lions.dev.entity.story.Story;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.UUID;
|
||||
import lombok.AllArgsConstructor;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
|
||||
/**
|
||||
* DTO (Data Transfer Object) pour la réponse d'une story.
|
||||
*
|
||||
* Cette classe sert de représentation simplifiée d'une story
|
||||
* pour la réponse de l'API.
|
||||
*/
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@AllArgsConstructor
|
||||
public class StoryResponseDTO {
|
||||
|
||||
private UUID id;
|
||||
private UUID userId;
|
||||
private String userFirstName;
|
||||
private String userLastName;
|
||||
private String userProfileImageUrl;
|
||||
private boolean userIsVerified;
|
||||
private MediaType mediaType;
|
||||
private String mediaUrl;
|
||||
private String thumbnailUrl;
|
||||
private Integer durationSeconds;
|
||||
private LocalDateTime createdAt;
|
||||
private LocalDateTime expiresAt;
|
||||
private boolean isActive;
|
||||
private int viewsCount;
|
||||
private boolean hasViewed; // Indique si l'utilisateur actuel a vu cette story
|
||||
|
||||
/**
|
||||
* Constructeur à partir d'une entité Story.
|
||||
*
|
||||
* @param story L'entité Story
|
||||
*/
|
||||
public StoryResponseDTO(Story story) {
|
||||
if (story != null) {
|
||||
this.id = story.getId();
|
||||
this.userId = story.getUser() != null ? story.getUser().getId() : null;
|
||||
this.userFirstName = story.getUser() != null ? story.getUser().getPrenoms() : null;
|
||||
this.userLastName = story.getUser() != null ? story.getUser().getNom() : null;
|
||||
this.userProfileImageUrl = story.getUser() != null ? story.getUser().getProfileImageUrl() : null;
|
||||
this.userIsVerified = story.getUser() != null && story.getUser().isVerified();
|
||||
this.mediaType = story.getMediaType();
|
||||
this.mediaUrl = story.getMediaUrl();
|
||||
this.thumbnailUrl = story.getThumbnailUrl();
|
||||
this.durationSeconds = story.getDurationSeconds();
|
||||
this.createdAt = story.getCreatedAt();
|
||||
this.expiresAt = story.getExpiresAt();
|
||||
this.isActive = story.isActive();
|
||||
this.viewsCount = story.getViewsCount();
|
||||
this.hasViewed = false; // Par défaut, sera mis à jour par la logique métier
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructeur à partir d'une entité Story avec indication si l'utilisateur l'a vue.
|
||||
*
|
||||
* @param story L'entité Story
|
||||
* @param viewerId L'ID de l'utilisateur actuel
|
||||
*/
|
||||
public StoryResponseDTO(Story story, UUID viewerId) {
|
||||
this(story);
|
||||
if (story != null && viewerId != null) {
|
||||
this.hasViewed = story.hasBeenViewedBy(viewerId);
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,102 @@
|
||||
package com.lions.dev.dto.response.users;
|
||||
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* DTO de réponse pour les suggestions d'amis.
|
||||
*
|
||||
* Ce DTO contient les informations essentielles d'un utilisateur suggéré,
|
||||
* incluant le nombre d'amis communs pour justifier la suggestion.
|
||||
*/
|
||||
public class FriendSuggestionResponseDTO {
|
||||
|
||||
private UUID userId;
|
||||
private String nom;
|
||||
private String prenoms;
|
||||
private String email;
|
||||
private String profileImageUrl;
|
||||
private int mutualFriendsCount;
|
||||
private String suggestionReason;
|
||||
|
||||
/**
|
||||
* Constructeur par défaut.
|
||||
*/
|
||||
public FriendSuggestionResponseDTO() {
|
||||
}
|
||||
|
||||
/**
|
||||
* Constructeur à partir d'un utilisateur.
|
||||
*
|
||||
* @param user L'utilisateur suggéré
|
||||
* @param mutualFriendsCount Le nombre d'amis en commun
|
||||
* @param reason La raison de la suggestion
|
||||
*/
|
||||
public FriendSuggestionResponseDTO(Users user, int mutualFriendsCount, String reason) {
|
||||
this.userId = user.getId();
|
||||
this.nom = user.getNom();
|
||||
this.prenoms = user.getPrenoms();
|
||||
this.email = user.getEmail();
|
||||
this.profileImageUrl = user.getProfileImageUrl();
|
||||
this.mutualFriendsCount = mutualFriendsCount;
|
||||
this.suggestionReason = reason;
|
||||
}
|
||||
|
||||
// Getters et Setters
|
||||
|
||||
public UUID getUserId() {
|
||||
return userId;
|
||||
}
|
||||
|
||||
public void setUserId(UUID userId) {
|
||||
this.userId = userId;
|
||||
}
|
||||
|
||||
public String getNom() {
|
||||
return nom;
|
||||
}
|
||||
|
||||
public void setNom(String nom) {
|
||||
this.nom = nom;
|
||||
}
|
||||
|
||||
public String getPrenoms() {
|
||||
return prenoms;
|
||||
}
|
||||
|
||||
public void setPrenoms(String prenoms) {
|
||||
this.prenoms = prenoms;
|
||||
}
|
||||
|
||||
public String getEmail() {
|
||||
return email;
|
||||
}
|
||||
|
||||
public void setEmail(String email) {
|
||||
this.email = email;
|
||||
}
|
||||
|
||||
public String getProfileImageUrl() {
|
||||
return profileImageUrl;
|
||||
}
|
||||
|
||||
public void setProfileImageUrl(String profileImageUrl) {
|
||||
this.profileImageUrl = profileImageUrl;
|
||||
}
|
||||
|
||||
public int getMutualFriendsCount() {
|
||||
return mutualFriendsCount;
|
||||
}
|
||||
|
||||
public void setMutualFriendsCount(int mutualFriendsCount) {
|
||||
this.mutualFriendsCount = mutualFriendsCount;
|
||||
}
|
||||
|
||||
public String getSuggestionReason() {
|
||||
return suggestionReason;
|
||||
}
|
||||
|
||||
public void setSuggestionReason(String suggestionReason) {
|
||||
this.suggestionReason = suggestionReason;
|
||||
}
|
||||
}
|
||||
@@ -47,6 +47,11 @@ public class UserResponseDTO {
|
||||
*/
|
||||
private String profileImageUrl;
|
||||
|
||||
/**
|
||||
* Indique si l'utilisateur est vérifié (compte officiel).
|
||||
*/
|
||||
private boolean isVerified;
|
||||
|
||||
/**
|
||||
* Constructeur de DTO à partir d'une entité Users.
|
||||
* <p>
|
||||
@@ -64,6 +69,7 @@ public class UserResponseDTO {
|
||||
this.prenoms = user.getPrenoms(); // Prénom(s)
|
||||
this.email = user.getEmail(); // Email
|
||||
this.profileImageUrl = user.getProfileImageUrl(); // URL de l'image de profil
|
||||
this.isVerified = user.isVerified(); // Statut de vérification
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
129
src/main/java/com/lions/dev/entity/chat/Conversation.java
Normal file
129
src/main/java/com/lions/dev/entity/chat/Conversation.java
Normal file
@@ -0,0 +1,129 @@
|
||||
package com.lions.dev.entity.chat;
|
||||
|
||||
import com.lions.dev.entity.BaseEntity;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.ArrayList;
|
||||
import java.util.List;
|
||||
|
||||
/**
|
||||
* Entité représentant une conversation entre deux utilisateurs.
|
||||
*
|
||||
* Une conversation contient tous les messages échangés entre deux utilisateurs.
|
||||
* Elle garde une trace du dernier message et du timestamp de la dernière activité.
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "conversations")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@ToString(exclude = {"messages"})
|
||||
public class Conversation extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user1_id", nullable = false)
|
||||
private Users user1; // Premier utilisateur de la conversation
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user2_id", nullable = false)
|
||||
private Users user2; // Deuxième utilisateur de la conversation
|
||||
|
||||
@Column(name = "last_message_content", length = 500)
|
||||
private String lastMessageContent; // Contenu du dernier message
|
||||
|
||||
@Column(name = "last_message_timestamp")
|
||||
private LocalDateTime lastMessageTimestamp; // Timestamp du dernier message
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "last_message_sender_id")
|
||||
private Users lastMessageSender; // Expéditeur du dernier message
|
||||
|
||||
@Column(name = "unread_count_user1")
|
||||
private int unreadCountUser1 = 0; // Nombre de messages non lus pour user1
|
||||
|
||||
@Column(name = "unread_count_user2")
|
||||
private int unreadCountUser2 = 0; // Nombre de messages non lus pour user2
|
||||
|
||||
@OneToMany(mappedBy = "conversation", cascade = CascadeType.ALL, orphanRemoval = true)
|
||||
private List<Message> messages = new ArrayList<>(); // Liste des messages de la conversation
|
||||
|
||||
/**
|
||||
* Constructeur avec les deux utilisateurs.
|
||||
*/
|
||||
public Conversation(Users user1, Users user2) {
|
||||
this.user1 = user1;
|
||||
this.user2 = user2;
|
||||
this.lastMessageTimestamp = LocalDateTime.now();
|
||||
System.out.println("[LOG] Conversation créée entre " + user1.getEmail() + " et " + user2.getEmail());
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour les informations du dernier message.
|
||||
*/
|
||||
public void updateLastMessage(Message message) {
|
||||
this.lastMessageContent = message.getContent();
|
||||
this.lastMessageTimestamp = LocalDateTime.now();
|
||||
this.lastMessageSender = message.getSender();
|
||||
|
||||
// Incrémenter le compteur de non-lus pour le destinataire
|
||||
if (message.getSender().equals(user1)) {
|
||||
unreadCountUser2++;
|
||||
} else {
|
||||
unreadCountUser1++;
|
||||
}
|
||||
|
||||
System.out.println("[LOG] Dernier message mis à jour pour la conversation " + this.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque tous les messages comme lus pour un utilisateur.
|
||||
*/
|
||||
public void markAllAsReadForUser(Users user) {
|
||||
if (user.equals(user1)) {
|
||||
unreadCountUser1 = 0;
|
||||
System.out.println("[LOG] Messages marqués comme lus pour user1 dans la conversation " + this.getId());
|
||||
} else if (user.equals(user2)) {
|
||||
unreadCountUser2 = 0;
|
||||
System.out.println("[LOG] Messages marqués comme lus pour user2 dans la conversation " + this.getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le nombre de messages non lus pour un utilisateur.
|
||||
*/
|
||||
public int getUnreadCountForUser(Users user) {
|
||||
if (user.equals(user1)) {
|
||||
return unreadCountUser1;
|
||||
} else if (user.equals(user2)) {
|
||||
return unreadCountUser2;
|
||||
}
|
||||
return 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère l'autre utilisateur de la conversation.
|
||||
*/
|
||||
public Users getOtherUser(Users user) {
|
||||
if (user.equals(user1)) {
|
||||
return user2;
|
||||
} else if (user.equals(user2)) {
|
||||
return user1;
|
||||
}
|
||||
return null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si un utilisateur fait partie de cette conversation.
|
||||
*/
|
||||
public boolean containsUser(Users user) {
|
||||
return user.equals(user1) || user.equals(user2);
|
||||
}
|
||||
}
|
||||
103
src/main/java/com/lions/dev/entity/chat/Message.java
Normal file
103
src/main/java/com/lions/dev/entity/chat/Message.java
Normal file
@@ -0,0 +1,103 @@
|
||||
package com.lions.dev.entity.chat;
|
||||
|
||||
import com.lions.dev.entity.BaseEntity;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* Entité représentant un message dans le système de messagerie AfterWork.
|
||||
*
|
||||
* Chaque message appartient à une conversation et a un expéditeur et un destinataire.
|
||||
* Les messages peuvent être de type texte, image, ou autres types de médias.
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "messages")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
public class Message extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "conversation_id", nullable = false)
|
||||
private Conversation conversation; // La conversation à laquelle appartient ce message
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "sender_id", nullable = false)
|
||||
private Users sender; // L'utilisateur qui a envoyé le message
|
||||
|
||||
@Column(name = "content", nullable = false, length = 5000)
|
||||
private String content; // Le contenu du message
|
||||
|
||||
@Column(name = "message_type", nullable = false, length = 50)
|
||||
private String messageType = "text"; // Le type de message (text, image, video, file)
|
||||
|
||||
@Column(name = "media_url", length = 500)
|
||||
private String mediaUrl; // URL du média (si applicable)
|
||||
|
||||
@Column(name = "is_read", nullable = false)
|
||||
private boolean isRead = false; // Indique si le message a été lu
|
||||
|
||||
@Column(name = "is_delivered", nullable = false)
|
||||
private boolean isDelivered = false; // Indique si le message a été délivré
|
||||
|
||||
/**
|
||||
* Constructeur avec les paramètres essentiels.
|
||||
*/
|
||||
public Message(Conversation conversation, Users sender, String content) {
|
||||
this.conversation = conversation;
|
||||
this.sender = sender;
|
||||
this.content = content;
|
||||
this.messageType = "text";
|
||||
this.isRead = false;
|
||||
this.isDelivered = false;
|
||||
System.out.println("[LOG] Message créé par " + sender.getEmail() + " dans la conversation " + conversation.getId());
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque le message comme lu.
|
||||
*/
|
||||
public void markAsRead() {
|
||||
if (!this.isRead) {
|
||||
this.isRead = true;
|
||||
System.out.println("[LOG] Message " + this.getId() + " marqué comme lu");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque le message comme délivré.
|
||||
*/
|
||||
public void markAsDelivered() {
|
||||
if (!this.isDelivered) {
|
||||
this.isDelivered = true;
|
||||
System.out.println("[LOG] Message " + this.getId() + " marqué comme délivré");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le message contient un média.
|
||||
*/
|
||||
public boolean hasMedia() {
|
||||
return this.mediaUrl != null && !this.mediaUrl.isEmpty();
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le message est un message texte.
|
||||
*/
|
||||
public boolean isTextMessage() {
|
||||
return "text".equalsIgnoreCase(this.messageType);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si le message est une image.
|
||||
*/
|
||||
public boolean isImageMessage() {
|
||||
return "image".equalsIgnoreCase(this.messageType);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,97 @@
|
||||
package com.lions.dev.entity.notification;
|
||||
|
||||
import com.lions.dev.entity.BaseEntity;
|
||||
import com.lions.dev.entity.events.Events;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* Entité représentant une notification dans le système AfterWork.
|
||||
*
|
||||
* Les notifications peuvent être de différents types :
|
||||
* - event : Notification liée à un événement
|
||||
* - friend : Notification liée à une demande d'ami
|
||||
* - reminder : Rappel d'événement
|
||||
* - other : Autres types de notifications
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "notifications")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
public class Notification extends BaseEntity {
|
||||
|
||||
@Column(name = "title", nullable = false, length = 200)
|
||||
private String title; // Le titre de la notification
|
||||
|
||||
@Column(name = "message", nullable = false, length = 1000)
|
||||
private String message; // Le message de la notification
|
||||
|
||||
@Column(name = "type", nullable = false, length = 50)
|
||||
private String type; // Le type de notification (event, friend, reminder, other)
|
||||
|
||||
@Column(name = "is_read", nullable = false)
|
||||
private boolean isRead = false; // Indique si la notification a été lue
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private Users user; // L'utilisateur destinataire de la notification
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "event_id")
|
||||
private Events event; // L'événement associé (optionnel)
|
||||
|
||||
@Column(name = "metadata", length = 2000)
|
||||
private String metadata; // Métadonnées supplémentaires au format JSON (optionnel)
|
||||
|
||||
/**
|
||||
* Constructeur avec les paramètres essentiels.
|
||||
*/
|
||||
public Notification(String title, String message, String type, Users user) {
|
||||
this.title = title;
|
||||
this.message = message;
|
||||
this.type = type;
|
||||
this.user = user;
|
||||
this.isRead = false;
|
||||
System.out.println("[LOG] Notification créée : " + title + " pour l'utilisateur " + user.getEmail());
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque la notification comme lue.
|
||||
*/
|
||||
public void markAsRead() {
|
||||
if (!this.isRead) {
|
||||
this.isRead = true;
|
||||
System.out.println("[LOG] Notification marquée comme lue : " + this.title);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la notification est liée à un événement.
|
||||
*/
|
||||
public boolean isEventNotification() {
|
||||
return "event".equalsIgnoreCase(this.type) && this.event != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la notification est liée à une demande d'ami.
|
||||
*/
|
||||
public boolean isFriendNotification() {
|
||||
return "friend".equalsIgnoreCase(this.type);
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la notification est un rappel.
|
||||
*/
|
||||
public boolean isReminderNotification() {
|
||||
return "reminder".equalsIgnoreCase(this.type);
|
||||
}
|
||||
}
|
||||
|
||||
92
src/main/java/com/lions/dev/entity/social/SocialPost.java
Normal file
92
src/main/java/com/lions/dev/entity/social/SocialPost.java
Normal file
@@ -0,0 +1,92 @@
|
||||
package com.lions.dev.entity.social;
|
||||
|
||||
import com.lions.dev.entity.BaseEntity;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
/**
|
||||
* Entité représentant un post social dans le système AfterWork.
|
||||
*
|
||||
* Un post social contient du contenu textuel, optionnellement une image,
|
||||
* et des statistiques d'interaction (likes, comments, shares).
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "social_posts")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
public class SocialPost extends BaseEntity {
|
||||
|
||||
@Column(name = "content", nullable = false, length = 2000)
|
||||
private String content; // Le contenu textuel du post
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private Users user; // L'utilisateur créateur du post
|
||||
|
||||
@Column(name = "image_url", length = 500)
|
||||
private String imageUrl; // URL de l'image associée (optionnel)
|
||||
|
||||
@Column(name = "likes_count", nullable = false)
|
||||
private int likesCount = 0; // Nombre de likes
|
||||
|
||||
@Column(name = "comments_count", nullable = false)
|
||||
private int commentsCount = 0; // Nombre de commentaires
|
||||
|
||||
@Column(name = "shares_count", nullable = false)
|
||||
private int sharesCount = 0; // Nombre de partages
|
||||
|
||||
/**
|
||||
* Constructeur avec les paramètres essentiels.
|
||||
*/
|
||||
public SocialPost(String content, Users user) {
|
||||
this.content = content;
|
||||
this.user = user;
|
||||
this.likesCount = 0;
|
||||
this.commentsCount = 0;
|
||||
this.sharesCount = 0;
|
||||
System.out.println("[LOG] Post social créé par l'utilisateur : " + user.getEmail());
|
||||
}
|
||||
|
||||
/**
|
||||
* Incrémente le nombre de likes.
|
||||
*/
|
||||
public void incrementLikes() {
|
||||
this.likesCount++;
|
||||
System.out.println("[LOG] Like ajouté au post ID : " + this.getId() + " (total: " + this.likesCount + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Décrémente le nombre de likes.
|
||||
*/
|
||||
public void decrementLikes() {
|
||||
if (this.likesCount > 0) {
|
||||
this.likesCount--;
|
||||
System.out.println("[LOG] Like retiré du post ID : " + this.getId() + " (total: " + this.likesCount + ")");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Incrémente le nombre de commentaires.
|
||||
*/
|
||||
public void incrementComments() {
|
||||
this.commentsCount++;
|
||||
System.out.println("[LOG] Commentaire ajouté au post ID : " + this.getId() + " (total: " + this.commentsCount + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Incrémente le nombre de partages.
|
||||
*/
|
||||
public void incrementShares() {
|
||||
this.sharesCount++;
|
||||
System.out.println("[LOG] Partage ajouté au post ID : " + this.getId() + " (total: " + this.sharesCount + ")");
|
||||
}
|
||||
}
|
||||
|
||||
9
src/main/java/com/lions/dev/entity/story/MediaType.java
Normal file
9
src/main/java/com/lions/dev/entity/story/MediaType.java
Normal file
@@ -0,0 +1,9 @@
|
||||
package com.lions.dev.entity.story;
|
||||
|
||||
/**
|
||||
* Enumération représentant les types de médias supportés dans les stories.
|
||||
*/
|
||||
public enum MediaType {
|
||||
IMAGE, // Story contenant une image
|
||||
VIDEO // Story contenant une vidéo
|
||||
}
|
||||
117
src/main/java/com/lions/dev/entity/story/Story.java
Normal file
117
src/main/java/com/lions/dev/entity/story/Story.java
Normal file
@@ -0,0 +1,117 @@
|
||||
package com.lions.dev.entity.story;
|
||||
|
||||
import com.lions.dev.entity.BaseEntity;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import jakarta.persistence.*;
|
||||
import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.HashSet;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Entité représentant une story dans le système AfterWork.
|
||||
*
|
||||
* Une story est un contenu éphémère (24h) partagé par un utilisateur.
|
||||
* Elle peut contenir une image ou une vidéo avec des métadonnées associées.
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@Entity
|
||||
@Table(name = "stories")
|
||||
@Getter
|
||||
@Setter
|
||||
@NoArgsConstructor
|
||||
@ToString
|
||||
public class Story extends BaseEntity {
|
||||
|
||||
@ManyToOne(fetch = FetchType.LAZY)
|
||||
@JoinColumn(name = "user_id", nullable = false)
|
||||
private Users user; // L'utilisateur créateur de la story
|
||||
|
||||
@Enumerated(EnumType.STRING)
|
||||
@Column(name = "media_type", nullable = false, length = 20)
|
||||
private MediaType mediaType; // Type de média (IMAGE ou VIDEO)
|
||||
|
||||
@Column(name = "media_url", nullable = false, length = 500)
|
||||
private String mediaUrl; // URL du média (image ou vidéo)
|
||||
|
||||
@Column(name = "thumbnail_url", length = 500)
|
||||
private String thumbnailUrl; // URL du thumbnail (pour les vidéos principalement)
|
||||
|
||||
@Column(name = "duration_seconds")
|
||||
private Integer durationSeconds; // Durée en secondes (pour les vidéos)
|
||||
|
||||
@Column(name = "expires_at", nullable = false)
|
||||
private LocalDateTime expiresAt; // Date d'expiration (24h après création)
|
||||
|
||||
@Column(name = "is_active", nullable = false)
|
||||
private boolean isActive = true; // Indique si la story est active (non expirée)
|
||||
|
||||
@Column(name = "views_count", nullable = false)
|
||||
private int viewsCount = 0; // Nombre de vues
|
||||
|
||||
@ElementCollection
|
||||
@CollectionTable(name = "story_views", joinColumns = @JoinColumn(name = "story_id"))
|
||||
@Column(name = "viewer_id")
|
||||
private Set<UUID> viewerIds = new HashSet<>(); // IDs des utilisateurs qui ont vu la story
|
||||
|
||||
/**
|
||||
* Constructeur avec les paramètres essentiels.
|
||||
*/
|
||||
public Story(Users user, MediaType mediaType, String mediaUrl) {
|
||||
this.user = user;
|
||||
this.mediaType = mediaType;
|
||||
this.mediaUrl = mediaUrl;
|
||||
this.expiresAt = LocalDateTime.now().plusHours(24); // Expire après 24h
|
||||
this.isActive = true;
|
||||
this.viewsCount = 0;
|
||||
System.out.println("[LOG] Story créée par l'utilisateur : " + user.getEmail());
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque une story comme vue par un utilisateur.
|
||||
*
|
||||
* @param viewerId L'ID de l'utilisateur qui voit la story
|
||||
* @return true si c'est une nouvelle vue, false si l'utilisateur avait déjà vu la story
|
||||
*/
|
||||
public boolean markAsViewed(UUID viewerId) {
|
||||
if (viewerIds.add(viewerId)) {
|
||||
this.viewsCount++;
|
||||
System.out.println("[LOG] Story ID : " + this.getId() + " vue par l'utilisateur ID : " + viewerId + " (total: " + this.viewsCount + ")");
|
||||
return true;
|
||||
}
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si la story est expirée.
|
||||
*
|
||||
* @return true si la story est expirée, false sinon
|
||||
*/
|
||||
public boolean isExpired() {
|
||||
return LocalDateTime.now().isAfter(this.expiresAt);
|
||||
}
|
||||
|
||||
/**
|
||||
* Désactive la story (si expirée ou supprimée manuellement).
|
||||
*/
|
||||
public void deactivate() {
|
||||
this.isActive = false;
|
||||
System.out.println("[LOG] Story ID : " + this.getId() + " désactivée");
|
||||
}
|
||||
|
||||
/**
|
||||
* Vérifie si un utilisateur a déjà vu cette story.
|
||||
*
|
||||
* @param viewerId L'ID de l'utilisateur
|
||||
* @return true si l'utilisateur a vu la story, false sinon
|
||||
*/
|
||||
public boolean hasBeenViewedBy(UUID viewerId) {
|
||||
return viewerIds.contains(viewerId);
|
||||
}
|
||||
}
|
||||
@@ -9,7 +9,7 @@ import lombok.Getter;
|
||||
import lombok.NoArgsConstructor;
|
||||
import lombok.Setter;
|
||||
import lombok.ToString;
|
||||
import org.springframework.security.crypto.bcrypt.BCryptPasswordEncoder;
|
||||
import at.favre.lib.crypto.bcrypt.BCrypt;
|
||||
|
||||
/**
|
||||
* Représentation de l'entité Utilisateur dans le système AfterWork.
|
||||
@@ -47,8 +47,17 @@ public class Users extends BaseEntity {
|
||||
@Column(name = "preferred_category")
|
||||
private String preferredCategory; // La catégorie préférée de l'utilisateur
|
||||
|
||||
@Column(name = "is_verified", nullable = false)
|
||||
private boolean isVerified = false; // Indique si l'utilisateur est vérifié (compte officiel)
|
||||
|
||||
@Column(name = "is_online", nullable = false)
|
||||
private boolean isOnline = false; // Indique si l'utilisateur est actuellement en ligne
|
||||
|
||||
@Column(name = "last_seen")
|
||||
private java.time.LocalDateTime lastSeen; // Dernière fois que l'utilisateur était en ligne
|
||||
|
||||
// Utilisation de BCrypt pour hacher les mots de passe de manière sécurisée
|
||||
private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
// private static final BCryptPasswordEncoder encoder = new BCryptPasswordEncoder();
|
||||
|
||||
/**
|
||||
* Hache le mot de passe avec BCrypt et le stocke dans l'attribut `motDePasse`.
|
||||
@@ -56,7 +65,7 @@ public class Users extends BaseEntity {
|
||||
* @param motDePasse Le mot de passe en texte clair à hacher.
|
||||
*/
|
||||
public void setMotDePasse(String motDePasse) {
|
||||
this.motDePasse = encoder.encode(motDePasse);
|
||||
this.motDePasse = BCrypt.withDefaults().hashToString(12, motDePasse.toCharArray());
|
||||
System.out.println("[LOG] Mot de passe haché pour l'utilisateur : " + this.email);
|
||||
}
|
||||
|
||||
@@ -67,7 +76,8 @@ public class Users extends BaseEntity {
|
||||
* @return true si le mot de passe est correct, false sinon.
|
||||
*/
|
||||
public boolean verifierMotDePasse(String motDePasse) {
|
||||
boolean isValid = encoder.matches(motDePasse, this.motDePasse);
|
||||
BCrypt.Result result = BCrypt.verifyer().verify(motDePasse.toCharArray(), this.motDePasse);
|
||||
boolean isValid = result.verified;
|
||||
System.out.println("[LOG] Vérification du mot de passe pour l'utilisateur : " + this.email + " - Résultat : " + isValid);
|
||||
return isValid;
|
||||
}
|
||||
@@ -127,4 +137,22 @@ public class Users extends BaseEntity {
|
||||
System.out.println("[LOG] Catégorie préférée définie pour l'utilisateur : " + this.email + " - Catégorie : " + category);
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour la présence de l'utilisateur (marque comme en ligne et met à jour lastSeen).
|
||||
*/
|
||||
public void updatePresence() {
|
||||
this.isOnline = true;
|
||||
this.lastSeen = java.time.LocalDateTime.now();
|
||||
System.out.println("[LOG] Présence mise à jour pour l'utilisateur : " + this.email + " - Online: true");
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque l'utilisateur comme hors ligne.
|
||||
*/
|
||||
public void setOffline() {
|
||||
this.isOnline = false;
|
||||
this.lastSeen = java.time.LocalDateTime.now();
|
||||
System.out.println("[LOG] Utilisateur marqué hors ligne : " + this.email);
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -0,0 +1,118 @@
|
||||
package com.lions.dev.repository;
|
||||
|
||||
import com.lions.dev.entity.chat.Conversation;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Repository pour la gestion des conversations.
|
||||
*
|
||||
* Ce repository gère les opérations CRUD sur les conversations,
|
||||
* avec des méthodes de recherche pour trouver les conversations d'un utilisateur.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class ConversationRepository implements PanacheRepositoryBase<Conversation, UUID> {
|
||||
|
||||
/**
|
||||
* Trouve une conversation entre deux utilisateurs.
|
||||
*
|
||||
* @param user1 Le premier utilisateur
|
||||
* @param user2 Le deuxième utilisateur
|
||||
* @return La conversation si elle existe, null sinon
|
||||
*/
|
||||
public Conversation findBetweenUsers(Users user1, Users user2) {
|
||||
System.out.println("[LOG] Recherche de conversation entre " + user1.getEmail() + " et " + user2.getEmail());
|
||||
|
||||
// Une conversation peut avoir user1 et user2 dans n'importe quel ordre
|
||||
return find("(user1 = ?1 and user2 = ?2) or (user1 = ?2 and user2 = ?1)", user1, user2)
|
||||
.firstResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Trouve ou crée une conversation entre deux utilisateurs.
|
||||
*
|
||||
* @param user1 Le premier utilisateur
|
||||
* @param user2 Le deuxième utilisateur
|
||||
* @return La conversation (existante ou nouvellement créée)
|
||||
*/
|
||||
public Conversation findOrCreate(Users user1, Users user2) {
|
||||
Conversation conversation = findBetweenUsers(user1, user2);
|
||||
|
||||
if (conversation == null) {
|
||||
conversation = new Conversation(user1, user2);
|
||||
persist(conversation);
|
||||
System.out.println("[LOG] Nouvelle conversation créée avec l'ID : " + conversation.getId());
|
||||
} else {
|
||||
System.out.println("[LOG] Conversation existante trouvée avec l'ID : " + conversation.getId());
|
||||
}
|
||||
|
||||
return conversation;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère toutes les conversations d'un utilisateur.
|
||||
*
|
||||
* @param user L'utilisateur
|
||||
* @return Liste des conversations triées par date du dernier message
|
||||
*/
|
||||
public List<Conversation> findByUser(Users user) {
|
||||
System.out.println("[LOG] Récupération des conversations pour l'utilisateur : " + user.getEmail());
|
||||
return find("user1 = ?1 or user2 = ?1", Sort.by("lastMessageTimestamp", Sort.Direction.Descending), user)
|
||||
.list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère toutes les conversations d'un utilisateur avec un nombre de messages non lus > 0.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Liste des conversations avec des messages non lus
|
||||
*/
|
||||
public List<Conversation> findConversationsWithUnreadMessages(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des conversations avec messages non lus pour l'utilisateur ID : " + userId);
|
||||
|
||||
return find(
|
||||
"(user1.id = ?1 and unreadCountUser1 > 0) or (user2.id = ?1 and unreadCountUser2 > 0)",
|
||||
Sort.by("lastMessageTimestamp", Sort.Direction.Descending),
|
||||
userId
|
||||
).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte le nombre total de messages non lus pour un utilisateur (tous les conversations).
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Le nombre total de messages non lus
|
||||
*/
|
||||
public long countTotalUnreadMessages(UUID userId) {
|
||||
List<Conversation> conversations = find("user1.id = ?1 or user2.id = ?1", userId).list();
|
||||
|
||||
long total = 0;
|
||||
for (Conversation conv : conversations) {
|
||||
if (conv.getUser1().getId().equals(userId)) {
|
||||
total += conv.getUnreadCountUser1();
|
||||
} else {
|
||||
total += conv.getUnreadCountUser2();
|
||||
}
|
||||
}
|
||||
|
||||
System.out.println("[LOG] Total de messages non lus pour l'utilisateur " + userId + " : " + total);
|
||||
return total;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime une conversation.
|
||||
*
|
||||
* @param conversationId L'ID de la conversation
|
||||
* @return true si la conversation a été supprimée, false sinon
|
||||
*/
|
||||
public boolean deleteConversation(UUID conversationId) {
|
||||
System.out.println("[LOG] Suppression de la conversation ID : " + conversationId);
|
||||
return deleteById(conversationId);
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ package com.lions.dev.repository;
|
||||
|
||||
import com.lions.dev.entity.events.Events;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
|
||||
import io.quarkus.panache.common.Page;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
@@ -49,4 +51,33 @@ public class EventsRepository implements PanacheRepositoryBase<Events, UUID> {
|
||||
LOG.info("[LOG] Nombre d'événements trouvés entre les dates " + startDate + " et " + endDate + " : " + events.size());
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les événements créés par l'utilisateur et ses amis (relations d'amitié acceptées).
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @param friendIds Liste des IDs des amis de l'utilisateur
|
||||
* @param page Le numéro de la page (0-indexé)
|
||||
* @param size La taille de la page
|
||||
* @return Liste paginée des événements de l'utilisateur et de ses amis
|
||||
*/
|
||||
public List<Events> findEventsByFriends(UUID userId, List<UUID> friendIds, int page, int size) {
|
||||
LOG.info("[LOG] Récupération des événements des amis pour l'utilisateur ID : " + userId);
|
||||
|
||||
if (friendIds == null || friendIds.isEmpty()) {
|
||||
LOG.info("[LOG] Aucun ami trouvé pour l'utilisateur ID : " + userId);
|
||||
return List.of();
|
||||
}
|
||||
|
||||
// Récupérer les événements de l'utilisateur ET de ses amis
|
||||
List<UUID> allUserIds = new java.util.ArrayList<>(friendIds);
|
||||
allUserIds.add(userId);
|
||||
|
||||
List<Events> events = find("creator.id IN ?1", Sort.by("createdAt", Sort.Direction.Descending), allUserIds)
|
||||
.page(Page.of(page, size))
|
||||
.list();
|
||||
|
||||
LOG.info("[LOG] " + events.size() + " événement(s) récupéré(s) des amis");
|
||||
return events;
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,92 @@
|
||||
package com.lions.dev.repository;
|
||||
|
||||
import com.lions.dev.entity.chat.Conversation;
|
||||
import com.lions.dev.entity.chat.Message;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
|
||||
import io.quarkus.panache.common.Page;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Repository pour la gestion des messages.
|
||||
*
|
||||
* Ce repository gère les opérations CRUD sur les messages,
|
||||
* avec des méthodes de recherche et de pagination.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class MessageRepository implements PanacheRepositoryBase<Message, UUID> {
|
||||
|
||||
/**
|
||||
* 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 (0-indexé)
|
||||
* @param size La taille de la page
|
||||
* @return Liste paginée des messages triés par date (du plus récent au plus ancien)
|
||||
*/
|
||||
public List<Message> findByConversationId(UUID conversationId, int page, int size) {
|
||||
System.out.println("[LOG] Récupération des messages pour la conversation ID : " + conversationId);
|
||||
return find("conversation.id = ?1", Sort.by("createdAt", Sort.Direction.Descending), conversationId)
|
||||
.page(Page.of(page, size))
|
||||
.list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère tous les messages d'une conversation (sans pagination).
|
||||
*
|
||||
* @param conversation La conversation
|
||||
* @return Liste de tous les messages triés par date
|
||||
*/
|
||||
public List<Message> findByConversation(Conversation conversation) {
|
||||
System.out.println("[LOG] Récupération de tous les messages pour la conversation ID : " + conversation.getId());
|
||||
return find("conversation = ?1", Sort.by("createdAt", Sort.Direction.Ascending), conversation).list();
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte le nombre de messages non lus dans une conversation pour un destinataire.
|
||||
*
|
||||
* @param conversationId L'ID de la conversation
|
||||
* @param recipientId L'ID du destinataire
|
||||
* @return Le nombre de messages non lus
|
||||
*/
|
||||
public long countUnreadMessages(UUID conversationId, UUID recipientId) {
|
||||
return count("conversation.id = ?1 and sender.id != ?2 and isRead = false", conversationId, recipientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque tous les messages comme lus pour un destinataire dans une conversation.
|
||||
*
|
||||
* @param conversationId L'ID de la conversation
|
||||
* @param recipientId L'ID du destinataire
|
||||
* @return Le nombre de messages mis à jour
|
||||
*/
|
||||
public int markAllAsRead(UUID conversationId, UUID recipientId) {
|
||||
System.out.println("[LOG] Marquage de tous les messages comme lus pour la conversation " + conversationId);
|
||||
return update("isRead = true where conversation.id = ?1 and sender.id != ?2 and isRead = false", conversationId, recipientId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le dernier message d'une conversation.
|
||||
*
|
||||
* @param conversationId L'ID de la conversation
|
||||
* @return Le dernier message ou null
|
||||
*/
|
||||
public Message findLastMessage(UUID conversationId) {
|
||||
return find("conversation.id = ?1", Sort.by("createdAt", Sort.Direction.Descending), conversationId)
|
||||
.firstResult();
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime tous les messages d'une conversation.
|
||||
*
|
||||
* @param conversationId L'ID de la conversation
|
||||
* @return Le nombre de messages supprimés
|
||||
*/
|
||||
public long deleteByConversationId(UUID conversationId) {
|
||||
System.out.println("[LOG] Suppression de tous les messages pour la conversation " + conversationId);
|
||||
return delete("conversation.id = ?1", conversationId);
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,104 @@
|
||||
package com.lions.dev.repository;
|
||||
|
||||
import com.lions.dev.entity.notification.Notification;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
|
||||
import io.quarkus.panache.common.Page;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Repository pour l'entité Notification.
|
||||
*
|
||||
* Ce repository gère les opérations de base (CRUD) sur les notifications
|
||||
* ainsi que des méthodes personnalisées pour récupérer les notifications
|
||||
* par utilisateur, par statut de lecture, etc.
|
||||
*
|
||||
* Utilisation de Panache pour simplifier les opérations sur la base de données.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class NotificationRepository implements PanacheRepositoryBase<Notification, UUID> {
|
||||
|
||||
/**
|
||||
* Récupère toutes les notifications d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Liste des notifications de l'utilisateur, triées par date de création décroissante
|
||||
*/
|
||||
public List<Notification> findByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des notifications pour l'utilisateur ID : " + userId);
|
||||
List<Notification> notifications = find("user.id", Sort.by("createdAt", Sort.Direction.Descending), userId).list();
|
||||
System.out.println("[LOG] " + notifications.size() + " notification(s) trouvée(s) pour l'utilisateur ID : " + userId);
|
||||
return notifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère toutes les notifications non lues d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Liste des notifications non lues de l'utilisateur
|
||||
*/
|
||||
public List<Notification> findUnreadByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des notifications non lues pour l'utilisateur ID : " + userId);
|
||||
List<Notification> notifications = find("user.id = ?1 AND isRead = false", Sort.by("createdAt", Sort.Direction.Descending), userId).list();
|
||||
System.out.println("[LOG] " + notifications.size() + " notification(s) non lue(s) trouvée(s) pour l'utilisateur ID : " + userId);
|
||||
return notifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les notifications d'un utilisateur avec pagination.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @param page Le numéro de la page (0-indexé)
|
||||
* @param size La taille de la page
|
||||
* @return Liste paginée des notifications
|
||||
*/
|
||||
public List<Notification> findByUserIdWithPagination(UUID userId, int page, int size) {
|
||||
System.out.println("[LOG] Récupération paginée des notifications pour l'utilisateur ID : " + userId + " (page: " + page + ", size: " + size + ")");
|
||||
List<Notification> notifications = find("user.id", Sort.by("createdAt", Sort.Direction.Descending), userId)
|
||||
.page(Page.of(page, size))
|
||||
.list();
|
||||
System.out.println("[LOG] " + notifications.size() + " notification(s) récupérée(s) pour la page " + page);
|
||||
return notifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte le nombre de notifications non lues d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Le nombre de notifications non lues
|
||||
*/
|
||||
public long countUnreadByUserId(UUID userId) {
|
||||
long count = count("user.id = ?1 AND isRead = false", userId);
|
||||
System.out.println("[LOG] " + count + " notification(s) non lue(s) pour l'utilisateur ID : " + userId);
|
||||
return count;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque toutes les notifications d'un utilisateur comme lues.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Le nombre de notifications mises à jour
|
||||
*/
|
||||
public int markAllAsReadByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Marquage de toutes les notifications comme lues pour l'utilisateur ID : " + userId);
|
||||
int updated = update("isRead = true WHERE user.id = ?1", userId);
|
||||
System.out.println("[LOG] " + updated + " notification(s) marquée(s) comme lue(s)");
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime toutes les notifications lues d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Le nombre de notifications supprimées
|
||||
*/
|
||||
public long deleteReadByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Suppression des notifications lues pour l'utilisateur ID : " + userId);
|
||||
long deleted = delete("user.id = ?1 AND isRead = true", userId);
|
||||
System.out.println("[LOG] " + deleted + " notification(s) supprimée(s)");
|
||||
return deleted;
|
||||
}
|
||||
}
|
||||
|
||||
109
src/main/java/com/lions/dev/repository/SocialPostRepository.java
Normal file
109
src/main/java/com/lions/dev/repository/SocialPostRepository.java
Normal file
@@ -0,0 +1,109 @@
|
||||
package com.lions.dev.repository;
|
||||
|
||||
import com.lions.dev.entity.social.SocialPost;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
|
||||
import io.quarkus.panache.common.Page;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Repository pour l'entité SocialPost.
|
||||
*
|
||||
* Ce repository gère les opérations de base (CRUD) sur les posts sociaux
|
||||
* ainsi que des méthodes personnalisées pour la recherche et la pagination.
|
||||
*
|
||||
* Utilisation de Panache pour simplifier les opérations sur la base de données.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class SocialPostRepository implements PanacheRepositoryBase<SocialPost, UUID> {
|
||||
|
||||
/**
|
||||
* Récupère tous les posts avec pagination, triés par date de création décroissante.
|
||||
*
|
||||
* @param page Le numéro de la page (0-indexé)
|
||||
* @param size La taille de la page
|
||||
* @return Liste paginée des posts
|
||||
*/
|
||||
public List<SocialPost> findAllWithPagination(int page, int size) {
|
||||
System.out.println("[LOG] Récupération paginée des posts (page: " + page + ", size: " + size + ")");
|
||||
List<SocialPost> posts = findAll(Sort.by("createdAt", Sort.Direction.Descending))
|
||||
.page(Page.of(page, size))
|
||||
.list();
|
||||
System.out.println("[LOG] " + posts.size() + " post(s) récupéré(s)");
|
||||
return posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère tous les posts d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Liste des posts de l'utilisateur
|
||||
*/
|
||||
public List<SocialPost> findByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des posts pour l'utilisateur ID : " + userId);
|
||||
List<SocialPost> posts = find("user.id", Sort.by("createdAt", Sort.Direction.Descending), userId).list();
|
||||
System.out.println("[LOG] " + posts.size() + " post(s) trouvé(s) pour l'utilisateur ID : " + userId);
|
||||
return posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recherche des posts par contenu (recherche textuelle).
|
||||
*
|
||||
* @param query Le terme de recherche
|
||||
* @return Liste des posts correspondant à la recherche
|
||||
*/
|
||||
public List<SocialPost> searchByContent(String query) {
|
||||
System.out.println("[LOG] Recherche de posts avec la requête : " + query);
|
||||
String searchPattern = "%" + query.toLowerCase() + "%";
|
||||
List<SocialPost> posts = find("LOWER(content) LIKE ?1", Sort.by("createdAt", Sort.Direction.Descending), searchPattern).list();
|
||||
System.out.println("[LOG] " + posts.size() + " post(s) trouvé(s) pour la requête : " + query);
|
||||
return posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les posts les plus populaires (par nombre de likes).
|
||||
*
|
||||
* @param limit Le nombre maximum de posts à retourner
|
||||
* @return Liste des posts les plus populaires
|
||||
*/
|
||||
public List<SocialPost> findPopularPosts(int limit) {
|
||||
System.out.println("[LOG] Récupération des " + limit + " posts les plus populaires");
|
||||
List<SocialPost> posts = findAll(Sort.by("likesCount", Sort.Direction.Descending))
|
||||
.page(0, limit)
|
||||
.list();
|
||||
System.out.println("[LOG] " + posts.size() + " post(s) populaire(s) récupéré(s)");
|
||||
return posts;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les posts des amis d'un utilisateur (relations d'amitié acceptées).
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @param friendIds Liste des IDs des amis de l'utilisateur
|
||||
* @param page Le numéro de la page (0-indexé)
|
||||
* @param size La taille de la page
|
||||
* @return Liste paginée des posts des amis
|
||||
*/
|
||||
public List<SocialPost> findPostsByFriends(UUID userId, List<UUID> friendIds, int page, int size) {
|
||||
System.out.println("[LOG] Récupération des posts des amis pour l'utilisateur ID : " + userId);
|
||||
|
||||
if (friendIds == null || friendIds.isEmpty()) {
|
||||
System.out.println("[LOG] Aucun ami trouvé pour l'utilisateur ID : " + userId);
|
||||
return List.of();
|
||||
}
|
||||
|
||||
// Récupérer les posts de l'utilisateur ET de ses amis
|
||||
List<UUID> allUserIds = new java.util.ArrayList<>(friendIds);
|
||||
allUserIds.add(userId);
|
||||
|
||||
List<SocialPost> posts = find("user.id IN ?1", Sort.by("createdAt", Sort.Direction.Descending), allUserIds)
|
||||
.page(Page.of(page, size))
|
||||
.list();
|
||||
|
||||
System.out.println("[LOG] " + posts.size() + " post(s) récupéré(s) des amis");
|
||||
return posts;
|
||||
}
|
||||
}
|
||||
|
||||
107
src/main/java/com/lions/dev/repository/StoryRepository.java
Normal file
107
src/main/java/com/lions/dev/repository/StoryRepository.java
Normal file
@@ -0,0 +1,107 @@
|
||||
package com.lions.dev.repository;
|
||||
|
||||
import com.lions.dev.entity.story.Story;
|
||||
import io.quarkus.hibernate.orm.panache.PanacheRepositoryBase;
|
||||
import io.quarkus.panache.common.Sort;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Repository pour l'entité Story.
|
||||
*
|
||||
* Ce repository gère les opérations de base (CRUD) sur les stories
|
||||
* ainsi que des méthodes personnalisées pour gérer l'expiration et les vues.
|
||||
*
|
||||
* Utilisation de Panache pour simplifier les opérations sur la base de données.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class StoryRepository implements PanacheRepositoryBase<Story, UUID> {
|
||||
|
||||
/**
|
||||
* Récupère toutes les stories actives (non expirées), triées par date de création décroissante.
|
||||
*
|
||||
* @return Liste des stories actives
|
||||
*/
|
||||
public List<Story> findAllActive() {
|
||||
System.out.println("[LOG] Récupération de toutes les stories actives");
|
||||
List<Story> stories = find("isActive = true AND expiresAt > ?1",
|
||||
Sort.by("createdAt", Sort.Direction.Descending),
|
||||
LocalDateTime.now()).list();
|
||||
System.out.println("[LOG] " + stories.size() + " story(ies) active(s) récupérée(s)");
|
||||
return stories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère toutes les stories actives d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Liste des stories actives de l'utilisateur
|
||||
*/
|
||||
public List<Story> findActiveByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des stories actives pour l'utilisateur ID : " + userId);
|
||||
List<Story> stories = find("user.id = ?1 AND isActive = true AND expiresAt > ?2",
|
||||
Sort.by("createdAt", Sort.Direction.Descending),
|
||||
userId,
|
||||
LocalDateTime.now()).list();
|
||||
System.out.println("[LOG] " + stories.size() + " story(ies) active(s) trouvée(s) pour l'utilisateur ID : " + userId);
|
||||
return stories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère toutes les stories (actives et inactives) d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Liste de toutes les stories de l'utilisateur
|
||||
*/
|
||||
public List<Story> findAllByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Récupération de toutes les stories pour l'utilisateur ID : " + userId);
|
||||
List<Story> stories = find("user.id", Sort.by("createdAt", Sort.Direction.Descending), userId).list();
|
||||
System.out.println("[LOG] " + stories.size() + " story(ies) trouvée(s) pour l'utilisateur ID : " + userId);
|
||||
return stories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère toutes les stories expirées qui sont encore marquées comme actives.
|
||||
* Utile pour un job de nettoyage périodique.
|
||||
*
|
||||
* @return Liste des stories expirées
|
||||
*/
|
||||
public List<Story> findExpiredStories() {
|
||||
System.out.println("[LOG] Récupération des stories expirées");
|
||||
List<Story> stories = find("isActive = true AND expiresAt <= ?1", LocalDateTime.now()).list();
|
||||
System.out.println("[LOG] " + stories.size() + " story(ies) expirée(s) trouvée(s)");
|
||||
return stories;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque toutes les stories expirées comme inactives.
|
||||
* Cette méthode doit être appelée périodiquement par un job.
|
||||
*
|
||||
* @return Le nombre de stories désactivées
|
||||
*/
|
||||
public int deactivateExpiredStories() {
|
||||
System.out.println("[LOG] Désactivation des stories expirées");
|
||||
int updated = update("isActive = false WHERE isActive = true AND expiresAt <= ?1", LocalDateTime.now());
|
||||
System.out.println("[LOG] " + updated + " story(ies) désactivée(s)");
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les stories les plus vues.
|
||||
*
|
||||
* @param limit Le nombre maximum de stories à retourner
|
||||
* @return Liste des stories les plus vues
|
||||
*/
|
||||
public List<Story> findMostViewedStories(int limit) {
|
||||
System.out.println("[LOG] Récupération des " + limit + " stories les plus vues");
|
||||
List<Story> stories = find("isActive = true AND expiresAt > ?1",
|
||||
Sort.by("viewsCount", Sort.Direction.Descending),
|
||||
LocalDateTime.now())
|
||||
.page(0, limit)
|
||||
.list();
|
||||
System.out.println("[LOG] " + stories.size() + " story(ies) populaire(s) récupérée(s)");
|
||||
return stories;
|
||||
}
|
||||
}
|
||||
@@ -834,5 +834,39 @@ public class EventsResource {
|
||||
return Response.ok("L'événement a été réouvert avec succès.").build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint pour récupérer les événements de l'utilisateur et de ses amis.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @param page Le numéro de la page (0-indexé)
|
||||
* @param size La taille de la page
|
||||
* @return Liste paginée des événements de l'utilisateur et de ses amis
|
||||
*/
|
||||
@GET
|
||||
@Path("/friends/{userId}")
|
||||
@Operation(
|
||||
summary = "Récupérer les événements des amis",
|
||||
description = "Retourne les événements créés par l'utilisateur et ses amis (relations d'amitié acceptées)")
|
||||
public Response getEventsByFriends(
|
||||
@PathParam("userId") UUID userId,
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size) {
|
||||
LOG.info("[LOG] Récupération des événements des amis pour l'utilisateur ID : " + userId);
|
||||
|
||||
try {
|
||||
List<Events> events = eventService.getEventsByFriends(userId, page, size);
|
||||
List<EventCreateResponseDTO> responseDTOs = events.stream()
|
||||
.map(EventCreateResponseDTO::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Response.ok(responseDTOs).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération des événements des amis : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération des événements des amis.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
|
||||
@@ -486,4 +486,53 @@ public class FriendshipResource {
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer les suggestions d'amis pour un utilisateur.
|
||||
*
|
||||
* @param userId ID de l'utilisateur.
|
||||
* @param limit Nombre maximum de suggestions (optionnel, par défaut 10).
|
||||
* @return Liste des suggestions d'amis.
|
||||
*/
|
||||
@GET
|
||||
@Path("/suggestions/{userId}")
|
||||
@Operation(
|
||||
summary = "Récupérer les suggestions d'amis",
|
||||
description = "Retourne une liste d'utilisateurs suggérés comme amis potentiels basée sur les amis en commun")
|
||||
@APIResponses({
|
||||
@APIResponse(
|
||||
responseCode = "200",
|
||||
description = "Suggestions d'amis récupérées",
|
||||
content =
|
||||
@Content(
|
||||
mediaType = MediaType.APPLICATION_JSON,
|
||||
schema = @Schema(implementation = com.lions.dev.dto.response.users.FriendSuggestionResponseDTO.class))),
|
||||
@APIResponse(responseCode = "404", description = "Utilisateur non trouvé"),
|
||||
@APIResponse(
|
||||
responseCode = "500",
|
||||
description = "Erreur lors de la récupération des suggestions d'amis")
|
||||
})
|
||||
public Response getFriendSuggestions(
|
||||
@PathParam("userId") UUID userId,
|
||||
@QueryParam("limit") @DefaultValue("10") int limit) {
|
||||
logger.info("[LOG] Récupération des suggestions d'amis pour l'utilisateur : " + userId);
|
||||
|
||||
try {
|
||||
List<com.lions.dev.dto.response.users.FriendSuggestionResponseDTO> suggestions =
|
||||
friendshipService.getFriendSuggestions(userId, limit);
|
||||
logger.info("[LOG] " + suggestions.size() + " suggestions d'amis récupérées avec succès.");
|
||||
return Response.ok(suggestions).build();
|
||||
} catch (UserNotFoundException e) {
|
||||
logger.error("[ERROR] Utilisateur non trouvé : " + e.getMessage());
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Utilisateur non trouvé.\"}")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
logger.error(
|
||||
"[ERROR] Erreur lors de la récupération des suggestions d'amis : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération des suggestions d'amis.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
333
src/main/java/com/lions/dev/resource/MessageResource.java
Normal file
333
src/main/java/com/lions/dev/resource/MessageResource.java
Normal file
@@ -0,0 +1,333 @@
|
||||
package com.lions.dev.resource;
|
||||
|
||||
import com.lions.dev.dto.request.chat.SendMessageRequestDTO;
|
||||
import com.lions.dev.dto.response.chat.ConversationResponseDTO;
|
||||
import com.lions.dev.dto.response.chat.MessageResponseDTO;
|
||||
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.repository.UsersRepository;
|
||||
import com.lions.dev.service.MessageService;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Resource REST pour la gestion des messages et conversations.
|
||||
*
|
||||
* Cette classe expose les endpoints HTTP pour:
|
||||
* - Envoyer des messages
|
||||
* - Récupérer les conversations
|
||||
* - Récupérer les messages d'une conversation
|
||||
* - Marquer les messages comme lus
|
||||
* - Supprimer des messages et conversations
|
||||
*/
|
||||
@Path("/messages")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Messages", description = "Gestion des messages et conversations")
|
||||
public class MessageResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(MessageResource.class);
|
||||
|
||||
@Inject
|
||||
MessageService messageService;
|
||||
|
||||
@Inject
|
||||
UsersRepository usersRepository;
|
||||
|
||||
/**
|
||||
* Envoie un nouveau message.
|
||||
*
|
||||
* @param request Le DTO contenant les informations du message
|
||||
* @return Le message créé
|
||||
*/
|
||||
@POST
|
||||
@Operation(summary = "Envoyer un message", description = "Envoie un nouveau message à un utilisateur")
|
||||
public Response sendMessage(SendMessageRequestDTO request) {
|
||||
LOG.info("[LOG] Réception d'une demande d'envoi de message");
|
||||
|
||||
try {
|
||||
// Validation
|
||||
if (!request.isValid()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity("{\"message\": \"Données invalides\"}")
|
||||
.build();
|
||||
}
|
||||
|
||||
// Envoyer le message
|
||||
Message message = messageService.sendMessage(
|
||||
request.getSenderId(),
|
||||
request.getRecipientId(),
|
||||
request.getContent(),
|
||||
request.getMessageType(),
|
||||
request.getMediaUrl()
|
||||
);
|
||||
|
||||
MessageResponseDTO response = new MessageResponseDTO(message);
|
||||
return Response.status(Response.Status.CREATED).entity(response).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de l'envoi du message : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de l'envoi du message\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère toutes les conversations d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Liste des conversations
|
||||
*/
|
||||
@GET
|
||||
@Path("/conversations/{userId}")
|
||||
@Operation(summary = "Récupérer les conversations", description = "Récupère toutes les conversations d'un utilisateur")
|
||||
public Response getUserConversations(@PathParam("userId") UUID userId) {
|
||||
LOG.info("[LOG] Récupération des conversations pour l'utilisateur ID : " + userId);
|
||||
|
||||
try {
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Utilisateur non trouvé\"}")
|
||||
.build();
|
||||
}
|
||||
|
||||
List<Conversation> conversations = messageService.getUserConversations(userId);
|
||||
List<ConversationResponseDTO> response = conversations.stream()
|
||||
.map(conv -> new ConversationResponseDTO(conv, user))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Response.ok(response).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération des conversations : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération des conversations\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les messages d'une conversation.
|
||||
*
|
||||
* @param conversationId L'ID de la conversation
|
||||
* @param page Le numéro de la page (défaut: 0)
|
||||
* @param size La taille de la page (défaut: 50)
|
||||
* @return Liste des messages
|
||||
*/
|
||||
@GET
|
||||
@Path("/conversation/{conversationId}")
|
||||
@Operation(summary = "Récupérer les messages", description = "Récupère les messages d'une conversation avec pagination")
|
||||
public Response getConversationMessages(
|
||||
@PathParam("conversationId") UUID conversationId,
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("50") int size) {
|
||||
LOG.info("[LOG] Récupération des messages pour la conversation ID : " + conversationId);
|
||||
|
||||
try {
|
||||
List<Message> messages = messageService.getConversationMessages(conversationId, page, size);
|
||||
List<MessageResponseDTO> response = messages.stream()
|
||||
.map(MessageResponseDTO::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Response.ok(response).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération des messages : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération des messages\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* 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
|
||||
*/
|
||||
@GET
|
||||
@Path("/conversation/between/{user1Id}/{user2Id}")
|
||||
@Operation(summary = "Récupérer une conversation", description = "Récupère la conversation entre deux utilisateurs")
|
||||
public Response getConversationBetweenUsers(
|
||||
@PathParam("user1Id") UUID user1Id,
|
||||
@PathParam("user2Id") UUID user2Id) {
|
||||
LOG.info("[LOG] Recherche de conversation entre " + user1Id + " et " + user2Id);
|
||||
|
||||
try {
|
||||
Users user1 = usersRepository.findById(user1Id);
|
||||
if (user1 == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Utilisateur non trouvé\"}")
|
||||
.build();
|
||||
}
|
||||
|
||||
Conversation conversation = messageService.getConversationBetweenUsers(user1Id, user2Id);
|
||||
|
||||
if (conversation == null) {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Conversation non trouvée\"}")
|
||||
.build();
|
||||
}
|
||||
|
||||
ConversationResponseDTO response = new ConversationResponseDTO(conversation, user1);
|
||||
return Response.ok(response).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération de la conversation : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération de la conversation\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque un message comme lu.
|
||||
*
|
||||
* @param messageId L'ID du message
|
||||
* @return Le message mis à jour
|
||||
*/
|
||||
@PUT
|
||||
@Path("/{messageId}/read")
|
||||
@Operation(summary = "Marquer comme lu", description = "Marque un message comme lu")
|
||||
public Response markMessageAsRead(@PathParam("messageId") UUID messageId) {
|
||||
LOG.info("[LOG] Marquage du message comme lu : " + messageId);
|
||||
|
||||
try {
|
||||
Message message = messageService.markMessageAsRead(messageId);
|
||||
MessageResponseDTO response = new MessageResponseDTO(message);
|
||||
return Response.ok(response).build();
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors du marquage du message : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors du marquage du message\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque tous les messages d'une conversation comme lus.
|
||||
*
|
||||
* @param conversationId L'ID de la conversation
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Le nombre de messages marqués comme lus
|
||||
*/
|
||||
@PUT
|
||||
@Path("/conversation/{conversationId}/read/{userId}")
|
||||
@Operation(summary = "Marquer tout comme lu", description = "Marque tous les messages d'une conversation comme lus")
|
||||
public Response markAllMessagesAsRead(
|
||||
@PathParam("conversationId") UUID conversationId,
|
||||
@PathParam("userId") UUID userId) {
|
||||
LOG.info("[LOG] Marquage de tous les messages comme lus pour la conversation " + conversationId);
|
||||
|
||||
try {
|
||||
int count = messageService.markAllMessagesAsRead(conversationId, userId);
|
||||
return Response.ok("{\"messagesMarkedAsRead\": " + count + "}").build();
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors du marquage des messages : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors du marquage des messages\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le nombre total de messages non lus pour un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Le nombre de messages non lus
|
||||
*/
|
||||
@GET
|
||||
@Path("/unread/count/{userId}")
|
||||
@Operation(summary = "Compter les non lus", description = "Compte le nombre total de messages non lus")
|
||||
public Response getTotalUnreadCount(@PathParam("userId") UUID userId) {
|
||||
LOG.info("[LOG] Récupération du nombre de messages non lus pour l'utilisateur " + userId);
|
||||
|
||||
try {
|
||||
long count = messageService.getTotalUnreadCount(userId);
|
||||
return Response.ok("{\"unreadCount\": " + count + "}").build();
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors du comptage des messages non lus : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors du comptage\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime un message.
|
||||
*
|
||||
* @param messageId L'ID du message
|
||||
* @return Confirmation de suppression
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/{messageId}")
|
||||
@Operation(summary = "Supprimer un message", description = "Supprime un message")
|
||||
public Response deleteMessage(@PathParam("messageId") UUID messageId) {
|
||||
LOG.info("[LOG] Suppression du message ID : " + messageId);
|
||||
|
||||
try {
|
||||
boolean deleted = messageService.deleteMessage(messageId);
|
||||
|
||||
if (deleted) {
|
||||
return Response.ok("{\"message\": \"Message supprimé avec succès\"}").build();
|
||||
} else {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Message non trouvé\"}")
|
||||
.build();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la suppression du message : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la suppression\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime une conversation.
|
||||
*
|
||||
* @param conversationId L'ID de la conversation
|
||||
* @return Confirmation de suppression
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/conversation/{conversationId}")
|
||||
@Operation(summary = "Supprimer une conversation", description = "Supprime une conversation et tous ses messages")
|
||||
public Response deleteConversation(@PathParam("conversationId") UUID conversationId) {
|
||||
LOG.info("[LOG] Suppression de la conversation ID : " + conversationId);
|
||||
|
||||
try {
|
||||
boolean deleted = messageService.deleteConversation(conversationId);
|
||||
|
||||
if (deleted) {
|
||||
return Response.ok("{\"message\": \"Conversation supprimée avec succès\"}").build();
|
||||
} else {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Conversation non trouvée\"}")
|
||||
.build();
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la suppression de la conversation : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la suppression\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
247
src/main/java/com/lions/dev/resource/NotificationResource.java
Normal file
247
src/main/java/com/lions/dev/resource/NotificationResource.java
Normal file
@@ -0,0 +1,247 @@
|
||||
package com.lions.dev.resource;
|
||||
|
||||
import com.lions.dev.dto.response.notifications.NotificationResponseDTO;
|
||||
import com.lions.dev.entity.notification.Notification;
|
||||
import com.lions.dev.service.NotificationService;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Ressource REST pour la gestion des notifications dans le système AfterWork.
|
||||
*
|
||||
* Cette classe expose des endpoints pour créer, récupérer, mettre à jour
|
||||
* et supprimer des notifications.
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@Path("/notifications")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Notifications", description = "Opérations liées à la gestion des notifications")
|
||||
public class NotificationResource {
|
||||
|
||||
@Inject
|
||||
NotificationService notificationService;
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(NotificationResource.class);
|
||||
|
||||
/**
|
||||
* Récupère toutes les notifications d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Liste des notifications de l'utilisateur
|
||||
*/
|
||||
@GET
|
||||
@Path("/user/{userId}")
|
||||
@Operation(
|
||||
summary = "Récupérer les notifications d'un utilisateur",
|
||||
description = "Retourne la liste de toutes les notifications d'un utilisateur, triées par date de création décroissante")
|
||||
public Response getNotificationsByUserId(@PathParam("userId") UUID userId) {
|
||||
LOG.info("[LOG] Récupération des notifications pour l'utilisateur ID : " + userId);
|
||||
|
||||
try {
|
||||
List<Notification> notifications = notificationService.getNotificationsByUserId(userId);
|
||||
List<NotificationResponseDTO> responseDTOs = notifications.stream()
|
||||
.map(NotificationResponseDTO::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
LOG.info("[LOG] " + responseDTOs.size() + " notification(s) récupérée(s) pour l'utilisateur ID : " + userId);
|
||||
return Response.ok(responseDTOs).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération des notifications : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération des notifications.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les notifications d'un utilisateur avec pagination.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @param page Le numéro de la page (0-indexé)
|
||||
* @param size La taille de la page
|
||||
* @return Liste paginée des notifications
|
||||
*/
|
||||
@GET
|
||||
@Path("/user/{userId}/paginated")
|
||||
@Operation(
|
||||
summary = "Récupérer les notifications d'un utilisateur avec pagination",
|
||||
description = "Retourne une liste paginée des notifications d'un utilisateur")
|
||||
public Response getNotificationsByUserIdWithPagination(
|
||||
@PathParam("userId") UUID userId,
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size) {
|
||||
LOG.info("[LOG] Récupération paginée des notifications pour l'utilisateur ID : " + userId);
|
||||
|
||||
try {
|
||||
List<Notification> notifications = notificationService.getNotificationsByUserIdWithPagination(userId, page, size);
|
||||
List<NotificationResponseDTO> responseDTOs = notifications.stream()
|
||||
.map(NotificationResponseDTO::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Response.ok(responseDTOs).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération paginée des notifications : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération des notifications.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque une notification comme lue.
|
||||
*
|
||||
* @param notificationId L'ID de la notification
|
||||
* @return La notification mise à jour
|
||||
*/
|
||||
@PUT
|
||||
@Path("/{id}/read")
|
||||
@Transactional
|
||||
@Operation(
|
||||
summary = "Marquer une notification comme lue",
|
||||
description = "Marque une notification spécifique comme lue")
|
||||
public Response markAsRead(@PathParam("id") UUID notificationId) {
|
||||
LOG.info("[LOG] Marquage de la notification ID : " + notificationId + " comme lue");
|
||||
|
||||
try {
|
||||
Notification notification = notificationService.markAsRead(notificationId);
|
||||
NotificationResponseDTO responseDTO = new NotificationResponseDTO(notification);
|
||||
return Response.ok(responseDTO).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warn("[WARN] Notification non trouvée : " + e.getMessage());
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Notification non trouvée.\"}")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors du marquage de la notification : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors du marquage de la notification.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque toutes les notifications d'un utilisateur comme lues.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Nombre de notifications mises à jour
|
||||
*/
|
||||
@PUT
|
||||
@Path("/user/{userId}/mark-all-read")
|
||||
@Transactional
|
||||
@Operation(
|
||||
summary = "Marquer toutes les notifications comme lues",
|
||||
description = "Marque toutes les notifications d'un utilisateur comme lues")
|
||||
public Response markAllAsRead(@PathParam("userId") UUID userId) {
|
||||
LOG.info("[LOG] Marquage de toutes les notifications comme lues pour l'utilisateur ID : " + userId);
|
||||
|
||||
try {
|
||||
int updated = notificationService.markAllAsRead(userId);
|
||||
return Response.ok("{\"message\": \"" + updated + " notification(s) marquée(s) comme lue(s).\"}").build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors du marquage de toutes les notifications : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors du marquage des notifications.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime une notification.
|
||||
*
|
||||
* @param notificationId L'ID de la notification
|
||||
* @return Réponse de confirmation
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/{id}")
|
||||
@Transactional
|
||||
@Operation(
|
||||
summary = "Supprimer une notification",
|
||||
description = "Supprime une notification spécifique")
|
||||
public Response deleteNotification(@PathParam("id") UUID notificationId) {
|
||||
LOG.info("[LOG] Suppression de la notification ID : " + notificationId);
|
||||
|
||||
try {
|
||||
boolean deleted = notificationService.deleteNotification(notificationId);
|
||||
if (deleted) {
|
||||
return Response.noContent().build();
|
||||
} else {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Notification non trouvée.\"}")
|
||||
.build();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la suppression de la notification : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la suppression de la notification.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère une notification par son ID.
|
||||
*
|
||||
* @param notificationId L'ID de la notification
|
||||
* @return La notification trouvée
|
||||
*/
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(
|
||||
summary = "Récupérer une notification par ID",
|
||||
description = "Retourne les détails d'une notification spécifique")
|
||||
public Response getNotificationById(@PathParam("id") UUID notificationId) {
|
||||
LOG.info("[LOG] Récupération de la notification ID : " + notificationId);
|
||||
|
||||
try {
|
||||
Notification notification = notificationService.getNotificationById(notificationId);
|
||||
NotificationResponseDTO responseDTO = new NotificationResponseDTO(notification);
|
||||
return Response.ok(responseDTO).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warn("[WARN] Notification non trouvée : " + e.getMessage());
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Notification non trouvée.\"}")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération de la notification : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération de la notification.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte le nombre de notifications non lues d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Le nombre de notifications non lues
|
||||
*/
|
||||
@GET
|
||||
@Path("/user/{userId}/unread-count")
|
||||
@Operation(
|
||||
summary = "Compter les notifications non lues",
|
||||
description = "Retourne le nombre de notifications non lues d'un utilisateur")
|
||||
public Response getUnreadCount(@PathParam("userId") UUID userId) {
|
||||
LOG.info("[LOG] Comptage des notifications non lues pour l'utilisateur ID : " + userId);
|
||||
|
||||
try {
|
||||
long count = notificationService.countUnreadNotifications(userId);
|
||||
return Response.ok("{\"count\": " + count + "}").build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors du comptage des notifications : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors du comptage des notifications.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
394
src/main/java/com/lions/dev/resource/SocialPostResource.java
Normal file
394
src/main/java/com/lions/dev/resource/SocialPostResource.java
Normal file
@@ -0,0 +1,394 @@
|
||||
package com.lions.dev.resource;
|
||||
|
||||
import com.lions.dev.dto.request.social.SocialPostCreateRequestDTO;
|
||||
import com.lions.dev.dto.response.social.SocialPostResponseDTO;
|
||||
import com.lions.dev.entity.social.SocialPost;
|
||||
import com.lions.dev.service.SocialPostService;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Ressource REST pour la gestion des posts sociaux dans le système AfterWork.
|
||||
*
|
||||
* Cette classe expose des endpoints pour créer, récupérer, mettre à jour,
|
||||
* supprimer et rechercher des posts sociaux.
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@Path("/posts")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Social Posts", description = "Opérations liées à la gestion des posts sociaux")
|
||||
public class SocialPostResource {
|
||||
|
||||
@Inject
|
||||
SocialPostService socialPostService;
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(SocialPostResource.class);
|
||||
|
||||
/**
|
||||
* Récupère tous les posts avec pagination.
|
||||
*
|
||||
* @param page Le numéro de la page (0-indexé)
|
||||
* @param size La taille de la page
|
||||
* @return Liste paginée des posts
|
||||
*/
|
||||
@GET
|
||||
@Operation(
|
||||
summary = "Récupérer tous les posts",
|
||||
description = "Retourne une liste paginée de tous les posts sociaux, triés par date de création décroissante")
|
||||
public Response getAllPosts(
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size) {
|
||||
LOG.info("[LOG] Récupération de tous les posts (page: " + page + ", size: " + size + ")");
|
||||
|
||||
try {
|
||||
List<SocialPost> posts = socialPostService.getAllPosts(page, size);
|
||||
List<SocialPostResponseDTO> responseDTOs = posts.stream()
|
||||
.map(SocialPostResponseDTO::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Response.ok(responseDTOs).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération des posts : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération des posts.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère un post par son ID.
|
||||
*
|
||||
* @param postId L'ID du post
|
||||
* @return Le post trouvé
|
||||
*/
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(
|
||||
summary = "Récupérer un post par ID",
|
||||
description = "Retourne les détails d'un post social spécifique")
|
||||
public Response getPostById(@PathParam("id") UUID postId) {
|
||||
LOG.info("[LOG] Récupération du post ID : " + postId);
|
||||
|
||||
try {
|
||||
SocialPost post = socialPostService.getPostById(postId);
|
||||
SocialPostResponseDTO responseDTO = new SocialPostResponseDTO(post);
|
||||
return Response.ok(responseDTO).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warn("[WARN] Post non trouvé : " + e.getMessage());
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Post non trouvé.\"}")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération du post : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération du post.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée un nouveau post social.
|
||||
*
|
||||
* @param requestDTO Le DTO contenant les informations du post à créer
|
||||
* @return Le post créé
|
||||
*/
|
||||
@POST
|
||||
@Transactional
|
||||
@Operation(
|
||||
summary = "Créer un nouveau post",
|
||||
description = "Crée un nouveau post social et retourne ses détails")
|
||||
public Response createPost(@Valid SocialPostCreateRequestDTO requestDTO) {
|
||||
LOG.info("[LOG] Création d'un nouveau post par l'utilisateur ID : " + requestDTO.getUserId());
|
||||
|
||||
try {
|
||||
SocialPost post = socialPostService.createPost(
|
||||
requestDTO.getContent(),
|
||||
requestDTO.getUserId(),
|
||||
requestDTO.getImageUrl()
|
||||
);
|
||||
SocialPostResponseDTO responseDTO = new SocialPostResponseDTO(post);
|
||||
return Response.status(Response.Status.CREATED).entity(responseDTO).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la création du post : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la création du post.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour un post.
|
||||
*
|
||||
* @param postId L'ID du post
|
||||
* @param content Le nouveau contenu
|
||||
* @param imageUrl La nouvelle URL d'image (optionnel)
|
||||
* @return Le post mis à jour
|
||||
*/
|
||||
@PUT
|
||||
@Path("/{id}")
|
||||
@Transactional
|
||||
@Operation(
|
||||
summary = "Mettre à jour un post",
|
||||
description = "Met à jour le contenu et/ou l'image d'un post existant")
|
||||
public Response updatePost(
|
||||
@PathParam("id") UUID postId,
|
||||
@QueryParam("content") String content,
|
||||
@QueryParam("imageUrl") String imageUrl) {
|
||||
LOG.info("[LOG] Mise à jour du post ID : " + postId);
|
||||
|
||||
try {
|
||||
SocialPost post = socialPostService.updatePost(postId, content, imageUrl);
|
||||
SocialPostResponseDTO responseDTO = new SocialPostResponseDTO(post);
|
||||
return Response.ok(responseDTO).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warn("[WARN] Post non trouvé : " + e.getMessage());
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Post non trouvé.\"}")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la mise à jour du post : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la mise à jour du post.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime un post.
|
||||
*
|
||||
* @param postId L'ID du post
|
||||
* @return Réponse de confirmation
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/{id}")
|
||||
@Transactional
|
||||
@Operation(
|
||||
summary = "Supprimer un post",
|
||||
description = "Supprime un post social spécifique")
|
||||
public Response deletePost(@PathParam("id") UUID postId) {
|
||||
LOG.info("[LOG] Suppression du post ID : " + postId);
|
||||
|
||||
try {
|
||||
boolean deleted = socialPostService.deletePost(postId);
|
||||
if (deleted) {
|
||||
return Response.noContent().build();
|
||||
} else {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Post non trouvé.\"}")
|
||||
.build();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la suppression du post : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la suppression du post.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Recherche des posts par contenu.
|
||||
*
|
||||
* @param query Le terme de recherche
|
||||
* @return Liste des posts correspondant à la recherche
|
||||
*/
|
||||
@GET
|
||||
@Path("/search")
|
||||
@Operation(
|
||||
summary = "Rechercher des posts",
|
||||
description = "Recherche des posts sociaux par contenu textuel")
|
||||
public Response searchPosts(@QueryParam("q") String query) {
|
||||
LOG.info("[LOG] Recherche de posts avec la requête : " + query);
|
||||
|
||||
if (query == null || query.trim().isEmpty()) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity("{\"message\": \"Le paramètre de recherche 'q' est requis.\"}")
|
||||
.build();
|
||||
}
|
||||
|
||||
try {
|
||||
List<SocialPost> posts = socialPostService.searchPosts(query);
|
||||
List<SocialPostResponseDTO> responseDTOs = posts.stream()
|
||||
.map(SocialPostResponseDTO::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Response.ok(responseDTOs).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la recherche de posts : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la recherche de posts.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Like un post.
|
||||
*
|
||||
* @param postId L'ID du post
|
||||
* @return Le post mis à jour
|
||||
*/
|
||||
@POST
|
||||
@Path("/{id}/like")
|
||||
@Transactional
|
||||
@Operation(
|
||||
summary = "Liker un post",
|
||||
description = "Incrémente le compteur de likes d'un post")
|
||||
public Response likePost(@PathParam("id") UUID postId) {
|
||||
LOG.info("[LOG] Like du post ID : " + postId);
|
||||
|
||||
try {
|
||||
SocialPost post = socialPostService.likePost(postId);
|
||||
SocialPostResponseDTO responseDTO = new SocialPostResponseDTO(post);
|
||||
return Response.ok(responseDTO).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warn("[WARN] Post non trouvé : " + e.getMessage());
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Post non trouvé.\"}")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors du like du post : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors du like du post.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute un commentaire à un post.
|
||||
*
|
||||
* @param postId L'ID du post
|
||||
* @return Le post mis à jour
|
||||
*/
|
||||
@POST
|
||||
@Path("/{id}/comment")
|
||||
@Transactional
|
||||
@Operation(
|
||||
summary = "Commenter un post",
|
||||
description = "Incrémente le compteur de commentaires d'un post")
|
||||
public Response addComment(@PathParam("id") UUID postId) {
|
||||
LOG.info("[LOG] Ajout de commentaire au post ID : " + postId);
|
||||
|
||||
try {
|
||||
SocialPost post = socialPostService.addComment(postId);
|
||||
SocialPostResponseDTO responseDTO = new SocialPostResponseDTO(post);
|
||||
return Response.ok(responseDTO).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warn("[WARN] Post non trouvé : " + e.getMessage());
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Post non trouvé.\"}")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de l'ajout du commentaire : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de l'ajout du commentaire.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Partage un post.
|
||||
*
|
||||
* @param postId L'ID du post
|
||||
* @return Le post mis à jour
|
||||
*/
|
||||
@POST
|
||||
@Path("/{id}/share")
|
||||
@Transactional
|
||||
@Operation(
|
||||
summary = "Partager un post",
|
||||
description = "Incrémente le compteur de partages d'un post")
|
||||
public Response sharePost(@PathParam("id") UUID postId) {
|
||||
LOG.info("[LOG] Partage du post ID : " + postId);
|
||||
|
||||
try {
|
||||
SocialPost post = socialPostService.sharePost(postId);
|
||||
SocialPostResponseDTO responseDTO = new SocialPostResponseDTO(post);
|
||||
return Response.ok(responseDTO).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warn("[WARN] Post non trouvé : " + e.getMessage());
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Post non trouvé.\"}")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors du partage du post : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors du partage du post.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère tous les posts d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Liste des posts de l'utilisateur
|
||||
*/
|
||||
@GET
|
||||
@Path("/user/{userId}")
|
||||
@Operation(
|
||||
summary = "Récupérer les posts d'un utilisateur",
|
||||
description = "Retourne tous les posts créés par un utilisateur spécifique")
|
||||
public Response getPostsByUserId(@PathParam("userId") UUID userId) {
|
||||
LOG.info("[LOG] Récupération des posts pour l'utilisateur ID : " + userId);
|
||||
|
||||
try {
|
||||
List<SocialPost> posts = socialPostService.getPostsByUserId(userId);
|
||||
List<SocialPostResponseDTO> responseDTOs = posts.stream()
|
||||
.map(SocialPostResponseDTO::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Response.ok(responseDTOs).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération des posts : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération des posts.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les posts de l'utilisateur et de ses amis.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @param page Le numéro de la page (0-indexé)
|
||||
* @param size La taille de la page
|
||||
* @return Liste paginée des posts de l'utilisateur et de ses amis
|
||||
*/
|
||||
@GET
|
||||
@Path("/friends/{userId}")
|
||||
@Operation(
|
||||
summary = "Récupérer les posts des amis",
|
||||
description = "Retourne les posts de l'utilisateur et de ses amis (relations d'amitié acceptées)")
|
||||
public Response getPostsByFriends(
|
||||
@PathParam("userId") UUID userId,
|
||||
@QueryParam("page") @DefaultValue("0") int page,
|
||||
@QueryParam("size") @DefaultValue("20") int size) {
|
||||
LOG.info("[LOG] Récupération des posts des amis pour l'utilisateur ID : " + userId);
|
||||
|
||||
try {
|
||||
List<SocialPost> posts = socialPostService.getPostsByFriends(userId, page, size);
|
||||
List<SocialPostResponseDTO> responseDTOs = posts.stream()
|
||||
.map(SocialPostResponseDTO::new)
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Response.ok(responseDTOs).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération des posts des amis : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération des posts des amis.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
263
src/main/java/com/lions/dev/resource/StoryResource.java
Normal file
263
src/main/java/com/lions/dev/resource/StoryResource.java
Normal file
@@ -0,0 +1,263 @@
|
||||
package com.lions.dev.resource;
|
||||
|
||||
import com.lions.dev.dto.request.story.StoryCreateRequestDTO;
|
||||
import com.lions.dev.dto.response.story.StoryResponseDTO;
|
||||
import com.lions.dev.entity.story.Story;
|
||||
import com.lions.dev.service.StoryService;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import jakarta.validation.Valid;
|
||||
import jakarta.ws.rs.*;
|
||||
import jakarta.ws.rs.core.MediaType;
|
||||
import jakarta.ws.rs.core.Response;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
import org.eclipse.microprofile.openapi.annotations.Operation;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
/**
|
||||
* Ressource REST pour la gestion des stories dans le système AfterWork.
|
||||
*
|
||||
* Cette classe expose des endpoints pour créer, récupérer, supprimer
|
||||
* et marquer les stories comme vues.
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@Path("/stories")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Stories", description = "Opérations liées à la gestion des stories")
|
||||
public class StoryResource {
|
||||
|
||||
@Inject
|
||||
StoryService storyService;
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(StoryResource.class);
|
||||
|
||||
/**
|
||||
* Récupère toutes les stories actives (non expirées).
|
||||
*
|
||||
* @param viewerId ID de l'utilisateur actuel (optionnel) pour marquer les stories vues
|
||||
* @return Liste des stories actives
|
||||
*/
|
||||
@GET
|
||||
@Operation(
|
||||
summary = "Récupérer toutes les stories actives",
|
||||
description = "Retourne une liste de toutes les stories actives (non expirées), triées par date de création décroissante")
|
||||
public Response getAllActiveStories(@QueryParam("viewerId") UUID viewerId) {
|
||||
LOG.info("[LOG] Récupération de toutes les stories actives");
|
||||
|
||||
try {
|
||||
List<Story> stories = storyService.getAllActiveStories();
|
||||
List<StoryResponseDTO> responseDTOs = stories.stream()
|
||||
.map(story -> viewerId != null ? new StoryResponseDTO(story, viewerId) : new StoryResponseDTO(story))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Response.ok(responseDTOs).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération des stories : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération des stories.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère une story par son ID.
|
||||
*
|
||||
* @param storyId L'ID de la story
|
||||
* @param viewerId ID de l'utilisateur actuel (optionnel)
|
||||
* @return La story trouvée
|
||||
*/
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(
|
||||
summary = "Récupérer une story par ID",
|
||||
description = "Retourne les détails d'une story spécifique")
|
||||
public Response getStoryById(@PathParam("id") UUID storyId, @QueryParam("viewerId") UUID viewerId) {
|
||||
LOG.info("[LOG] Récupération de la story ID : " + storyId);
|
||||
|
||||
try {
|
||||
Story story = storyService.getStoryById(storyId);
|
||||
StoryResponseDTO responseDTO = viewerId != null
|
||||
? new StoryResponseDTO(story, viewerId)
|
||||
: new StoryResponseDTO(story);
|
||||
return Response.ok(responseDTO).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warn("[WARN] Story non trouvée : " + e.getMessage());
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Story non trouvée.\"}")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération de la story : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération de la story.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère toutes les stories actives d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @param viewerId ID de l'utilisateur actuel (optionnel)
|
||||
* @return Liste des stories actives de l'utilisateur
|
||||
*/
|
||||
@GET
|
||||
@Path("/user/{userId}")
|
||||
@Operation(
|
||||
summary = "Récupérer les stories d'un utilisateur",
|
||||
description = "Retourne toutes les stories actives d'un utilisateur spécifique")
|
||||
public Response getStoriesByUserId(@PathParam("userId") UUID userId, @QueryParam("viewerId") UUID viewerId) {
|
||||
LOG.info("[LOG] Récupération des stories pour l'utilisateur ID : " + userId);
|
||||
|
||||
try {
|
||||
List<Story> stories = storyService.getActiveStoriesByUserId(userId);
|
||||
List<StoryResponseDTO> responseDTOs = stories.stream()
|
||||
.map(story -> viewerId != null ? new StoryResponseDTO(story, viewerId) : new StoryResponseDTO(story))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Response.ok(responseDTOs).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération des stories : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération des stories.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une nouvelle story.
|
||||
*
|
||||
* @param requestDTO Le DTO contenant les informations de la story à créer
|
||||
* @return La story créée
|
||||
*/
|
||||
@POST
|
||||
@Transactional
|
||||
@Operation(
|
||||
summary = "Créer une nouvelle story",
|
||||
description = "Crée une nouvelle story et retourne ses détails")
|
||||
public Response createStory(@Valid StoryCreateRequestDTO requestDTO) {
|
||||
LOG.info("[LOG] Création d'une nouvelle story par l'utilisateur ID : " + requestDTO.getUserId());
|
||||
|
||||
try {
|
||||
Story story = storyService.createStory(
|
||||
requestDTO.getUserId(),
|
||||
requestDTO.getMediaType(),
|
||||
requestDTO.getMediaUrl(),
|
||||
requestDTO.getThumbnailUrl(),
|
||||
requestDTO.getDurationSeconds()
|
||||
);
|
||||
StoryResponseDTO responseDTO = new StoryResponseDTO(story);
|
||||
return Response.status(Response.Status.CREATED).entity(responseDTO).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la création de la story : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity("{\"message\": \"" + e.getMessage() + "\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque une story comme vue par un utilisateur.
|
||||
*
|
||||
* @param storyId L'ID de la story
|
||||
* @param viewerId L'ID de l'utilisateur qui voit la story
|
||||
* @return La story mise à jour
|
||||
*/
|
||||
@POST
|
||||
@Path("/{id}/view")
|
||||
@Transactional
|
||||
@Operation(
|
||||
summary = "Marquer une story comme vue",
|
||||
description = "Marque une story comme vue par un utilisateur et incrémente le compteur de vues")
|
||||
public Response markStoryAsViewed(@PathParam("id") UUID storyId, @QueryParam("userId") UUID viewerId) {
|
||||
LOG.info("[LOG] Marquage de la story ID : " + storyId + " comme vue par l'utilisateur ID : " + viewerId);
|
||||
|
||||
if (viewerId == null) {
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity("{\"message\": \"L'ID de l'utilisateur est obligatoire.\"}")
|
||||
.build();
|
||||
}
|
||||
|
||||
try {
|
||||
Story story = storyService.markStoryAsViewed(storyId, viewerId);
|
||||
StoryResponseDTO responseDTO = new StoryResponseDTO(story, viewerId);
|
||||
return Response.ok(responseDTO).build();
|
||||
} catch (IllegalArgumentException e) {
|
||||
LOG.warn("[WARN] Story non trouvée : " + e.getMessage());
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Story non trouvée.\"}")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors du marquage de la story : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors du marquage de la story.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime une story.
|
||||
*
|
||||
* @param storyId L'ID de la story
|
||||
* @return Confirmation de suppression
|
||||
*/
|
||||
@DELETE
|
||||
@Path("/{id}")
|
||||
@Transactional
|
||||
@Operation(
|
||||
summary = "Supprimer une story",
|
||||
description = "Supprime définitivement une story")
|
||||
public Response deleteStory(@PathParam("id") UUID storyId) {
|
||||
LOG.info("[LOG] Suppression de la story ID : " + storyId);
|
||||
|
||||
try {
|
||||
boolean deleted = storyService.deleteStory(storyId);
|
||||
if (deleted) {
|
||||
return Response.ok("{\"message\": \"Story supprimée avec succès.\"}").build();
|
||||
} else {
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Story non trouvée.\"}")
|
||||
.build();
|
||||
}
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la suppression de la story : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la suppression de la story.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les stories les plus vues.
|
||||
*
|
||||
* @param limit Le nombre maximum de stories à retourner
|
||||
* @param viewerId ID de l'utilisateur actuel (optionnel)
|
||||
* @return Liste des stories les plus vues
|
||||
*/
|
||||
@GET
|
||||
@Path("/popular")
|
||||
@Operation(
|
||||
summary = "Récupérer les stories les plus vues",
|
||||
description = "Retourne les stories les plus populaires basées sur le nombre de vues")
|
||||
public Response getMostViewedStories(@QueryParam("limit") @DefaultValue("10") int limit, @QueryParam("viewerId") UUID viewerId) {
|
||||
LOG.info("[LOG] Récupération des " + limit + " stories les plus vues");
|
||||
|
||||
try {
|
||||
List<Story> stories = storyService.getMostViewedStories(limit);
|
||||
List<StoryResponseDTO> responseDTOs = stories.stream()
|
||||
.map(story -> viewerId != null ? new StoryResponseDTO(story, viewerId) : new StoryResponseDTO(story))
|
||||
.collect(Collectors.toList());
|
||||
|
||||
return Response.ok(responseDTOs).build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la récupération des stories populaires : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la récupération des stories.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -6,6 +6,7 @@ import com.lions.dev.dto.response.users.UserAuthenticateResponseDTO;
|
||||
import com.lions.dev.dto.response.users.UserCreateResponseDTO;
|
||||
import com.lions.dev.dto.response.users.UserDeleteResponseDto;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import com.lions.dev.exception.UserNotFoundException;
|
||||
import com.lions.dev.service.UsersService;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
@@ -225,4 +226,43 @@ public class UsersResource {
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Endpoint pour rechercher un utilisateur par email.
|
||||
*
|
||||
* @param email L'email de l'utilisateur à rechercher.
|
||||
* @return Une réponse HTTP contenant les informations de l'utilisateur.
|
||||
*/
|
||||
@GET
|
||||
@Path("/search")
|
||||
@Operation(
|
||||
summary = "Rechercher un utilisateur par email",
|
||||
description = "Retourne les détails de l'utilisateur correspondant à l'email fourni")
|
||||
public Response searchUserByEmail(@QueryParam("email") String email) {
|
||||
if (email == null || email.isBlank()) {
|
||||
LOG.warn("Tentative de recherche avec un email vide ou null");
|
||||
return Response.status(Response.Status.BAD_REQUEST)
|
||||
.entity("{\"message\": \"L'email est requis pour la recherche.\"}")
|
||||
.build();
|
||||
}
|
||||
|
||||
LOG.info("Recherche de l'utilisateur avec l'email : " + email);
|
||||
|
||||
try {
|
||||
Users user = userService.getUserByEmail(email);
|
||||
UserCreateResponseDTO responseDTO = new UserCreateResponseDTO(user);
|
||||
LOG.info("Utilisateur trouvé : " + user.getEmail());
|
||||
return Response.ok(responseDTO).build();
|
||||
} catch (UserNotFoundException e) {
|
||||
LOG.warn("Utilisateur non trouvé avec l'email : " + email);
|
||||
return Response.status(Response.Status.NOT_FOUND)
|
||||
.entity("{\"message\": \"Utilisateur non trouvé avec cet email.\"}")
|
||||
.build();
|
||||
} catch (Exception e) {
|
||||
LOG.error("[ERROR] Erreur lors de la recherche de l'utilisateur : " + e.getMessage(), e);
|
||||
return Response.status(Response.Status.INTERNAL_SERVER_ERROR)
|
||||
.entity("{\"message\": \"Erreur lors de la recherche de l'utilisateur.\"}")
|
||||
.build();
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
@@ -5,14 +5,19 @@ import org.slf4j.LoggerFactory;
|
||||
import jakarta.transaction.Transactional;
|
||||
import com.lions.dev.dto.request.events.EventCreateRequestDTO;
|
||||
import com.lions.dev.entity.events.Events;
|
||||
import com.lions.dev.entity.friends.Friendship;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import com.lions.dev.repository.EventsRepository;
|
||||
import com.lions.dev.exception.EventNotFoundException;
|
||||
import com.lions.dev.exception.UserNotFoundException;
|
||||
import com.lions.dev.repository.EventsRepository;
|
||||
import com.lions.dev.repository.FriendshipRepository;
|
||||
import com.lions.dev.repository.UsersRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Service de gestion des événements.
|
||||
@@ -25,6 +30,15 @@ public class EventService {
|
||||
@Inject
|
||||
EventsRepository eventsRepository;
|
||||
|
||||
@Inject
|
||||
FriendshipRepository friendshipRepository;
|
||||
|
||||
@Inject
|
||||
UsersRepository usersRepository;
|
||||
|
||||
@Inject
|
||||
NotificationService notificationService;
|
||||
|
||||
private static final Logger logger = LoggerFactory.getLogger(EventService.class);
|
||||
|
||||
/**
|
||||
@@ -51,6 +65,33 @@ public class EventService {
|
||||
// Persiste l'événement dans la base de données
|
||||
eventsRepository.persist(event);
|
||||
logger.info("[logger] Événement créé avec succès : {}", event.getTitle());
|
||||
|
||||
// Créer des notifications pour tous les amis
|
||||
try {
|
||||
List<Friendship> friendships = friendshipRepository.findFriendsByUser(creator, 0, Integer.MAX_VALUE);
|
||||
String creatorName = creator.getPrenoms() + " " + creator.getNom();
|
||||
|
||||
for (Friendship friendship : friendships) {
|
||||
Users friend = friendship.getUser().equals(creator)
|
||||
? friendship.getFriend()
|
||||
: friendship.getUser();
|
||||
|
||||
String notificationTitle = "Nouvel événement de " + creatorName;
|
||||
String notificationMessage = creatorName + " a créé un nouvel événement : " + event.getTitle();
|
||||
|
||||
notificationService.createNotification(
|
||||
notificationTitle,
|
||||
notificationMessage,
|
||||
"event",
|
||||
friend.getId(),
|
||||
event.getId()
|
||||
);
|
||||
}
|
||||
logger.info("[logger] Notifications créées pour {} ami(s)", friendships.size());
|
||||
} catch (Exception e) {
|
||||
logger.error("[ERROR] Erreur lors de la création des notifications : {}", e.getMessage());
|
||||
}
|
||||
|
||||
return event;
|
||||
}
|
||||
|
||||
@@ -282,4 +323,42 @@ public class EventService {
|
||||
logger.info("[logger] Nombre d'événements recommandés pour l'utilisateur : " + events.size());
|
||||
return events;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les événements de l'utilisateur et de ses amis (relations d'amitié acceptées).
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @param page Le numéro de la page (0-indexé)
|
||||
* @param size La taille de la page
|
||||
* @return Liste paginée des événements de l'utilisateur et de ses amis
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
public List<Events> getEventsByFriends(UUID userId, int page, int size) {
|
||||
logger.info("[logger] Récupération des événements des amis pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
logger.error("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
// Récupérer toutes les relations d'amitié acceptées
|
||||
List<Friendship> friendships = friendshipRepository.findFriendsByUser(user, 0, Integer.MAX_VALUE);
|
||||
|
||||
// Extraire les IDs des amis
|
||||
List<UUID> friendIds = friendships.stream()
|
||||
.map(friendship -> {
|
||||
// L'ami est soit dans 'user' soit dans 'friend', selon qui a initié la relation
|
||||
return friendship.getUser().equals(user)
|
||||
? friendship.getFriend().getId()
|
||||
: friendship.getUser().getId();
|
||||
})
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
logger.info("[logger] " + friendIds.size() + " ami(s) trouvé(s) pour l'utilisateur ID : " + userId);
|
||||
|
||||
// Récupérer les événements de l'utilisateur et de ses amis
|
||||
return eventsRepository.findEventsByFriends(userId, friendIds, page, size);
|
||||
}
|
||||
}
|
||||
|
||||
@@ -13,11 +13,15 @@ import com.lions.dev.exception.FriendshipNotFoundException;
|
||||
import com.lions.dev.exception.UserNotFoundException;
|
||||
import com.lions.dev.repository.FriendshipRepository;
|
||||
import com.lions.dev.repository.UsersRepository;
|
||||
import com.lions.dev.websocket.NotificationWebSocket;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import java.util.ArrayList;
|
||||
import java.util.HashMap;
|
||||
import java.util.HashSet;
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import org.jboss.logging.Logger;
|
||||
@@ -33,6 +37,8 @@ public class FriendshipService {
|
||||
FriendshipRepository friendshipRepository; // Injecte le repository des amitiés
|
||||
@Inject
|
||||
UsersRepository usersRepository; // Injecte le repository des utilisateurs
|
||||
@Inject
|
||||
NotificationService notificationService; // Injecte le service de notifications
|
||||
|
||||
private static final Logger logger = Logger.getLogger(FriendshipService.class);
|
||||
|
||||
@@ -56,6 +62,12 @@ public class FriendshipService {
|
||||
throw new UserNotFoundException("Utilisateur avec l'ID " + notFoundId + " introuvable.");
|
||||
}
|
||||
|
||||
// VALIDATION: Empêcher l'utilisateur de s'ajouter lui-même comme ami
|
||||
if (user.getId().equals(friend.getId())) {
|
||||
logger.error("[ERROR] Tentative d'ajout de soi-même comme ami bloquée pour l'utilisateur : " + user.getId());
|
||||
throw new IllegalArgumentException("Vous ne pouvez pas vous ajouter vous-même comme ami.");
|
||||
}
|
||||
|
||||
// Vérifier s'il existe déjà une relation d'amitié
|
||||
Friendship existingFriendship = friendshipRepository.findByUsers(user, friend).orElse(null);
|
||||
if (existingFriendship != null) {
|
||||
@@ -67,6 +79,26 @@ public class FriendshipService {
|
||||
Friendship friendship = new Friendship(user, friend, FriendshipStatus.PENDING);
|
||||
friendshipRepository.persist(friendship);
|
||||
|
||||
// TEMPS RÉEL: Notifier le destinataire via WebSocket
|
||||
try {
|
||||
Map<String, Object> notificationData = new HashMap<>();
|
||||
notificationData.put("requestId", friendship.getId().toString());
|
||||
notificationData.put("senderId", user.getId().toString());
|
||||
notificationData.put("senderName", user.getPrenoms() + " " + user.getNom());
|
||||
notificationData.put("senderProfileImage", user.getProfileImageUrl() != null ? user.getProfileImageUrl() : "");
|
||||
|
||||
NotificationWebSocket.sendNotificationToUser(
|
||||
friend.getId(),
|
||||
"friend_request_received",
|
||||
notificationData
|
||||
);
|
||||
|
||||
logger.info("[LOG] Notification WebSocket envoyée au destinataire : " + friend.getId());
|
||||
} catch (Exception e) {
|
||||
logger.error("[ERROR] Erreur lors de l'envoi de la notification WebSocket : " + e.getMessage(), e);
|
||||
// Ne pas bloquer la demande d'amitié si le WebSocket échoue
|
||||
}
|
||||
|
||||
logger.info("[LOG] Demande d'amitié envoyée avec succès.");
|
||||
return new FriendshipCreateOneResponseDTO(friendship);
|
||||
}
|
||||
@@ -107,6 +139,60 @@ public class FriendshipService {
|
||||
// Log de succès
|
||||
logger.info(String.format("[LOG] Demande d'amitié acceptée avec succès pour l'ID: %s", friendshipId)); // Correctement formaté
|
||||
|
||||
// TEMPS RÉEL: Notifier l'émetteur de la demande via WebSocket
|
||||
try {
|
||||
Users user = friendship.getUser();
|
||||
Users friend = friendship.getFriend();
|
||||
String friendName = friend.getPrenoms() + " " + friend.getNom();
|
||||
|
||||
Map<String, Object> notificationData = new HashMap<>();
|
||||
notificationData.put("acceptedBy", friendName);
|
||||
notificationData.put("friendshipId", friendshipId.toString());
|
||||
notificationData.put("accepterId", friend.getId().toString());
|
||||
notificationData.put("accepterProfileImage", friend.getProfileImageUrl() != null ? friend.getProfileImageUrl() : "");
|
||||
|
||||
NotificationWebSocket.sendNotificationToUser(
|
||||
user.getId(),
|
||||
"friend_request_accepted",
|
||||
notificationData
|
||||
);
|
||||
|
||||
logger.info("[LOG] Notification WebSocket d'acceptation envoyée à : " + user.getId());
|
||||
} catch (Exception e) {
|
||||
logger.error("[ERROR] Erreur lors de l'envoi de la notification WebSocket d'acceptation : " + e.getMessage(), e);
|
||||
// Ne pas bloquer l'acceptation si le WebSocket échoue
|
||||
}
|
||||
|
||||
// Créer des notifications pour les deux utilisateurs
|
||||
try {
|
||||
Users user = friendship.getUser();
|
||||
Users friend = friendship.getFriend();
|
||||
String userName = user.getPrenoms() + " " + user.getNom();
|
||||
String friendName = friend.getPrenoms() + " " + friend.getNom();
|
||||
|
||||
// Notification pour l'utilisateur qui a envoyé la demande
|
||||
notificationService.createNotification(
|
||||
"Demande d'amitié acceptée",
|
||||
friendName + " a accepté votre demande d'amitié",
|
||||
"friend",
|
||||
user.getId(),
|
||||
null
|
||||
);
|
||||
|
||||
// Notification pour l'utilisateur qui a accepté la demande
|
||||
notificationService.createNotification(
|
||||
"Nouveaux amis",
|
||||
"Vous êtes maintenant ami(e) avec " + userName,
|
||||
"friend",
|
||||
friend.getId(),
|
||||
null
|
||||
);
|
||||
|
||||
logger.info("[LOG] Notifications d'amitié créées pour les deux utilisateurs");
|
||||
} catch (Exception e) {
|
||||
logger.error("[ERROR] Erreur lors de la création des notifications d'amitié : " + e.getMessage());
|
||||
}
|
||||
|
||||
// Retourner la réponse avec les informations de la relation d'amitié
|
||||
return new FriendshipCreateOneResponseDTO(friendship);
|
||||
}
|
||||
@@ -126,6 +212,26 @@ public class FriendshipService {
|
||||
friendship.setStatus(FriendshipStatus.REJECTED);
|
||||
friendshipRepository.persist(friendship);
|
||||
|
||||
// TEMPS RÉEL: Notifier l'émetteur de la demande via WebSocket (optionnel selon UX)
|
||||
try {
|
||||
Users user = friendship.getUser();
|
||||
|
||||
Map<String, Object> notificationData = new HashMap<>();
|
||||
notificationData.put("friendshipId", friendshipId.toString());
|
||||
notificationData.put("rejectedAt", System.currentTimeMillis());
|
||||
|
||||
NotificationWebSocket.sendNotificationToUser(
|
||||
user.getId(),
|
||||
"friend_request_rejected",
|
||||
notificationData
|
||||
);
|
||||
|
||||
logger.info("[LOG] Notification WebSocket de rejet envoyée à : " + user.getId());
|
||||
} catch (Exception e) {
|
||||
logger.error("[ERROR] Erreur lors de l'envoi de la notification WebSocket de rejet : " + e.getMessage(), e);
|
||||
// Ne pas bloquer le rejet si le WebSocket échoue
|
||||
}
|
||||
|
||||
logger.info("[LOG] Demande d'amitié rejetée.");
|
||||
}
|
||||
|
||||
@@ -297,4 +403,126 @@ public class FriendshipService {
|
||||
|
||||
return friendships.stream().map(FriendshipReadStatusResponseDTO::new).toList();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupérer les suggestions d'amis pour un utilisateur.
|
||||
*
|
||||
* L'algorithme suggère des utilisateurs basés sur :
|
||||
* 1. Amis d'amis (utilisateurs qui ont des amis en commun)
|
||||
* 2. Utilisateurs récents (qui ne sont ni amis ni ont de demandes en attente)
|
||||
*
|
||||
* @param userId ID de l'utilisateur.
|
||||
* @param limit Nombre maximum de suggestions à retourner.
|
||||
* @return Liste des suggestions d'amis.
|
||||
*/
|
||||
public List<com.lions.dev.dto.response.users.FriendSuggestionResponseDTO> getFriendSuggestions(UUID userId, int limit) {
|
||||
logger.info("[LOG] Récupération des suggestions d'amis pour l'utilisateur : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
logger.error("[ERROR] Utilisateur non trouvé.");
|
||||
throw new UserNotFoundException("Utilisateur introuvable.");
|
||||
}
|
||||
|
||||
// Récupérer tous les amis actuels de l'utilisateur (ACCEPTED)
|
||||
// Utiliser une taille de page élevée pour récupérer tous les résultats
|
||||
List<Friendship> currentFriendships = friendshipRepository.findByUserAndStatus(user, FriendshipStatus.ACCEPTED, 0, 10000);
|
||||
Set<UUID> currentFriendIds = new HashSet<>();
|
||||
for (Friendship friendship : currentFriendships) {
|
||||
// Ajouter les IDs des amis (que l'utilisateur ait envoyé ou reçu la demande)
|
||||
if (friendship.getUser().getId().equals(userId)) {
|
||||
currentFriendIds.add(friendship.getFriend().getId());
|
||||
} else {
|
||||
currentFriendIds.add(friendship.getUser().getId());
|
||||
}
|
||||
}
|
||||
|
||||
// Récupérer toutes les demandes en attente pour exclure ces utilisateurs
|
||||
List<Friendship> pendingFriendships = friendshipRepository.findByUserAndStatus(user, FriendshipStatus.PENDING, 0, 10000);
|
||||
Set<UUID> pendingUserIds = new HashSet<>();
|
||||
for (Friendship friendship : pendingFriendships) {
|
||||
if (friendship.getUser().getId().equals(userId)) {
|
||||
pendingUserIds.add(friendship.getFriend().getId());
|
||||
} else {
|
||||
pendingUserIds.add(friendship.getUser().getId());
|
||||
}
|
||||
}
|
||||
|
||||
// Map pour compter les amis en commun
|
||||
Map<UUID, Integer> mutualFriendsCount = new HashMap<>();
|
||||
|
||||
// Pour chaque ami, trouver ses amis (amis d'amis)
|
||||
for (UUID friendId : currentFriendIds) {
|
||||
Users friend = usersRepository.findById(friendId);
|
||||
if (friend == null) continue;
|
||||
|
||||
List<Friendship> friendsOfFriend = friendshipRepository.findByUserAndStatus(friend, FriendshipStatus.ACCEPTED, 0, 10000);
|
||||
|
||||
for (Friendship fof : friendsOfFriend) {
|
||||
UUID potentialFriendId;
|
||||
if (fof.getUser().getId().equals(friendId)) {
|
||||
potentialFriendId = fof.getFriend().getId();
|
||||
} else {
|
||||
potentialFriendId = fof.getUser().getId();
|
||||
}
|
||||
|
||||
// Exclure l'utilisateur lui-même, ses amis actuels et les demandes en attente
|
||||
if (!potentialFriendId.equals(userId) &&
|
||||
!currentFriendIds.contains(potentialFriendId) &&
|
||||
!pendingUserIds.contains(potentialFriendId)) {
|
||||
mutualFriendsCount.put(potentialFriendId, mutualFriendsCount.getOrDefault(potentialFriendId, 0) + 1);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Trier par nombre d'amis en commun (décroissant)
|
||||
List<Map.Entry<UUID, Integer>> sortedSuggestions = mutualFriendsCount.entrySet()
|
||||
.stream()
|
||||
.sorted(Map.Entry.<UUID, Integer>comparingByValue().reversed())
|
||||
.limit(limit)
|
||||
.toList();
|
||||
|
||||
// Créer les DTOs
|
||||
List<com.lions.dev.dto.response.users.FriendSuggestionResponseDTO> suggestions = new ArrayList<>();
|
||||
for (Map.Entry<UUID, Integer> entry : sortedSuggestions) {
|
||||
Users suggestedUser = usersRepository.findById(entry.getKey());
|
||||
if (suggestedUser != null) {
|
||||
String reason = entry.getValue() > 1
|
||||
? entry.getValue() + " amis en commun"
|
||||
: "1 ami en commun";
|
||||
suggestions.add(new com.lions.dev.dto.response.users.FriendSuggestionResponseDTO(
|
||||
suggestedUser,
|
||||
entry.getValue(),
|
||||
reason
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
// Si pas assez de suggestions, ajouter des utilisateurs récents
|
||||
if (suggestions.size() < limit) {
|
||||
int remaining = limit - suggestions.size();
|
||||
Set<UUID> excludedIds = new HashSet<>(currentFriendIds);
|
||||
excludedIds.addAll(pendingUserIds);
|
||||
excludedIds.add(userId);
|
||||
excludedIds.addAll(mutualFriendsCount.keySet());
|
||||
|
||||
// Récupérer des utilisateurs récents qui ne sont pas dans les exclusions
|
||||
List<Users> recentUsers = usersRepository.findAll()
|
||||
.stream()
|
||||
.filter(u -> !excludedIds.contains(u.getId()))
|
||||
.limit(remaining)
|
||||
.toList();
|
||||
|
||||
for (Users recentUser : recentUsers) {
|
||||
suggestions.add(new com.lions.dev.dto.response.users.FriendSuggestionResponseDTO(
|
||||
recentUser,
|
||||
0,
|
||||
"Nouvel utilisateur"
|
||||
));
|
||||
}
|
||||
}
|
||||
|
||||
logger.info("[LOG] " + suggestions.size() + " suggestions d'amis générées.");
|
||||
return suggestions;
|
||||
}
|
||||
}
|
||||
|
||||
339
src/main/java/com/lions/dev/service/MessageService.java
Normal file
339
src/main/java/com/lions/dev/service/MessageService.java
Normal 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);
|
||||
}
|
||||
}
|
||||
218
src/main/java/com/lions/dev/service/NotificationService.java
Normal file
218
src/main/java/com/lions/dev/service/NotificationService.java
Normal file
@@ -0,0 +1,218 @@
|
||||
package com.lions.dev.service;
|
||||
|
||||
import com.lions.dev.entity.events.Events;
|
||||
import com.lions.dev.entity.notification.Notification;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import com.lions.dev.exception.UserNotFoundException;
|
||||
import com.lions.dev.repository.EventsRepository;
|
||||
import com.lions.dev.repository.NotificationRepository;
|
||||
import com.lions.dev.repository.UsersRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Service de gestion des notifications.
|
||||
*
|
||||
* Ce service contient la logique métier pour la création, récupération,
|
||||
* mise à jour et suppression des notifications.
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class NotificationService {
|
||||
|
||||
@Inject
|
||||
NotificationRepository notificationRepository;
|
||||
|
||||
@Inject
|
||||
UsersRepository usersRepository;
|
||||
|
||||
@Inject
|
||||
EventsRepository eventsRepository;
|
||||
|
||||
/**
|
||||
* Récupère toutes les notifications d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Liste des notifications de l'utilisateur
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
public List<Notification> getNotificationsByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des notifications pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
List<Notification> notifications = notificationRepository.findByUserId(userId);
|
||||
System.out.println("[LOG] " + notifications.size() + " notification(s) récupérée(s) pour l'utilisateur ID : " + userId);
|
||||
return notifications;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les notifications d'un utilisateur avec pagination.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @param page Le numéro de la page (0-indexé)
|
||||
* @param size La taille de la page
|
||||
* @return Liste paginée des notifications
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
public List<Notification> getNotificationsByUserIdWithPagination(UUID userId, int page, int size) {
|
||||
System.out.println("[LOG] Récupération paginée des notifications pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
return notificationRepository.findByUserIdWithPagination(userId, page, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une nouvelle notification.
|
||||
*
|
||||
* @param title Le titre de la notification
|
||||
* @param message Le message de la notification
|
||||
* @param type Le type de notification (event, friend, reminder, other)
|
||||
* @param userId L'ID de l'utilisateur destinataire
|
||||
* @param eventId L'ID de l'événement associé (optionnel)
|
||||
* @return La notification créée
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
@Transactional
|
||||
public Notification createNotification(
|
||||
String title,
|
||||
String message,
|
||||
String type,
|
||||
UUID userId,
|
||||
UUID eventId) {
|
||||
System.out.println("[LOG] Création d'une notification : " + title + " pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
Notification notification = new Notification(title, message, type, user);
|
||||
|
||||
if (eventId != null) {
|
||||
Events event = eventsRepository.findById(eventId);
|
||||
if (event != null) {
|
||||
notification.setEvent(event);
|
||||
System.out.println("[LOG] Notification associée à l'événement ID : " + eventId);
|
||||
}
|
||||
}
|
||||
|
||||
notificationRepository.persist(notification);
|
||||
System.out.println("[LOG] Notification créée avec succès : " + notification.getId());
|
||||
return notification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque une notification comme lue.
|
||||
*
|
||||
* @param notificationId L'ID de la notification
|
||||
* @return La notification mise à jour
|
||||
*/
|
||||
@Transactional
|
||||
public Notification markAsRead(UUID notificationId) {
|
||||
System.out.println("[LOG] Marquage de la notification ID : " + notificationId + " comme lue");
|
||||
|
||||
Notification notification = notificationRepository.findById(notificationId);
|
||||
if (notification == null) {
|
||||
System.out.println("[ERROR] Notification non trouvée avec l'ID : " + notificationId);
|
||||
throw new IllegalArgumentException("Notification non trouvée avec l'ID : " + notificationId);
|
||||
}
|
||||
|
||||
notification.markAsRead();
|
||||
notificationRepository.persist(notification);
|
||||
System.out.println("[LOG] Notification marquée comme lue avec succès");
|
||||
return notification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque toutes les notifications d'un utilisateur comme lues.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Le nombre de notifications mises à jour
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
@Transactional
|
||||
public int markAllAsRead(UUID userId) {
|
||||
System.out.println("[LOG] Marquage de toutes les notifications comme lues pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
int updated = notificationRepository.markAllAsReadByUserId(userId);
|
||||
System.out.println("[LOG] " + updated + " notification(s) marquée(s) comme lue(s)");
|
||||
return updated;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime une notification.
|
||||
*
|
||||
* @param notificationId L'ID de la notification
|
||||
* @return true si la notification a été supprimée, false sinon
|
||||
*/
|
||||
@Transactional
|
||||
public boolean deleteNotification(UUID notificationId) {
|
||||
System.out.println("[LOG] Suppression de la notification ID : " + notificationId);
|
||||
|
||||
Notification notification = notificationRepository.findById(notificationId);
|
||||
if (notification == null) {
|
||||
System.out.println("[ERROR] Notification non trouvée avec l'ID : " + notificationId);
|
||||
return false;
|
||||
}
|
||||
|
||||
notificationRepository.delete(notification);
|
||||
System.out.println("[LOG] Notification supprimée avec succès");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère une notification par son ID.
|
||||
*
|
||||
* @param notificationId L'ID de la notification
|
||||
* @return La notification trouvée
|
||||
*/
|
||||
public Notification getNotificationById(UUID notificationId) {
|
||||
System.out.println("[LOG] Récupération de la notification ID : " + notificationId);
|
||||
|
||||
Notification notification = notificationRepository.findById(notificationId);
|
||||
if (notification == null) {
|
||||
System.out.println("[ERROR] Notification non trouvée avec l'ID : " + notificationId);
|
||||
throw new IllegalArgumentException("Notification non trouvée avec l'ID : " + notificationId);
|
||||
}
|
||||
|
||||
return notification;
|
||||
}
|
||||
|
||||
/**
|
||||
* Compte le nombre de notifications non lues d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Le nombre de notifications non lues
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
public long countUnreadNotifications(UUID userId) {
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
return notificationRepository.countUnreadByUserId(userId);
|
||||
}
|
||||
}
|
||||
|
||||
122
src/main/java/com/lions/dev/service/PresenceService.java
Normal file
122
src/main/java/com/lions/dev/service/PresenceService.java
Normal file
@@ -0,0 +1,122 @@
|
||||
package com.lions.dev.service;
|
||||
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import com.lions.dev.repository.UsersRepository;
|
||||
import com.lions.dev.websocket.NotificationWebSocket;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
|
||||
import java.time.LocalDateTime;
|
||||
import java.util.*;
|
||||
|
||||
/**
|
||||
* Service pour gérer la présence des utilisateurs (online/offline).
|
||||
*
|
||||
* Ce service gère:
|
||||
* - Le marquage des utilisateurs comme en ligne/hors ligne
|
||||
* - Le heartbeat pour maintenir le statut online
|
||||
* - La diffusion de la présence aux amis via WebSocket
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class PresenceService {
|
||||
|
||||
@Inject
|
||||
UsersRepository usersRepository;
|
||||
|
||||
/**
|
||||
* Marque un utilisateur comme en ligne et broadcast sa présence.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
*/
|
||||
@Transactional
|
||||
public void setUserOnline(UUID userId) {
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user != null) {
|
||||
user.updatePresence();
|
||||
usersRepository.persist(user);
|
||||
|
||||
// Broadcast présence aux autres utilisateurs
|
||||
broadcastPresenceToAll(userId, true, user.getLastSeen());
|
||||
|
||||
System.out.println("[PRESENCE] Utilisateur " + userId + " marqué online");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque un utilisateur comme hors ligne.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
*/
|
||||
@Transactional
|
||||
public void setUserOffline(UUID userId) {
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user != null) {
|
||||
user.setOffline();
|
||||
usersRepository.persist(user);
|
||||
|
||||
// Broadcast présence aux autres utilisateurs
|
||||
broadcastPresenceToAll(userId, false, user.getLastSeen());
|
||||
|
||||
System.out.println("[PRESENCE] Utilisateur " + userId + " marqué offline");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour le heartbeat d'un utilisateur (keep-alive).
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
*/
|
||||
@Transactional
|
||||
public void heartbeat(UUID userId) {
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user != null) {
|
||||
user.updatePresence();
|
||||
usersRepository.persist(user);
|
||||
System.out.println("[PRESENCE] Heartbeat reçu pour utilisateur " + userId);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast la présence d'un utilisateur à tous les utilisateurs connectés via WebSocket.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @param isOnline Le statut online
|
||||
* @param lastSeen La dernière fois que l'utilisateur était en ligne
|
||||
*/
|
||||
private void broadcastPresenceToAll(UUID userId, boolean isOnline, LocalDateTime lastSeen) {
|
||||
try {
|
||||
Map<String, Object> presenceData = new HashMap<>();
|
||||
presenceData.put("userId", userId.toString());
|
||||
presenceData.put("isOnline", isOnline);
|
||||
presenceData.put("lastSeen", lastSeen != null ? lastSeen.toString() : null);
|
||||
presenceData.put("timestamp", System.currentTimeMillis());
|
||||
|
||||
// Envoyer via NotificationWebSocket
|
||||
NotificationWebSocket.broadcastPresenceUpdate(presenceData);
|
||||
|
||||
System.out.println("[PRESENCE] Broadcast de la présence de " + userId + " : " + isOnline);
|
||||
} catch (Exception e) {
|
||||
System.out.println("[ERROR] Erreur lors du broadcast de présence : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le statut de présence d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Map contenant isOnline et lastSeen
|
||||
*/
|
||||
public Map<String, Object> getUserPresence(UUID userId) {
|
||||
Users user = usersRepository.findById(userId);
|
||||
Map<String, Object> presence = new HashMap<>();
|
||||
|
||||
if (user != null) {
|
||||
presence.put("userId", userId.toString());
|
||||
presence.put("isOnline", user.isOnline());
|
||||
presence.put("lastSeen", user.getLastSeen() != null ? user.getLastSeen().toString() : null);
|
||||
}
|
||||
|
||||
return presence;
|
||||
}
|
||||
}
|
||||
308
src/main/java/com/lions/dev/service/SocialPostService.java
Normal file
308
src/main/java/com/lions/dev/service/SocialPostService.java
Normal file
@@ -0,0 +1,308 @@
|
||||
package com.lions.dev.service;
|
||||
|
||||
import com.lions.dev.entity.friends.Friendship;
|
||||
import com.lions.dev.entity.friends.FriendshipStatus;
|
||||
import com.lions.dev.entity.social.SocialPost;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import com.lions.dev.exception.UserNotFoundException;
|
||||
import com.lions.dev.repository.FriendshipRepository;
|
||||
import com.lions.dev.repository.SocialPostRepository;
|
||||
import com.lions.dev.repository.UsersRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
import java.util.stream.Collectors;
|
||||
|
||||
/**
|
||||
* Service de gestion des posts sociaux.
|
||||
*
|
||||
* Ce service contient la logique métier pour la création, récupération,
|
||||
* mise à jour et suppression des posts sociaux.
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class SocialPostService {
|
||||
|
||||
@Inject
|
||||
SocialPostRepository socialPostRepository;
|
||||
|
||||
@Inject
|
||||
UsersRepository usersRepository;
|
||||
|
||||
@Inject
|
||||
FriendshipRepository friendshipRepository;
|
||||
|
||||
@Inject
|
||||
NotificationService notificationService;
|
||||
|
||||
/**
|
||||
* Récupère tous les posts avec pagination.
|
||||
*
|
||||
* @param page Le numéro de la page (0-indexé)
|
||||
* @param size La taille de la page
|
||||
* @return Liste paginée des posts
|
||||
*/
|
||||
public List<SocialPost> getAllPosts(int page, int size) {
|
||||
System.out.println("[LOG] Récupération de tous les posts (page: " + page + ", size: " + size + ")");
|
||||
return socialPostRepository.findAllWithPagination(page, size);
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère tous les posts d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Liste des posts de l'utilisateur
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
public List<SocialPost> getPostsByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des posts pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
return socialPostRepository.findByUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée un nouveau post social.
|
||||
*
|
||||
* @param content Le contenu du post
|
||||
* @param userId L'ID de l'utilisateur créateur
|
||||
* @param imageUrl L'URL de l'image (optionnel)
|
||||
* @return Le post créé
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
@Transactional
|
||||
public SocialPost createPost(String content, UUID userId, String imageUrl) {
|
||||
System.out.println("[LOG] Création d'un post par l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
SocialPost post = new SocialPost(content, user);
|
||||
if (imageUrl != null && !imageUrl.isEmpty()) {
|
||||
post.setImageUrl(imageUrl);
|
||||
}
|
||||
|
||||
socialPostRepository.persist(post);
|
||||
System.out.println("[LOG] Post créé avec succès : " + post.getId());
|
||||
|
||||
// Créer des notifications pour tous les amis
|
||||
try {
|
||||
List<Friendship> friendships = friendshipRepository.findFriendsByUser(user, 0, Integer.MAX_VALUE);
|
||||
String userName = user.getPrenoms() + " " + user.getNom();
|
||||
|
||||
for (Friendship friendship : friendships) {
|
||||
Users friend = friendship.getUser().equals(user)
|
||||
? friendship.getFriend()
|
||||
: friendship.getUser();
|
||||
|
||||
String notificationTitle = "Nouveau post de " + userName;
|
||||
String notificationMessage = userName + " a publié un nouveau post : " +
|
||||
(content.length() > 50 ? content.substring(0, 50) + "..." : content);
|
||||
|
||||
notificationService.createNotification(
|
||||
notificationTitle,
|
||||
notificationMessage,
|
||||
"post",
|
||||
friend.getId(),
|
||||
null
|
||||
);
|
||||
}
|
||||
System.out.println("[LOG] Notifications créées pour " + friendships.size() + " ami(s)");
|
||||
} catch (Exception e) {
|
||||
System.out.println("[ERROR] Erreur lors de la création des notifications : " + e.getMessage());
|
||||
}
|
||||
|
||||
return post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère un post par son ID.
|
||||
*
|
||||
* @param postId L'ID du post
|
||||
* @return Le post trouvé
|
||||
*/
|
||||
public SocialPost getPostById(UUID postId) {
|
||||
System.out.println("[LOG] Récupération du post ID : " + postId);
|
||||
|
||||
SocialPost post = socialPostRepository.findById(postId);
|
||||
if (post == null) {
|
||||
System.out.println("[ERROR] Post non trouvé avec l'ID : " + postId);
|
||||
throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId);
|
||||
}
|
||||
|
||||
return post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Met à jour un post.
|
||||
*
|
||||
* @param postId L'ID du post
|
||||
* @param content Le nouveau contenu
|
||||
* @param imageUrl La nouvelle URL d'image (optionnel)
|
||||
* @return Le post mis à jour
|
||||
*/
|
||||
@Transactional
|
||||
public SocialPost updatePost(UUID postId, String content, String imageUrl) {
|
||||
System.out.println("[LOG] Mise à jour du post ID : " + postId);
|
||||
|
||||
SocialPost post = socialPostRepository.findById(postId);
|
||||
if (post == null) {
|
||||
System.out.println("[ERROR] Post non trouvé avec l'ID : " + postId);
|
||||
throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId);
|
||||
}
|
||||
|
||||
post.setContent(content);
|
||||
if (imageUrl != null) {
|
||||
post.setImageUrl(imageUrl);
|
||||
}
|
||||
|
||||
socialPostRepository.persist(post);
|
||||
System.out.println("[LOG] Post mis à jour avec succès");
|
||||
return post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime un post.
|
||||
*
|
||||
* @param postId L'ID du post
|
||||
* @return true si le post a été supprimé, false sinon
|
||||
*/
|
||||
@Transactional
|
||||
public boolean deletePost(UUID postId) {
|
||||
System.out.println("[LOG] Suppression du post ID : " + postId);
|
||||
|
||||
SocialPost post = socialPostRepository.findById(postId);
|
||||
if (post == null) {
|
||||
System.out.println("[ERROR] Post non trouvé avec l'ID : " + postId);
|
||||
return false;
|
||||
}
|
||||
|
||||
socialPostRepository.delete(post);
|
||||
System.out.println("[LOG] Post supprimé avec succès");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recherche des posts par contenu.
|
||||
*
|
||||
* @param query Le terme de recherche
|
||||
* @return Liste des posts correspondant à la recherche
|
||||
*/
|
||||
public List<SocialPost> searchPosts(String query) {
|
||||
System.out.println("[LOG] Recherche de posts avec la requête : " + query);
|
||||
return socialPostRepository.searchByContent(query);
|
||||
}
|
||||
|
||||
/**
|
||||
* Like un post (incrémente le compteur de likes).
|
||||
*
|
||||
* @param postId L'ID du post
|
||||
* @return Le post mis à jour
|
||||
*/
|
||||
@Transactional
|
||||
public SocialPost likePost(UUID postId) {
|
||||
System.out.println("[LOG] Like du post ID : " + postId);
|
||||
|
||||
SocialPost post = socialPostRepository.findById(postId);
|
||||
if (post == null) {
|
||||
System.out.println("[ERROR] Post non trouvé avec l'ID : " + postId);
|
||||
throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId);
|
||||
}
|
||||
|
||||
post.incrementLikes();
|
||||
socialPostRepository.persist(post);
|
||||
return post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Ajoute un commentaire à un post (incrémente le compteur de commentaires).
|
||||
*
|
||||
* @param postId L'ID du post
|
||||
* @return Le post mis à jour
|
||||
*/
|
||||
@Transactional
|
||||
public SocialPost addComment(UUID postId) {
|
||||
System.out.println("[LOG] Ajout de commentaire au post ID : " + postId);
|
||||
|
||||
SocialPost post = socialPostRepository.findById(postId);
|
||||
if (post == null) {
|
||||
System.out.println("[ERROR] Post non trouvé avec l'ID : " + postId);
|
||||
throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId);
|
||||
}
|
||||
|
||||
post.incrementComments();
|
||||
socialPostRepository.persist(post);
|
||||
return post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Partage un post (incrémente le compteur de partages).
|
||||
*
|
||||
* @param postId L'ID du post
|
||||
* @return Le post mis à jour
|
||||
*/
|
||||
@Transactional
|
||||
public SocialPost sharePost(UUID postId) {
|
||||
System.out.println("[LOG] Partage du post ID : " + postId);
|
||||
|
||||
SocialPost post = socialPostRepository.findById(postId);
|
||||
if (post == null) {
|
||||
System.out.println("[ERROR] Post non trouvé avec l'ID : " + postId);
|
||||
throw new IllegalArgumentException("Post non trouvé avec l'ID : " + postId);
|
||||
}
|
||||
|
||||
post.incrementShares();
|
||||
socialPostRepository.persist(post);
|
||||
return post;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les posts de l'utilisateur et de ses amis (relations d'amitié acceptées).
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @param page Le numéro de la page (0-indexé)
|
||||
* @param size La taille de la page
|
||||
* @return Liste paginée des posts de l'utilisateur et de ses amis
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
public List<SocialPost> getPostsByFriends(UUID userId, int page, int size) {
|
||||
System.out.println("[LOG] Récupération des posts des amis pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
// Récupérer toutes les relations d'amitié acceptées
|
||||
List<Friendship> friendships = friendshipRepository.findFriendsByUser(user, 0, Integer.MAX_VALUE);
|
||||
|
||||
// Extraire les IDs des amis
|
||||
List<UUID> friendIds = friendships.stream()
|
||||
.map(friendship -> {
|
||||
// L'ami est soit dans 'user' soit dans 'friend', selon qui a initié la relation
|
||||
return friendship.getUser().equals(user)
|
||||
? friendship.getFriend().getId()
|
||||
: friendship.getUser().getId();
|
||||
})
|
||||
.distinct()
|
||||
.collect(Collectors.toList());
|
||||
|
||||
System.out.println("[LOG] " + friendIds.size() + " ami(s) trouvé(s) pour l'utilisateur ID : " + userId);
|
||||
|
||||
// Récupérer les posts de l'utilisateur et de ses amis
|
||||
return socialPostRepository.findPostsByFriends(userId, friendIds, page, size);
|
||||
}
|
||||
}
|
||||
|
||||
212
src/main/java/com/lions/dev/service/StoryService.java
Normal file
212
src/main/java/com/lions/dev/service/StoryService.java
Normal file
@@ -0,0 +1,212 @@
|
||||
package com.lions.dev.service;
|
||||
|
||||
import com.lions.dev.entity.story.MediaType;
|
||||
import com.lions.dev.entity.story.Story;
|
||||
import com.lions.dev.entity.users.Users;
|
||||
import com.lions.dev.exception.UserNotFoundException;
|
||||
import com.lions.dev.repository.StoryRepository;
|
||||
import com.lions.dev.repository.UsersRepository;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.transaction.Transactional;
|
||||
import java.util.List;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Service de gestion des stories.
|
||||
*
|
||||
* Ce service contient la logique métier pour la création, récupération,
|
||||
* mise à jour et suppression des stories.
|
||||
*
|
||||
* Tous les logs nécessaires pour la traçabilité sont intégrés.
|
||||
*/
|
||||
@ApplicationScoped
|
||||
public class StoryService {
|
||||
|
||||
@Inject
|
||||
StoryRepository storyRepository;
|
||||
|
||||
@Inject
|
||||
UsersRepository usersRepository;
|
||||
|
||||
/**
|
||||
* Récupère toutes les stories actives (non expirées).
|
||||
*
|
||||
* @return Liste des stories actives
|
||||
*/
|
||||
public List<Story> getAllActiveStories() {
|
||||
System.out.println("[LOG] Récupération de toutes les stories actives");
|
||||
return storyRepository.findAllActive();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère toutes les stories actives d'un utilisateur.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @return Liste des stories actives de l'utilisateur
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
*/
|
||||
public List<Story> getActiveStoriesByUserId(UUID userId) {
|
||||
System.out.println("[LOG] Récupération des stories actives pour l'utilisateur ID : " + userId);
|
||||
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
return storyRepository.findActiveByUserId(userId);
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une nouvelle story.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur créateur
|
||||
* @param mediaType Le type de média (IMAGE ou VIDEO)
|
||||
* @param mediaUrl L'URL du média
|
||||
* @param thumbnailUrl L'URL du thumbnail (optionnel, pour les vidéos)
|
||||
* @param durationSeconds La durée en secondes (optionnel, pour les vidéos)
|
||||
* @return La story créée
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
* @throws IllegalArgumentException Si les paramètres sont invalides
|
||||
*/
|
||||
@Transactional
|
||||
public Story createStory(UUID userId, MediaType mediaType, String mediaUrl, String thumbnailUrl, Integer durationSeconds) {
|
||||
System.out.println("[LOG] Création d'une story par l'utilisateur ID : " + userId);
|
||||
|
||||
// Validation de l'utilisateur
|
||||
Users user = usersRepository.findById(userId);
|
||||
if (user == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + userId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + userId);
|
||||
}
|
||||
|
||||
// Validation des paramètres
|
||||
if (mediaUrl == null || mediaUrl.trim().isEmpty()) {
|
||||
System.out.println("[ERROR] L'URL du média est obligatoire");
|
||||
throw new IllegalArgumentException("L'URL du média est obligatoire");
|
||||
}
|
||||
|
||||
if (mediaType == null) {
|
||||
System.out.println("[ERROR] Le type de média est obligatoire");
|
||||
throw new IllegalArgumentException("Le type de média est obligatoire");
|
||||
}
|
||||
|
||||
// Création de la story
|
||||
Story story = new Story(user, mediaType, mediaUrl);
|
||||
|
||||
if (thumbnailUrl != null && !thumbnailUrl.trim().isEmpty()) {
|
||||
story.setThumbnailUrl(thumbnailUrl);
|
||||
}
|
||||
|
||||
if (durationSeconds != null && durationSeconds > 0) {
|
||||
story.setDurationSeconds(durationSeconds);
|
||||
}
|
||||
|
||||
storyRepository.persist(story);
|
||||
System.out.println("[LOG] Story créée avec succès : " + story.getId());
|
||||
return story;
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère une story par son ID.
|
||||
*
|
||||
* @param storyId L'ID de la story
|
||||
* @return La story trouvée
|
||||
* @throws IllegalArgumentException Si la story n'existe pas
|
||||
*/
|
||||
public Story getStoryById(UUID storyId) {
|
||||
System.out.println("[LOG] Récupération de la story ID : " + storyId);
|
||||
|
||||
Story story = storyRepository.findById(storyId);
|
||||
if (story == null) {
|
||||
System.out.println("[ERROR] Story non trouvée avec l'ID : " + storyId);
|
||||
throw new IllegalArgumentException("Story non trouvée avec l'ID : " + storyId);
|
||||
}
|
||||
|
||||
return story;
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque une story comme vue par un utilisateur.
|
||||
*
|
||||
* @param storyId L'ID de la story
|
||||
* @param viewerId L'ID de l'utilisateur qui voit la story
|
||||
* @return La story mise à jour
|
||||
* @throws UserNotFoundException Si l'utilisateur n'existe pas
|
||||
* @throws IllegalArgumentException Si la story n'existe pas
|
||||
*/
|
||||
@Transactional
|
||||
public Story markStoryAsViewed(UUID storyId, UUID viewerId) {
|
||||
System.out.println("[LOG] Marquage de la story ID : " + storyId + " comme vue par l'utilisateur ID : " + viewerId);
|
||||
|
||||
// Validation de l'utilisateur
|
||||
Users viewer = usersRepository.findById(viewerId);
|
||||
if (viewer == null) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'ID : " + viewerId);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'ID : " + viewerId);
|
||||
}
|
||||
|
||||
// Validation de la story
|
||||
Story story = storyRepository.findById(storyId);
|
||||
if (story == null) {
|
||||
System.out.println("[ERROR] Story non trouvée avec l'ID : " + storyId);
|
||||
throw new IllegalArgumentException("Story non trouvée avec l'ID : " + storyId);
|
||||
}
|
||||
|
||||
// Marquer comme vue
|
||||
boolean isNewView = story.markAsViewed(viewerId);
|
||||
if (isNewView) {
|
||||
storyRepository.persist(story);
|
||||
System.out.println("[LOG] Story marquée comme vue (nouvelle vue)");
|
||||
} else {
|
||||
System.out.println("[LOG] Story déjà vue par cet utilisateur");
|
||||
}
|
||||
|
||||
return story;
|
||||
}
|
||||
|
||||
/**
|
||||
* Supprime une story.
|
||||
*
|
||||
* @param storyId L'ID de la story
|
||||
* @return true si la story a été supprimée, false sinon
|
||||
*/
|
||||
@Transactional
|
||||
public boolean deleteStory(UUID storyId) {
|
||||
System.out.println("[LOG] Suppression de la story ID : " + storyId);
|
||||
|
||||
Story story = storyRepository.findById(storyId);
|
||||
if (story == null) {
|
||||
System.out.println("[ERROR] Story non trouvée avec l'ID : " + storyId);
|
||||
return false;
|
||||
}
|
||||
|
||||
storyRepository.delete(story);
|
||||
System.out.println("[LOG] Story supprimée avec succès");
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Désactive toutes les stories expirées.
|
||||
* Cette méthode doit être appelée périodiquement par un job schedulé.
|
||||
*
|
||||
* @return Le nombre de stories désactivées
|
||||
*/
|
||||
@Transactional
|
||||
public int deactivateExpiredStories() {
|
||||
System.out.println("[LOG] Désactivation des stories expirées");
|
||||
return storyRepository.deactivateExpiredStories();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère les stories les plus vues.
|
||||
*
|
||||
* @param limit Le nombre maximum de stories à retourner
|
||||
* @return Liste des stories les plus vues
|
||||
*/
|
||||
public List<Story> getMostViewedStories(int limit) {
|
||||
System.out.println("[LOG] Récupération des " + limit + " stories les plus vues");
|
||||
return storyRepository.findMostViewedStories(limit);
|
||||
}
|
||||
}
|
||||
@@ -191,4 +191,21 @@ public class UsersService {
|
||||
}
|
||||
return deleted;
|
||||
}
|
||||
|
||||
/**
|
||||
* Recherche un utilisateur par son email.
|
||||
*
|
||||
* @param email L'email de l'utilisateur à rechercher.
|
||||
* @return L'utilisateur trouvé.
|
||||
* @throws UserNotFoundException Si l'utilisateur n'est pas trouvé.
|
||||
*/
|
||||
public Users getUserByEmail(String email) {
|
||||
Optional<Users> userOptional = usersRepository.findByEmail(email);
|
||||
if (userOptional.isEmpty()) {
|
||||
System.out.println("[ERROR] Utilisateur non trouvé avec l'email : " + email);
|
||||
throw new UserNotFoundException("Utilisateur non trouvé avec l'email : " + email);
|
||||
}
|
||||
System.out.println("[LOG] Utilisateur trouvé avec l'email : " + email);
|
||||
return userOptional.get();
|
||||
}
|
||||
}
|
||||
|
||||
361
src/main/java/com/lions/dev/websocket/ChatWebSocket.java
Normal file
361
src/main/java/com/lions/dev/websocket/ChatWebSocket.java
Normal file
@@ -0,0 +1,361 @@
|
||||
package com.lions.dev.websocket;
|
||||
|
||||
import com.lions.dev.dto.request.chat.SendMessageRequestDTO;
|
||||
import com.lions.dev.dto.response.chat.MessageResponseDTO;
|
||||
import com.lions.dev.entity.chat.Message;
|
||||
import com.lions.dev.service.MessageService;
|
||||
import io.quarkus.logging.Log;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.websocket.*;
|
||||
import jakarta.websocket.server.PathParam;
|
||||
import jakarta.websocket.server.ServerEndpoint;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* WebSocket endpoint pour le chat en temps réel.
|
||||
*
|
||||
* Ce endpoint gère:
|
||||
* - La connexion/déconnexion des utilisateurs
|
||||
* - L'envoi et la réception de messages en temps réel
|
||||
* - Les indicateurs de frappe (typing indicators)
|
||||
* - Les confirmations de lecture (read receipts)
|
||||
*
|
||||
* URL: ws://localhost:8080/chat/ws/{userId}
|
||||
*/
|
||||
@ServerEndpoint("/chat/ws/{userId}")
|
||||
@ApplicationScoped
|
||||
public class ChatWebSocket {
|
||||
|
||||
@Inject
|
||||
MessageService messageService;
|
||||
|
||||
// Map pour stocker les sessions WebSocket des utilisateurs connectés
|
||||
private static final Map<UUID, Session> sessions = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Appelé lorsqu'un utilisateur se connecte.
|
||||
*
|
||||
* @param session La session WebSocket
|
||||
* @param userId L'ID de l'utilisateur
|
||||
*/
|
||||
@OnOpen
|
||||
public void onOpen(Session session, @PathParam("userId") String userId) {
|
||||
try {
|
||||
UUID userUUID = UUID.fromString(userId);
|
||||
sessions.put(userUUID, session);
|
||||
Log.info("[LOG] WebSocket ouvert pour l'utilisateur ID : " + userId);
|
||||
|
||||
// Envoyer un message de confirmation
|
||||
sendToUser(userUUID, "{\"type\":\"connected\",\"message\":\"Connecté au chat\"}");
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.error("[ERROR] Erreur lors de la connexion WebSocket : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appelé lorsqu'un message est reçu.
|
||||
*
|
||||
* @param message Le message reçu (au format JSON)
|
||||
* @param userId L'ID de l'utilisateur qui envoie le message
|
||||
*/
|
||||
@OnMessage
|
||||
public void onMessage(String message, @PathParam("userId") String userId) {
|
||||
try {
|
||||
Log.info("[LOG] Message reçu de l'utilisateur " + userId + " : " + message);
|
||||
|
||||
// Parser le message JSON
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
Map<String, Object> messageData = mapper.readValue(message, Map.class);
|
||||
|
||||
String type = (String) messageData.get("type");
|
||||
|
||||
switch (type) {
|
||||
case "message":
|
||||
handleChatMessage(messageData, userId);
|
||||
break;
|
||||
case "typing":
|
||||
handleTypingIndicator(messageData, userId);
|
||||
break;
|
||||
case "read":
|
||||
handleReadReceipt(messageData, userId);
|
||||
break;
|
||||
default:
|
||||
Log.warn("[WARN] Type de message inconnu : " + type);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.error("[ERROR] Erreur lors du traitement du message : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appelé lorsqu'une erreur se produit.
|
||||
*
|
||||
* @param session La session WebSocket
|
||||
* @param error L'erreur
|
||||
*/
|
||||
@OnError
|
||||
public void onError(Session session, Throwable error) {
|
||||
Log.error("[ERROR] Erreur WebSocket : " + error.getMessage(), error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appelé lorsqu'un utilisateur se déconnecte.
|
||||
*
|
||||
* @param session La session WebSocket
|
||||
* @param userId L'ID de l'utilisateur
|
||||
*/
|
||||
@OnClose
|
||||
public void onClose(Session session, @PathParam("userId") String userId) {
|
||||
try {
|
||||
UUID userUUID = UUID.fromString(userId);
|
||||
sessions.remove(userUUID);
|
||||
Log.info("[LOG] WebSocket fermé pour l'utilisateur ID : " + userId);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.error("[ERROR] Erreur lors de la fermeture WebSocket : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère l'envoi d'un message de chat.
|
||||
*/
|
||||
private void handleChatMessage(Map<String, Object> messageData, String senderId) {
|
||||
try {
|
||||
UUID senderUUID = UUID.fromString(senderId);
|
||||
UUID recipientUUID = UUID.fromString((String) messageData.get("recipientId"));
|
||||
String content = (String) messageData.get("content");
|
||||
String messageType = messageData.getOrDefault("messageType", "text").toString();
|
||||
String mediaUrl = (String) messageData.get("mediaUrl");
|
||||
|
||||
// Enregistrer le message dans la base de données
|
||||
Message message = messageService.sendMessage(
|
||||
senderUUID,
|
||||
recipientUUID,
|
||||
content,
|
||||
messageType,
|
||||
mediaUrl
|
||||
);
|
||||
|
||||
// Créer le DTO de réponse
|
||||
MessageResponseDTO response = new MessageResponseDTO(message);
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
String responseJson = mapper.writeValueAsString(Map.of(
|
||||
"type", "message",
|
||||
"data", response
|
||||
));
|
||||
|
||||
// Envoyer le message au destinataire s'il est connecté
|
||||
sendToUser(recipientUUID, responseJson);
|
||||
|
||||
// Envoyer une confirmation à l'expéditeur
|
||||
sendToUser(senderUUID, responseJson);
|
||||
|
||||
Log.info("[LOG] Message envoyé de " + senderId + " à " + recipientUUID);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.error("[ERROR] Erreur lors de l'envoi du message : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère les indicateurs de frappe.
|
||||
*/
|
||||
private void handleTypingIndicator(Map<String, Object> messageData, String userId) {
|
||||
try {
|
||||
UUID recipientUUID = UUID.fromString((String) messageData.get("recipientId"));
|
||||
boolean isTyping = (boolean) messageData.getOrDefault("isTyping", false);
|
||||
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
String response = mapper.writeValueAsString(Map.of(
|
||||
"type", "typing",
|
||||
"userId", userId,
|
||||
"isTyping", isTyping
|
||||
));
|
||||
|
||||
sendToUser(recipientUUID, response);
|
||||
|
||||
Log.info("[LOG] Indicateur de frappe envoyé de " + userId + " à " + recipientUUID);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.error("[ERROR] Erreur lors de l'envoi de l'indicateur de frappe : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère les confirmations de lecture.
|
||||
*/
|
||||
private void handleReadReceipt(Map<String, Object> messageData, String userId) {
|
||||
try {
|
||||
UUID messageUUID = UUID.fromString((String) messageData.get("messageId"));
|
||||
|
||||
// Marquer le message comme lu
|
||||
Message message = messageService.markMessageAsRead(messageUUID);
|
||||
|
||||
// Notifier l'expéditeur que le message a été lu
|
||||
UUID senderUUID = message.getSender().getId();
|
||||
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
String response = mapper.writeValueAsString(Map.of(
|
||||
"type", "read",
|
||||
"messageId", messageUUID.toString(),
|
||||
"readBy", userId
|
||||
));
|
||||
|
||||
sendToUser(senderUUID, response);
|
||||
|
||||
Log.info("[LOG] Confirmation de lecture envoyée pour le message " + messageUUID);
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.error("[ERROR] Erreur lors de l'envoi de la confirmation de lecture : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie un message à un utilisateur spécifique.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @param message Le message à envoyer
|
||||
*/
|
||||
private void sendToUser(UUID userId, String message) {
|
||||
Session session = sessions.get(userId);
|
||||
if (session != null && session.isOpen()) {
|
||||
try {
|
||||
session.getAsyncRemote().sendText(message);
|
||||
} catch (Exception e) {
|
||||
Log.error("[ERROR] Erreur lors de l'envoi du message à l'utilisateur " + userId + " : " + e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
Log.warn("[WARN] Utilisateur " + userId + " non connecté ou session fermée");
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Diffuse un message à tous les utilisateurs connectés.
|
||||
*
|
||||
* @param message Le message à diffuser
|
||||
*/
|
||||
public void broadcast(String message) {
|
||||
sessions.values().forEach(session -> {
|
||||
if (session.isOpen()) {
|
||||
try {
|
||||
session.getAsyncRemote().sendText(message);
|
||||
} catch (Exception e) {
|
||||
Log.error("[ERROR] Erreur lors de la diffusion : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie un message chat à un utilisateur spécifique via WebSocket.
|
||||
*
|
||||
* Cette méthode est statique pour permettre son appel depuis les services
|
||||
* (comme MessageService) sans nécessiter une instance de ChatWebSocket.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur destinataire
|
||||
* @param messageData Les données du message (id, conversationId, content, etc.)
|
||||
*/
|
||||
public static void sendMessageToUser(UUID userId, Map<String, Object> messageData) {
|
||||
Session session = sessions.get(userId);
|
||||
|
||||
if (session == null || !session.isOpen()) {
|
||||
Log.warn("[CHAT-WS] Utilisateur " + userId + " non connecté, message non envoyé");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
// Construire le message JSON au format attendu par le frontend
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
Map<String, Object> envelope = Map.of(
|
||||
"type", "message",
|
||||
"data", messageData
|
||||
);
|
||||
String json = mapper.writeValueAsString(envelope);
|
||||
|
||||
session.getAsyncRemote().sendText(json);
|
||||
|
||||
Log.info("[CHAT-WS] Message envoyé à l'utilisateur " + userId);
|
||||
} catch (Exception e) {
|
||||
Log.error("[CHAT-WS] Erreur lors de l'envoi du message à " + userId + " : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie une confirmation de délivrance à l'expéditeur via WebSocket.
|
||||
*
|
||||
* Cette méthode est appelée lorsqu'un message est délivré au destinataire
|
||||
* pour notifier l'expéditeur que le message a bien été reçu.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur (expéditeur) à notifier
|
||||
* @param deliveryData Les données de confirmation (messageId, isDelivered, timestamp)
|
||||
*/
|
||||
public static void sendDeliveryConfirmation(UUID userId, Map<String, Object> deliveryData) {
|
||||
Session session = sessions.get(userId);
|
||||
|
||||
if (session == null || !session.isOpen()) {
|
||||
Log.warn("[CHAT-WS] Utilisateur " + userId + " non connecté, confirmation de délivrance non envoyée");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
Map<String, Object> envelope = Map.of(
|
||||
"type", "delivered",
|
||||
"data", deliveryData
|
||||
);
|
||||
String json = mapper.writeValueAsString(envelope);
|
||||
|
||||
session.getAsyncRemote().sendText(json);
|
||||
|
||||
Log.info("[CHAT-WS] Confirmation de délivrance envoyée à l'utilisateur " + userId);
|
||||
} catch (Exception e) {
|
||||
Log.error("[CHAT-WS] Erreur lors de l'envoi de la confirmation de délivrance : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie une confirmation de lecture à l'expéditeur via WebSocket.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur expéditeur
|
||||
* @param readData Les données de confirmation de lecture
|
||||
*/
|
||||
public static void sendReadConfirmation(UUID userId, Map<String, Object> readData) {
|
||||
Session session = sessions.get(userId);
|
||||
|
||||
if (session == null || !session.isOpen()) {
|
||||
Log.warn("[CHAT-WS] Utilisateur " + userId + " non connecté, confirmation de lecture non envoyée");
|
||||
return;
|
||||
}
|
||||
|
||||
try {
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
Map<String, Object> envelope = Map.of(
|
||||
"type", "read",
|
||||
"data", readData
|
||||
);
|
||||
String json = mapper.writeValueAsString(envelope);
|
||||
|
||||
session.getAsyncRemote().sendText(json);
|
||||
|
||||
Log.info("[CHAT-WS] Confirmation de lecture envoyée à l'utilisateur " + userId);
|
||||
} catch (Exception e) {
|
||||
Log.error("[CHAT-WS] Erreur lors de l'envoi de la confirmation de lecture : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le nombre d'utilisateurs connectés.
|
||||
*
|
||||
* @return Le nombre d'utilisateurs connectés
|
||||
*/
|
||||
public static int getConnectedUsersCount() {
|
||||
return sessions.size();
|
||||
}
|
||||
}
|
||||
365
src/main/java/com/lions/dev/websocket/NotificationWebSocket.java
Normal file
365
src/main/java/com/lions/dev/websocket/NotificationWebSocket.java
Normal file
@@ -0,0 +1,365 @@
|
||||
package com.lions.dev.websocket;
|
||||
|
||||
import com.lions.dev.service.NotificationService;
|
||||
import com.lions.dev.service.FriendshipService;
|
||||
import io.quarkus.logging.Log;
|
||||
import jakarta.enterprise.context.ApplicationScoped;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.websocket.*;
|
||||
import jakarta.websocket.server.PathParam;
|
||||
import jakarta.websocket.server.ServerEndpoint;
|
||||
|
||||
import java.io.IOException;
|
||||
import java.util.Map;
|
||||
import java.util.Set;
|
||||
import java.util.UUID;
|
||||
import java.util.concurrent.ConcurrentHashMap;
|
||||
|
||||
/**
|
||||
* WebSocket endpoint pour les notifications en temps réel.
|
||||
*
|
||||
* Ce endpoint gère:
|
||||
* - Les notifications de demandes d'amitié (envoi, acceptation, rejet)
|
||||
* - Les notifications système (événements, rappels)
|
||||
* - Les alertes de messages
|
||||
*
|
||||
* URL: ws://localhost:8080/notifications/ws/{userId}
|
||||
*/
|
||||
@ServerEndpoint("/notifications/ws/{userId}")
|
||||
@ApplicationScoped
|
||||
public class NotificationWebSocket {
|
||||
|
||||
@Inject
|
||||
NotificationService notificationService;
|
||||
|
||||
@Inject
|
||||
FriendshipService friendshipService;
|
||||
|
||||
@Inject
|
||||
com.lions.dev.service.PresenceService presenceService;
|
||||
|
||||
// Map pour stocker les sessions WebSocket par utilisateur
|
||||
// Support de plusieurs sessions par utilisateur (multi-device)
|
||||
private static final Map<UUID, Set<Session>> userSessions = new ConcurrentHashMap<>();
|
||||
|
||||
/**
|
||||
* Appelé lorsqu'un utilisateur se connecte.
|
||||
*
|
||||
* @param session La session WebSocket
|
||||
* @param userId L'ID de l'utilisateur
|
||||
*/
|
||||
@OnOpen
|
||||
public void onOpen(Session session, @PathParam("userId") String userId) {
|
||||
try {
|
||||
UUID userUUID = UUID.fromString(userId);
|
||||
|
||||
// Ajouter la session à l'ensemble des sessions de l'utilisateur
|
||||
userSessions.computeIfAbsent(userUUID, k -> ConcurrentHashMap.newKeySet()).add(session);
|
||||
|
||||
Log.info("[NOTIFICATION-WS] Connexion ouverte pour l'utilisateur ID : " + userId +
|
||||
" (Total sessions: " + userSessions.get(userUUID).size() + ")");
|
||||
|
||||
// Envoyer un message de confirmation
|
||||
String confirmationMessage = buildNotificationJson("connected",
|
||||
Map.of("message", "Connecté au service de notifications en temps réel"));
|
||||
|
||||
session.getAsyncRemote().sendText(confirmationMessage);
|
||||
|
||||
// Marquer l'utilisateur comme en ligne
|
||||
presenceService.setUserOnline(userUUID);
|
||||
|
||||
} catch (IllegalArgumentException e) {
|
||||
Log.error("[NOTIFICATION-WS] UUID invalide : " + userId, e);
|
||||
try {
|
||||
session.close(new CloseReason(CloseReason.CloseCodes.CANNOT_ACCEPT, "UUID invalide"));
|
||||
} catch (IOException ioException) {
|
||||
Log.error("[NOTIFICATION-WS] Erreur lors de la fermeture de session", ioException);
|
||||
}
|
||||
} catch (Exception e) {
|
||||
Log.error("[NOTIFICATION-WS] Erreur lors de la connexion : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appelé lorsqu'un message est reçu.
|
||||
*
|
||||
* Gère les messages de type ping, ack, etc.
|
||||
*
|
||||
* @param message Le message reçu (au format JSON)
|
||||
* @param userId L'ID de l'utilisateur qui envoie le message
|
||||
*/
|
||||
@OnMessage
|
||||
public void onMessage(String message, @PathParam("userId") String userId) {
|
||||
try {
|
||||
Log.info("[NOTIFICATION-WS] Message reçu de l'utilisateur " + userId + " : " + message);
|
||||
|
||||
// Parser le message JSON
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
Map<String, Object> messageData = mapper.readValue(message, Map.class);
|
||||
|
||||
String type = (String) messageData.get("type");
|
||||
|
||||
switch (type) {
|
||||
case "ping":
|
||||
handlePing(userId);
|
||||
break;
|
||||
case "ack":
|
||||
handleAcknowledgement(messageData, userId);
|
||||
break;
|
||||
default:
|
||||
Log.warn("[NOTIFICATION-WS] Type de message inconnu : " + type);
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.error("[NOTIFICATION-WS] Erreur lors du traitement du message : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Appelé lorsqu'une erreur se produit.
|
||||
*
|
||||
* @param session La session WebSocket
|
||||
* @param error L'erreur
|
||||
*/
|
||||
@OnError
|
||||
public void onError(Session session, Throwable error) {
|
||||
Log.error("[NOTIFICATION-WS] Erreur WebSocket : " + error.getMessage(), error);
|
||||
}
|
||||
|
||||
/**
|
||||
* Appelé lorsqu'un utilisateur se déconnecte.
|
||||
*
|
||||
* @param session La session WebSocket
|
||||
* @param userId L'ID de l'utilisateur
|
||||
*/
|
||||
@OnClose
|
||||
public void onClose(Session session, @PathParam("userId") String userId) {
|
||||
try {
|
||||
UUID userUUID = UUID.fromString(userId);
|
||||
|
||||
// Supprimer la session de l'ensemble
|
||||
Set<Session> sessions = userSessions.get(userUUID);
|
||||
if (sessions != null) {
|
||||
sessions.remove(session);
|
||||
|
||||
// Si l'utilisateur n'a plus de sessions, supprimer l'entrée et marquer hors ligne
|
||||
if (sessions.isEmpty()) {
|
||||
userSessions.remove(userUUID);
|
||||
presenceService.setUserOffline(userUUID);
|
||||
Log.info("[NOTIFICATION-WS] Toutes les sessions fermées pour l'utilisateur ID : " + userId);
|
||||
} else {
|
||||
Log.info("[NOTIFICATION-WS] Session fermée pour l'utilisateur ID : " + userId +
|
||||
" (Sessions restantes: " + sessions.size() + ")");
|
||||
}
|
||||
}
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.error("[NOTIFICATION-WS] Erreur lors de la fermeture : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère les messages de type ping (keep-alive).
|
||||
*/
|
||||
private void handlePing(String userId) {
|
||||
try {
|
||||
UUID userUUID = UUID.fromString(userId);
|
||||
|
||||
// Mettre à jour le heartbeat de présence
|
||||
presenceService.heartbeat(userUUID);
|
||||
|
||||
String pongMessage = buildNotificationJson("pong", Map.of("timestamp", System.currentTimeMillis()));
|
||||
sendToUser(userUUID, pongMessage);
|
||||
|
||||
Log.debug("[NOTIFICATION-WS] Pong envoyé à l'utilisateur : " + userId);
|
||||
} catch (Exception e) {
|
||||
Log.error("[NOTIFICATION-WS] Erreur lors de l'envoi du pong : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Gère les accusés de réception des notifications.
|
||||
*/
|
||||
private void handleAcknowledgement(Map<String, Object> messageData, String userId) {
|
||||
try {
|
||||
String notificationId = (String) messageData.get("notificationId");
|
||||
Log.info("[NOTIFICATION-WS] ACK reçu pour la notification " + notificationId + " de l'utilisateur " + userId);
|
||||
|
||||
// Optionnel: marquer la notification comme délivrée en base de données
|
||||
|
||||
} catch (Exception e) {
|
||||
Log.error("[NOTIFICATION-WS] Erreur lors du traitement de l'ACK : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie une notification à toutes les sessions d'un utilisateur spécifique.
|
||||
*
|
||||
* Cette méthode est statique pour permettre son appel depuis les services
|
||||
* sans nécessiter une instance de NotificationWebSocket.
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @param notificationType Le type de notification
|
||||
* @param data Les données de la notification
|
||||
*/
|
||||
public static void sendNotificationToUser(UUID userId, String notificationType, Map<String, Object> data) {
|
||||
Set<Session> sessions = userSessions.get(userId);
|
||||
|
||||
if (sessions == null || sessions.isEmpty()) {
|
||||
Log.warn("[NOTIFICATION-WS] Utilisateur " + userId + " non connecté ou aucune session active");
|
||||
return;
|
||||
}
|
||||
|
||||
String json = buildNotificationJson(notificationType, data);
|
||||
|
||||
int successCount = 0;
|
||||
int failCount = 0;
|
||||
|
||||
for (Session session : sessions) {
|
||||
if (session.isOpen()) {
|
||||
try {
|
||||
session.getAsyncRemote().sendText(json);
|
||||
successCount++;
|
||||
} catch (Exception e) {
|
||||
failCount++;
|
||||
Log.error("[NOTIFICATION-WS] Erreur lors de l'envoi à une session de l'utilisateur " + userId + " : " + e.getMessage(), e);
|
||||
}
|
||||
} else {
|
||||
failCount++;
|
||||
}
|
||||
}
|
||||
|
||||
Log.info("[NOTIFICATION-WS] Notification " + notificationType + " envoyée à l'utilisateur " + userId +
|
||||
" (Succès: " + successCount + ", Échec: " + failCount + ")");
|
||||
}
|
||||
|
||||
/**
|
||||
* Envoie un message à toutes les sessions d'un utilisateur.
|
||||
*
|
||||
* Version privée pour usage interne (ping/pong, etc.)
|
||||
*
|
||||
* @param userId L'ID de l'utilisateur
|
||||
* @param message Le message à envoyer
|
||||
*/
|
||||
private void sendToUser(UUID userId, String message) {
|
||||
Set<Session> sessions = userSessions.get(userId);
|
||||
|
||||
if (sessions != null) {
|
||||
for (Session session : sessions) {
|
||||
if (session.isOpen()) {
|
||||
try {
|
||||
session.getAsyncRemote().sendText(message);
|
||||
} catch (Exception e) {
|
||||
Log.error("[NOTIFICATION-WS] Erreur lors de l'envoi à l'utilisateur " + userId + " : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Diffuse une notification à tous les utilisateurs connectés.
|
||||
*
|
||||
* @param notificationType Le type de notification
|
||||
* @param data Les données de la notification
|
||||
*/
|
||||
public static void broadcastNotification(String notificationType, Map<String, Object> data) {
|
||||
String json = buildNotificationJson(notificationType, data);
|
||||
|
||||
int totalSessions = 0;
|
||||
int successCount = 0;
|
||||
|
||||
for (Set<Session> sessions : userSessions.values()) {
|
||||
for (Session session : sessions) {
|
||||
totalSessions++;
|
||||
if (session.isOpen()) {
|
||||
try {
|
||||
session.getAsyncRemote().sendText(json);
|
||||
successCount++;
|
||||
} catch (Exception e) {
|
||||
Log.error("[NOTIFICATION-WS] Erreur lors de la diffusion : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.info("[NOTIFICATION-WS] Notification diffusée à " + successCount + " sessions sur " + totalSessions);
|
||||
}
|
||||
|
||||
/**
|
||||
* Construit un message JSON pour les notifications.
|
||||
*
|
||||
* Format: {"type": "notification_type", "data": {...}}
|
||||
*
|
||||
* @param type Le type de notification
|
||||
* @param data Les données de la notification
|
||||
* @return Le JSON sous forme de String
|
||||
*/
|
||||
private static String buildNotificationJson(String type, Map<String, Object> data) {
|
||||
try {
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
Map<String, Object> message = Map.of(
|
||||
"type", type,
|
||||
"data", data,
|
||||
"timestamp", System.currentTimeMillis()
|
||||
);
|
||||
return mapper.writeValueAsString(message);
|
||||
} catch (Exception e) {
|
||||
Log.error("[NOTIFICATION-WS] Erreur lors de la construction du JSON : " + e.getMessage(), e);
|
||||
return "{\"type\":\"error\",\"data\":{\"message\":\"Erreur de construction du message\"}}";
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le nombre total d'utilisateurs connectés.
|
||||
*
|
||||
* @return Le nombre d'utilisateurs connectés
|
||||
*/
|
||||
public static int getConnectedUsersCount() {
|
||||
return userSessions.size();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère le nombre total de sessions actives.
|
||||
*
|
||||
* @return Le nombre total de sessions
|
||||
*/
|
||||
public static int getTotalSessionsCount() {
|
||||
return userSessions.values().stream()
|
||||
.mapToInt(Set::size)
|
||||
.sum();
|
||||
}
|
||||
|
||||
/**
|
||||
* Broadcast une mise à jour de présence à tous les utilisateurs connectés.
|
||||
*
|
||||
* @param presenceData Les données de présence (userId, isOnline, lastSeen)
|
||||
*/
|
||||
public static void broadcastPresenceUpdate(Map<String, Object> presenceData) {
|
||||
try {
|
||||
com.fasterxml.jackson.databind.ObjectMapper mapper = new com.fasterxml.jackson.databind.ObjectMapper();
|
||||
Map<String, Object> envelope = Map.of(
|
||||
"type", "presence",
|
||||
"data", presenceData
|
||||
);
|
||||
String json = mapper.writeValueAsString(envelope);
|
||||
|
||||
// Envoyer à tous les utilisateurs connectés
|
||||
for (Set<Session> sessions : userSessions.values()) {
|
||||
for (Session session : sessions) {
|
||||
if (session.isOpen()) {
|
||||
try {
|
||||
session.getAsyncRemote().sendText(json);
|
||||
} catch (Exception e) {
|
||||
Log.error("[NOTIFICATION-WS] Erreur broadcast présence : " + e.getMessage());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Log.debug("[NOTIFICATION-WS] Présence broadcastée : " + presenceData.get("userId"));
|
||||
} catch (Exception e) {
|
||||
Log.error("[NOTIFICATION-WS] Erreur lors du broadcast de présence : " + e.getMessage(), e);
|
||||
}
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user