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
Original file line number Diff line number Diff line change
@@ -1,4 +1,31 @@
package org.example.backend.domain.notification.controller;

import lombok.RequiredArgsConstructor;
import org.example.backend.domain.notification.dto.response.NotificationResponseDTO;
import org.example.backend.domain.notification.service.NotificationService;
import org.example.backend.global.ApiResponse;
import org.example.backend.global.security.auth.CustomSecurityUtil;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

import java.util.List;
import java.util.UUID;

@RestController
@RequestMapping("/api/notifications")
@RequiredArgsConstructor
// 알림 목록 조회
public class NotificationController {
private final NotificationService notificationService;
private final CustomSecurityUtil customSecurityUtil;

@GetMapping("")
public ApiResponse<List<NotificationResponseDTO>> getNotifications() {
UUID userId = customSecurityUtil.getUserId();


List<NotificationResponseDTO> notifications = notificationService.getNotificationsByUserId(userId);
return ApiResponse.onSuccess(notifications);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package org.example.backend.domain.notification.converter;

import lombok.RequiredArgsConstructor;
import org.example.backend.domain.notification.dto.response.NotificationResponseDTO;
import org.example.backend.domain.notification.entity.Notification;
import org.springframework.stereotype.Component;

import java.util.List;
import java.util.Map;
import java.util.UUID;

@Component
@RequiredArgsConstructor
public class NotificationConverter {

public NotificationResponseDTO toDTO(Notification notification, String className) {
return NotificationResponseDTO.builder()
.notificationId(notification.getId())
.className(className)
.alarmType(notification.getAlarmType())
.isRead(notification.isRead())
.createdAt(notification.getCreatedAt())
.build();
}

public List<NotificationResponseDTO> toResponseDTO(List<Notification> notifications, Map<UUID, String> classNameMap) {
return notifications.stream()
.map(notification -> toDTO(notification, classNameMap.get(notification.getLecture().getId())))
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package org.example.backend.domain.notification.dto.response;

import com.fasterxml.jackson.annotation.JsonProperty;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import org.example.backend.domain.notification.entity.AlarmType;

import java.time.LocalDateTime;
import java.util.UUID;

@Getter
@Builder
@AllArgsConstructor
@NoArgsConstructor
public class NotificationResponseDTO {
private UUID notificationId;
private String className;
private AlarmType alarmType;
@JsonProperty("isRead")
private boolean isRead;
private LocalDateTime createdAt;
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package org.example.backend.domain.notification.entity;

import com.fasterxml.jackson.annotation.JsonProperty;
import jakarta.persistence.*;
import lombok.*;
import org.example.backend.domain.lecture.entity.Lecture;
Expand Down Expand Up @@ -33,6 +34,7 @@ public class Notification extends BaseEntity {
@Column(name = "alarm_type", nullable = false)
private AlarmType alarmType;

@JsonProperty("isRead")
@Column(name = "is_read", nullable = false)
private boolean isRead;

Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,14 @@
package org.example.backend.domain.notification.repository;

public interface NotificationRepository {
import org.example.backend.domain.classroom.entity.Classroom;
import org.example.backend.domain.notification.entity.Notification;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.UUID;

@Repository
public interface NotificationRepository extends JpaRepository<Notification, UUID> {
List<Notification> findByUserIdOrderByCreatedAtDesc(UUID userId);
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,42 @@
package org.example.backend.domain.notification.service;

public class NotificationService {
import lombok.RequiredArgsConstructor;
import org.example.backend.domain.classroom.entity.Classroom;
import org.example.backend.domain.classroom.repository.ClassroomRepository;
import org.example.backend.domain.lecture.entity.Lecture;
import org.example.backend.domain.lecture.repository.LectureRepository;
import org.example.backend.domain.notification.converter.NotificationConverter;
import org.example.backend.domain.notification.dto.response.NotificationResponseDTO;
import org.example.backend.domain.notification.entity.Notification;
import org.example.backend.domain.notification.repository.NotificationRepository;
import org.springframework.stereotype.Service;

import java.util.List;
import java.util.UUID;

@RequiredArgsConstructor
@Service
public class NotificationService implements NotificationServiceImpl{
private final NotificationRepository notificationRepository;
private final LectureRepository lectureRepository;
private final ClassroomRepository classroomRepository;
private final NotificationConverter notificationConverter;;

public List<NotificationResponseDTO> getNotificationsByUserId(UUID userId) {
List<Notification> notificationList =
notificationRepository.findByUserIdOrderByCreatedAtDesc(userId);

return notificationList.stream()
.map(notification -> {
Lecture lecture = notification.getLecture();

String className = null;
if (lecture != null && lecture.getClassroom() != null) {
className = lecture.getClassroom().getClassName();
}

return notificationConverter.toDTO(notification, className);
})
.toList();
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
package org.example.backend.domain.notification.service;

import org.springframework.stereotype.Service;

@Service
public interface NotificationServiceImpl {
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,35 @@
package org.example.backend.domain.notificationSetting.controller;

import lombok.RequiredArgsConstructor;
import org.example.backend.domain.notificationSetting.dto.request.NotificationSettingPatchRequest;
import org.example.backend.domain.notificationSetting.dto.response.NotificationSettingResponseDTO;
import org.example.backend.domain.notificationSetting.service.NotificationSettingService;
import org.example.backend.global.ApiResponse;
import org.example.backend.global.security.auth.CustomSecurityUtil;
import org.springframework.web.bind.annotation.*;

import java.util.UUID;

@RestController
@RequestMapping("/api/notifications/setting")
@RequiredArgsConstructor
public class NotificationSettingController {
private final NotificationSettingService notificationSettingService;
private final CustomSecurityUtil customSecurityUtil;

@GetMapping("")
public ApiResponse<NotificationSettingResponseDTO> getNotiSetting(){
UUID userId = customSecurityUtil.getUserId();

NotificationSettingResponseDTO response = notificationSettingService.getNotiSetting(userId);
return ApiResponse.onSuccess(response);

}

@PatchMapping("")
public ApiResponse<Void> patchSettings(@RequestBody NotificationSettingPatchRequest req) {
UUID userId = customSecurityUtil.getUserId();
notificationSettingService.patchSettings(userId, req);
return ApiResponse.onSuccess(null);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package org.example.backend.domain.notificationSetting.converter;

import org.example.backend.domain.notificationSetting.dto.response.NotificationSettingResponseDTO;
import org.example.backend.domain.notificationSetting.entity.NotificationSetting;

public class NotificationSettingConverter {
public static NotificationSettingResponseDTO toDTO(NotificationSetting setting) {
return NotificationSettingResponseDTO.builder()
.quizUpload(setting.isQuizUpload())
.quizAnswerUpload(setting.isQuizAnswerUpload())
.lectureNoteUpload(setting.isLectureNoteUpload())
.lectureUpload(setting.isLectureUpload())
.recordUpload(setting.isRecordUpload())
.build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.example.backend.domain.notificationSetting.dto.request;

public record NotificationSettingPatchRequest(
Boolean quizUpload,
Boolean quizAnswerUpload,
Boolean lectureNoteUpload,
Boolean lectureUpload,
Boolean recordUpload
) {}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package org.example.backend.domain.notificationSetting.dto.response;

import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Getter
@NoArgsConstructor
@AllArgsConstructor
@Builder
public class NotificationSettingResponseDTO {

private boolean quizUpload;
private boolean quizAnswerUpload;
private boolean lectureNoteUpload;
private boolean lectureUpload;
private boolean recordUpload;
}
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,9 @@ public class NotificationSetting extends BaseEntity {
@Column(name = "user_id")
private String userId;

@Column(name = "token", nullable = false, unique = true, length = 512)
private String token;

@Column(name = "quiz_upload", nullable = false)
@Builder.Default
private boolean quizUpload = true;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
package org.example.backend.domain.notificationSetting.repository;

public interface NotificationSettingRepository {
import org.example.backend.domain.notificationSetting.entity.NotificationSetting;
import org.springframework.data.jpa.repository.JpaRepository;

public interface NotificationSettingRepository extends JpaRepository<NotificationSetting, String> {
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,44 @@
package org.example.backend.domain.notificationSetting.service;

public class NotificationSettingService {
import lombok.RequiredArgsConstructor;
import org.example.backend.domain.notificationSetting.converter.NotificationSettingConverter;
import org.example.backend.domain.notificationSetting.dto.request.NotificationSettingPatchRequest;
import org.example.backend.domain.notificationSetting.dto.response.NotificationSettingResponseDTO;
import org.example.backend.domain.notificationSetting.entity.NotificationSetting;
import org.example.backend.domain.notificationSetting.repository.NotificationSettingRepository;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.UUID;

@Service
@RequiredArgsConstructor
public class NotificationSettingService implements NotificationSettingServiceImpl{
private final NotificationSettingRepository notificationSettingRepository;

@Override
public NotificationSettingResponseDTO getNotiSetting(UUID userId) {
NotificationSetting setting = notificationSettingRepository.findById(userId.toString())
.orElseGet(() -> NotificationSetting.builder()
.userId(userId.toString())
.quizUpload(true)
.quizAnswerUpload(true)
.lectureNoteUpload(true)
.lectureUpload(true)
.recordUpload(true)
.build());

return NotificationSettingConverter.toDTO(setting);
}

@Transactional
public void patchSettings(UUID userId, NotificationSettingPatchRequest req) {
NotificationSetting entity = notificationSettingRepository.findById(userId.toString())
.orElseThrow(() -> new IllegalArgumentException("알림 설정이 존재하지 않습니다."));

if (req.quizUpload() != null) entity.setQuizUpload(req.quizUpload());
if (req.quizAnswerUpload() != null) entity.setQuizAnswerUpload(req.quizAnswerUpload());
if (req.lectureNoteUpload() != null) entity.setLectureNoteUpload(req.lectureNoteUpload());
if (req.lectureUpload() != null) entity.setLectureUpload(req.lectureUpload());
if (req.recordUpload() != null) entity.setRecordUpload(req.recordUpload());
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,12 @@
package org.example.backend.domain.notificationSetting.service;

import org.example.backend.domain.notificationSetting.dto.request.NotificationSettingPatchRequest;
import org.example.backend.domain.notificationSetting.dto.response.NotificationSettingResponseDTO;

import java.util.UUID;

public interface NotificationSettingServiceImpl {
NotificationSettingResponseDTO getNotiSetting(UUID userId);

void patchSettings(UUID userId, NotificationSettingPatchRequest req);
}
49 changes: 49 additions & 0 deletions frontend/api/notifications/fetchNotification.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
import { axiosInstance } from "@/api/axiosInstance";
import axios from "axios";
import { ENDPOINTS } from "@/constants/endpoints";
import { ApiResponse } from "@/types/apiResponseTypes";

export interface NotificationResponse {
notificationId: string;
className: string | null;
alarmType: string;
isRead: boolean;
createdAt: string;
}

const alarmTypeLabels: Record<string, string> = {
quizUpload: "새 퀴즈가 업로드되었습니다",
quizAnswerUpload: "퀴즈 답안이 업로드되었습니다",
lectureNoteUpload: "새 강의자료가 올라왔습니다",
startLecture: "강의가 시작되었습니다",
recordUpload: "녹음 파일이 업로드되었습니다",
};

export function getAlarmMessage(alarmType: string) {
return alarmTypeLabels[alarmType] ?? alarmType;
}

// 알림 목록 조회 API
export async function fetchNotifications() {
try {
const response = await axiosInstance.get<
ApiResponse<Omit<NotificationResponse, "alarmMessage">[]>
>(ENDPOINTS.NOTIFICATIONS.LIST);

if (response.data.isSuccess && response.data.result) {
// alarmType → alarmMessage 변환 추가
const mapped = response.data.result.map((n) => ({
...n,
alarmMessage: alarmTypeLabels[n.alarmType] ?? n.alarmType,
}));
return { ...response.data, result: mapped };
}

return response.data;
} catch (error: unknown) {
if (axios.isAxiosError(error) && error.response) {
return error.response.data as ApiResponse<NotificationResponse[]>;
}
throw error;
}
}
Loading