feat(backend): implémentation complète du système de messagerie
Ajoute l'infrastructure backend complète pour les conversations et messages :
## Entités
- Conversation : conversations individuelles, groupes, broadcast, annonces
- Message : messages avec statut, priorité, pièces jointes, soft delete
## Repositories
- ConversationRepository : findByParticipant, findByIdAndParticipant (sécurité)
- MessageRepository : findByConversation, countUnread, markAsRead
## Services
- ConversationService : CRUD conversations, archive, mute, pin
- MessageService : send, edit, delete, getMessages
## REST Endpoints (12 total)
- GET /api/conversations - Lister mes conversations
- GET /api/conversations/{id} - Récupérer une conversation
- POST /api/conversations - Créer conversation
- PUT /api/conversations/{id}/archive - Archiver
- PUT /api/conversations/{id}/mark-read - Marquer comme lu
- PUT /api/conversations/{id}/toggle-mute - Activer/désactiver son
- PUT /api/conversations/{id}/toggle-pin - Épingler
- GET /api/messages?conversationId=X - Lister messages
- POST /api/messages - Envoyer message
- PUT /api/messages/{id} - Éditer message
- DELETE /api/messages/{id} - Supprimer message
## Database
- Migration V6 : tables conversations, messages, conversation_participants
- Indexes sur organisation, type, archived, deleted pour performance
## Sécurité
- SecuriteHelper.resolveMembreId() : résolution membre depuis JWT
- Vérification accès conversation avant toute opération
- @RolesAllowed sur tous les endpoints
Débloquer la fonctionnalité Communication mobile (actuellement 100% stubs).
Co-Authored-By: Claude Sonnet 4.5 <noreply@anthropic.com>
This commit is contained in:
@@ -0,0 +1,145 @@
|
||||
package dev.lions.unionflow.server.resource;
|
||||
|
||||
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.service.ConversationService;
|
||||
import dev.lions.unionflow.server.service.support.SecuriteHelper;
|
||||
import jakarta.annotation.security.RolesAllowed;
|
||||
import jakarta.inject.Inject;
|
||||
import jakarta.validation.Valid;
|
||||
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.parameters.Parameter;
|
||||
import org.eclipse.microprofile.openapi.annotations.responses.APIResponse;
|
||||
import org.eclipse.microprofile.openapi.annotations.tags.Tag;
|
||||
import org.jboss.logging.Logger;
|
||||
|
||||
import java.util.List;
|
||||
import java.util.Map;
|
||||
import java.util.UUID;
|
||||
|
||||
/**
|
||||
* Resource REST pour la gestion des conversations
|
||||
*
|
||||
* @author UnionFlow Team
|
||||
* @version 1.0
|
||||
* @since 2026-03-16
|
||||
*/
|
||||
@Path("/api/conversations")
|
||||
@Produces(MediaType.APPLICATION_JSON)
|
||||
@Consumes(MediaType.APPLICATION_JSON)
|
||||
@Tag(name = "Communication", description = "Gestion des conversations et messages")
|
||||
@RolesAllowed({"ADMIN", "SUPER_ADMIN", "MEMBRE", "ADMIN_ORGANISATION"})
|
||||
public class ConversationResource {
|
||||
|
||||
private static final Logger LOG = Logger.getLogger(ConversationResource.class);
|
||||
|
||||
@Inject
|
||||
ConversationService conversationService;
|
||||
|
||||
@Inject
|
||||
SecuriteHelper securiteHelper;
|
||||
|
||||
/**
|
||||
* Liste les conversations de l'utilisateur connecté
|
||||
*/
|
||||
@GET
|
||||
@Operation(summary = "Lister mes conversations")
|
||||
@APIResponse(responseCode = "200", description = "Liste des conversations")
|
||||
public Response getConversations(
|
||||
@Parameter(description = "Inclure conversations archivées")
|
||||
@QueryParam("includeArchived") @DefaultValue("false") boolean includeArchived,
|
||||
@Parameter(description = "Filtrer par organisation")
|
||||
@QueryParam("organisationId") String organisationId
|
||||
) {
|
||||
UUID membreId = securiteHelper.resolveMembreId();
|
||||
UUID orgId = organisationId != null ? UUID.fromString(organisationId) : null;
|
||||
|
||||
List<ConversationResponse> conversations = conversationService.getConversations(membreId, orgId, includeArchived);
|
||||
return Response.ok(conversations).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Récupère une conversation par ID
|
||||
*/
|
||||
@GET
|
||||
@Path("/{id}")
|
||||
@Operation(summary = "Récupérer une conversation")
|
||||
@APIResponse(responseCode = "200", description = "Conversation trouvée")
|
||||
@APIResponse(responseCode = "404", description = "Conversation non trouvée")
|
||||
public Response getConversationById(@PathParam("id") UUID conversationId) {
|
||||
UUID membreId = securiteHelper.resolveMembreId();
|
||||
ConversationResponse conversation = conversationService.getConversationById(conversationId, membreId);
|
||||
return Response.ok(conversation).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Crée une nouvelle conversation
|
||||
*/
|
||||
@POST
|
||||
@Operation(summary = "Créer une conversation")
|
||||
@APIResponse(responseCode = "201", description = "Conversation créée")
|
||||
@APIResponse(responseCode = "400", description = "Données invalides")
|
||||
public Response createConversation(@Valid CreateConversationRequest request) {
|
||||
UUID creatorId = securiteHelper.resolveMembreId();
|
||||
ConversationResponse conversation = conversationService.createConversation(request, creatorId);
|
||||
return Response.status(Response.Status.CREATED).entity(conversation).build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Archive une conversation
|
||||
*/
|
||||
@PUT
|
||||
@Path("/{id}/archive")
|
||||
@Operation(summary = "Archiver/désarchiver une conversation")
|
||||
@APIResponse(responseCode = "204", description = "Conversation archivée")
|
||||
public Response archiveConversation(
|
||||
@PathParam("id") UUID conversationId,
|
||||
@QueryParam("archive") @DefaultValue("true") boolean archive
|
||||
) {
|
||||
UUID membreId = securiteHelper.resolveMembreId();
|
||||
conversationService.archiveConversation(conversationId, membreId, archive);
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Marque une conversation comme lue
|
||||
*/
|
||||
@PUT
|
||||
@Path("/{id}/mark-read")
|
||||
@Operation(summary = "Marquer conversation comme lue")
|
||||
@APIResponse(responseCode = "204", description = "Marquée comme lue")
|
||||
public Response markAsRead(@PathParam("id") UUID conversationId) {
|
||||
UUID membreId = securiteHelper.resolveMembreId();
|
||||
conversationService.markAsRead(conversationId, membreId);
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle mute conversation
|
||||
*/
|
||||
@PUT
|
||||
@Path("/{id}/toggle-mute")
|
||||
@Operation(summary = "Activer/désactiver le son")
|
||||
@APIResponse(responseCode = "204", description = "Paramètre modifié")
|
||||
public Response toggleMute(@PathParam("id") UUID conversationId) {
|
||||
UUID membreId = securiteHelper.resolveMembreId();
|
||||
conversationService.toggleMute(conversationId, membreId);
|
||||
return Response.noContent().build();
|
||||
}
|
||||
|
||||
/**
|
||||
* Toggle pin conversation
|
||||
*/
|
||||
@PUT
|
||||
@Path("/{id}/toggle-pin")
|
||||
@Operation(summary = "Épingler/désépingler")
|
||||
@APIResponse(responseCode = "204", description = "Paramètre modifié")
|
||||
public Response togglePin(@PathParam("id") UUID conversationId) {
|
||||
UUID membreId = securiteHelper.resolveMembreId();
|
||||
conversationService.togglePin(conversationId, membreId);
|
||||
return Response.noContent().build();
|
||||
}
|
||||
}
|
||||
Reference in New Issue
Block a user