262 lines
8.1 KiB
Go
262 lines
8.1 KiB
Go
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
|
||
}
|