[BE-API-011] be-api: Implement conversation participants endpoints

- Added RemoveMember method to RoomService and RoomServiceInterface
- Corrected RemoveMember in RoomRepository to use uuid.UUID
- Added AddParticipant and RemoveParticipant handlers
- Added POST /conversations/:id/participants route
- Added DELETE /conversations/:id/participants/:userId route
- Handlers use standard API response format
- Handlers reuse AddMember/RemoveMember service methods

Phase: PHASE-2
Priority: P1
Progress: 20/267 (7.5%)
This commit is contained in:
senke 2025-12-23 10:49:17 +01:00
parent 41c11e25fc
commit 1fede8f346
5 changed files with 128 additions and 2 deletions

View file

@ -1600,7 +1600,20 @@
"description": "POST /api/v1/conversations/:id/participants and DELETE /api/v1/conversations/:id/participants/:userId",
"owner": "backend",
"estimated_hours": 3,
"status": "todo",
"status": "completed",
"completion": {
"completed_at": "2025-12-23T09:48:25Z",
"actual_hours": 0.75,
"commits": [],
"files_changed": [
"veza-backend-api/internal/repositories/room_repository.go",
"veza-backend-api/internal/services/room_service.go",
"veza-backend-api/internal/handlers/room_handler.go",
"veza-backend-api/internal/api/router.go"
],
"notes": "Added RemoveMember method to RoomService and RoomServiceInterface. Corrected RemoveMember in RoomRepository to use uuid.UUID instead of int64. Added AddParticipant and RemoveParticipant handlers in RoomHandler. Added POST /conversations/:id/participants and DELETE /conversations/:id/participants/:userId routes. Handlers use standard API response format (RespondSuccess, RespondWithAppError). Handlers reuse AddMember/RemoveMember service methods.",
"issues_encountered": []
},
"files_involved": [],
"implementation_steps": [
{

View file

@ -865,6 +865,8 @@ func (r *APIRouter) setupCoreProtectedRoutes(v1 *gin.RouterGroup) {
conversations.GET("/:id", roomHandler.GetRoom)
conversations.DELETE("/:id", roomHandler.DeleteRoom) // BE-API-010: Delete conversation endpoint
conversations.POST("/:id/members", roomHandler.AddMember)
conversations.POST("/:id/participants", roomHandler.AddParticipant) // BE-API-011: Add participant endpoint
conversations.DELETE("/:id/participants/:userId", roomHandler.RemoveParticipant) // BE-API-011: Remove participant endpoint
conversations.GET("/:id/history", roomHandler.GetRoomHistory)
}

View file

@ -20,6 +20,7 @@ type RoomServiceInterface interface {
GetUserRooms(ctx context.Context, userID uuid.UUID) ([]*services.RoomResponse, error)
GetRoom(ctx context.Context, roomID uuid.UUID) (*services.RoomResponse, error)
AddMember(ctx context.Context, roomID, userID uuid.UUID) error
RemoveMember(ctx context.Context, roomID, userID uuid.UUID) error // BE-API-011: Remove member method
GetRoomHistory(ctx context.Context, roomID uuid.UUID, limit, offset int) ([]services.ChatMessageResponse, error)
DeleteRoom(ctx context.Context, roomID uuid.UUID, userID uuid.UUID) error // BE-API-010: Delete room method
}
@ -270,3 +271,84 @@ func (h *RoomHandler) DeleteRoom(c *gin.Context) {
RespondSuccess(c, http.StatusOK, gin.H{"message": "Conversation deleted successfully"})
}
// AddParticipant ajoute un participant à une conversation
// POST /api/v1/conversations/:id/participants
// BE-API-011: Implement conversation participants endpoints
func (h *RoomHandler) AddParticipant(c *gin.Context) {
// Récupérer l'ID de la room depuis l'URL
roomIDStr := c.Param("id")
roomID, err := uuid.Parse(roomIDStr)
if err != nil {
RespondWithAppError(c, apperrors.NewValidationError("invalid room id"))
return
}
// Parser la requête
var req AddMemberRequest
if appErr := h.commonHandler.BindAndValidateJSON(c, &req); appErr != nil {
RespondWithAppError(c, appErr)
return
}
// Ajouter le participant (utilise la même logique que AddMember)
if err := h.roomService.AddMember(c.Request.Context(), roomID, req.UserID); err != nil {
if errors.Is(err, services.ErrRoomNotFound) {
RespondWithAppError(c, apperrors.NewNotFoundError("conversation"))
return
}
h.logger.Error("failed to add participant to room",
zap.Error(err),
zap.String("room_id", roomID.String()),
zap.String("user_id", req.UserID.String()))
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to add participant", err))
return
}
h.logger.Info("participant added to room",
zap.String("room_id", roomID.String()),
zap.String("user_id", req.UserID.String()))
RespondSuccess(c, http.StatusOK, gin.H{"message": "Participant added successfully"})
}
// RemoveParticipant retire un participant d'une conversation
// DELETE /api/v1/conversations/:id/participants/:userId
// BE-API-011: Implement conversation participants endpoints
func (h *RoomHandler) RemoveParticipant(c *gin.Context) {
// Récupérer l'ID de la room depuis l'URL
roomIDStr := c.Param("id")
roomID, err := uuid.Parse(roomIDStr)
if err != nil {
RespondWithAppError(c, apperrors.NewValidationError("invalid room id"))
return
}
// Récupérer l'ID de l'utilisateur depuis l'URL
userIDStr := c.Param("userId")
userID, err := uuid.Parse(userIDStr)
if err != nil {
RespondWithAppError(c, apperrors.NewValidationError("invalid user id"))
return
}
// Retirer le participant
if err := h.roomService.RemoveMember(c.Request.Context(), roomID, userID); err != nil {
if errors.Is(err, services.ErrRoomNotFound) {
RespondWithAppError(c, apperrors.NewNotFoundError("conversation"))
return
}
h.logger.Error("failed to remove participant from room",
zap.Error(err),
zap.String("room_id", roomID.String()),
zap.String("user_id", userID.String()))
RespondWithAppError(c, apperrors.Wrap(apperrors.ErrCodeInternal, "Failed to remove participant", err))
return
}
h.logger.Info("participant removed from room",
zap.String("room_id", roomID.String()),
zap.String("user_id", userID.String()))
RespondSuccess(c, http.StatusOK, gin.H{"message": "Participant removed successfully"})
}

View file

@ -73,7 +73,8 @@ func (r *RoomRepository) AddMember(ctx context.Context, member *models.RoomMembe
}
// RemoveMember retire un membre d'une room
func (r *RoomRepository) RemoveMember(ctx context.Context, roomID uuid.UUID, userID int64) error {
// MIGRATION UUID: userID migré vers uuid.UUID
func (r *RoomRepository) RemoveMember(ctx context.Context, roomID uuid.UUID, userID uuid.UUID) error {
return r.db.WithContext(ctx).
Where("room_id = ? AND user_id = ?", roomID, userID).
Delete(&models.RoomMember{}).Error

View file

@ -217,6 +217,34 @@ func (s *RoomService) AddMember(ctx context.Context, roomID uuid.UUID, userID uu
return nil
}
// RemoveMember retire un membre d'une room
// BE-API-011: Implement conversation participants endpoints
func (s *RoomService) RemoveMember(ctx context.Context, roomID uuid.UUID, userID uuid.UUID) error {
// Vérifier que la room existe
_, err := s.roomRepo.GetByID(ctx, roomID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return ErrRoomNotFound
}
return fmt.Errorf("failed to get room: %w", err)
}
// Retirer le membre
if err := s.roomRepo.RemoveMember(ctx, roomID, userID); err != nil {
s.logger.Error("failed to remove member from room",
zap.Error(err),
zap.String("room_id", roomID.String()),
zap.String("user_id", userID.String()))
return fmt.Errorf("failed to remove member: %w", err)
}
s.logger.Info("member removed from room",
zap.String("room_id", roomID.String()),
zap.String("user_id", userID.String()))
return nil
}
// ChatMessageResponse pour la réponse d'historique
type ChatMessageResponse struct {
ID uuid.UUID `json:"id"`