Skip to content
Snippets Groups Projects
chatService.js 6.36 KiB
const ChatRoom = require('../models/chatRooms');
const { v4: uuidv4 } = require('uuid');

class ChatService {
  // 채팅방 생성
  async createChatRoom({ meeting_id, participants, chatRoomName }) {
  try {
    const chatRoomId = uuidv4();
    const newRoom = new ChatRoom({
      chatRoomId,
      chatRoomName,
      meeting_id,
      messages: [],
    });

    const joinMessage = {
      message: `${participants[0].name}님이 번개 모임을 생성했습니다.`,
      timestamp: new Date(),
      type: 'join',
    };

    newRoom.messages.push(joinMessage);
    await newRoom.save();

    return { success: true, chatRoomId };
  } catch (err) {
    console.error('Error creating chat room:', err);
    throw new Error('Failed to create chat room');
  }
}

  // 채팅방 목록 조회
  async getChatRooms() {
    const rooms = await ChatRoom.find({}, { chatRoomId: 1, chatRoomName: 1, messages: { $slice: -1 } });
    return rooms.map(room => {
      const lastMessage = room.messages[0] || {};
      return {
        chatRoomId: room.chatRoomId,
        chatRoomName: room.chatRoomName,
        lastMessage: {
          sender: lastMessage.sender || '없음',
          message: lastMessage.message || '메시지 없음',
          timestamp: lastMessage.timestamp || null,
        },
      };
    });
  }

  // 사용자 상태 업데이트
  async updateStatus(chatRoomId, nickname, isOnline) {
    await ChatRoom.updateOne(
      { chatRoomId, "participants.name": nickname },
      { $set: { [`isOnline.${nickname}`]: isOnline } }
    );
  }

  // 읽음 상태 업데이트
  async updateReadStatus(chatRoomId, nickname) {
    const now = new Date();
    await ChatRoom.updateOne(
      { chatRoomId, "participants.name": nickname },
      { $set: { [`lastReadAt.${nickname}`]: now } }
    );
  }

  // 읽지 않은 메시지 조회
  async getUnreadMessages(nickname) {
    const chatRooms = await ChatRoom.find({ "participants.name": nickname });
    return await Promise.all(chatRooms.map(async (chatRoom) => {
      const lastReadAt = chatRoom.lastReadAt.get(nickname) || new Date(0);
      const unreadMessagesCount = chatRoom.messages.filter(message => 
        message.timestamp > lastReadAt
      ).length;
      return {
        chatRoomId: chatRoom.chatRoomId,
        unreadCount: unreadMessagesCount,
      };
    }));
  }

  // 읽지 않은 메시지 수 조회
  async getUnreadCount(chatRoomId) {
    const chatRoom = await ChatRoom.findOne({ chatRoomId });
    if (!chatRoom) {
      throw new Error('Chat room not found');
    }

    const unreadCounts = chatRoom.participants
      .filter(participant => chatRoom.lastReadLogId.has(participant.name)) // Map에 존재하는 키만 처리
      .map(participant => chatRoom.lastReadLogId.get(participant.name)) // lastReadLogId 값 추출
      .reduce((acc, logId) => {
        acc[logId] = (acc[logId] || 0) + 1; // logId 기준으로 등장 횟수 누적
        return acc;
      }, {});

    let count = 0;
    const sortedUnreadCounts = Object.entries(unreadCounts)
      .sort(([logId1], [logId2]) => logId1.localeCompare(logId2)) // logId 기준 오름차순 정렬
      .reduce((acc, [logId, value]) => {
        count += value; // 누적 합계
        acc[count] = logId; // 누적 합계를 키로 저장
        return acc;
      }, {});

    return sortedUnreadCounts;
  }

  // 읽은 메시지 로그 ID 업데이트
  async updateReadLogId(chatRoomId, nickname, logId) {
    await ChatRoom.updateOne(
      { chatRoomId, "participants.name": nickname },
      { $set: { [`lastReadLogId.${nickname}`]: logId } }
    );
  }

  // FCM 토큰 업데이트
  async updateFcmToken(chatRoomId, nickname, fcmToken) {
    const chatRoom = await ChatRoom.findOne({ chatRoomId, "participants.name": nickname });
    if (!chatRoom) {
      throw new Error('Chat room or participant not found');
    }

    const participant = chatRoom.participants.find(p => p.name === nickname);
    if (participant) {
      if (!participant.fcmTokens.includes(fcmToken)) {
        participant.fcmTokens.push(fcmToken);
        await chatRoom.save();
      }
    }
  }

  // 상태와 로그 ID 동시 업데이트
  async updateStatusAndLogId(chatRoomId, nickname, isOnline, logId) {
    let finalLogId = logId;

    if (!isOnline && logId === null) {
      const chatRoom = await ChatRoom.findOne({ chatRoomId });
      if (chatRoom && chatRoom.messages.length > 0) {
        finalLogId = chatRoom.messages[chatRoom.messages.length - 1]._id;
      }
    }

    await ChatRoom.updateOne(
      { chatRoomId, "participants.name": nickname },
      {
        $set: {
          [`isOnline.${nickname}`]: isOnline,
          [`lastReadLogId.${nickname}`]: isOnline ? null : finalLogId,
        },
      }
    );
  }

  // 메시지 전송
  async sendMessage(chatRoomId, sender, messageContent) {
    try {
      // 채팅방 조회
      const chatRoom = await ChatRoom.findOne({ chatRoomId });
      if (!chatRoom) {
        throw new Error('Chat room not found');
      }

      // 메시지 추가
      const newMessage = {
        sender,
        message: messageContent,
        timestamp: new Date(),
        type: 'message',
      };
      chatRoom.messages.push(newMessage);
      await chatRoom.save();

      // 오프라인 사용자 찾기
      const offlineParticipants = chatRoom.participants.filter(
        participant => !chatRoom.isOnline[participant.name]
      );

      // 오프라인 사용자들에게 FCM 푸시 알림 전송
      for (const participant of offlineParticipants) {
        const tokens = participant.fcmTokens || [];
        if (tokens.length > 0) {
          const message = {
            notification: {
              title: `새 메시지: ${chatRoom.chatRoomName}`,
              body: `${sender}: ${messageContent}`,
            },
            tokens,
          };

          try {
            const response = await admin.messaging().sendMulticast(message);
            console.log(
              `푸시 알림 전송 성공 (${participant.name}):`,
              response.successCount
            );
          } catch (error) {
            console.error(
              `푸시 알림 전송 실패 (${participant.name}):`,
              error
            );
          }
        }
      }

      return newMessage;
    } catch (error) {
      console.error('Error sending message:', error);
      throw new Error('Failed to send message');
    }
  }

}

module.exports = new ChatService();