package graph import ( "context" "errors" "fmt" "tailly_back_v2/internal/domain" "tailly_back_v2/internal/ws" "time" ) type messageResolver struct{ *Resolver } // GetChatHistory - возвращает историю сообщений func (r *queryResolver) GetChatHistory(ctx context.Context, userID int) ([]*domain.Message, error) { currentUserID, err := getUserIDFromContext(ctx) if err != nil { return nil, errors.New("не авторизован") } chat, err := r.Services.Chat.GetOrCreateChat(ctx, currentUserID, userID) if err != nil { return nil, fmt.Errorf("ошибка получения чата: %v", err) } messages, err := r.Services.Chat.GetChatMessages(ctx, chat.ID, currentUserID, 50, 0) if err != nil { return nil, fmt.Errorf("ошибка получения сообщений: %v", err) } return messages, nil } // GetUserChats - возвращает чаты пользователя func (r *queryResolver) GetUserChats(ctx context.Context) ([]*ChatSession, error) { userID, err := getUserIDFromContext(ctx) if err != nil { return nil, errors.New("не авторизован") } // Проверяем инициализацию сервиса if r.Services == nil || r.Services.Chat == nil { return nil, errors.New("chat service not initialized") } // Получаем чаты пользователя chats, err := r.Services.Chat.GetUserChats(ctx, userID) if err != nil { return nil, fmt.Errorf("ошибка получения чатов: %v", err) } var sessions []*ChatSession for _, chat := range chats { // Определяем другого участника чата otherUserID := chat.User1ID if userID == chat.User1ID { otherUserID = chat.User2ID } // Получаем данные другого пользователя otherUser, err := r.Services.User.GetByID(ctx, otherUserID) if err != nil { return nil, fmt.Errorf("ошибка получения пользователя %d: %v", otherUserID, err) } // Получаем последнее сообщение messages, err := r.Services.Chat.GetChatMessages(ctx, chat.ID, userID, 1, 0) if err != nil { return nil, fmt.Errorf("ошибка получения сообщений: %v", err) } var lastMessage *domain.Message if len(messages) > 0 { lastMessage = messages[0] } else { // Создаем пустое сообщение, если чат новый lastMessage = &domain.Message{ ChatID: chat.ID, Content: "Чат создан", Status: "system", CreatedAt: chat.CreatedAt, } } // Получаем количество непрочитанных сообщений unreadCount, err := r.Services.Chat.GetUnreadCount(ctx, chat.ID, userID) if err != nil { return nil, fmt.Errorf("ошибка получения количества непрочитанных: %v", err) } sessions = append(sessions, &ChatSession{ User: otherUser, LastMessage: lastMessage, UnreadCount: unreadCount, }) } return sessions, nil } // Sender - возвращает отправителя сообщения func (r *messageResolver) Sender(ctx context.Context, obj *domain.Message) (*domain.User, error) { user, err := r.Services.User.GetByID(ctx, obj.SenderID) if err != nil { return nil, fmt.Errorf("ошибка получения отправителя: %v", err) } return user, nil } // Receiver - возвращает получателя сообщения func (r *messageResolver) Receiver(ctx context.Context, obj *domain.Message) (*domain.User, error) { chat, err := r.chatRepo.GetChatByID(ctx, obj.ChatID) if err != nil { return nil, fmt.Errorf("ошибка получения чата: %v", err) } receiverID := chat.User1ID if obj.SenderID == chat.User1ID { receiverID = chat.User2ID } user, err := r.Services.User.GetByID(ctx, receiverID) if err != nil { return nil, fmt.Errorf("ошибка получения получателя: %v", err) } return user, nil } // CreatedAt - форматирует время сообщения func (r *messageResolver) CreatedAt(ctx context.Context, obj *domain.Message) (string, error) { return obj.CreatedAt.Format(time.RFC3339), nil } // SendMessage - отправляет сообщение func (r *mutationResolver) SendMessage(ctx context.Context, receiverID int, content string) (*domain.Message, error) { senderID, err := getUserIDFromContext(ctx) if err != nil { return nil, errors.New("не авторизован") } // Проверяем, что не отправляем сообщение себе if senderID == receiverID { return nil, errors.New("cannot send message to yourself") } chat, err := r.Services.Chat.GetOrCreateChat(ctx, senderID, receiverID) if err != nil { return nil, fmt.Errorf("ошибка создания чата: %v", err) } return r.Services.Chat.SendMessage(ctx, senderID, chat.ID, content) } // MarkAsRead - помечает сообщение как прочитанное func (r *mutationResolver) MarkAsRead(ctx context.Context, messageID int) (bool, error) { userID, err := getUserIDFromContext(ctx) if err != nil { return false, errors.New("не авторизован") } // Получаем сообщение напрямую из репозитория message, err := r.chatRepo.GetMessageByID(ctx, messageID) if err != nil { return false, fmt.Errorf("ошибка получения сообщения: %v", err) } // Проверяем доступ к сообщению chat, err := r.chatRepo.GetChatByID(ctx, message.ChatID) if err != nil { return false, fmt.Errorf("ошибка получения чата: %v", err) } if userID != chat.User1ID && userID != chat.User2ID { return false, errors.New("нет доступа к сообщению") } err = r.Services.Chat.MarkAsRead(ctx, messageID) if err != nil { return false, fmt.Errorf("ошибка обновления статуса: %v", err) } return true, nil } type subscriptionResolver struct{ *Resolver } // Subscription returns SubscriptionResolver implementation. func (r *Resolver) Subscription() SubscriptionResolver { return &subscriptionResolver{r} } // MessageReceived - подписка на новые сообщения func (r *subscriptionResolver) MessageReceived(ctx context.Context) (<-chan *domain.Message, error) { userID, err := getUserIDFromContext(ctx) if err != nil { return nil, errors.New("не авторизован") } messageChan := make(chan *domain.Message, 1) // Создаем клиента для хаба client := &ws.Client{ UserID: userID, Send: messageChan, } // Регистрируем клиента в хабе r.Services.ChatHub.RegisterClient(client) // Горутина для обработки отключения go func() { <-ctx.Done() r.Services.ChatHub.UnregisterClient(client) close(messageChan) }() return messageChan, nil } // CreateChat is the resolver for the createChat field. func (r *mutationResolver) CreateChat(ctx context.Context, userID int) (*ChatSession, error) { currentUserID, err := getUserIDFromContext(ctx) if err != nil { return nil, errors.New("не авторизован") } // Создаем или получаем существующий чат chat, err := r.Services.Chat.GetOrCreateChat(ctx, currentUserID, userID) if err != nil { return nil, fmt.Errorf("ошибка создания чата: %v", err) } // Получаем данные другого пользователя otherUser, err := r.Services.User.GetByID(ctx, userID) if err != nil { return nil, fmt.Errorf("ошибка получения пользователя: %v", err) } // Создаем пустое последнее сообщение (или можно вернуть nil, если схема позволяет) emptyMessage := &domain.Message{ ChatID: chat.ID, SenderID: currentUserID, Content: "Чат создан", Status: "system", CreatedAt: time.Now(), } return &ChatSession{ User: otherUser, LastMessage: emptyMessage, UnreadCount: 0, }, nil }