From b2e5c63368ef3ed58420a190c73597607c17fbea Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Mon, 9 Dec 2024 02:49:38 +0900 Subject: [PATCH 1/5] =?UTF-8?q?feat(NotificationSubType):=20COMMENT=5FADDE?= =?UTF-8?q?D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 댓글, 대댓글 알림 이벤트 서브 타입 --- .../com/somemore/notification/domain/NotificationSubType.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/somemore/notification/domain/NotificationSubType.java b/src/main/java/com/somemore/notification/domain/NotificationSubType.java index 1313069d8..15fa91d9a 100644 --- a/src/main/java/com/somemore/notification/domain/NotificationSubType.java +++ b/src/main/java/com/somemore/notification/domain/NotificationSubType.java @@ -8,7 +8,8 @@ public enum NotificationSubType { NOTE_BLAH_BLAH("쪽지"), VOLUNTEER_REVIEW_REQUEST("봉사 후기 요청"), - VOLUNTEER_APPLY_STATUS_CHANGE("신청 상태 변경") + VOLUNTEER_APPLY_STATUS_CHANGE("신청 상태 변경"), + COMMENT_ADDED("댓글 대댓글 추가") ; private final String description; From 2c874714b5bc10232fa5fabf624161af265d5520 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Mon, 9 Dec 2024 03:01:18 +0900 Subject: [PATCH 2/5] =?UTF-8?q?feat(CommentAddedEvent):=20ServerEvent=20?= =?UTF-8?q?=ED=95=98=EC=9C=84=20=ED=81=B4=EB=9E=98=EC=8A=A4=20CommentAdded?= =?UTF-8?q?Event=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - volunteerId와 communityBoardId를 주요 속성으로 포함 - volunteerId - 대댓글이라면 부모 댓글 작성자 - 댓글이라면 커뮤니티 글 작성자 --- .../community/event/CommentAddedEvent.java | 30 +++++++++++++++++++ 1 file changed, 30 insertions(+) create mode 100644 src/main/java/com/somemore/community/event/CommentAddedEvent.java diff --git a/src/main/java/com/somemore/community/event/CommentAddedEvent.java b/src/main/java/com/somemore/community/event/CommentAddedEvent.java new file mode 100644 index 000000000..79a648d5c --- /dev/null +++ b/src/main/java/com/somemore/community/event/CommentAddedEvent.java @@ -0,0 +1,30 @@ +package com.somemore.community.event; + +import com.fasterxml.jackson.annotation.JsonCreator; +import com.fasterxml.jackson.annotation.JsonProperty; +import com.somemore.global.common.event.ServerEvent; +import com.somemore.global.common.event.ServerEventType; +import com.somemore.notification.domain.NotificationSubType; +import lombok.Getter; +import lombok.experimental.SuperBuilder; + +import java.time.LocalDateTime; +import java.util.UUID; + +@Getter +@SuperBuilder +public class CommentAddedEvent extends ServerEvent { + + private final UUID volunteerId; + private final Long communityBoardId; + + @JsonCreator + public CommentAddedEvent( + @JsonProperty(value = "volunteerId", required = true) UUID volunteerId, + @JsonProperty(value = "communityBoardId", required = true) Long communityBoardId + ) { + super(ServerEventType.NOTIFICATION, NotificationSubType.COMMENT_ADDED, LocalDateTime.now()); + this.volunteerId = volunteerId; + this.communityBoardId = communityBoardId; + } +} From ba23e9075b165095d3a7fc3b54f56bf2e316e4f2 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Mon, 9 Dec 2024 03:03:39 +0900 Subject: [PATCH 3/5] =?UTF-8?q?feat(publishCommentAddedEvent):=20=EB=8C=93?= =?UTF-8?q?=EA=B8=80=20=EC=B6=94=EA=B0=80=20=EC=9D=B4=EB=B2=A4=ED=8A=B8=20?= =?UTF-8?q?=EB=B0=9C=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - volunteerId - 대댓글이라면 부모 댓글 작성자 - 댓글이라면 커뮤니티 글 작성자 --- .../CreateCommunityCommentService.java | 40 +++++++++++++++++++ 1 file changed, 40 insertions(+) diff --git a/src/main/java/com/somemore/community/service/comment/CreateCommunityCommentService.java b/src/main/java/com/somemore/community/service/comment/CreateCommunityCommentService.java index 5d3a34d1a..deb39a2c2 100644 --- a/src/main/java/com/somemore/community/service/comment/CreateCommunityCommentService.java +++ b/src/main/java/com/somemore/community/service/comment/CreateCommunityCommentService.java @@ -2,10 +2,15 @@ import com.somemore.community.domain.CommunityComment; import com.somemore.community.dto.request.CommunityCommentCreateRequestDto; +import com.somemore.community.event.CommentAddedEvent; import com.somemore.community.repository.board.CommunityBoardRepository; import com.somemore.community.repository.comment.CommunityCommentRepository; import com.somemore.community.usecase.comment.CreateCommunityCommentUseCase; +import com.somemore.global.common.event.ServerEventPublisher; +import com.somemore.global.common.event.ServerEventType; import com.somemore.global.exception.BadRequestException; +import com.somemore.notification.domain.NotificationSubType; +import jakarta.persistence.EntityNotFoundException; import lombok.RequiredArgsConstructor; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -22,6 +27,7 @@ public class CreateCommunityCommentService implements CreateCommunityCommentUseC private final CommunityBoardRepository communityBoardRepository; private final CommunityCommentRepository communityCommentRepository; + private final ServerEventPublisher serverEventPublisher; @Override public Long createCommunityComment(CommunityCommentCreateRequestDto requestDto, UUID writerId, Long communityBoardId) { @@ -33,6 +39,7 @@ public Long createCommunityComment(CommunityCommentCreateRequestDto requestDto, validateParentCommentExists(communityComment.getParentCommentId()); } + publishCommentAddedEvent(communityComment); return communityCommentRepository.save(communityComment).getId(); } @@ -47,4 +54,37 @@ private void validateParentCommentExists(Long parentCommentId) { throw new BadRequestException(NOT_EXISTS_COMMUNITY_COMMENT.getMessage()); } } + + private void publishCommentAddedEvent(CommunityComment communityComment) { + Long parentCommentId = communityComment.getParentCommentId(); + + UUID targetVolunteerId = getTargetVolunteerId(communityComment, parentCommentId); + + CommentAddedEvent event = CommentAddedEvent.builder() + .type(ServerEventType.NOTIFICATION) + .subType(NotificationSubType.COMMENT_ADDED) + .volunteerId(targetVolunteerId) + .communityBoardId(communityComment.getCommunityBoardId()) + .build(); + + serverEventPublisher.publish(event); + } + + private UUID getTargetVolunteerId(CommunityComment communityComment, Long parentCommentId) { + UUID targetVolunteerId; + + if (parentCommentId == null) { + targetVolunteerId = communityBoardRepository.findById(communityComment.getCommunityBoardId()) + .orElseThrow(EntityNotFoundException::new) + .getWriterId(); + + return targetVolunteerId; + } + + targetVolunteerId = communityCommentRepository.findById(parentCommentId) + .map(CommunityComment::getWriterId) + .orElse(null); + + return targetVolunteerId; + } } From 74312be38a51ca7ca8b0330b32ab77c6243841f9 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Mon, 9 Dec 2024 03:04:36 +0900 Subject: [PATCH 4/5] =?UTF-8?q?feat(ServerEvent):=20=EC=84=9C=EB=B8=8C?= =?UTF-8?q?=ED=83=80=EC=9E=85=20=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 서버 이벤트 타입과 그 서브 타입은 관련이 있어야 함. --- .../global/common/event/ServerEvent.java | 11 +++++++++++ .../converter/MessageConverter.java | 18 +++++++++++++++++- 2 files changed, 28 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/somemore/global/common/event/ServerEvent.java b/src/main/java/com/somemore/global/common/event/ServerEvent.java index 922b2832d..17636eaba 100644 --- a/src/main/java/com/somemore/global/common/event/ServerEvent.java +++ b/src/main/java/com/somemore/global/common/event/ServerEvent.java @@ -24,8 +24,19 @@ protected ServerEvent( @JsonProperty(value = "subType", required = true) T subType, @JsonProperty(value = "createdAt", required = true) LocalDateTime createdAt ) { + validate(type, subType); this.type = type; this.subType = subType; this.createdAt = (createdAt == null) ? LocalDateTime.now() : createdAt; } + + private void validate(ServerEventType type, T subType) { + if (!type.getSubtype().isInstance(subType)) { + throw new IllegalArgumentException(String.format( + "잘못된 서브타입: %s는 %s와 일치하지 않습니다.", + subType, + type.getSubtype() + )); + } + } } diff --git a/src/main/java/com/somemore/notification/converter/MessageConverter.java b/src/main/java/com/somemore/notification/converter/MessageConverter.java index 60bb54c87..8c8a241bc 100644 --- a/src/main/java/com/somemore/notification/converter/MessageConverter.java +++ b/src/main/java/com/somemore/notification/converter/MessageConverter.java @@ -1,9 +1,9 @@ package com.somemore.notification.converter; import com.fasterxml.jackson.core.JsonProcessingException; -import com.fasterxml.jackson.databind.JsonMappingException; import com.fasterxml.jackson.databind.JsonNode; import com.fasterxml.jackson.databind.ObjectMapper; +import com.somemore.community.event.CommentAddedEvent; import com.somemore.facade.event.VolunteerReviewRequestEvent; import com.somemore.notification.domain.Notification; import com.somemore.notification.domain.NotificationSubType; @@ -29,6 +29,7 @@ public Notification from(String message) { case NOTE_BLAH_BLAH -> throw new UnsupportedOperationException("NOTE 알림 타입 처리 로직 미구현"); case VOLUNTEER_REVIEW_REQUEST -> buildVolunteerReviewRequestNotification(message); case VOLUNTEER_APPLY_STATUS_CHANGE -> buildVolunteerApplyStatusChangeNotification(message); + case COMMENT_ADDED -> buildCommentAddedNotification(message); }; } catch (Exception e) { log.error(e.getMessage()); @@ -58,6 +59,17 @@ private Notification buildVolunteerApplyStatusChangeNotification(String message) .build(); } + private Notification buildCommentAddedNotification(String message) throws JsonProcessingException { + CommentAddedEvent event = objectMapper.readValue(message, CommentAddedEvent.class); + + return Notification.builder() + .receiverId(event.getVolunteerId()) + .title(createCommentAddedNotificationTitle()) + .type(NotificationSubType.COMMENT_ADDED) + .relatedId(event.getCommunityBoardId()) + .build(); + } + private String createVolunteerReviewRequestNotificationTitle() { return "최근 활동하신 활동의 후기를 작성해 주세요!"; } @@ -72,4 +84,8 @@ private String createVolunteerApplyStatusChangeNotificationTitle(ApplyStatus new } }; } + + private String createCommentAddedNotificationTitle() { + return "새로운 댓글이 작성되었습니다."; + } } From 6e3268883878d0c1405e20e248c616ba61163822 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?=EC=A1=B0=EC=9E=AC=EC=A4=91?= <126754298+m-a-king@users.noreply.github.com> Date: Mon, 9 Dec 2024 03:46:18 +0900 Subject: [PATCH 5/5] =?UTF-8?q?feat(NotificationResponseDto):=20=EC=95=8C?= =?UTF-8?q?=EB=A6=BC=20ID=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 읽음 처리할 때 필요. --- .../somemore/notification/dto/NotificationResponseDto.java | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/somemore/notification/dto/NotificationResponseDto.java b/src/main/java/com/somemore/notification/dto/NotificationResponseDto.java index a21f864a6..3a15848a9 100644 --- a/src/main/java/com/somemore/notification/dto/NotificationResponseDto.java +++ b/src/main/java/com/somemore/notification/dto/NotificationResponseDto.java @@ -9,8 +9,10 @@ import java.time.LocalDateTime; @JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class) -@Schema(description = "알림 응답 전송 DTO") +@Schema(description = "알림 응답 DTO") public record NotificationResponseDto( + @Schema(description = "알림 ID", example = "1") + Long id, @Schema(description = "알림 제목", example = "봉사 활동 신청이 승인되었습니다.") String title, @@ -25,6 +27,7 @@ public record NotificationResponseDto( public static NotificationResponseDto from(Notification notification) { return new NotificationResponseDto( + notification.getId(), notification.getTitle(), notification.getType(), notification.getRelatedId(),