// services/scheduleService.js const sequelize = require('../config/sequelize'); const { Op } = require('sequelize'); const Schedule = require('../models/Schedule'); const ScheduleResponseDTO = require('../dtos/ScheduleResponseDTO'); class ScheduleService { /** * 스케줄 생성 (벌크) * @param {object} [transaction] - Sequelize 트랜잭션 객체 -> 미팅방에서 쓰기위해 트랜잭션을 넘겨받는걸 추가 */ async createSchedules({ userId, title, is_fixed, events }, transaction = null) { const scheduleDTOs = []; for (const event of events) { const { time_idx } = event; // 중복 스케줄 검사 const overlap = await this.checkScheduleOverlap(userId, time_idx, transaction); if (overlap) { throw new Error(`Schedule overlaps with existing schedule at time_idx ${time_idx}`); } const scheduleData = { user_id: userId, title, time_idx, is_fixed, }; const schedule = await Schedule.create(scheduleData, { transaction }); scheduleDTOs.push(new ScheduleResponseDTO(schedule)); } return scheduleDTOs; } /** * 스케줄 수정 (벌크) * @param {Array} updates - 수정할 스케줄 배열 */ async updateSchedules(userId, updates, transaction = null) { const updatedSchedules = []; for (const update of updates) { const { time_idx, title, is_fixed } = update; const schedule = await Schedule.findOne({ where: { user_id: userId, time_idx }, transaction, }); if (!schedule) { throw new Error(`Schedule not found at time_idx ${time_idx}`); } const updatedData = {}; if (title !== undefined) updatedData.title = title; if (is_fixed !== undefined) updatedData.is_fixed = is_fixed; const updatedSchedule = await schedule.update(updatedData, { transaction }); updatedSchedules.push(new ScheduleResponseDTO(updatedSchedule)); } return updatedSchedules; } /** * 스케줄 삭제 (벌크) * @param {number} userId - 사용자 ID * @param {Array<number>} time_idxs - 삭제할 스케줄의 time_idx 배열 * @param {object} [transaction] - Sequelize 트랜잭션 객체 */ async deleteSchedules(userId, time_idxs, transaction = null) { const deleted_time_idxs = []; for (const time_idx of time_idxs) { const deletedCount = await Schedule.destroy({ where: { user_id: userId, time_idx }, transaction, }); if (deletedCount === 0) { throw new Error(`Schedule not found at time_idx ${time_idx}`); } deleted_time_idxs.push(time_idx); } return { deleted_time_idxs }; } /** * 특정 time_idx로 스케줄 조회 */ async getScheduleByTimeIdx(userId, time_idx) { const schedule = await Schedule.findOne({ where: { user_id: userId, time_idx }, }); if (!schedule) { throw new Error('Schedule not found'); } return new ScheduleResponseDTO(schedule); } /** * 모든 스케줄 조회 */ async getAllSchedules(userId) { try { const schedules = await Schedule.findAll({ where: { user_id: userId }, order: [['time_idx', 'ASC']], }); return schedules.map((schedule) => new ScheduleResponseDTO(schedule)); } catch (error) { throw new Error(`Failed to fetch schedules: ${error.message}`); } } /** * 중복 스케줄 검사 */ async checkScheduleOverlap(userId, time_idx, transaction = null) { const overlappingSchedule = await Schedule.findOne({ where: { user_id: userId, time_idx }, transaction, }); return !!overlappingSchedule; } async checkScheduleOverlapByTime(userId, time_idx_start, time_idx_end, transaction = null) { console.log( `checkScheduleOverlapByTime 호출: userId=${userId}, time_idx_start=${time_idx_start}, time_idx_end=${time_idx_end}` ); const overlappingSchedule = await Schedule.findOne({ where: { user_id: userId, time_idx: { [Op.between]: [time_idx_start, time_idx_end] } }, transaction, }); console.log(`중복 스케줄: ${JSON.stringify(overlappingSchedule)}`); const result = !!overlappingSchedule; console.log(`스케줄 충돌 결과: ${result}`); return result; } /** * 만료된 스케줄 삭제 */ async cleanExpiredSchedules() { try { const deletedCount = await Schedule.destroy({ where: { is_fixed: false }, }); //console.log(`Deleted ${deletedCount} flexible schedules.`); } catch (error) { console.error('Failed to clean expired schedules:', error); throw error; } } } module.exports = new ScheduleService();