Skip to content
Snippets Groups Projects
Commit d28f525a authored by 조대희's avatar 조대희
Browse files

refactor: 스케줄 response 변경 및 서비스 로직 수정

parent b82d73cd
Branches
No related tags found
1 merge request!42[#25] 배포코드 master브랜치로 이동
// dtos/ScheduleResponseDTO.js // dtos/ScheduleResponseDTO.js
class ScheduleResponseDTO { class ScheduleResponseDTO {
constructor(schedule) { static groupSchedules(schedules) {
this.id = schedule.id; const grouped = schedules.reduce((acc, schedule) => {
this.user_id = schedule.user_id; const key = `${schedule.title}-${schedule.is_fixed}`;
this.title = schedule.title; if (!acc[key]) {
this.time_idx = schedule.time_idx; // 새로운 time_idx 필드 추가 acc[key] = {
this.is_fixed = schedule.is_fixed; id: schedule.id,
this.createdAt = schedule.createdAt; user_id: schedule.user_id,
this.updatedAt = schedule.updatedAt; title: schedule.title,
is_fixed: schedule.is_fixed,
time_indices: [],
createdAt: schedule.createdAt,
updatedAt: schedule.updatedAt
};
}
acc[key].time_indices.push(schedule.time_idx);
return acc;
}, {});
return Object.values(grouped);
} }
} }
......
...@@ -9,158 +9,222 @@ class ScheduleService { ...@@ -9,158 +9,222 @@ class ScheduleService {
* 스케줄 생성 (벌크) * 스케줄 생성 (벌크)
* @param {object} [transaction] - Sequelize 트랜잭션 객체 -> 미팅방에서 쓰기위해 트랜잭션을 넘겨받는걸 추가 * @param {object} [transaction] - Sequelize 트랜잭션 객체 -> 미팅방에서 쓰기위해 트랜잭션을 넘겨받는걸 추가
*/ */
async createSchedules({ userId, title, is_fixed, events }, transaction = null) { async createSchedules({ userId, title, is_fixed, time_indices }, transaction = null) {
const scheduleDTOs = []; // 중복 검사
for (const time_idx of time_indices) {
for (const event of events) {
const { time_idx } = event;
// 중복 스케줄 검사
const overlap = await this.checkScheduleOverlap(userId, time_idx, transaction); const overlap = await this.checkScheduleOverlap(userId, time_idx, transaction);
if (overlap) { if (overlap) {
throw new Error(`Schedule overlaps with existing schedule at time_idx ${time_idx}`); throw new Error(`Schedule overlaps at time_idx ${time_idx}`);
}
} }
const scheduleData = { const createdSchedules = await Promise.all(
time_indices.map(time_idx =>
Schedule.create({
user_id: userId, user_id: userId,
title, title,
time_idx, time_idx,
is_fixed
}, { transaction })
)
);
return {
id: createdSchedules[0].id,
user_id: userId,
title,
is_fixed, is_fixed,
time_indices,
createdAt: createdSchedules[0].createdAt,
updatedAt: createdSchedules[0].updatedAt
}; };
const schedule = await Schedule.create(scheduleData, { transaction });
scheduleDTOs.push(new ScheduleResponseDTO(schedule));
} }
return scheduleDTOs; async getAllSchedules(userId) {
try {
const schedules = await Schedule.findAll({
where: { user_id: userId },
order: [['time_idx', 'ASC']]
});
return ScheduleResponseDTO.groupSchedules(schedules);
} catch (error) {
throw new Error(`Failed to fetch schedules: ${error.message}`);
}
} }
/**
* 스케줄 수정 (벌크)
* @param {Array} updates - 수정할 스케줄 배열
*/
async updateSchedules(userId, updates, transaction = null) { async updateSchedules(userId, updates, transaction = null) {
const updatedSchedules = []; const { originalTitle, title, is_fixed, time_indices } = updates;
for (const update of updates) { // 기존 스케줄 조회
const { time_idx, title, is_fixed } = update; const existingSchedules = await Schedule.findAll({
where: {
const schedule = await Schedule.findOne({ user_id: userId,
where: { user_id: userId, time_idx }, title: originalTitle
transaction, },
transaction
}); });
if (!schedule) { if (existingSchedules.length === 0) {
throw new Error(`Schedule not found at time_idx ${time_idx}`); throw new Error('Schedule not found');
} }
const updatedData = {}; const existingTimeIndices = existingSchedules.map(s => s.time_idx); // 기존 시간대
if (title !== undefined) updatedData.title = title; const toDelete = existingTimeIndices.filter(idx => !time_indices.includes(idx)); // 삭제할 시간대
if (is_fixed !== undefined) updatedData.is_fixed = is_fixed; const toAdd = time_indices.filter(idx => !existingTimeIndices.includes(idx)); // 추가할 시간대
const t = transaction || await sequelize.transaction();
const updatedSchedule = await schedule.update(updatedData, { transaction }); try {
updatedSchedules.push(new ScheduleResponseDTO(updatedSchedule)); // 삭제
if (toDelete.length > 0) {
await Schedule.destroy({
where: {
user_id: userId,
title: originalTitle,
time_idx: {
[Op.in]: toDelete
} }
},
return updatedSchedules; transaction: t
});
} }
/** // 제목, 고정/유동 업데이트
* 스케줄 삭제 (벌크) await Schedule.update(
* @param {number} userId - 사용자 ID {
* @param {Array<number>} time_idxs - 삭제할 스케줄의 time_idx 배열 title,
* @param {object} [transaction] - Sequelize 트랜잭션 객체 is_fixed
*/ },
async deleteSchedules(userId, time_idxs, transaction = null) { {
const deleted_time_idxs = []; where: {
user_id: userId,
title: originalTitle
},
transaction: t
}
);
for (const time_idx of time_idxs) { // 새로운 time_indices 추가
const deletedCount = await Schedule.destroy({ if (toAdd.length > 0) {
where: { user_id: userId, time_idx }, await Promise.all(
transaction, toAdd.map(time_idx =>
}); Schedule.create({
user_id: userId,
title,
time_idx,
is_fixed
}, { transaction: t })
)
);
}
if (deletedCount === 0) { if (!transaction) {
throw new Error(`Schedule not found at time_idx ${time_idx}`); await t.commit();
} }
deleted_time_idxs.push(time_idx); return {
id: existingSchedules[0].id,
user_id: userId,
title,
is_fixed,
time_indices,
createdAt: existingSchedules[0].createdAt,
updatedAt: new Date()
};
} catch (error) {
if (!transaction) {
await t.rollback();
}
throw error;
}
} }
return { deleted_time_idxs }; async deleteSchedules(userId, title, transaction = null) {
const deletedSchedules = await Schedule.destroy({
where: {
user_id: userId,
title
},
transaction
});
return { deletedCount: deletedSchedules };
} }
/** /**
* 특정 time_idx로 스케줄 조회 * 특정 time_idx로 스케줄 조회
*/ */
async getScheduleByTimeIdx(userId, time_idx) { async getScheduleByTimeIdx(userId, time_idx) {
// 해당 time_idx의 스케줄 찾기
const schedule = await Schedule.findOne({ const schedule = await Schedule.findOne({
where: { user_id: userId, time_idx }, where: { user_id: userId, time_idx }
}); });
if (!schedule) { if (!schedule) {
throw new Error('Schedule not found'); throw new Error('Schedule not found');
} }
return new ScheduleResponseDTO(schedule); // 같은 제목의 모든 스케줄 찾기
const relatedSchedules = await Schedule.findAll({
where: {
user_id: userId,
title: schedule.title,
is_fixed: schedule.is_fixed
},
order: [['time_idx', 'ASC']]
});
return ScheduleResponseDTO.groupSchedules(relatedSchedules)[0];
} }
/**
* 모든 스케줄 조회
*/
async getAllSchedules(userId) { async getAllSchedules(userId) {
try { try {
const schedules = await Schedule.findAll({ const schedules = await Schedule.findAll({
where: { user_id: userId }, where: { user_id: userId },
order: [['time_idx', 'ASC']], order: [['time_idx', 'ASC']]
}); });
return schedules.map((schedule) => new ScheduleResponseDTO(schedule)); return ScheduleResponseDTO.groupSchedules(schedules);
} catch (error) { } catch (error) {
throw new Error(`Failed to fetch schedules: ${error.message}`); throw new Error(`Failed to fetch schedules: ${error.message}`);
} }
} }
/**
* 중복 스케줄 검사
*/
async checkScheduleOverlap(userId, time_idx, transaction = null) { async checkScheduleOverlap(userId, time_idx, transaction = null) {
const overlappingSchedule = await Schedule.findOne({ const overlappingSchedule = await Schedule.findOne({
where: { user_id: userId, time_idx }, where: { user_id: userId, time_idx },
transaction, transaction
}); });
return !!overlappingSchedule; return !!overlappingSchedule;
} }
async checkScheduleOverlapByTime(userId, time_idx_start, time_idx_end, transaction = null) { async checkScheduleOverlapByTime(userId, time_idx_start, time_idx_end, transaction = null) {
console.log( const overlappingSchedules = await Schedule.findAll({
`checkScheduleOverlapByTime 호출: userId=${userId}, time_idx_start=${time_idx_start}, time_idx_end=${time_idx_end}`
);
const overlappingSchedule = await Schedule.findOne({
where: { where: {
user_id: userId, user_id: userId,
time_idx: { time_idx: {
[Op.between]: [time_idx_start, time_idx_end] [Op.between]: [time_idx_start, time_idx_end]
} }
}, },
transaction, transaction
}); });
console.log(`중복 스케줄: ${JSON.stringify(overlappingSchedule)}`);
const result = !!overlappingSchedule; const groupedSchedules = ScheduleResponseDTO.groupSchedules(overlappingSchedules);
const result = groupedSchedules.length > 0;
console.log(`checkScheduleOverlapByTime 호출: userId=${userId}, time_idx_start=${time_idx_start}, time_idx_end=${time_idx_end}`);
console.log(`중복 스케줄: ${JSON.stringify(groupedSchedules)}`);
console.log(`스케줄 충돌 결과: ${result}`); console.log(`스케줄 충돌 결과: ${result}`);
return result; return result;
} }
/**
* 만료된 스케줄 삭제
*/
async cleanExpiredSchedules() { async cleanExpiredSchedules() {
try { try {
const deletedCount = await Schedule.destroy({ const deletedCount = await Schedule.destroy({
where: { is_fixed: false }, where: { is_fixed: false }
}); });
//console.log(`Deleted ${deletedCount} flexible schedules.`); return { deletedCount };
} catch (error) { } catch (error) {
console.error('Failed to clean expired schedules:', error); console.error('Failed to clean expired schedules:', error);
throw error; throw error;
......
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Please register or to comment