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
Expand Up @@ -4,6 +4,7 @@
import com.back.domain.notification.dto.NotificationResponse;
import com.back.domain.notification.dto.NotificationListResponse;
import com.back.domain.notification.entity.Notification;
import com.back.domain.notification.entity.NotificationSettingType;
import com.back.domain.notification.service.NotificationService;
import com.back.domain.studyroom.entity.Room;
import com.back.domain.studyroom.repository.RoomRepository;
Expand Down Expand Up @@ -76,12 +77,14 @@ public ResponseEntity<RsData<NotificationResponse>> createNotification(
User actor = userRepository.findById(request.actorId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

// 개인 알림 생성
yield notificationService.createPersonalNotification(
receiver,
actor,
request.title(),
request.message(),
request.redirectUrl()
request.redirectUrl(),
NotificationSettingType.SYSTEM
);
}
case "ROOM" -> {
Expand All @@ -93,12 +96,14 @@ public ResponseEntity<RsData<NotificationResponse>> createNotification(
User actor = userRepository.findById(request.actorId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

// 스터디룸 알림 생성
yield notificationService.createRoomNotification(
room,
actor,
request.title(),
request.message(),
request.redirectUrl()
request.redirectUrl(),
NotificationSettingType.ROOM_NOTICE
);
}
case "COMMUNITY" -> {
Expand All @@ -110,12 +115,14 @@ public ResponseEntity<RsData<NotificationResponse>> createNotification(
User actor = userRepository.findById(request.actorId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

// 커뮤니티 알림 생성
yield notificationService.createCommunityNotification(
receiver,
actor,
request.title(),
request.message(),
request.redirectUrl()
request.redirectUrl(),
NotificationSettingType.POST_COMMENT
);
}
case "SYSTEM" -> {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,54 @@
package com.back.domain.notification.controller;

import com.back.domain.notification.dto.NotificationSettingDto.*;
import com.back.domain.notification.entity.NotificationSettingType;
import com.back.domain.notification.service.NotificationSettingService;
import com.back.global.common.dto.RsData;
import com.back.global.security.user.CurrentUser;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/users/me/notification-settings")
@Tag(name = "Notification Setting API", description = "알림 설정 API")
public class NotificationSettingController {

private final NotificationSettingService settingService;
private final CurrentUser currentUser;

@GetMapping
@Operation(summary = "알림 설정 조회", description = "사용자의 모든 알림 설정을 조회합니다.")
public ResponseEntity<RsData<SettingsResponse>> getSettings() {

SettingsResponse response = settingService.getUserSettings(currentUser.getUserId());

return ResponseEntity.ok(RsData.success("알림 설정 조회 성공", response));
}

@PutMapping("/{type}")
@Operation(summary = "개별 알림 설정 토글", description = "특정 알림 타입의 활성화 상태를 토글합니다.")
public ResponseEntity<RsData<Void>> toggleSetting(
@Parameter(description = "알림 타입", example = "ROOM_JOIN")
@PathVariable NotificationSettingType type) {

settingService.toggleSetting(currentUser.getUserId(), type);

return ResponseEntity.ok(RsData.success(null));
}

@PutMapping("/all")
@Operation(summary = "전체 알림 ON/OFF", description = "모든 알림을 한 번에 활성화하거나 비활성화합니다.")
public ResponseEntity<RsData<Void>> toggleAllSettings(
@Parameter(description = "활성화 여부", example = "true")
@RequestParam boolean enable) {

settingService.toggleAllSettings(currentUser.getUserId(), enable);

return ResponseEntity.ok(RsData.success(null));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.back.domain.notification.dto;

import com.back.domain.notification.entity.NotificationSetting;
import com.back.domain.notification.entity.NotificationSettingType;
import io.swagger.v3.oas.annotations.media.Schema;

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

public class NotificationSettingDto {

// 응답 DTO - 단일 설정
@Schema(description = "알림 설정 정보")
public record SettingInfo(
@Schema(description = "알림 타입", example = "ROOM_JOIN")
NotificationSettingType type,

@Schema(description = "활성화 여부", example = "true")
boolean enabled
) {
public static SettingInfo from(NotificationSetting setting) {
return new SettingInfo(
setting.getType(),
setting.isEnabled()
);
}
}

// 응답 DTO - 전체 설정
@Schema(description = "사용자 알림 설정 전체 응답")
public record SettingsResponse(
@Schema(description = "전체 알림 켜기/끄기", example = "true")
boolean allEnabled,

@Schema(description = "개별 알림 설정 목록")
List<SettingInfo> settings
) {
public static SettingsResponse of(boolean allEnabled, List<NotificationSetting> settings) {
return new SettingsResponse(
allEnabled,
settings.stream()
.map(SettingInfo::from)
.toList()
);
}
}

// 요청 DTO - 일괄 변경
@Schema(description = "알림 설정 일괄 변경 요청")
public record UpdateSettingsRequest(
@Schema(description = "알림 타입별 활성화 여부",
example = "{\"ROOM_JOIN\": true, \"POST_COMMENT\": false}")
Map<NotificationSettingType, Boolean> settings
) {}

// 요청 DTO - 전체 ON/OFF
@Schema(description = "전체 알림 ON/OFF 요청")
public record ToggleAllRequest(
@Schema(description = "전체 활성화 여부", example = "true")
boolean enable
) {}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.back.domain.notification.event.community;

import com.back.domain.notification.entity.NotificationSettingType;
import com.back.domain.notification.service.NotificationService;
import com.back.domain.user.entity.User;
import com.back.domain.user.repository.UserRepository;
Expand Down Expand Up @@ -27,7 +28,6 @@ public void handleCommentCreated(CommentCreatedEvent event) {
event.getPostId(), event.getCommentId(), event.getActorId());

try {

User actor = userRepository.findById(event.getActorId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

Expand All @@ -39,7 +39,8 @@ public void handleCommentCreated(CommentCreatedEvent event) {
actor,
event.getTitle(),
event.getContent(),
"/posts/" + event.getPostId()
"/posts/" + event.getPostId(),
NotificationSettingType.POST_COMMENT
);

log.info("[알림] 댓글 작성 알림 전송 완료");
Expand All @@ -57,7 +58,6 @@ public void handleReplyCreated(ReplyCreatedEvent event) {
event.getParentCommentId(), event.getReplyId(), event.getActorId());

try {

User actor = userRepository.findById(event.getActorId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

Expand All @@ -69,7 +69,8 @@ public void handleReplyCreated(ReplyCreatedEvent event) {
actor,
event.getTitle(),
event.getContent(),
"/posts/" + event.getPostId() + "#comment-" + event.getParentCommentId()
"/posts/" + event.getPostId() + "#comment-" + event.getParentCommentId(),
NotificationSettingType.POST_COMMENT
);

log.info("[알림] 대댓글 작성 알림 전송 완료");
Expand All @@ -87,7 +88,6 @@ public void handlePostLiked(PostLikedEvent event) {
event.getPostId(), event.getActorId());

try {

User actor = userRepository.findById(event.getActorId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

Expand All @@ -99,7 +99,8 @@ public void handlePostLiked(PostLikedEvent event) {
actor,
event.getTitle(),
event.getContent(),
"/posts/" + event.getPostId()
"/posts/" + event.getPostId(),
NotificationSettingType.POST_LIKE
);

log.info("[알림] 게시글 좋아요 알림 전송 완료");
Expand All @@ -117,7 +118,6 @@ public void handleCommentLiked(CommentLikedEvent event) {
event.getCommentId(), event.getActorId());

try {

User actor = userRepository.findById(event.getActorId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

Expand All @@ -129,7 +129,8 @@ public void handleCommentLiked(CommentLikedEvent event) {
actor,
event.getTitle(),
event.getContent(),
"/posts/" + event.getPostId() + "#comment-" + event.getCommentId()
"/posts/" + event.getPostId() + "#comment-" + event.getCommentId(),
NotificationSettingType.POST_LIKE
);

log.info("[알림] 댓글 좋아요 알림 전송 완료");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.back.domain.notification.event.study;

import com.back.domain.notification.entity.NotificationSettingType;
import com.back.domain.notification.service.NotificationService;
import com.back.domain.user.entity.User;
import com.back.domain.user.repository.UserRepository;
Expand Down Expand Up @@ -30,13 +31,12 @@ public void handleStudyRecordCreated(StudyRecordCreatedEvent event) {
User user = userRepository.findById(event.getUserId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

// 본인에게 개인 알림
notificationService.createPersonalNotification(
user, // receiver (본인)
user, // actor (본인)
notificationService.createSelfNotification(
user,
event.getTitle(),
event.getContent(),
"/study/records/" + event.getStudyRecordId()
"/study/records/" + event.getStudyRecordId(),
NotificationSettingType.SYSTEM
);

log.info("[알림] 학습 기록 알림 전송 완료");
Expand All @@ -58,12 +58,12 @@ public void handleDailyGoalAchieved(DailyGoalAchievedEvent event) {
User user = userRepository.findById(event.getUserId())
.orElseThrow(() -> new CustomException(ErrorCode.USER_NOT_FOUND));

notificationService.createPersonalNotification(
user,
notificationService.createSelfNotification(
user,
event.getTitle(),
event.getContent(),
"/study/plans?date=" + event.getAchievedDate()
"/study/plans?date=" + event.getAchievedDate(),
NotificationSettingType.SYSTEM
);

log.info("[알림] 일일 목표 달성 알림 전송 완료");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.back.domain.notification.event.studyroom;

import com.back.domain.notification.entity.NotificationSettingType;
import com.back.domain.notification.service.NotificationService;
import com.back.domain.studyroom.entity.Room;
import com.back.domain.studyroom.repository.RoomMemberRepository;
Expand Down Expand Up @@ -45,8 +46,9 @@ public void handleNoticeCreated(StudyRoomNoticeCreatedEvent event) {
room,
actor,
event.getTitle(),
event.getNoticeTitle(), // content에 공지 제목
"/rooms/" + event.getStudyRoomId() + "/notices"
event.getNoticeTitle(),
"/rooms/" + event.getStudyRoomId() + "/notices",
NotificationSettingType.ROOM_NOTICE
);

log.info("[알림] 스터디룸 공지사항 알림 전송 완료");
Expand Down Expand Up @@ -79,7 +81,8 @@ public void handleMemberRoleChanged(MemberRoleChangedEvent event) {
actor,
event.getTitle(),
event.getContent(),
"/rooms/" + event.getStudyRoomId()
"/rooms/" + event.getStudyRoomId(),
NotificationSettingType.ROOM_JOIN
);

log.info("[알림] 권한 변경 알림 전송 완료");
Expand Down Expand Up @@ -108,7 +111,8 @@ public void handleMemberKicked(MemberKickedEvent event) {
actor,
event.getTitle(),
event.getContent(),
"/rooms"
"/rooms",
NotificationSettingType.ROOM_JOIN
);

log.info("[알림] 멤버 추방 알림 전송 완료");
Expand Down Expand Up @@ -137,7 +141,8 @@ public void handleOwnerTransferred(OwnerTransferredEvent event) {
actor,
event.getTitle(),
event.getContent(),
"/rooms/" + event.getStudyRoomId()
"/rooms/" + event.getStudyRoomId(),
NotificationSettingType.ROOM_JOIN
);

log.info("[알림] 방장 위임 알림 전송 완료");
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,9 @@
package com.back.domain.notification.repository;

import com.back.domain.notification.entity.NotificationSetting;
import com.back.domain.notification.entity.NotificationSettingType;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import java.util.List;
import java.util.Optional;

public interface NotificationSettingRepository extends JpaRepository<NotificationSetting, Long> {

// 특정 유저의 모든 알림 설정 조회
List<NotificationSetting> findByUserId(Long userId);

// 특정 유저의 특정 타입 알림 설정 조회
Optional<NotificationSetting> findByUserIdAndType(Long userId, NotificationSettingType type);

// 특정 유저의 특정 타입 알림 설정 존재 여부
boolean existsByUserIdAndType(Long userId, NotificationSettingType type);

// 특정 유저의 활성화된 알림 설정만 조회
List<NotificationSetting> findByUserIdAndEnabledTrue(Long userId);
@Repository
public interface NotificationSettingRepository extends JpaRepository<NotificationSetting, Long>, NotificationSettingRepositoryCustom {
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,22 @@
package com.back.domain.notification.repository;

import com.back.domain.notification.entity.NotificationSetting;
import com.back.domain.notification.entity.NotificationSettingType;

import java.util.List;
import java.util.Optional;

public interface NotificationSettingRepositoryCustom {

// 특정 사용자의 모든 알림 설정 조회
List<NotificationSetting> findAllByUserId(Long userId);

// 특정 사용자의 특정 타입 알림 설정 조회
Optional<NotificationSetting> findByUserIdAndType(Long userId, NotificationSettingType type);

// 특정 사용자의 설정이 존재하는지 확인
boolean existsByUserId(Long userId);

// 특정 사용자의 활성화된 알림 설정만 조회
List<NotificationSetting> findEnabledSettingsByUserId(Long userId);
}
Loading