Skip to content
Snippets Groups Projects
meetingService.js 9.19 KiB
Newer Older
  • Learn to ignore specific revisions
  • 
    const { Meeting, MeetingParticipant, User, Schedule } = require('../models');
    
    const ChatRoom = require('../models/chatRooms');
    
    const FcmToken = require('../models/fcmToken');
    
    const MeetingResponseDTO = require('../dtos/MeetingResponseDTO');
    
    tpgus2603's avatar
    tpgus2603 committed
    const MeetingDetailResponseDTO = require('../dtos/MeetingDetailResponseDTO');
    
    const CreateMeetingRequestDTO = require('../dtos/CreateMeetingRequestDTO');
    const ScheduleService = require('./scheduleService'); // ScheduleService 임포트
    
    const chatService = require('./chatService');
    
    
    class MeetingService {
    
        /**
         * 번개 모임 생성
         * @returns 생성된 모임 ID와 채팅방 ID
         */
        async createMeeting(meetingData) {
            const createMeetingDTO = new CreateMeetingRequestDTO(meetingData);
            createMeetingDTO.validate();
    
            const { title, description, start_time, end_time, location, deadline, type, created_by } = meetingData;
    
    
            // 사용자와 FCM 토큰 조회
            const user = await this._findUserWithFcmTokens(created_by);
            const userFcmTokens = user.fcmTokenList.map((fcmToken) => fcmToken.token);
    
            // 스케줄 충돌 확인
            const hasConflict = await ScheduleService.checkScheduleOverlap(
                created_by,
                new Date(start_time),
                new Date(end_time)
            );
    
            if (hasConflict) {
                throw new Error('스케줄이 겹칩니다. 다른 시간을 선택해주세요.');
            }
    
            // 트랜잭션을 사용하여 모임 생성과 스케줄 추가를 원자적으로 처리
    
            return await Meeting.sequelize.transaction(async (transaction) => {
                const chatRoomData = this._constructChatRoomData(title, user, userFcmTokens);
                const chatRoomResponse = await chatService.createChatRoom(chatRoomData);
    
                if (!chatRoomResponse.success) {
                    throw new Error('채팅방 생성 실패');
                }
    
                const chatRoomId = chatRoomResponse.chatRoomId;
    
                const newMeeting = await Meeting.create({
                    title,
                    description,
                    start_time,
                    end_time,
                    location,
                    deadline,
                    type,
                    created_by,
                    chatRoomId,
                }, { transaction });
    
                await MeetingParticipant.create({
                    meeting_id: newMeeting.id,
                    user_id: created_by,
                }, { transaction });
    
                await ScheduleService.createSchedule({
                    userId: created_by,
                    title: `번개 모임: ${title}`,
                    start_time: new Date(start_time),
                    end_time: new Date(end_time),
                    is_fixed: true,
                });
    
    
                const chatRoom = await ChatRoom.findOne({ chatRoomId: chatRoomId });
    
                if (chatRoom) {
                    console.log("채팅방 찾음");
                    this._addParticipantToChatRoom(chatRoom, user, userFcmTokens);
                }
    
    
                return { meeting_id: newMeeting.id, chatRoomId };
            });
    
         * @return 모임 목록 DTO 배열
    
         */
        async getMeetings(userId) {
            const meetings = await Meeting.findAll({
                attributes: ['id', 'title', 'description', 'start_time', 'end_time', 'location', 'deadline', 'type'],
                include: [
                    {
                        model: User,
                        as: 'creator',
                        attributes: ['name'],
                    },
                    {
                        model: MeetingParticipant,
                        as: 'participants',
                        attributes: ['user_id'],
                    },
                ],
            });
    
            return meetings.map((meeting) => {
                const creatorName = meeting.creator ? meeting.creator.name : 'Unknown';
                const isParticipant = meeting.participants.some(participant => participant.user_id === parseInt(userId, 10));
    
                return new MeetingResponseDTO(
                    meeting,
                    isParticipant,
                    false, // isScheduleConflict: 필요 시 추가 로직 구현
                    creatorName
                );
            });
    
         * 번개 모임 참가
    
         */
        async joinMeeting(meetingId, userId) {
            const meeting = await Meeting.findByPk(meetingId);
    
            if (!meeting) {
                throw new Error('모임을 찾을 수 없습니다.');
            }
    
            if (meeting.type === 'CLOSE') {
                throw new Error('이미 마감된 모임입니다.');
            }
    
            if (new Date() > new Date(meeting.deadline)) {
                throw new Error('참가 신청이 마감되었습니다.');
            }
    
            const existingParticipant = await MeetingParticipant.findOne({ 
                where: { meeting_id: meetingId, user_id: userId } 
    
            if (existingParticipant) {
                throw new Error('이미 참가한 사용자입니다.');
    
            await Meeting.sequelize.transaction(async (transaction) => {
                const hasConflict = await ScheduleService.checkScheduleOverlap(
                    userId,
                    new Date(meeting.start_time),
                    new Date(meeting.end_time)
                );
                if (hasConflict) {
                    throw new Error('스케줄이 겹칩니다. 다른 모임에 참가하세요.');
                }
    
    
                await MeetingParticipant.create({ meeting_id: meetingId, user_id: userId }, { transaction });
    
    
                await ScheduleService.createSchedule({
    
                    title: `번개 모임: ${meeting.title}`,
                    start_time: new Date(meeting.start_time),
                    end_time: new Date(meeting.end_time),
                    is_fixed: true,
                });
    
    
                // 사용자와 FCM 토큰 조회
                const user = await this._findUserWithFcmTokens(userId);
                const userFcmTokens = user.fcmTokenList.map((fcmToken) => fcmToken.token);
    
                const chatRoom = await ChatRoom.findOne({ chatRoomId: meeting.chatRoomId });
    
                if (chatRoom) {
                    console.log("채팅방 찾음");
                    this._addParticipantToChatRoom(chatRoom, user, userFcmTokens);
    
        /**
         * 번개 모임 상세 조회
         * @return 모임 상세 DTO
         */
        async getMeetingDetail(meetingId) {
            const meeting = await Meeting.findByPk(meetingId, {
                include: [
                    {
                        model: User,
                        as: 'creator',
    
                        attributes: ['name'],
    
                    },
                    {
                        model: MeetingParticipant,
                        as: 'participants',
                        include: [
                            {
                                model: User,
                                as: 'participantUser',
    
                                attributes: ['name', 'email'],
                            },
                        ],
                    },
                ],
    
            });
    
            if (!meeting) {
                throw new Error('모임을 찾을 수 없습니다.');
            }
    
            return new MeetingDetailResponseDTO(meeting);
        }
    
    
        /**
         * 번개 모임 마감
         */
        async closeMeeting(meetingId) {
            const meeting = await Meeting.findByPk(meetingId);
    
            if (!meeting) {
                throw new Error('모임을 찾을 수 없습니다.');
            }
    
            if (meeting.type === 'CLOSE') {
                throw new Error('이미 마감된 모임입니다.');
            }
    
            meeting.type = 'CLOSE';
            await meeting.save();
            return meeting;
        }
    
        // Helper functions
        async _findUserWithFcmTokens(userId) {
            const user = await User.findOne({
                where: { id: userId },
                include: [
                    {
                        model: FcmToken,
                        as: 'fcmTokenList',
                        attributes: ['token'],
                    },
                ],
            });
    
            if (!user) {
                throw new Error('사용자를 찾을 수 없습니다.');
            }
    
            return user;
        }
    
        _constructChatRoomData(title, user, userFcmTokens) {
            return {
                meeting_id: null,
                participants: [
                    {
                        name: user.name,
                        fcmTokens: userFcmTokens || [],
                        isOnline: true,
                        lastReadAt: new Date(),
                        lastReadLogId: null,
                    },
                ],
                chatRoomName: title,
            };
        }
    
        _addParticipantToChatRoom(chatRoom, user, userFcmTokens) {
            // Map 필드가 초기화되지 않은 경우 기본값 설정
            if (!chatRoom.isOnline) chatRoom.isOnline = new Map();
            if (!chatRoom.lastReadAt) chatRoom.lastReadAt = new Map();
            if (!chatRoom.lastReadLogId) chatRoom.lastReadLogId = new Map();
    
            // 참가자 추가 로직
            if (!chatRoom.participants.some(participant => participant.name === user.name)) {
                chatRoom.participants.push({ name: user.name, fcmTokens: userFcmTokens });
                chatRoom.isOnline.set(user.name, true);
                chatRoom.lastReadAt.set(user.name, new Date());
                chatRoom.lastReadLogId.set(user.name, null);
            }
    
            // 저장
            chatRoom.save();
        }
    
    module.exports = new MeetingService();