Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
62 changes: 51 additions & 11 deletions src/controllers/alarm.controller.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import { BadRequestError } from "../errors/custom.error.js";
import { BadRequestError, NotFoundError } from "../errors/custom.error.js";
import * as AlarmService from "../services/alarm.service.js";

// ✅ GET /v1/api/alarm - 알람 목록 조회
// 알람 목록 조회
export const handleAlarmList = async (req, res) => {
const userId = req.user.id;
// req.user는 로그인 미들웨어에서 설정됨
Expand All @@ -17,9 +17,8 @@ export const handleAlarmList = async (req, res) => {
return res.success(result, "알림 목록 조회 성공");
};

// ✅ DELETE /v1/api/alarm/:alarmId - 개별 알림 삭제
// 개별 알림 삭제
export const handleAlarmDelete = async (req, res) => {
// userId (임시 - 로그인 미들웨어 생성 후 req.user.id 사용)
const userId = req.user.id;

// alarmId 파라미터 파싱 및 검증
Expand All @@ -42,7 +41,7 @@ export const handleAlarmDelete = async (req, res) => {
return res.success(null, "개별 알림 삭제 성공");
};

// ✅ DELETE /v1/api/alarm - 전체 알림 삭제
// 전체 알림 삭제
export const handleAlarmDeleteAll = async (req, res) => {
const userId = req.user.id;

Expand All @@ -53,14 +52,18 @@ export const handleAlarmDeleteAll = async (req, res) => {
return res.success(null, "알림 전체 삭제 성공");
};

// ✅ PATCH /v1/api/alarm/deadline - 최종 마감 알림 수정
// 최종 마감 알림 수정
export const handleAlarmUpdateDeadline = async (req, res) => {
const userId = req.user.id;
const deadlineAlarm = req.body.deadlineAlarm;

if (!userId) {
throw new NotFoundError("USER_NOT_FOUND", "사용자를 찾을 수 없습니다.");
}

// 400 에러: 파라미터가 숫자가 아닌 경우
if (!deadlineAlarm || isNaN(parseInt(deadlineAlarm))) {
throw new BadRequestError("INVALID_BODY", "Body의 데이터 형식이 다를 경우");
throw new BadRequestError("INVALID_BODY", "Body의 deadlineAlarm 데이터는 number 형식으로 보내야합니다.");
}

const parsedDeadlineAlarm = parseInt(deadlineAlarm);
Expand All @@ -77,9 +80,13 @@ export const handleAlarmUpdateTask = async (req, res) => {
const userId = req.user.id;
const taskAlarm = req.body.taskAlarm;


if (!userId) {
throw new NotFoundError("USER_NOT_FOUND", "사용자를 찾을 수 없습니다.");
}
// 400 에러: 파라미터가 숫자가 아닌 경우
if (!taskAlarm || isNaN(parseInt(taskAlarm))) {
throw new BadRequestError("INVALID_BODY", "Body의 데이터 형식이 다를 경우");
throw new BadRequestError("INVALID_BODY", "Body의 taskAlarm 데이터는 number 형식으로 보내야합니다.");
}

const parsedTaskAlarm = parseInt(taskAlarm);
Expand All @@ -91,14 +98,29 @@ export const handleAlarmUpdateTask = async (req, res) => {
return res.success(result, "과제 마감 시간의 알림 전송 시간을 변경했습니다.");
};

// 과제 알림 여부 설정
// 과제 알림 여부 설정
export const handleAlarmUpdateTaskStatus = async (req, res) => {
const userId = req.user.id;
const taskId = req.params.taskId;
const isAlarm = req.body.isAlarm;

if (!userId) {
throw new NotFoundError("USER_NOT_FOUND", "사용자를 찾을 수 없습니다.");
}

if (!taskId) {
throw new BadRequestError("INVALID_PARAMETER", "요청하신 taskId가 존재하지 않습니다.");
}
if (isNaN(parseInt(taskId))) {
throw new BadRequestError("INVALID_PARAMETER", "params의 taskId는 숫자로 보내야합니다.");
}
if (typeof isAlarm !== "boolean") {
throw new BadRequestError("INVALID_BODY", "Body의 isAlarm 데이터는 Boolean 형식으로 보내야합니다.");
}

const parsedTaskId = parseInt(taskId);


// Service 호출
const result = await AlarmService.updateTaskAlarmStatus(
userId,
Expand All @@ -110,12 +132,26 @@ export const handleAlarmUpdateTaskStatus = async (req, res) => {
return res.success(result, "과제에 대한 알림 여부를 변경하였습니다..");
};

// ✅ PATCH /v1/api/alarm/subtask/:subTaskId - subtask 알림 여부 설정
// subtask 알림 여부 설정
export const handleAlarmUpdateSubtaskStatus = async (req, res) => {
const userId = req.user.id;
const subTaskId = req.params.subtaskId;
const isAlarm = req.body.isAlarm;

if (!userId) {
throw new NotFoundError("USER_NOT_FOUND", "사용자를 찾을 수 없습니다.");
}

if (!subTaskId) {
throw new BadRequestError("INVALID_PARAMETER", "요청하신 subTaskId가 존재하지 않습니다.");
}
if (isNaN(parseInt(subTaskId))) {
throw new BadRequestError("INVALID_PARAMETER", "params의 subTaskId는 숫자로 보내야합니다.");
}
if (typeof isAlarm !== "boolean") {
throw new BadRequestError("INVALID_BODY", "Body의 isAlarm 데이터는 Boolean 형식으로 보내야합니다.");
}

const parsedSubTaskId = parseInt(subTaskId);

// Service 호출
Expand All @@ -129,14 +165,18 @@ export const handleAlarmUpdateSubtaskStatus = async (req, res) => {
return res.success(result, "세부과제에 대한 알림 여부를 변경하였습니다..");
};

// ✅ PATCH /v1/api/alarm/read/:alarmId - 알림 읽음 처리
// 알림 읽음 처리
export const handleAlarmUpdateAlarmReadStatus = async (req, res) => {
const userId = req.user.id;
const alarmId = req.params.alarmId;
const isRead = req.body.isRead;

const parsedAlarmId = parseInt(alarmId);

if (!alarmId || isNaN(parseInt(alarmId))) {
throw new BadRequestError("INVALID_PARAMETER", "params는 숫자로 보내야합니다.");
}

// Service 호출
const result = await AlarmService.updateAlarmReadStatus(
userId,
Expand Down
14 changes: 11 additions & 3 deletions src/repositories/user.repository.js
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,7 @@ export const getUserData = async (userId) => {
const user = await prisma.user.findUnique({
where: { id: userId },

include: { folders: true },
include: { folders: true },
});

return user;
Expand All @@ -20,6 +20,14 @@ export const updateProfile = async (userId, data) => {
profileImage: data.profileImage,
},
});

return updatedUser;
}
}

//탈퇴한 회원인지 확인
export const checkDeletedUser = async (userId) => {
const user = await prisma.user.findUnique({
where: { id: userId },
select: { id: true, deletedAt: true },
});
return user;
}
39 changes: 38 additions & 1 deletion src/services/alarm.service.js
Original file line number Diff line number Diff line change
Expand Up @@ -23,6 +23,7 @@ import {
BadRequestError,
} from "../errors/custom.error.js";
import prisma from "../db.config.js";
import { checkDeletedUser } from "../repositories/user.repository.js";

export const getAlarms = async (userId, cursor, limit, orderBy, order) => {
// 알람 조회
Expand All @@ -40,6 +41,15 @@ export const getAlarms = async (userId, cursor, limit, orderBy, order) => {
const nextCursor =
hasNextPage && data.length > 0 ? data[data.length - 1].id : null;

const user = await checkDeletedUser(userId);
if (user.deletedAt) {
throw new ForbiddenError("USER_DELETED", "탈퇴한 유저는 알림을 조회할 수 없습니다.");
}

if (!user) {
throw new NotFoundError("USER_NOT_FOUND", "사용자를 찾을 수 없습니다.");
}

// DTO 변환
const responseData = alarmListResponseDto(data);

Expand All @@ -56,6 +66,21 @@ export const getAlarms = async (userId, cursor, limit, orderBy, order) => {
export const deleteAlarm = async (userId, alarmId) => {
// 알림 존재 여부 확인
const alarm = await findAlarmById(alarmId);
const user = await checkDeletedUser(userId);

if (user.deletedAt) {
throw new ForbiddenError(
"USER_DELETED",
"탈퇴한 유저는 알림을 삭제할 수 없습니다."
);
}

if (!userId) {
throw new NotFoundError(
"USER_NOT_FOUND",
"사용자를 찾을 수 없습니다."
);
}

if (!alarm) {
throw new NotFoundError(
Expand All @@ -80,6 +105,12 @@ export const deleteAlarm = async (userId, alarmId) => {

// 전체 알림 삭제
export const deleteAllAlarms = async (userId) => {
if (!userId) {
throw new NotFoundError(
"USER_NOT_FOUND",
"사용자를 찾을 수 없습니다."
);
}
// 유저의 모든 알림 삭제
await deleteAllAlarmsByUserId(userId);

Expand All @@ -98,6 +129,11 @@ export const updateDeadline = async (userId, deadlineAlarm) => {
// Task 마감 알림 수정
export const updateTask = async (userId, taskAlarm) => {
// Task 마감 알림 수정 (Repository 호출)

if (!userId) {
throw new NotFoundError("USER_NOT_FOUND", "사용자를 찾을 수 없습니다.");
}

const updatedUser = await updateTaskAlarm(userId, taskAlarm);

// DTO 변환
Expand Down Expand Up @@ -230,6 +266,7 @@ export const updateSubtaskAlarmStatus = async (userId, subTaskId, isAlarm) => {
export const updateAlarmReadStatus = async (userId, alarmId, isRead) => {
// 알림 존재 여부 확인
const alarm = await findAlarmById(alarmId);

if (!alarm) {
throw new NotFoundError("ALARM_NOT_FOUND", "알림을 찾을 수 없습니다.");
}
Expand All @@ -243,7 +280,7 @@ export const updateAlarmReadStatus = async (userId, alarmId, isRead) => {
if (typeof isRead !== "boolean") {
throw new BadRequestError(
"INVALID_BODY",
"Body의 isRead 데이터는 boolean 형식으로 보내야합니다."
"Body의 isRead 데이터는 Boolean 형식으로 보내야합니다."
);
}

Expand Down