From 9f0dcff29a789a8fac8b79801539875da76fffb1 Mon Sep 17 00:00:00 2001
From: =?UTF-8?q?=EC=A1=B0=EB=8C=80=ED=9D=AC?= <joedaehui@ajou.ac.kr>
Date: Tue, 10 Dec 2024 08:38:40 +0900
Subject: [PATCH] =?UTF-8?q?feat/refactor:=20=EB=AA=A8=EC=9E=84=20=EC=82=AD?=
 =?UTF-8?q?=EC=A0=9C,=20=EC=83=81=EC=84=B8=EC=A1=B0=ED=9A=8C=20res=20?=
 =?UTF-8?q?=EC=88=98=EC=A0=95?=
MIME-Version: 1.0
Content-Type: text/plain; charset=UTF-8
Content-Transfer-Encoding: 8bit

---
 controllers/meetingController.js |  18 +++
 dtos/MeetingDetailResponseDTO.js |   4 +-
 routes/meetingRoute.js           |   3 +-
 services/meetingService.js       | 261 ++++++++++++++++++-------------
 4 files changed, 172 insertions(+), 114 deletions(-)

diff --git a/controllers/meetingController.js b/controllers/meetingController.js
index 3522174..ec2379a 100644
--- a/controllers/meetingController.js
+++ b/controllers/meetingController.js
@@ -166,6 +166,24 @@ class MeetingController {
         }
     }
 
+    /**
+     * 踰덇컻 紐⑥엫 ��젣
+     * DELETE /api/meeting/:meetingId
+     */
+    // controllers/meetingController.js
+    async deleteMeeting(req, res) {
+        const { meetingId } = req.params;
+        const userId = req.user.id;
+
+        try {
+            await MeetingService.deleteMeeting(meetingId, userId);
+            res.status(200).json({ message: '紐⑥엫�� ��젣�섏뿀�듬땲��.' });
+        } catch (err) {
+            console.error('紐⑥엫 ��젣 �ㅻ쪟:', err);
+            res.status(500).json({ error: err.message || '紐⑥엫 ��젣 �ㅽ뙣' });
+        }
+    }
+
 }
 
 module.exports = new MeetingController();
diff --git a/dtos/MeetingDetailResponseDTO.js b/dtos/MeetingDetailResponseDTO.js
index c9a07ed..ef60bc6 100644
--- a/dtos/MeetingDetailResponseDTO.js
+++ b/dtos/MeetingDetailResponseDTO.js
@@ -13,8 +13,8 @@ class MeetingDetailResponseDTO {
       this.isScheduleConflict = isScheduleConflict; 
       this.participants = meeting.participants.map(participant => ({
           userId: participant.user_id,
-          name: participant.participantUser ? participant.participantUser.name : 'Unknown',
-          email: participant.participantUser ? participant.participantUser.email : 'Unknown'
+          name: participant.user  ? participant.user.name : 'Unknown',
+          email: participant.user  ? participant.user.email : 'Unknown'
       }));
   }
 }
diff --git a/routes/meetingRoute.js b/routes/meetingRoute.js
index a85d202..edf3593 100644
--- a/routes/meetingRoute.js
+++ b/routes/meetingRoute.js
@@ -28,6 +28,7 @@ router.get('/:meetingId', MeetingController.getMeetingDetail);
 // 踰덇컻 紐⑥엫 �덊눜
 router.delete('/:meetingId/leave', MeetingController.leaveMeeting);
 
-
+// 踰덇컻 紐⑥엫 ��젣
+router.delete('/:meetingId', MeetingController.deleteMeeting);
 
 module.exports = router;
\ No newline at end of file
diff --git a/services/meetingService.js b/services/meetingService.js
index 2d64e75..9be98b6 100644
--- a/services/meetingService.js
+++ b/services/meetingService.js
@@ -47,7 +47,7 @@ class MeetingService {
     //     };
     //     await this.publishToQueue('meeting_push_notifications', event); // meeting_push_notifications �먯뿉 硫붿떆吏� 諛쒗뻾
     // }
-     async sendMeetingPushNotificationRequest(meetingTitle, inviterName, inviteeTokens, type) {
+    async sendMeetingPushNotificationRequest(meetingTitle, inviterName, inviteeTokens, type) {
         const event = {
             meetingTitle,
             inviterName,
@@ -76,7 +76,7 @@ class MeetingService {
 
         // �ъ슜�먯� FCM �좏겙 議고쉶
         const user = await this._findUserWithFcmTokens(created_by);
-        console.log("user", user);  
+        console.log("user", user);
         const userFcmTokens = user.fcmTokenList.map((fcmToken) => fcmToken.token);
 
 
@@ -157,9 +157,9 @@ class MeetingService {
             // RabbitMQ 硫붿떆吏� 諛쒗뻾 (�몄떆 �뚮┝ �붿껌)
             if (inviteeTokens.length > 0) {
                 await this.sendMeetingPushNotificationRequest(
-                    title, 
-                    user.name, 
-                    inviteeTokens, 
+                    title,
+                    user.name,
+                    inviteeTokens,
                     'invite'
                 );
             }
@@ -229,7 +229,7 @@ class MeetingService {
         return availableFriendIds;
     }
 
-  
+
     async joinMeeting(meetingId, userId) {
         const meeting = await Meeting.findByPk(meetingId);
         console.log(`李몄뿬�섎젮�� 紐⑥엫: ${JSON.stringify(meeting)}`);
@@ -254,104 +254,104 @@ class MeetingService {
 
         // �몃옖��뀡�� �ъ슜�섏뿬 李멸��� 異붽� 諛� �ㅼ�以� �낅뜲�댄듃瑜� �먯옄�곸쑝濡� 泥섎━
         await sequelize.transaction(async (transaction) => {
-          if (meeting.cur_num >= meeting.max_num) {
-            throw new Error("紐⑥엫 �몄썝�� 紐⑤몢 李쇱뒿�덈떎.");
-          }
-          // �ㅼ�以� 異⑸룎 �뺤씤
-          const hasConflict = await ScheduleService.checkScheduleOverlapByTime(
-            userId,
-            meeting.time_idx_start,
-            meeting.time_idx_end,
-            transaction
-          );
-          console.log(`�ㅼ�以� 異⑸룎 寃곌낵: ${hasConflict}`);
-          if (hasConflict) {
-            throw new Error("�ㅼ�以꾩씠 寃뱀묩�덈떎. �ㅻⅨ 紐⑥엫�� 李멸��섏꽭��.");
-          }
-
-          await MeetingParticipant.create(
-            { meeting_id: meetingId, user_id: userId },
-            { transaction }
-          );
-
-        const time_indices = Array.from(
-            { length: meeting.time_idx_end - meeting.time_idx_start + 1 },
-            (_, i) => meeting.time_idx_start + i
-        );
-        
-        await ScheduleService.createSchedules({
-            userId: userId,
-            title: `踰덇컻 紐⑥엫: ${meeting.title}`,
-            is_fixed: false,
-            time_indices: time_indices,
-        }, transaction);
-
-          // 梨꾪똿諛� 李멸� (MongoDB)
-          const user = await User.findOne({
-            where: { id: userId },
-            include: [
-                {
-                    model: FcmToken,
-                    as: 'fcmTokenList', // FCM �좏겙 媛��몄삤湲�
-                    attributes: ['token'],
-                },
-            ],
-            transaction
-          });
-
-          const userFcmTokens = user.fcmTokenList.map((token) => token.token);
-
-          const chatRoom = await ChatRooms.findOne({
-            chatRoomId: meeting.chatRoomId,
-          });
-
-          console.log("�ш린源뚯�");
-          console.log("user.name", user.name);
-          console.log("李멸��섎뒗 �좎� fcm", userFcmTokens);
-          if (chatRoom && !chatRoom.participants.includes(user.name)) {
-            // 李멸��� 異붽�
-            chatRoom.participants.push({
-                name: user.name,
-                fcmTokens: userFcmTokens, // FCM �좏겙 異붽�
+            if (meeting.cur_num >= meeting.max_num) {
+                throw new Error("紐⑥엫 �몄썝�� 紐⑤몢 李쇱뒿�덈떎.");
+            }
+            // �ㅼ�以� 異⑸룎 �뺤씤
+            const hasConflict = await ScheduleService.checkScheduleOverlapByTime(
+                userId,
+                meeting.time_idx_start,
+                meeting.time_idx_end,
+                transaction
+            );
+            console.log(`�ㅼ�以� 異⑸룎 寃곌낵: ${hasConflict}`);
+            if (hasConflict) {
+                throw new Error("�ㅼ�以꾩씠 寃뱀묩�덈떎. �ㅻⅨ 紐⑥엫�� 李멸��섏꽭��.");
+            }
+
+            await MeetingParticipant.create(
+                { meeting_id: meetingId, user_id: userId },
+                { transaction }
+            );
+
+            const time_indices = Array.from(
+                { length: meeting.time_idx_end - meeting.time_idx_start + 1 },
+                (_, i) => meeting.time_idx_start + i
+            );
+
+            await ScheduleService.createSchedules({
+                userId: userId,
+                title: `踰덇컻 紐⑥엫: ${meeting.title}`,
+                is_fixed: false,
+                time_indices: time_indices,
+            }, transaction);
+
+            // 梨꾪똿諛� 李멸� (MongoDB)
+            const user = await User.findOne({
+                where: { id: userId },
+                include: [
+                    {
+                        model: FcmToken,
+                        as: 'fcmTokenList', // FCM �좏겙 媛��몄삤湲�
+                        attributes: ['token'],
+                    },
+                ],
+                transaction
             });
-            chatRoom.isOnline.set(user.name, false);
-            chatRoom.lastReadAt.set(user.name, new Date());
-            chatRoom.lastReadLogId.set(user.name, null);
 
-            const joinMessage = {
-                message: `${nickname}�섏씠 李멸��덉뒿�덈떎.`,
-                timestamp: new Date(),
-                type: 'join'
-            };
+            const userFcmTokens = user.fcmTokenList.map((token) => token.token);
 
-            chatRoom.messages.push(joinMessage);
+            const chatRoom = await ChatRooms.findOne({
+                chatRoomId: meeting.chatRoomId,
+            });
 
-            // 湲곗〈 李멸��� FCM �좏겙 媛��몄삤湲�
-            const otherParticipants = chatRoom.participants.filter(participant => participant.name !== user.name);
-            const otherParticipantTokens = otherParticipants.flatMap(participant => participant.fcmTokens);
+            console.log("�ш린源뚯�");
+            console.log("user.name", user.name);
+            console.log("李멸��섎뒗 �좎� fcm", userFcmTokens);
+            if (chatRoom && !chatRoom.participants.includes(user.name)) {
+                // 李멸��� 異붽�
+                chatRoom.participants.push({
+                    name: user.name,
+                    fcmTokens: userFcmTokens, // FCM �좏겙 異붽�
+                });
+                chatRoom.isOnline.set(user.name, false);
+                chatRoom.lastReadAt.set(user.name, new Date());
+                chatRoom.lastReadLogId.set(user.name, null);
+
+                const joinMessage = {
+                    message: `${nickname}�섏씠 李멸��덉뒿�덈떎.`,
+                    timestamp: new Date(),
+                    type: 'join'
+                };
+
+                chatRoom.messages.push(joinMessage);
+
+                // 湲곗〈 李멸��� FCM �좏겙 媛��몄삤湲�
+                const otherParticipants = chatRoom.participants.filter(participant => participant.name !== user.name);
+                const otherParticipantTokens = otherParticipants.flatMap(participant => participant.fcmTokens);
+
+                if (otherParticipantTokens.length > 0) {
+                    // RabbitMQ 硫붿떆吏� 諛쒗뻾
+                    await this.sendMeetingPushNotificationRequest(
+                        meeting.title,
+                        user.name,
+                        otherParticipantTokens,
+                        'join'
+                    );
+                }
 
-            if (otherParticipantTokens.length > 0) {
-                // RabbitMQ 硫붿떆吏� 諛쒗뻾
-                await this.sendMeetingPushNotificationRequest(
-                    meeting.title,
-                    user.name,
-                    otherParticipantTokens,
-                    'join'
-                );
+                await chatRoom.save();
             }
 
-            await chatRoom.save();
-          }
-
-          // �꾩옱 �몄썝 �� 利앷�
-          await meeting.increment("cur_num", { by: 1, transaction });
+            // �꾩옱 �몄썝 �� 利앷�
+            await meeting.increment("cur_num", { by: 1, transaction });
         });
     }
 
-    
+
     async getMeetings(userId, pagination) {
         const { limit = 20, offset = 0 } = pagination;
-    
+
         try {
             const meetings = await Meeting.findAll({
                 attributes: [
@@ -374,7 +374,7 @@ class MeetingService {
                 offset,
                 distinct: true
             });
-    
+
             const hasNext = meetings.length > limit;
             const content = await Promise.all(
                 meetings.slice(0, limit).map(async (meeting) => {
@@ -384,13 +384,13 @@ class MeetingService {
                             user_id: userId
                         }
                     });
-    
+
                     const hasConflict = await ScheduleService.checkScheduleOverlapByTime(
                         userId,
                         meeting.time_idx_start,
                         meeting.time_idx_end
                     );
-    
+
                     return new MeetingResponseDTO(
                         meeting,
                         !!isParticipant,
@@ -399,7 +399,7 @@ class MeetingService {
                     );
                 })
             );
-    
+
             return { content, hasNext };
         } catch (error) {
             console.error('getMeetings error:', error);
@@ -409,7 +409,7 @@ class MeetingService {
 
     async getMyMeetings(userId, pagination) {
         const { limit = 20, offset = 0 } = pagination;
-    
+
         try {
             const meetings = await Meeting.findAll({
                 attributes: [
@@ -438,7 +438,7 @@ class MeetingService {
                 offset,
                 distinct: true
             });
-    
+
             const hasNext = meetings.length > limit;
             const content = meetings.slice(0, limit).map(meeting => {
                 return new MeetingResponseDTO(
@@ -448,14 +448,14 @@ class MeetingService {
                     meeting.creator?.name || 'Unknown'
                 );
             });
-    
+
             return { content, hasNext };
         } catch (error) {
             console.error('getMyMeetings error:', error);
             throw new Error('Failed to fetch my meetings');
         }
     }
-    
+
     async getMeetingDetail(meetingId, userId) {
         const meeting = await Meeting.findByPk(meetingId, {
             include: [
@@ -477,17 +477,17 @@ class MeetingService {
                 }
             ]
         });
-    
+
         if (!meeting) {
             throw new Error("紐⑥엫�� 李얠쓣 �� �놁뒿�덈떎.");
         }
-    
+
         const hasConflict = await ScheduleService.checkScheduleOverlapByTime(
             userId,
             meeting.time_idx_start,
             meeting.time_idx_end
         );
-    
+
         return new MeetingDetailResponseDTO(meeting, hasConflict);
     }
 
@@ -563,13 +563,13 @@ class MeetingService {
         // ����
         chatRoom.save();
     }
-    
+
     async leaveMeeting(meetingId, userId) {
         const meeting = await Meeting.findByPk(meetingId);
         if (!meeting) {
             throw new Error('紐⑥엫�� 李얠쓣 �� �놁뒿�덈떎.');
         }
-    
+
         await sequelize.transaction(async (transaction) => {
             // 李멸��� �뺤씤
             const participant = await MeetingParticipant.findOne({
@@ -579,16 +579,16 @@ class MeetingService {
                 },
                 transaction
             });
-    
+
             if (!participant) {
                 throw new Error('李멸��섏� �딆� 紐⑥엫�낅땲��.');
             }
-    
+
             // �앹꽦�먮뒗 �덊눜�� �� �놁쓬
             if (meeting.created_by === userId) {
                 throw new Error('紐⑥엫 �앹꽦�먮뒗 �덊눜�� �� �놁뒿�덈떎.');
             }
-    
+
             // 李멸��� �쒓굅
             await MeetingParticipant.destroy({
                 where: {
@@ -597,7 +597,7 @@ class MeetingService {
                 },
                 transaction
             });
-    
+
             // 愿��� �ㅼ�以� ��젣
             await Schedule.destroy({
                 where: {
@@ -609,7 +609,7 @@ class MeetingService {
                 },
                 transaction
             });
-    
+
             // 梨꾪똿諛⑹뿉�� �쒓굅
             const chatRoom = await ChatRooms.findOne({
                 chatRoomId: meeting.chatRoomId
@@ -622,11 +622,50 @@ class MeetingService {
                 chatRoom.lastReadLogId.delete(user.name);
                 await chatRoom.save();
             }
-    
+
             // �꾩옱 �몄썝 �� 媛먯냼
             await meeting.decrement('cur_num', { by: 1, transaction });
         });
     }
+
+    async deleteMeeting(meetingId, userId) {
+        const meeting = await Meeting.findByPk(meetingId);
+
+        if (!meeting) {
+            throw new Error('紐⑥엫�� 李얠쓣 �� �놁뒿�덈떎.');
+        }
+
+        if (meeting.created_by !== userId) {
+            throw new Error('紐⑥엫 �앹꽦�먮쭔 ��젣�� �� �덉뒿�덈떎.');
+        }
+
+        return await sequelize.transaction(async (transaction) => {
+            const participants = await MeetingParticipant.findAll({
+                where: { meeting_id: meetingId },
+                attributes: ['user_id'],
+                transaction
+            });
+
+            const participantIds = participants.map(p => p.user_id);
+
+            // 紐⑤뱺 李멸��먯쓽 �ㅼ�以� ��젣
+            await Schedule.destroy({
+                where: {
+                    user_id: { [Op.in]: participantIds },
+                    title: `踰덇컻 紐⑥엫: ${meeting.title}`,
+                    time_idx: {
+                        [Op.between]: [meeting.time_idx_start, meeting.time_idx_end]
+                    }
+                },
+                transaction
+            });
+
+            await ChatRooms.deleteOne({ chatRoomId: meeting.chatRoomId });
+
+            // 紐⑥엫 愿��� �곗씠�� ��젣
+            await meeting.destroy({ transaction });
+        });
+    }
 }
 
 module.exports = new MeetingService();
\ No newline at end of file
-- 
GitLab