Skip to content
Snippets Groups Projects
Commit 72516146 authored by 심재엽's avatar 심재엽
Browse files

refactor: fcm 채팅 푸시 알림 추가, 메시지 읽음 수 확인 로직 수정

parent acaba97c
No related branches found
No related tags found
2 merge requests!31Develop,!25[#17] 채팅 푸시알림 추가
...@@ -2,58 +2,46 @@ const ChatRoom = require('../models/chatRooms'); ...@@ -2,58 +2,46 @@ const ChatRoom = require('../models/chatRooms');
const { v4: uuidv4 } = require('uuid'); const { v4: uuidv4 } = require('uuid');
class ChatService { class ChatService {
// 채팅방 생성 // 채팅방 생성
async createChatRoom({ meeting_id, participants }) { async createChatRoom({ meeting_id, participants, chatRoomName }) {
try { try {
const chatRoomId = uuidv4(); const chatRoomId = uuidv4();
const newRoom = new ChatRoom({ const newRoom = new ChatRoom({
chatRoomId: chatRoomId, chatRoomId,
meeting_id, chatRoomName,
participants, meeting_id,
messages: [], 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',
};
newRoom.messages.push(joinMessage); const joinMessage = {
await newRoom.save(); message: `${participants[0].name}님이 번개 모임을 생성했습니다.`,
timestamp: new Date(),
type: 'join',
};
return { success: true, chatRoomId }; newRoom.messages.push(joinMessage);
} catch (err) { await newRoom.save();
console.error('Error creating chat room:', err);
throw new Error('Failed to create chat room'); return { success: true, chatRoomId };
} } catch (err) {
console.error('Error creating chat room:', err);
throw new Error('Failed to create chat room');
} }
}
// 채팅방 목록 조회 // 채팅방 목록 조회
async getChatRooms() { 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 => { return rooms.map(room => {
const lastMessage = room.messages[0] || {}; const lastMessage = room.messages[0] || {};
return { return {
chatRoomId: room.chatRoomId, chatRoomId: room.chatRoomId,
chatRoomName: room.chatRoomName,
lastMessage: { lastMessage: {
sender: lastMessage.sender || '없음', sender: lastMessage.sender || '없음',
message: lastMessage.message || '메시지 없음', message: lastMessage.message || '메시지 없음',
timestamp: lastMessage.timestamp || null, timestamp: lastMessage.timestamp || null,
} },
}; };
}); });
} }
...@@ -61,7 +49,7 @@ class ChatService { ...@@ -61,7 +49,7 @@ class ChatService {
// 사용자 상태 업데이트 // 사용자 상태 업데이트
async updateStatus(chatRoomId, nickname, isOnline) { async updateStatus(chatRoomId, nickname, isOnline) {
await ChatRoom.updateOne( await ChatRoom.updateOne(
{ chatRoomId }, { chatRoomId, "participants.name": nickname },
{ $set: { [`isOnline.${nickname}`]: isOnline } } { $set: { [`isOnline.${nickname}`]: isOnline } }
); );
} }
...@@ -70,14 +58,14 @@ class ChatService { ...@@ -70,14 +58,14 @@ class ChatService {
async updateReadStatus(chatRoomId, nickname) { async updateReadStatus(chatRoomId, nickname) {
const now = new Date(); const now = new Date();
await ChatRoom.updateOne( await ChatRoom.updateOne(
{ chatRoomId }, { chatRoomId, "participants.name": nickname },
{ $set: { [`lastReadAt.${nickname}`]: now } } { $set: { [`lastReadAt.${nickname}`]: now } }
); );
} }
// 읽지 않은 메시지 조회 // 읽지 않은 메시지 조회
async getUnreadMessages(nickname) { 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) => { return await Promise.all(chatRooms.map(async (chatRoom) => {
const lastReadAt = chatRoom.lastReadAt.get(nickname) || new Date(0); const lastReadAt = chatRoom.lastReadAt.get(nickname) || new Date(0);
const unreadMessagesCount = chatRoom.messages.filter(message => const unreadMessagesCount = chatRoom.messages.filter(message =>
...@@ -98,31 +86,49 @@ class ChatService { ...@@ -98,31 +86,49 @@ class ChatService {
} }
const unreadCounts = chatRoom.participants const unreadCounts = chatRoom.participants
.filter(user => chatRoom.lastReadLogId.get(user)) .filter(participant => chatRoom.lastReadLogId.has(participant.name)) // Map에 존재하는 키만 처리
.map(user => chatRoom.lastReadLogId.get(user)) .map(participant => chatRoom.lastReadLogId.get(participant.name)) // lastReadLogId 값 추출
.reduce((acc, logId) => { .reduce((acc, logId) => {
acc[logId] = (acc[logId] || 0) + 1; acc[logId] = (acc[logId] || 0) + 1; // logId 기준으로 등장 횟수 누적
return acc; return acc;
}, {}); }, {});
let count = 0; let count = 0;
return Object.entries(unreadCounts) const sortedUnreadCounts = Object.entries(unreadCounts)
.sort(([logId1], [logId2]) => logId1.localeCompare(logId2)) .sort(([logId1], [logId2]) => logId1.localeCompare(logId2)) // logId 기준 오름차순 정렬
.reduce((acc, [logId, value]) => { .reduce((acc, [logId, value]) => {
count += value; count += value; // 누적 합계
acc[count] = logId; acc[count] = logId; // 누적 합계를 키로 저장
return acc; return acc;
}, {}); }, {});
return sortedUnreadCounts;
} }
// 읽은 메시지 로그 ID 업데이트 // 읽은 메시지 로그 ID 업데이트
async updateReadLogId(chatRoomId, nickname, logId) { async updateReadLogId(chatRoomId, nickname, logId) {
await ChatRoom.updateOne( await ChatRoom.updateOne(
{ chatRoomId }, { chatRoomId, "participants.name": nickname },
{ $set: { [`lastReadLogId.${nickname}`]: logId } } { $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 동시 업데이트 // 상태와 로그 ID 동시 업데이트
async updateStatusAndLogId(chatRoomId, nickname, isOnline, logId) { async updateStatusAndLogId(chatRoomId, nickname, isOnline, logId) {
let finalLogId = logId; let finalLogId = logId;
...@@ -135,7 +141,7 @@ class ChatService { ...@@ -135,7 +141,7 @@ class ChatService {
} }
await ChatRoom.updateOne( await ChatRoom.updateOne(
{ chatRoomId }, { chatRoomId, "participants.name": nickname },
{ {
$set: { $set: {
[`isOnline.${nickname}`]: isOnline, [`isOnline.${nickname}`]: isOnline,
...@@ -144,6 +150,65 @@ class ChatService { ...@@ -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
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment