veza/veza-backend-api/internal/services/room_service.go

257 lines
8.1 KiB
Go

package services
import (
"context"
"errors"
"fmt"
"time" // Add time import
"veza-backend-api/internal/models"
"veza-backend-api/internal/repositories"
"github.com/google/uuid" // Add uuid import
"go.uber.org/zap"
"gorm.io/gorm"
)
// RoomService gère la logique métier pour les rooms
type RoomService struct {
roomRepo *repositories.RoomRepository
messageRepo *repositories.ChatMessageRepository
logger *zap.Logger
}
// NewRoomService crée une nouvelle instance de RoomService
func NewRoomService(roomRepo *repositories.RoomRepository, messageRepo *repositories.ChatMessageRepository, logger *zap.Logger) *RoomService {
return &RoomService{
roomRepo: roomRepo,
messageRepo: messageRepo,
logger: logger,
}
}
// CreateRoomRequest représente une requête de création de room
type CreateRoomRequest struct {
Name string `json:"name" binding:"required,min=1,max=255"`
Description *string `json:"description,omitempty"`
Type string `json:"type" binding:"required,oneof=public private direct"`
IsPrivate bool `json:"is_private"`
}
// RoomResponse représente une réponse de room pour l'API
// MIGRATION UUID: CreatedBy et Participants migrés vers UUID
type RoomResponse struct {
ID uuid.UUID `json:"id"`
Name string `json:"name"`
Description string `json:"description,omitempty"`
Type string `json:"type"`
IsPrivate bool `json:"is_private"`
CreatedBy *uuid.UUID `json:"created_by"`
Participants []uuid.UUID `json:"participants"`
CreatedAt string `json:"created_at"`
UpdatedAt string `json:"updated_at"`
}
// CreateRoom crée une nouvelle room
func (s *RoomService) CreateRoom(ctx context.Context, userID uuid.UUID, req CreateRoomRequest) (*RoomResponse, error) {
if req.Name == "" {
return nil, errors.New("room name is required")
}
// Créer la room
room := &models.Room{
Name: req.Name,
Description: "",
Type: req.Type,
IsPrivate: req.IsPrivate,
CreatedBy: userID, // Corrected: userID is uuid.UUID, models.Room.CreatedBy is uuid.UUID
}
if req.Description != nil {
room.Description = *req.Description
}
if err := s.roomRepo.Create(ctx, room); err != nil {
s.logger.Error("failed to create room",
zap.Error(err),
zap.String("user_id", userID.String()),
zap.String("room_name", req.Name))
return nil, fmt.Errorf("failed to create room: %w", err)
}
// Ajouter le créateur comme membre admin
member := &models.RoomMember{
RoomID: room.ID, // use uuid
UserID: userID,
Role: "admin",
}
if err := s.roomRepo.AddMember(ctx, member); err != nil {
s.logger.Error("failed to add creator as room member",
zap.Error(err),
zap.String("room_id", room.ID.String()),
zap.String("user_id", userID.String()))
// Ne pas retourner d'erreur, la room est créée
}
s.logger.Info("room created successfully",
zap.String("room_id", room.ID.String()),
zap.String("user_id", userID.String()),
zap.String("room_name", room.Name))
return &RoomResponse{
ID: room.ID,
Name: room.Name,
Description: room.Description,
Type: room.Type,
IsPrivate: room.IsPrivate,
CreatedBy: &room.CreatedBy, // Corrected: & to get pointer to uuid.UUID
Participants: []uuid.UUID{userID},
CreatedAt: room.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
UpdatedAt: room.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
}, nil
}
// GetUserRooms récupère toutes les rooms d'un utilisateur
func (s *RoomService) GetUserRooms(ctx context.Context, userID uuid.UUID) ([]*RoomResponse, error) {
rooms, err := s.roomRepo.GetByUserID(ctx, userID)
if err != nil {
s.logger.Error("failed to get user rooms",
zap.Error(err),
zap.String("user_id", userID.String()))
return nil, fmt.Errorf("failed to get user rooms: %w", err)
}
responses := make([]*RoomResponse, 0, len(rooms))
for _, room := range rooms {
// Récupérer les membres pour avoir la liste des participants
members, err := s.roomRepo.GetMembersByRoomID(ctx, room.ID)
if err != nil {
s.logger.Warn("failed to get room members",
zap.Error(err),
zap.String("room_id", room.ID.String()))
members = []*models.RoomMember{}
}
participants := make([]uuid.UUID, 0, len(members))
for _, member := range members {
participants = append(participants, member.UserID)
}
responses = append(responses, &RoomResponse{
ID: room.ID,
Name: room.Name,
Description: room.Description,
Type: room.Type,
IsPrivate: room.IsPrivate,
CreatedBy: &room.CreatedBy, // Corrected: & to get pointer to uuid.UUID
Participants: participants,
CreatedAt: room.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
UpdatedAt: room.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
})
}
return responses, nil
}
// GetRoom récupère une room par son ID
func (s *RoomService) GetRoom(ctx context.Context, roomID uuid.UUID) (*RoomResponse, error) {
room, err := s.roomRepo.GetByID(ctx, roomID)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) {
return nil, ErrRoomNotFound
}
s.logger.Error("failed to get room",
zap.Error(err),
zap.String("room_id", roomID.String()))
return nil, fmt.Errorf("failed to get room: %w", err)
}
// Récupérer les membres
members, err := s.roomRepo.GetMembersByRoomID(ctx, roomID)
if err != nil {
s.logger.Warn("failed to get room members",
zap.Error(err),
zap.String("room_id", roomID.String()))
members = []*models.RoomMember{}
}
participants := make([]uuid.UUID, 0, len(members))
for _, member := range members {
participants = append(participants, member.UserID)
}
return &RoomResponse{
ID: room.ID,
Name: room.Name,
Description: room.Description,
Type: room.Type,
IsPrivate: room.IsPrivate,
CreatedBy: &room.CreatedBy, // Corrected: & to get pointer to uuid.UUID
Participants: participants,
CreatedAt: room.CreatedAt.Format("2006-01-02T15:04:05Z07:00"),
UpdatedAt: room.UpdatedAt.Format("2006-01-02T15:04:05Z07:00"),
}, nil
}
// AddMember ajoute un membre à une room
func (s *RoomService) AddMember(ctx context.Context, roomID uuid.UUID, userID uuid.UUID) error {
member := &models.RoomMember{
RoomID: roomID,
UserID: userID,
Role: "member",
}
if err := s.roomRepo.AddMember(ctx, member); err != nil {
s.logger.Error("failed to add member to room",
zap.Error(err),
zap.String("room_id", roomID.String()),
zap.String("user_id", userID.String()))
return fmt.Errorf("failed to add member: %w", err)
}
s.logger.Info("member added to 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"`
ConversationID uuid.UUID `json:"conversation_id"`
SenderID uuid.UUID `json:"sender_id"`
Content string `json:"content"`
MessageType string `json:"message_type"`
CreatedAt time.Time `json:"created_at"`
}
// GetRoomHistory récupère l'historique des messages d'une room
func (s *RoomService) GetRoomHistory(ctx context.Context, roomID uuid.UUID, limit, offset int) ([]ChatMessageResponse, error) {
messages, err := s.messageRepo.GetConversationMessages(ctx, roomID, limit, offset)
if err != nil {
if errors.Is(err, gorm.ErrRecordNotFound) || err.Error() == "conversation not found" {
// Check if room exists first? Assuming Repo handles it or we could use GetRoom logic
// If messageRepo returns error on room not found
return nil, ErrRoomNotFound
}
s.logger.Error("failed to get room history",
zap.Error(err),
zap.String("room_id", roomID.String()))
return nil, fmt.Errorf("failed to get room history: %w", err)
}
responses := make([]ChatMessageResponse, len(messages))
for i, msg := range messages {
responses[i] = ChatMessageResponse{
ID: msg.ID,
ConversationID: msg.ConversationID,
SenderID: msg.SenderID,
Content: msg.Content,
MessageType: msg.MessageType,
CreatedAt: msg.CreatedAt,
}
}
return responses, nil
}