tailly_back_v2/internal/service/chat_service.go
madipo2611 a5a90bed7e v0.0.1
2025-04-28 15:14:02 +03:00

153 lines
4.1 KiB
Go

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
encryptor EncryptionService
}
func NewChatService(
chatRepo repository.ChatRepository,
userRepo repository.UserRepository,
hub *ws.ChatHub,
encryptor EncryptionService,
) ChatService {
return &chatService{
chatRepo: chatRepo,
userRepo: userRepo,
hub: hub,
encryptor: encryptor,
}
}
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")
}
// Шифруем сообщение (если используется E2EE)
encryptedContent, err := s.encryptor.EncryptMessage(content)
if err != nil {
return nil, err
}
message := &domain.Message{
ChatID: chatID,
SenderID: senderID,
Content: encryptedContent,
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.WSMessage{
Type: "new_message",
MessageID: message.ID,
ChatID: chatID,
SenderID: senderID,
Content: encryptedContent,
Recipient: recipientID,
})
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")
}
messages, err := s.chatRepo.GetMessagesByChat(ctx, chatID, limit, offset)
if err != nil {
return nil, err
}
// Расшифровываем сообщения (если используется E2EE)
for i := range messages {
decrypted, err := s.encryptor.DecryptMessage(messages[i].Content)
if err != nil {
return nil, err
}
messages[i].Content = decrypted
}
return messages, nil
}
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)
}