diff --git a/src/main/java/co/kr/jurumarble/config/WebConfig.java b/src/main/java/co/kr/jurumarble/config/WebConfig.java index 61068f8..09f7cb2 100644 --- a/src/main/java/co/kr/jurumarble/config/WebConfig.java +++ b/src/main/java/co/kr/jurumarble/config/WebConfig.java @@ -44,6 +44,7 @@ public void addInterceptors(InterceptorRegistry registry) { .addPathPatterns("/api/{commentType}/{typeId}/comments/{commentId}/restaurant") .addPathPatterns("/api/{commentType}/{typeId}/comments/{commentId}/restaurant/{contentId}") .addPathPatterns("/api/notifications") + .addPathPatterns("/api/notifications/v2") .addPathPatterns("/api/notifications/subscribe") .addPathPatterns("/api/reports/votes") .addPathPatterns("/api/reports/comments") diff --git a/src/main/java/co/kr/jurumarble/notification/controller/NotificationController.java b/src/main/java/co/kr/jurumarble/notification/controller/NotificationController.java index a5c5b61..08bccc0 100644 --- a/src/main/java/co/kr/jurumarble/notification/controller/NotificationController.java +++ b/src/main/java/co/kr/jurumarble/notification/controller/NotificationController.java @@ -2,6 +2,7 @@ import co.kr.jurumarble.notification.dto.CreateNotificationRequest; import co.kr.jurumarble.notification.dto.NotificationDto; +import co.kr.jurumarble.notification.dto.NotificationDtoV1; import co.kr.jurumarble.notification.service.NotificationService; import io.swagger.v3.oas.annotations.Operation; import io.swagger.v3.oas.annotations.tags.Tag; @@ -45,11 +46,19 @@ public ResponseEntity sendToAllUsers(@RequestBody @Valid CreateNotificationRe @Operation(summary = "알림 조회", description = "사용자의 모든 알림 메시지를 조회합니다.") @GetMapping("") + public ResponseEntity> getNotificationsV1(@RequestAttribute Long userId) { + List notifications = notificationService.getNotificationDtosV1(userId); + return new ResponseEntity(notifications, HttpStatus.OK); + } + + @Operation(summary = "알림 조회 v2", description = "사용자의 모든 알림 메시지를 조회합니다.") + @GetMapping("/v2") public ResponseEntity> getNotifications(@RequestAttribute Long userId) { List notifications = notificationService.getNotificationDtos(userId); return new ResponseEntity(notifications, HttpStatus.OK); } + @Operation(summary = "알림 읽음 처리", description = "사용자가 클릭한 알림을 읽음 처리 합니다.") @PostMapping("/{notificationId}/read") public ResponseEntity setNotificationAsRead(@PathVariable Long notificationId) { diff --git a/src/main/java/co/kr/jurumarble/notification/domain/Notification.java b/src/main/java/co/kr/jurumarble/notification/domain/Notification.java index 13282be..9785e5e 100644 --- a/src/main/java/co/kr/jurumarble/notification/domain/Notification.java +++ b/src/main/java/co/kr/jurumarble/notification/domain/Notification.java @@ -20,14 +20,11 @@ public class Notification extends BaseTimeEntity { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) private Long id; - + private String title; private String content; - private String url; - @Column(nullable = false, name = "is_read") private Boolean isRead; - @Enumerated(EnumType.STRING) @Column(nullable = false, name = "notification_type") private NotificationType notificationType; @@ -37,9 +34,10 @@ public class Notification extends BaseTimeEntity { private User receiver; @Builder - public Notification(User receiver, NotificationType notificationType, String content, String url, Boolean isRead) { + public Notification(User receiver, NotificationType notificationType, String title, String content, String url, Boolean isRead) { this.receiver = receiver; this.notificationType = notificationType; + this.title = title; this.content = content; this.url = url; this.isRead = isRead; diff --git a/src/main/java/co/kr/jurumarble/notification/dto/CreateNotificationRequest.java b/src/main/java/co/kr/jurumarble/notification/dto/CreateNotificationRequest.java index de605dd..f49c7f6 100644 --- a/src/main/java/co/kr/jurumarble/notification/dto/CreateNotificationRequest.java +++ b/src/main/java/co/kr/jurumarble/notification/dto/CreateNotificationRequest.java @@ -11,7 +11,10 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class CreateNotificationRequest { - @Schema(description = "알림 메시지", maxLength = 500) + @Schema(description = "알림 제목") + private String title; + + @Schema(description = "알림 내용") private String message; @Schema(description = "Related Url") @@ -19,13 +22,15 @@ public class CreateNotificationRequest { @Builder - public CreateNotificationRequest(String message, String relatedUrl) { + public CreateNotificationRequest(String title, String message, String relatedUrl) { + this.title = title; this.message = message; this.relatedUrl = relatedUrl; } public CreateNotificationServiceRequest toServiceRequest() { return CreateNotificationServiceRequest.builder() + .title(title) .message(message) .relatedUrl(relatedUrl) .build(); diff --git a/src/main/java/co/kr/jurumarble/notification/dto/NotificationDto.java b/src/main/java/co/kr/jurumarble/notification/dto/NotificationDto.java index ebf4a83..d5ec314 100644 --- a/src/main/java/co/kr/jurumarble/notification/dto/NotificationDto.java +++ b/src/main/java/co/kr/jurumarble/notification/dto/NotificationDto.java @@ -1,7 +1,10 @@ package co.kr.jurumarble.notification.dto; import co.kr.jurumarble.notification.domain.Notification; -import lombok.*; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; import java.time.LocalDateTime; @@ -9,20 +12,17 @@ @NoArgsConstructor(access = AccessLevel.PROTECTED) public class NotificationDto { Long id; - + String title; String url; - String content; - Notification.NotificationType type; - Boolean isRead; - LocalDateTime createdAt; @Builder - public NotificationDto(Long id, String url, String content, Notification.NotificationType type, Boolean isRead, LocalDateTime createdAt) { + public NotificationDto(Long id, String title, String url, String content, Notification.NotificationType type, Boolean isRead, LocalDateTime createdAt) { this.id = id; + this.title = title; this.url = url; this.content = content; this.type = type; @@ -34,6 +34,7 @@ public NotificationDto(Long id, String url, String content, Notification.Notific public static NotificationDto from(Notification notification) { return NotificationDto.builder() .id(notification.getId()) + .title(notification.getTitle()) .url(notification.getUrl()) .content(notification.getContent()) .type(notification.getNotificationType()) diff --git a/src/main/java/co/kr/jurumarble/notification/dto/NotificationDtoV1.java b/src/main/java/co/kr/jurumarble/notification/dto/NotificationDtoV1.java new file mode 100644 index 0000000..7eea468 --- /dev/null +++ b/src/main/java/co/kr/jurumarble/notification/dto/NotificationDtoV1.java @@ -0,0 +1,43 @@ +package co.kr.jurumarble.notification.dto; + +import co.kr.jurumarble.notification.domain.Notification; +import lombok.*; + +import java.time.LocalDateTime; + +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class NotificationDtoV1 { + Long id; + + String url; + + String content; + + Notification.NotificationType type; + + Boolean isRead; + + LocalDateTime createdAt; + + @Builder + public NotificationDtoV1(Long id, String url, String content, Notification.NotificationType type, Boolean isRead, LocalDateTime createdAt) { + this.id = id; + this.url = url; + this.content = content; + this.type = type; + this.isRead = isRead; + this.createdAt = createdAt; + } + + + public static NotificationDtoV1 from(Notification notification) { + return NotificationDtoV1.builder() + .id(notification.getId()) + .url(notification.getUrl()) + .content(notification.getContent()) + .type(notification.getNotificationType()) + .isRead(notification.getIsRead()) + .createdAt(notification.getCreatedDate()).build(); + } +} diff --git a/src/main/java/co/kr/jurumarble/notification/service/CreateNotificationServiceRequest.java b/src/main/java/co/kr/jurumarble/notification/service/CreateNotificationServiceRequest.java index 054563c..3c31cc6 100644 --- a/src/main/java/co/kr/jurumarble/notification/service/CreateNotificationServiceRequest.java +++ b/src/main/java/co/kr/jurumarble/notification/service/CreateNotificationServiceRequest.java @@ -5,13 +5,15 @@ @Getter public class CreateNotificationServiceRequest { + private String title; private String message; private String relatedUrl; @Builder - public CreateNotificationServiceRequest(String message, String relatedUrl) { + public CreateNotificationServiceRequest(String title, String message, String relatedUrl) { + this.title = title; this.message = message; this.relatedUrl = relatedUrl; } diff --git a/src/main/java/co/kr/jurumarble/notification/service/NotificationSender.java b/src/main/java/co/kr/jurumarble/notification/service/NotificationSender.java index e8ea813..66d99b1 100644 --- a/src/main/java/co/kr/jurumarble/notification/service/NotificationSender.java +++ b/src/main/java/co/kr/jurumarble/notification/service/NotificationSender.java @@ -46,34 +46,33 @@ public void handleDoVote(DoVoteEvent event) { public void sendNotificationForNewComments(Long voteId) { Vote vote = voteRepository.findById(voteId).orElseThrow(VoteNotFoundException::new); User receiver = userRepository.findById(vote.getPostedUserId()).orElseThrow(UserNotFoundException::new); - String content = "[" + vote.getTitle() + "] " + "투표에 댓글이 달렸습니다."; + String title = vote.getTitle(); + String content = "투표에 댓글이 달렸습니다."; String url = String.valueOf(voteId); - notificationService.send(receiver, Notification.NotificationType.COMMENT, content, url); + notificationService.send(receiver, Notification.NotificationType.COMMENT, title, content, url); log.info("Thread: {}, Notification sent to user: {}, type: {}, content: {}, url: {}", - Thread.currentThread().getName(), - receiver.getId(), - Notification.NotificationType.COMMENT, - content, url); + Thread.currentThread().getName(), receiver.getId(), Notification.NotificationType.COMMENT, title, content, url); } public void sendNotificationsForVoters(Long voteId) { Vote vote = voteRepository.findById(voteId).orElseThrow(VoteNotFoundException::new); Long count = voteResultRepository.countByVoteId(voteId); if (count % 10 == 0 && count != 0) { - String content = "[" + vote.getTitle() + "] " + "투표에 " + count + "명 이상이 참여했어요!"; + String title = vote.getTitle(); + String content = "투표에 " + count + "명 이상이 참여했어요!"; String url = String.valueOf(voteId); List voteResultList = voteResultRepository.findByVoteId(voteId); - sendNotificationsToVoters(content, url, voteResultList); + sendNotificationsToVoters(title, content, url, voteResultList); } } - private void sendNotificationsToVoters(String content, String url, List voteResultList) { + private void sendNotificationsToVoters(String title, String content, String url, List voteResultList) { for (VoteResult result : voteResultList) { CompletableFuture.runAsync(() -> { User receiver = userRepository.findById(result.getVotedUserId()).orElseThrow(UserNotFoundException::new); - notificationService.send(receiver, Notification.NotificationType.VOTE, content, url); + notificationService.send(receiver, Notification.NotificationType.VOTE, title, content, url); log.info("Thread: {}, Notification sent to user: {}, type: {}, content: {}, url: {}", - Thread.currentThread().getName(), receiver.getId(), Notification.NotificationType.COMMENT, content, url); + Thread.currentThread().getName(), receiver.getId(), Notification.NotificationType.COMMENT, title, content, url); }); } } diff --git a/src/main/java/co/kr/jurumarble/notification/service/NotificationService.java b/src/main/java/co/kr/jurumarble/notification/service/NotificationService.java index 9a51c27..7074b67 100644 --- a/src/main/java/co/kr/jurumarble/notification/service/NotificationService.java +++ b/src/main/java/co/kr/jurumarble/notification/service/NotificationService.java @@ -5,6 +5,7 @@ import co.kr.jurumarble.exception.user.UserNotFoundException; import co.kr.jurumarble.notification.domain.Notification; import co.kr.jurumarble.notification.dto.NotificationDto; +import co.kr.jurumarble.notification.dto.NotificationDtoV1; import co.kr.jurumarble.notification.repository.EmitterRepository; import co.kr.jurumarble.notification.repository.NotificationRepository; import co.kr.jurumarble.user.domain.User; @@ -46,15 +47,15 @@ public SseEmitter subscribe(Long userId, String lastEventId) { } @Transactional - public void send(User receiver, Notification.NotificationType notificationType, String content, String url) { - Notification notification = notificationRepository.save(createNotification(receiver, notificationType, content, url)); + public void send(User receiver, Notification.NotificationType notificationType, String title, String content, String url) { + Notification notification = notificationRepository.save(createNotification(receiver, notificationType, title, content, url)); String receiverId = String.valueOf(receiver.getId()); String eventId = receiverId + "_" + System.currentTimeMillis(); Map emitters = emitterRepository.findAllEmitterStartWithByUserId(receiverId); emitters.forEach( (key, emitter) -> { emitterRepository.saveEventCache(key, notification); - sendNotification(emitter, eventId, key, NotificationDto.from(notification)); + sendNotification(emitter, eventId, key, NotificationDtoV1.from(notification)); } ); } @@ -88,10 +89,11 @@ private void sendLostData(String lastEventId, Long userId, String emitterId, Sse .forEach(entry -> sendNotification(emitter, entry.getKey(), emitterId, entry.getValue())); } - private Notification createNotification(User receiver, Notification.NotificationType notificationType, String content, String url) { + private Notification createNotification(User receiver, Notification.NotificationType notificationType, String title, String content, String url) { return Notification.builder() .receiver(receiver) .notificationType(notificationType) + .title(title) .content(content) .url(url) .isRead(false) @@ -102,26 +104,34 @@ public void sendNotificationToUser(Long receiverId, Long adminId, CreateNotifica validateAdminId(adminId); User receiver = userRepository.findById(receiverId).orElseThrow(UserNotFoundException::new); Notification.NotificationType type = Notification.NotificationType.ADMIN_NOTIFY; + String title = request.getTitle(); String message = request.getMessage(); String relatedUrl = request.getRelatedUrl(); - send(receiver, type, message, relatedUrl); + send(receiver, type, title, message, relatedUrl); } public void sendNotificationToAllUsers(Long adminId, CreateNotificationServiceRequest request) { validateAdminId(adminId); List allUsers = userRepository.findAll(); Notification.NotificationType type = Notification.NotificationType.ADMIN_NOTIFY; + String title = request.getTitle(); String message = request.getMessage(); String relatedUrl = request.getRelatedUrl(); allUsers.stream() - .forEach(user -> CompletableFuture.runAsync(() -> sendAsync(user, type, message, relatedUrl))); + .forEach(user -> CompletableFuture.runAsync(() -> sendAsync(user, type, title, message, relatedUrl))); } - private void sendAsync(User receiver, Notification.NotificationType notificationType, String content, String relatedUrl) { - send(receiver, notificationType, content, relatedUrl); + private void sendAsync(User receiver, Notification.NotificationType notificationType, String title, String content, String relatedUrl) { + send(receiver, notificationType, title, content, relatedUrl); log.info("Thread: {}, Notification sent to user: {}, type: {}, content: {}, url: {}", - Thread.currentThread().getName(), receiver.getId(), Notification.NotificationType.COMMENT, content, relatedUrl); + Thread.currentThread().getName(), receiver.getId(), Notification.NotificationType.COMMENT, title, content, relatedUrl); + } + + public List getNotificationDtosV1(Long userId) { + return getNotificationsByUserId(userId).stream() + .map(NotificationDtoV1::from) + .collect(Collectors.toList()); } public List getNotificationDtos(Long userId) { diff --git a/src/main/resources/schema.sql b/src/main/resources/schema.sql index 4c00070..616078e 100644 --- a/src/main/resources/schema.sql +++ b/src/main/resources/schema.sql @@ -160,6 +160,7 @@ CREATE TABLE notification ( id BIGINT NOT NULL AUTO_INCREMENT, user_id BIGINT NOT NULL, + title VARCHAR(60) DEFAULT NULL, content VARCHAR(255) DEFAULT NULL, url VARCHAR(500) DEFAULT NULL, is_read BOOLEAN NOT NULL,