diff --git a/controllers/meetingController.js b/controllers/meetingController.js index 3522174b03f002e0c59423684f4b7d3ddd908520..ec2379a1dbf4a3f6cad3520006d4c0c54b1d0a1c 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 c9a07edd124db0d59702cc02460122e4a9f23d40..ef60bc6c6a6a82dbcc5c1247b190a50e2080aff8 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 a85d20297d3f8b8f51fd5c11645ac829c9a3d680..edf3593fabd15026cec39c9e5916d84926f6f3c9 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 2d64e7505efc92d0c8dc27d8aa00fb988666fa02..9be98b6de1b1fcb015840be628b7e9e771a1d952 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