tailly_back_v2/internal/http/graph/message_resolvers.go
madipo2611 c98667c5b5
All checks were successful
continuous-integration/drone/push Build is passing
v0.0.17.4
2025-08-11 18:37:52 +03:00

247 lines
7.8 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
}
func (r *messageResolver) Receiver(ctx context.Context, obj *domain.Message) (*domain.User, error) {
// 1. Если receiver явно указан в сообщении
if obj.ReceiverID != 0 {
return r.Services.User.GetByID(ctx, obj.ReceiverID)
}
// 2. Если есть chat, определяем получателя через чат
if obj.ChatID != 0 {
chat, err := r.chatRepo.GetChatByID(ctx, obj.ChatID)
if err != nil {
return nil, fmt.Errorf("failed to get chat: %v", err)
}
// Определяем получателя
receiverID := chat.User1ID
if obj.SenderID == chat.User1ID {
receiverID = chat.User2ID
}
return r.Services.User.GetByID(ctx, receiverID)
}
// 3. Если ничего не найдено
return nil, fmt.Errorf("cannot determine receiver - both receiver_id and chat_id are not set")
}
// 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
}