ConversationResponse/MessageResponse fields (muted, pinned, archived, edited, deleted) are primitive booleans — Lombok @Builder generates .muted() not .isMuted(). Also use Boolean.TRUE.equals() for null-safe unboxing from entity Boolean wrapper fields.
209 lines
7.6 KiB
Java
209 lines
7.6 KiB
Java
package dev.lions.unionflow.server.service;
|
|
|
|
import dev.lions.unionflow.server.api.dto.communication.request.CreateConversationRequest;
|
|
import dev.lions.unionflow.server.api.dto.communication.response.ConversationResponse;
|
|
import dev.lions.unionflow.server.api.dto.communication.response.MessageResponse;
|
|
import dev.lions.unionflow.server.entity.Conversation;
|
|
import dev.lions.unionflow.server.entity.Membre;
|
|
import dev.lions.unionflow.server.entity.Message;
|
|
import dev.lions.unionflow.server.entity.Organisation;
|
|
import dev.lions.unionflow.server.repository.ConversationRepository;
|
|
import dev.lions.unionflow.server.repository.MembreRepository;
|
|
import dev.lions.unionflow.server.repository.MessageRepository;
|
|
import dev.lions.unionflow.server.repository.OrganisationRepository;
|
|
import jakarta.enterprise.context.ApplicationScoped;
|
|
import jakarta.inject.Inject;
|
|
import jakarta.transaction.Transactional;
|
|
import jakarta.ws.rs.NotFoundException;
|
|
import org.jboss.logging.Logger;
|
|
|
|
import java.time.LocalDateTime;
|
|
import java.util.List;
|
|
import java.util.UUID;
|
|
import java.util.stream.Collectors;
|
|
|
|
/**
|
|
* Service de gestion des conversations
|
|
*
|
|
* @author UnionFlow Team
|
|
* @version 1.0
|
|
* @since 2026-03-16
|
|
*/
|
|
@ApplicationScoped
|
|
public class ConversationService {
|
|
|
|
private static final Logger LOG = Logger.getLogger(ConversationService.class);
|
|
|
|
@Inject
|
|
ConversationRepository conversationRepository;
|
|
|
|
@Inject
|
|
MessageRepository messageRepository;
|
|
|
|
@Inject
|
|
MembreRepository membreRepository;
|
|
|
|
@Inject
|
|
OrganisationRepository organisationRepository;
|
|
|
|
/**
|
|
* Liste les conversations d'un membre
|
|
*/
|
|
public List<ConversationResponse> getConversations(UUID membreId, UUID organisationId, boolean includeArchived) {
|
|
LOG.infof("Récupération conversations pour membre %s", membreId);
|
|
|
|
List<Conversation> conversations;
|
|
if (organisationId != null) {
|
|
conversations = conversationRepository.findByOrganisation(organisationId);
|
|
} else {
|
|
conversations = conversationRepository.findByParticipant(membreId, includeArchived);
|
|
}
|
|
|
|
return conversations.stream()
|
|
.map(c -> convertToResponse(c, membreId))
|
|
.collect(Collectors.toList());
|
|
}
|
|
|
|
/**
|
|
* Récupère une conversation par ID
|
|
*/
|
|
public ConversationResponse getConversationById(UUID conversationId, UUID membreId) {
|
|
Conversation conversation = conversationRepository.findByIdAndParticipant(conversationId, membreId)
|
|
.orElseThrow(() -> new NotFoundException("Conversation non trouvée ou accès refusé"));
|
|
|
|
return convertToResponse(conversation, membreId);
|
|
}
|
|
|
|
/**
|
|
* Crée une nouvelle conversation
|
|
*/
|
|
@Transactional
|
|
public ConversationResponse createConversation(CreateConversationRequest request, UUID creatorId) {
|
|
LOG.infof("Création conversation: %s (type: %s)", request.name(), request.type());
|
|
|
|
Conversation conversation = new Conversation();
|
|
conversation.setName(request.name());
|
|
conversation.setDescription(request.description());
|
|
conversation.setType(request.type());
|
|
|
|
// Ajouter les participants
|
|
List<Membre> participants = request.participantIds().stream()
|
|
.map(id -> membreRepository.findById(id))
|
|
.filter(membre -> membre != null)
|
|
.collect(Collectors.toList());
|
|
|
|
// Ajouter le créateur s'il n'est pas dans la liste
|
|
Membre creator = membreRepository.findById(creatorId);
|
|
if (creator != null && !participants.contains(creator)) {
|
|
participants.add(creator);
|
|
}
|
|
|
|
conversation.setParticipants(participants);
|
|
|
|
// Organisation
|
|
if (request.organisationId() != null) {
|
|
Organisation org = organisationRepository.findById(request.organisationId());
|
|
conversation.setOrganisation(org);
|
|
}
|
|
|
|
conversation.setUpdatedAt(LocalDateTime.now());
|
|
conversationRepository.persist(conversation);
|
|
|
|
return convertToResponse(conversation, creatorId);
|
|
}
|
|
|
|
/**
|
|
* Archive/désarchive une conversation
|
|
*/
|
|
@Transactional
|
|
public void archiveConversation(UUID conversationId, UUID membreId, boolean archive) {
|
|
Conversation conversation = conversationRepository.findByIdAndParticipant(conversationId, membreId)
|
|
.orElseThrow(() -> new NotFoundException("Conversation non trouvée"));
|
|
|
|
conversation.setIsArchived(archive);
|
|
conversation.setUpdatedAt(LocalDateTime.now());
|
|
conversationRepository.persist(conversation);
|
|
}
|
|
|
|
/**
|
|
* Marque une conversation comme lue
|
|
*/
|
|
@Transactional
|
|
public void markAsRead(UUID conversationId, UUID membreId) {
|
|
// Vérifier accès
|
|
conversationRepository.findByIdAndParticipant(conversationId, membreId)
|
|
.orElseThrow(() -> new NotFoundException("Conversation non trouvée"));
|
|
|
|
messageRepository.markAllAsReadByConversationAndMember(conversationId, membreId);
|
|
}
|
|
|
|
/**
|
|
* Toggle mute
|
|
*/
|
|
@Transactional
|
|
public void toggleMute(UUID conversationId, UUID membreId) {
|
|
Conversation conversation = conversationRepository.findByIdAndParticipant(conversationId, membreId)
|
|
.orElseThrow(() -> new NotFoundException("Conversation non trouvée"));
|
|
|
|
conversation.setIsMuted(!conversation.getIsMuted());
|
|
conversation.setUpdatedAt(LocalDateTime.now());
|
|
}
|
|
|
|
/**
|
|
* Toggle pin
|
|
*/
|
|
@Transactional
|
|
public void togglePin(UUID conversationId, UUID membreId) {
|
|
Conversation conversation = conversationRepository.findByIdAndParticipant(conversationId, membreId)
|
|
.orElseThrow(() -> new NotFoundException("Conversation non trouvée"));
|
|
|
|
conversation.setIsPinned(!conversation.getIsPinned());
|
|
conversation.setUpdatedAt(LocalDateTime.now());
|
|
}
|
|
|
|
/**
|
|
* Convertit Conversation en DTO
|
|
*/
|
|
private ConversationResponse convertToResponse(Conversation c, UUID currentUserId) {
|
|
Message lastMsg = messageRepository.findLastByConversation(c.getId());
|
|
long unreadCount = messageRepository.countUnreadByConversationAndMember(c.getId(), currentUserId);
|
|
|
|
return ConversationResponse.builder()
|
|
.id(c.getId())
|
|
.name(c.getName())
|
|
.description(c.getDescription())
|
|
.type(c.getType())
|
|
.participantIds(c.getParticipants().stream().map(Membre::getId).collect(Collectors.toList()))
|
|
.organisationId(c.getOrganisation() != null ? c.getOrganisation().getId() : null)
|
|
.lastMessage(lastMsg != null ? convertMessageToResponse(lastMsg) : null)
|
|
.unreadCount((int) unreadCount)
|
|
.muted(Boolean.TRUE.equals(c.getIsMuted()))
|
|
.pinned(Boolean.TRUE.equals(c.getIsPinned()))
|
|
.archived(Boolean.TRUE.equals(c.getIsArchived()))
|
|
.createdAt(c.getDateCreation())
|
|
.updatedAt(c.getUpdatedAt())
|
|
.avatarUrl(c.getAvatarUrl())
|
|
.build();
|
|
}
|
|
|
|
/**
|
|
* Convertit Message en DTO simple
|
|
*/
|
|
private MessageResponse convertMessageToResponse(Message m) {
|
|
return MessageResponse.builder()
|
|
.id(m.getId())
|
|
.conversationId(m.getConversation().getId())
|
|
.senderId(m.getSender().getId())
|
|
.senderName(m.getSenderName())
|
|
.senderAvatar(m.getSenderAvatar())
|
|
.content(m.getContent())
|
|
.type(m.getType())
|
|
.status(m.getStatus())
|
|
.priority(m.getPriority())
|
|
.createdAt(m.getDateCreation())
|
|
.edited(Boolean.TRUE.equals(m.getIsEdited()))
|
|
.deleted(Boolean.TRUE.equals(m.getIsDeleted()))
|
|
.build();
|
|
}
|
|
}
|