From 725161468253f2abb763371d433711f5d3641227 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=8B=AC=EC=9E=AC=EC=97=BD?= <jysim0326@ajou.ac.kr> Date: Mon, 25 Nov 2024 17:38:22 +0900 Subject: [PATCH] =?UTF-8?q?refactor:=20fcm=20=EC=B1=84=ED=8C=85=20?= =?UTF-8?q?=ED=91=B8=EC=8B=9C=20=EC=95=8C=EB=A6=BC=20=EC=B6=94=EA=B0=80,?= =?UTF-8?q?=20=EB=A9=94=EC=8B=9C=EC=A7=80=20=EC=9D=BD=EC=9D=8C=20=EC=88=98?= =?UTF-8?q?=20=ED=99=95=EC=9D=B8=20=EB=A1=9C=EC=A7=81=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- services/chatService.js | 165 ++++++++++++++++++++++++++++------------ 1 file changed, 115 insertions(+), 50 deletions(-) diff --git a/services/chatService.js b/services/chatService.js index 4135d15..c5e3a3c 100644 --- a/services/chatService.js +++ b/services/chatService.js @@ -2,58 +2,46 @@ const ChatRoom = require('../models/chatRooms'); const { v4: uuidv4 } = require('uuid'); class ChatService { - // 채팅방 생성 - async createChatRoom({ meeting_id, participants }) { - try { - const chatRoomId = uuidv4(); - const newRoom = new ChatRoom({ - chatRoomId: chatRoomId, - meeting_id, - participants, - messages: [], - lastReadAt: participants.reduce((acc, user) => { - acc[user] = new Date(); - return acc; - }, {}), - lastReadLogId: participants.reduce((acc, user) => { - acc[user] = null; - return acc; - }, {}), - isOnline: participants.reduce((acc, user) => { - acc[user] = true; - return acc; - }, {}), - }); - - const joinMessage = { - message: `${participants[0]}님이 번개 모임을 생성했습니다.`, - timestamp: new Date(), - type: 'join', - }; + async createChatRoom({ meeting_id, participants, chatRoomName }) { + try { + const chatRoomId = uuidv4(); + const newRoom = new ChatRoom({ + chatRoomId, + chatRoomName, + meeting_id, + messages: [], + }); - newRoom.messages.push(joinMessage); - await newRoom.save(); + const joinMessage = { + message: `${participants[0].name}님이 번개 모임을 생성했습니다.`, + timestamp: new Date(), + type: 'join', + }; - return { success: true, chatRoomId }; - } catch (err) { - console.error('Error creating chat room:', err); - throw new Error('Failed to create chat room'); - } + 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, messages: { $slice: -1 } }); + 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, - } + }, }; }); } @@ -61,7 +49,7 @@ class ChatService { // 사용자 상태 업데이트 async updateStatus(chatRoomId, nickname, isOnline) { await ChatRoom.updateOne( - { chatRoomId }, + { chatRoomId, "participants.name": nickname }, { $set: { [`isOnline.${nickname}`]: isOnline } } ); } @@ -70,14 +58,14 @@ class ChatService { async updateReadStatus(chatRoomId, nickname) { const now = new Date(); await ChatRoom.updateOne( - { chatRoomId }, + { chatRoomId, "participants.name": nickname }, { $set: { [`lastReadAt.${nickname}`]: now } } ); } // 읽지 않은 메시지 조회 async getUnreadMessages(nickname) { - const chatRooms = await ChatRoom.find({ participants: 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 => @@ -98,31 +86,49 @@ class ChatService { } const unreadCounts = chatRoom.participants - .filter(user => chatRoom.lastReadLogId.get(user)) - .map(user => chatRoom.lastReadLogId.get(user)) + .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; + acc[logId] = (acc[logId] || 0) + 1; // logId 기준으로 등장 횟수 누적 return acc; }, {}); let count = 0; - return Object.entries(unreadCounts) - .sort(([logId1], [logId2]) => logId1.localeCompare(logId2)) + const sortedUnreadCounts = Object.entries(unreadCounts) + .sort(([logId1], [logId2]) => logId1.localeCompare(logId2)) // logId 기준 오름차순 정렬 .reduce((acc, [logId, value]) => { - count += value; - acc[count] = logId; + count += value; // 누적 합계 + acc[count] = logId; // 누적 합계를 키로 저장 return acc; }, {}); + + return sortedUnreadCounts; } // 읽은 메시지 로그 ID 업데이트 async updateReadLogId(chatRoomId, nickname, logId) { await ChatRoom.updateOne( - { chatRoomId }, + { 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; @@ -135,7 +141,7 @@ class ChatService { } await ChatRoom.updateOne( - { chatRoomId }, + { chatRoomId, "participants.name": nickname }, { $set: { [`isOnline.${nickname}`]: isOnline, @@ -144,6 +150,65 @@ class ChatService { } ); } + + // 메시지 전송 + 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(); +module.exports = new ChatService(); \ No newline at end of file -- GitLab