tailly_back_v2/internal/http/graph/message_resolvers.go
madipo2611 db667f224b
All checks were successful
continuous-integration/drone/push Build is passing
v0.0.17.3 Переработан websocket, добавлена обработка ping/pong
2025-08-11 15:49:16 +03:00

262 lines
8.1 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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) {
// 1. Проверка на nil
if obj == nil {
return nil, fmt.Errorf("message is nil")
}
// 2. Если есть receiver_id - используем его напрямую
if obj.ReceiverID != 0 {
user, err := r.Services.User.GetByID(ctx, obj.ReceiverID)
if err != nil {
return nil, fmt.Errorf("failed to get receiver by ID: %v", err)
}
return user, nil
}
// 3. Альтернативный вариант через chat_id (если receiver_id не установлен)
if obj.ChatID == 0 {
return nil, fmt.Errorf("both receiver_id and chat_id are not set")
}
chat, err := r.chatRepo.GetChatByID(ctx, obj.ChatID)
if err != nil {
return nil, fmt.Errorf("failed to get chat: %v", err)
}
// Определяем ID получателя
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("failed to get receiver user: %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("не авторизован")
}
chat, err := r.Services.Chat.GetOrCreateChat(ctx, senderID, receiverID)
if err != nil {
return nil, fmt.Errorf("ошибка создания чата: %v", err)
}
message, err := r.Services.Chat.SendMessage(ctx, senderID, chat.ID, content)
if err != nil {
return nil, err
}
// Рассылаем сообщение через WebSocket
if r.Services.ChatHub != nil {
r.Services.ChatHub.Broadcast(message)
}
return message, nil
}
// MarkAsRead - помечает сообщение как прочитанное
func (r *mutationResolver) MarkAsRead(ctx context.Context, messageID int) (bool, error) {
if r.Services == nil || r.Services.Chat == nil {
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
}