package service import ( "context" "errors" "tailly_back_v2/internal/domain" "tailly_back_v2/internal/repository" "tailly_back_v2/internal/ws" "time" ) type ChatService interface { SendMessage(ctx context.Context, senderID, chatID int, content string) (*domain.Message, error) GetChatMessages(ctx context.Context, chatID, userID int, limit, offset int) ([]*domain.Message, error) MarkAsRead(ctx context.Context, messageID int) error DeleteMessage(ctx context.Context, messageID, userID int) error GetUserChats(ctx context.Context, userID int) ([]*domain.Chat, error) GetOrCreateChat(ctx context.Context, user1ID, user2ID int) (*domain.Chat, error) } type chatService struct { chatRepo repository.ChatRepository userRepo repository.UserRepository hub *ws.ChatHub } func NewChatService( chatRepo repository.ChatRepository, userRepo repository.UserRepository, hub *ws.ChatHub, ) ChatService { return &chatService{ chatRepo: chatRepo, userRepo: userRepo, hub: hub, } } func (s *chatService) SendMessage(ctx context.Context, senderID, chatID int, content string) (*domain.Message, error) { // Проверяем существование чата chat, err := s.chatRepo.GetChatByID(ctx, chatID) if err != nil { return nil, err } // Проверяем, что отправитель является участником чата if senderID != chat.User1ID && senderID != chat.User2ID { return nil, errors.New("user is not a participant of this chat") } message := &domain.Message{ ChatID: chatID, SenderID: senderID, Content: content, Status: "sent", CreatedAt: time.Now(), } if err := s.chatRepo.SaveMessage(ctx, message); err != nil { return nil, err } // Отправляем через WebSocket recipientID := chat.User1ID if senderID == chat.User1ID { recipientID = chat.User2ID } s.hub.Broadcast(&domain.Message{ ID: message.ID, ChatID: chatID, SenderID: senderID, ReceiverID: recipientID, Content: content, Status: "sent", CreatedAt: message.CreatedAt, }) return message, nil } func (s *chatService) GetChatMessages(ctx context.Context, chatID, userID int, limit, offset int) ([]*domain.Message, error) { // Проверяем доступ пользователя к чату chat, err := s.chatRepo.GetChatByID(ctx, chatID) if err != nil { return nil, err } if userID != chat.User1ID && userID != chat.User2ID { return nil, errors.New("access denied") } return s.chatRepo.GetMessagesByChat(ctx, chatID, limit, offset) } func (s *chatService) MarkAsRead(ctx context.Context, messageID int) error { return s.chatRepo.UpdateMessageStatus(ctx, messageID, "read") } func (s *chatService) DeleteMessage(ctx context.Context, messageID, userID int) error { message, err := s.chatRepo.GetMessageByID(ctx, messageID) if err != nil { return err } if message.SenderID != userID { return errors.New("only sender can delete the message") } return s.chatRepo.DeleteMessage(ctx, messageID) } func (s *chatService) GetUserChats(ctx context.Context, userID int) ([]*domain.Chat, error) { return s.chatRepo.GetUserChats(ctx, userID) } func (s *chatService) GetOrCreateChat(ctx context.Context, user1ID, user2ID int) (*domain.Chat, error) { // Проверяем существование чата chat, err := s.chatRepo.GetChatByParticipants(ctx, user1ID, user2ID) if err == nil { return chat, nil } if !errors.Is(err, repository.ErrChatNotFound) { return nil, err } // Создаем новый чат return s.chatRepo.CreateChat(ctx, user1ID, user2ID) }