-
Notifications
You must be signed in to change notification settings - Fork 0
feat/#71: 학생 알림 조회 기능 구현 #72
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from 2 commits
6a4326c
6f64836
b0e3871
e0ea397
1681fc1
cda0b5c
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.campus.campus.domain.notification.application.dto; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public record CursorResponse<T>( | ||
| List<T> items, | ||
| NextCursor nextCursor, | ||
| boolean hasNext | ||
| ) {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,8 @@ | ||
| package com.campus.campus.domain.notification.application.dto; | ||
|
|
||
| import java.time.LocalDateTime; | ||
|
|
||
| public record NextCursor( | ||
| LocalDateTime createdAt, | ||
| Long id | ||
| ) {} |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.campus.campus.domain.notification.application.dto; | ||
|
|
||
| import java.util.List; | ||
|
|
||
| public record NotificationListResponse( | ||
| List<NotificationResponse> notifications, | ||
| boolean hasNext | ||
| ) { | ||
| } | ||
coderabbitai[bot] marked this conversation as resolved.
Outdated
Show resolved
Hide resolved
|
||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,29 @@ | ||
| package com.campus.campus.domain.notification.application.dto; | ||
|
|
||
| import com.campus.campus.domain.notification.domain.entity.NotificationType; | ||
|
|
||
| import io.swagger.v3.oas.annotations.media.Schema; | ||
|
|
||
| public record NotificationResponse( | ||
| @Schema(description = "알림 ID", example = "1") | ||
| Long id, | ||
|
|
||
| @Schema(description = "알림 타입", example = "COUNCIL_POST_CREATED") | ||
| NotificationType type, | ||
|
|
||
| @Schema(description = "알림 제목", example = "총학생회") | ||
| String title, | ||
|
|
||
| @Schema(description = "알림 내용", example = "새 행사글이 등록되었습니다.") | ||
| String body, | ||
|
|
||
| @Schema(description = "참조 ID (게시글 ID 등)", example = "123") | ||
| Long referenceId, | ||
|
|
||
| @Schema(description = "읽음 여부", example = "false") | ||
| boolean isRead, | ||
|
|
||
| @Schema(description = "생성 시각 (상대 시간)", example = "5분 전") | ||
| String createdAt | ||
|
||
| ) { | ||
| } | ||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,21 @@ | ||
| package com.campus.campus.domain.notification.application.exception; | ||
|
|
||
| import org.springframework.http.HttpStatus; | ||
|
|
||
| import com.campus.campus.global.common.exception.ErrorCodeInterface; | ||
|
|
||
| import lombok.AllArgsConstructor; | ||
| import lombok.Getter; | ||
|
|
||
| @Getter | ||
| @AllArgsConstructor | ||
| public enum ErrorCode implements ErrorCodeInterface { | ||
|
|
||
| NOTIFICATION_NOT_FOUND(2801, HttpStatus.NOT_FOUND, "알림을 찾을 수 없습니다."), | ||
| NOTIFICATION_ACCESS_DENIED(2802, HttpStatus.FORBIDDEN, "해당 알림에 접근할 권한이 없습니다."), | ||
| INVALID_NOTIFICATION_IDS(2803, HttpStatus.BAD_REQUEST, "유효하지 않은 알림 ID 목록입니다."); | ||
|
|
||
| private final int code; | ||
| private final HttpStatus status; | ||
| private final String message; | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.campus.campus.domain.notification.application.exception; | ||
|
|
||
| import com.campus.campus.global.common.exception.ApplicationException; | ||
|
|
||
| public class NotificationAccessDeniedException extends ApplicationException { | ||
| public NotificationAccessDeniedException() { | ||
| super(ErrorCode.NOTIFICATION_ACCESS_DENIED); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,9 @@ | ||
| package com.campus.campus.domain.notification.application.exception; | ||
|
|
||
| import com.campus.campus.global.common.exception.ApplicationException; | ||
|
|
||
| public class NotificationNotFoundException extends ApplicationException { | ||
| public NotificationNotFoundException() { | ||
| super(ErrorCode.NOTIFICATION_NOT_FOUND); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,41 @@ | ||
| package com.campus.campus.domain.notification.application.mapper; | ||
|
|
||
| import org.springframework.stereotype.Component; | ||
|
|
||
| import com.campus.campus.domain.notification.application.dto.NotificationResponse; | ||
| import com.campus.campus.domain.notification.domain.entity.Notification; | ||
| import com.campus.campus.domain.notification.domain.entity.NotificationType; | ||
| import com.campus.campus.domain.notification.util.TimeFormatter; | ||
| import com.campus.campus.domain.user.domain.entity.User; | ||
|
|
||
| import lombok.RequiredArgsConstructor; | ||
|
|
||
| @Component | ||
| @RequiredArgsConstructor | ||
| public class NotificationMapper { | ||
|
|
||
| private final TimeFormatter timeFormatter; | ||
|
|
||
| public NotificationResponse toResponse(Notification notification) { | ||
| return new NotificationResponse( | ||
| notification.getId(), | ||
| notification.getType(), | ||
| notification.getTitle(), | ||
| notification.getBody(), | ||
| notification.getReferenceId(), | ||
| notification.isRead(), | ||
| timeFormatter.formatRelativeTime(notification.getCreatedAt()) | ||
| ); | ||
| } | ||
|
|
||
| public Notification createNotification(User user, NotificationType type, | ||
| String title, String body, Long referenceId) { | ||
| return Notification.builder() | ||
| .user(user) | ||
| .type(type) | ||
| .title(title) | ||
| .body(body) | ||
| .referenceId(referenceId) | ||
| .build(); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,122 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package com.campus.campus.domain.notification.application.service; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.time.LocalDateTime; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import java.util.List; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.data.domain.PageRequest; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.data.domain.Pageable; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.stereotype.Service; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.transaction.annotation.Propagation; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.transaction.annotation.Transactional; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.campus.campus.domain.councilpost.application.dto.request.CouncilPostCreatedEvent; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.campus.campus.domain.notification.application.dto.CursorResponse; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.campus.campus.domain.notification.application.dto.NextCursor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.campus.campus.domain.notification.application.dto.NotificationResponse; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.campus.campus.domain.notification.application.exception.NotificationAccessDeniedException; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.campus.campus.domain.notification.application.exception.NotificationNotFoundException; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.campus.campus.domain.notification.application.mapper.NotificationMapper; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.campus.campus.domain.notification.domain.entity.Notification; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.campus.campus.domain.notification.domain.entity.NotificationType; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.campus.campus.domain.notification.domain.repository.NotificationRepository; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.campus.campus.domain.user.application.exception.UserNotFoundException; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.campus.campus.domain.user.domain.entity.User; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.campus.campus.domain.user.domain.repository.UserRepository; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.RequiredArgsConstructor; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import lombok.extern.slf4j.Slf4j; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Slf4j | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Service | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @RequiredArgsConstructor | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public class NotificationService { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final NotificationRepository notificationRepository; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final NotificationMapper notificationMapper; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private final UserRepository userRepository; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public CursorResponse<NotificationResponse> getNotificationsByCursor( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Long userId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| LocalDateTime cursorCreatedAt, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Long cursorId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int limit | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| User user = userRepository.findById(userId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .orElseThrow(UserNotFoundException::new); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| int pageSize = Math.min(Math.max(limit, 1), 50); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Pageable pageable = PageRequest.of(0, pageSize); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| boolean isFirst = (cursorCreatedAt == null || cursorId == null); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<Notification> list = isFirst | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ? notificationRepository.findByUserOrderByCreatedAtDescIdDesc(user, pageable) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| : notificationRepository.findNextByCursor(user, cursorCreatedAt, cursorId, pageable); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<NotificationResponse> items = list.stream() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map(notificationMapper::toResponse) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .toList(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| boolean hasNext = list.size() == pageSize; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| NextCursor nextCursor = null; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!list.isEmpty()) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Notification last = list.get(list.size() - 1); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| nextCursor = new NextCursor(last.getCreatedAt(), last.getId()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return new CursorResponse<>(items, nextCursor, hasNext); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Transactional | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void markAsRead(Long userId, Long notificationId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| User user = userRepository.findById(userId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .orElseThrow(UserNotFoundException::new); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Notification notification = notificationRepository.findById(notificationId) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .orElseThrow(NotificationNotFoundException::new); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| if (!notification.getUser().getId().equals(user.getId())) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| throw new NotificationAccessDeniedException(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| notification.markAsRead(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Transactional(propagation = Propagation.REQUIRES_NEW) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public void saveCouncilPostCreated(CouncilPostCreatedEvent event, String title, String body) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<User> targetUsers = findUsersByTopic(event.topic()); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| List<Notification> notifications = targetUsers.stream() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .map(user -> notificationMapper.createNotification( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| user, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| NotificationType.COUNCIL_POST_CREATED, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| title, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| body, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| event.postId() | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| )) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| .toList(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| notificationRepository.saveAll(notifications); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Transactional(readOnly = true) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public boolean hasUnread(Long userId) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return notificationRepository.existsByUser_IdAndIsReadFalse(userId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| private List<User> findUsersByTopic(String topic) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String[] parts = topic.split("_"); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| String scope = parts[0]; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Long scopeId = Long.valueOf(parts[1]); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| return switch (scope) { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "major" -> userRepository.findAllByMajor_MajorIdAndDeletedAtIsNull(scopeId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "college" -> userRepository.findAllByCollege_CollegeIdAndDeletedAtIsNull(scopeId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| case "school" -> userRepository.findAllBySchool_SchoolIdAndDeletedAtIsNull(scopeId); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| default -> List.of(); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+109
to
+121
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 토픽 파싱 시 예외 처리가 필요합니다.
🔒️ 방어적 코드 추가 권장 private List<User> findUsersByTopic(String topic) {
+ if (topic == null || !topic.contains("_")) {
+ log.warn("Invalid topic format: {}", topic);
+ return List.of();
+ }
String[] parts = topic.split("_");
+ if (parts.length < 2) {
+ log.warn("Invalid topic format: {}", topic);
+ return List.of();
+ }
+
String scope = parts[0];
- Long scopeId = Long.valueOf(parts[1]);
+ Long scopeId;
+ try {
+ scopeId = Long.valueOf(parts[1]);
+ } catch (NumberFormatException e) {
+ log.warn("Invalid scopeId in topic: {}", topic);
+ return List.of();
+ }
return switch (scope) {
case "major" -> userRepository.findAllByMajor_MajorIdAndDeletedAtIsNull(scopeId);
case "college" -> userRepository.findAllByCollege_CollegeIdAndDeletedAtIsNull(scopeId);
case "school" -> userRepository.findAllBySchool_SchoolIdAndDeletedAtIsNull(scopeId);
default -> List.of();
};
}📝 Committable suggestion
Suggested change
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
Uh oh!
There was an error while loading. Please reload this page.