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

[#6] Schedule 컨트롤러, 라우터, 유틸 로직

parents 90a1dec6 f997bd99
No related branches found
No related tags found
2 merge requests!31Develop,!7[#6] Schedule 컨트롤러, 라우터, 로직 개발
......@@ -7,6 +7,7 @@ const express = require('express');
const session = require('express-session');
const passport = require('./passport'); // 변경된 경로
const flash = require('connect-flash');
const { initScheduleCleaner } = require('./utils/scheduler');
const app = express();
......@@ -35,6 +36,13 @@ app.use(flash());
const authRoutes = require('./routes/auth');
app.use('/auth', authRoutes);
// Schedule 라우터
const scheduleRoutes = require('./routes/schedule');
app.use('/api/schedule', scheduleRoutes);
initScheduleCleaner();
const PORT = process.env.PORT || 3000;
app.get('/', (req, res) => {
......
const ScheduleService = require('../services/scheduleService');
class scheduleController {
/**
* 스케줄 생성
* POST /api/schedule
* 해당 사용자 id는 auth 미들웨어에서 설정된 사용자 정보 이용
* req.user = User 모델의 인스턴스
*/
async createSchedule(req, res) {
try {
const userId = req.user.id;
const { title, start_time, end_time, is_fixed } = req.body;
const schedule = await ScheduleService.createSchedule({
userId,
title,
start_time,
end_time,
is_fixed
});
return res.status(201).json({
success: true,
data: {
schedule
}
});
} catch (error) {
return res.status(400).json({
success: false,
error: {
message: error.message,
code: 'SCHEDULE_CREATE_ERROR'
}
});
}
}
/**
* 스케줄 수정
* PUT /api/schedule/:id
*/
async updateSchedule(req, res) {
try {
const { id } = req.params;
const { title, start_time, end_time } = req.body;
const userId = req.user.id;
const schedule = await ScheduleService.updateSchedule(id, userId,
{
title,
start_time,
end_time
});
return res.status(200).json({
success: true,
data: schedule
});
} catch (error) {
if (error.message === 'Schedule not found') {
return res.status(404).json({
success: false,
error: {
message: error.message,
code: 'SCHEDULE_NOT_FOUND'
}
});
}
return res.status(400).json({
success: false,
error: {
message: error.message,
code: 'SCHEDULE_UPDATE_ERROR'
}
});
}
}
/**
* 스케줄 삭제
* DELETE /api/schedule/:id
*/
async deleteSchedule(req, res) {
try {
const { id } = req.params;
const userId = req.user.id;
await ScheduleService.deleteSchedule(id, userId);
return res.status(200).json({
success: true,
data: {
message: 'Schedule successfully deleted'
}
});
} catch (error) {
return res.status(404).json({
success: false,
error: {
message: error.message,
code: 'SCHEDULE_NOT_FOUND'
}
});
}
}
/**
* 해당 사용자 전체 스케줄 조회
* GET /api/schedule/all
*/
async getAllSchedules(req, res) {
try {
const userId = req.user.id;
const schedules = await ScheduleService.getAllSchedules(userId);
return res.status(200).json({
success: true,
data: schedules
});
} catch (error) {
return res.status(500).json({
success: false,
error: {
message: 'Failed to fetch schedules',
code: 'FETCH_ERROR'
}
});
}
}
/**
* 해당 사용자 특정 스케줄 조회
* GET /api/schedule/:id
*/
async getScheduleById(req, res) {
try {
const { id } = req.params;
const userId = req.user.id;
const schedule = await ScheduleService.getScheduleById(id, userId);
return res.status(200).json({
success: true,
data: schedule
});
} catch (error) {
if (error.message === 'Schedule not found') {
return res.status(404).json({
success: false,
error: {
message: error.message,
code: 'SCHEDULE_NOT_FOUND'
}
});
}
return res.status(500).json({
success: false,
error: {
message: 'Failed to fetch schedule',
code: 'FETCH_ERROR'
}
});
}
}
}
module.exports = new scheduleController();
\ No newline at end of file
// models/Schedule.js
const { DataTypes } = require('sequelize');
const sequelize = require('../config/sequelize');
const User = require('./User');
......@@ -17,12 +16,21 @@ const Schedule = sequelize.define('Schedule', {
type: DataTypes.DATE,
allowNull: false,
},
is_fixed: {
type: DataTypes.BOOLEAN,
allowNull: false,
defaultValue: false
},
expiry_date: {
type: DataTypes.DATE,
allowNull: true
}
}, {
tableName: 'Schedules',
timestamps: false,
timestamps: true, // created_at과 updated_at 자동 관리
});
Schedule.belongsTo(User, { foreignKey: 'user_id', as: 'user' });
User.hasMany(Schedule, { foreignKey: 'user_id', as: 'schedules' });
module.exports = Schedule;
module.exports = Schedule;
\ No newline at end of file
const express = require('express');
const router = express.Router();
const { isLoggedIn } = require('../middleware/auth');
const ScheduleController = require('../controllers/scheduleController');
/**
* 스케줄 API 라우트
* 기본 경로: /api/schedule -> app.js에서 등록
* isLoggedIn 미들웨어 사용해서 인증 체크
*/
router.use(isLoggedIn);
/**
* 전체 스케줄 조회
* GET /api/schedule/all
*/
router.get('/all', ScheduleController.getAllSchedules);
/**
* 개별 스케줄 조회
* Get /api/schedule/:id
*/
router.get('/:id', ScheduleController.getScheduleById);
/**
* 스케줄 생성
* POST /api/schedule
*/
router.post('/', ScheduleController.createSchedule);
/**
* 스케줄 수정
* PUT /api/schedule/:id
*/
router.put('/:id', ScheduleController.updateSchedule);
/**
* 스케줄 삭제
* DELETE /api/schedule/:id
*/
router.delete('/:id', ScheduleController.deleteSchedule);
module.exports = router;
\ No newline at end of file
const { Op } = require('sequelize');
const Schedule = require('../models/Schedule');
class schedulService {
class scheduleService {
/**
* transactin wrapper 함수
......@@ -54,13 +54,16 @@ class schedulService {
/**
* 유동 스케줄 만료일 구하기
*/
getNextMonday() {
const date = new Date();
getNextMonday(startTime) {
const date = new Date(startTime);
const day = date.getDay();
const daysUntilNextMonday = (8 - day) % 7;
date.setDate(date.getDate() + daysUntilNextMonday);
date.setHours(0, 0, 0, 0);
return date;
const daysUntilNextMonday = (7 - day + 1) % 7;
const nextMonday = new Date(date);
nextMonday.setDate(date.getDate() + daysUntilNextMonday);
nextMonday.setHours(0, 0, 0, 0); // 자정으로 설정
return nextMonday;
}
/**
......@@ -81,7 +84,7 @@ class schedulService {
start_time,
end_time,
is_fixed,
expiry_date: is_fixed ? null : this.getNextMonday()
expiry_date: is_fixed ? null : this.getNextMonday(start_time)
};
return Schedule.create(scheduleData, { transaction });
......@@ -114,8 +117,15 @@ class schedulService {
throw new Error('Schedule overlaps with existing schedule');
}
delete updateData.is_fixed;
return schedule.update(updateData, { transaction });
const is_fixed = schedule.is_fixed;
const updatedData = {
...updateData,
expiry_date: is_fixed ? null : this.getNextMonday(updateData.start_time),
updatedAt: new Date()
};
delete updatedData.is_fixed;
return schedule.update(updatedData, { transaction });
});
}
......
const cron = require('node-cron');
const scheduleService = require('../services/scheduleService');
// 매주 월요일 자정에 유동 스케줄 삭제하기
const initScheduleCleaner = () => {
cron.schedule('0 0 * * 1', async () => {
try {
await scheduleService.cleanExpiredSchedules();
} catch (error) {
console.error('Failed to clean expired schedules:', error);
}
}, {
timezone: "Asia/Seoul"
});
};
module.exports = {
initScheduleCleaner
};
\ No newline at end of file
0% Loading or .
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment