tailly_back_v2/internal/http/graph/message_resolvers.go
madipo2611 6ad760a85b
All checks were successful
continuous-integration/drone/push Build is passing
v0.0.16 Исправление работы мессенджера
2025-08-09 13:30:49 +03:00

257 lines
8.2 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) {
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.Register(client)
// Горутина для обработки отключения
go func() {
<-ctx.Done()
// Добавляем защиту от повторного закрытия
select {
case <-messageChan: // Если канал уже закрыт
default:
close(messageChan)
}
r.Services.ChatHub.Unregister(client)
}()
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
}