Skip to content

Commit b491e37

Browse files
authored
Feature/204 댓글 알림 (#214)
* feat(NotificationSubType): COMMENT_ADDED 추가 - 댓글, 대댓글 알림 이벤트 서브 타입 * feat(CommentAddedEvent): ServerEvent 하위 클래스 CommentAddedEvent 추가 - volunteerId와 communityBoardId를 주요 속성으로 포함 - volunteerId - 대댓글이라면 부모 댓글 작성자 - 댓글이라면 커뮤니티 글 작성자 * feat(publishCommentAddedEvent): 댓글 추가 이벤트 발행 - volunteerId - 대댓글이라면 부모 댓글 작성자 - 댓글이라면 커뮤니티 글 작성자 * feat(ServerEvent): 서브타입 검증 추가 - 서버 이벤트 타입과 그 서브 타입은 관련이 있어야 함. * feat(NotificationResponseDto): 알림 ID 추가 - 읽음 처리할 때 필요.
1 parent 7453e35 commit b491e37

File tree

6 files changed

+104
-3
lines changed

6 files changed

+104
-3
lines changed
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.somemore.community.event;
2+
3+
import com.fasterxml.jackson.annotation.JsonCreator;
4+
import com.fasterxml.jackson.annotation.JsonProperty;
5+
import com.somemore.global.common.event.ServerEvent;
6+
import com.somemore.global.common.event.ServerEventType;
7+
import com.somemore.notification.domain.NotificationSubType;
8+
import lombok.Getter;
9+
import lombok.experimental.SuperBuilder;
10+
11+
import java.time.LocalDateTime;
12+
import java.util.UUID;
13+
14+
@Getter
15+
@SuperBuilder
16+
public class CommentAddedEvent extends ServerEvent<NotificationSubType> {
17+
18+
private final UUID volunteerId;
19+
private final Long communityBoardId;
20+
21+
@JsonCreator
22+
public CommentAddedEvent(
23+
@JsonProperty(value = "volunteerId", required = true) UUID volunteerId,
24+
@JsonProperty(value = "communityBoardId", required = true) Long communityBoardId
25+
) {
26+
super(ServerEventType.NOTIFICATION, NotificationSubType.COMMENT_ADDED, LocalDateTime.now());
27+
this.volunteerId = volunteerId;
28+
this.communityBoardId = communityBoardId;
29+
}
30+
}

src/main/java/com/somemore/community/service/comment/CreateCommunityCommentService.java

Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,10 +2,15 @@
22

33
import com.somemore.community.domain.CommunityComment;
44
import com.somemore.community.dto.request.CommunityCommentCreateRequestDto;
5+
import com.somemore.community.event.CommentAddedEvent;
56
import com.somemore.community.repository.board.CommunityBoardRepository;
67
import com.somemore.community.repository.comment.CommunityCommentRepository;
78
import com.somemore.community.usecase.comment.CreateCommunityCommentUseCase;
9+
import com.somemore.global.common.event.ServerEventPublisher;
10+
import com.somemore.global.common.event.ServerEventType;
811
import com.somemore.global.exception.BadRequestException;
12+
import com.somemore.notification.domain.NotificationSubType;
13+
import jakarta.persistence.EntityNotFoundException;
914
import lombok.RequiredArgsConstructor;
1015
import org.springframework.stereotype.Service;
1116
import org.springframework.transaction.annotation.Transactional;
@@ -22,6 +27,7 @@ public class CreateCommunityCommentService implements CreateCommunityCommentUseC
2227

2328
private final CommunityBoardRepository communityBoardRepository;
2429
private final CommunityCommentRepository communityCommentRepository;
30+
private final ServerEventPublisher serverEventPublisher;
2531

2632
@Override
2733
public Long createCommunityComment(CommunityCommentCreateRequestDto requestDto, UUID writerId, Long communityBoardId) {
@@ -33,6 +39,7 @@ public Long createCommunityComment(CommunityCommentCreateRequestDto requestDto,
3339
validateParentCommentExists(communityComment.getParentCommentId());
3440
}
3541

42+
publishCommentAddedEvent(communityComment);
3643
return communityCommentRepository.save(communityComment).getId();
3744
}
3845

@@ -47,4 +54,37 @@ private void validateParentCommentExists(Long parentCommentId) {
4754
throw new BadRequestException(NOT_EXISTS_COMMUNITY_COMMENT.getMessage());
4855
}
4956
}
57+
58+
private void publishCommentAddedEvent(CommunityComment communityComment) {
59+
Long parentCommentId = communityComment.getParentCommentId();
60+
61+
UUID targetVolunteerId = getTargetVolunteerId(communityComment, parentCommentId);
62+
63+
CommentAddedEvent event = CommentAddedEvent.builder()
64+
.type(ServerEventType.NOTIFICATION)
65+
.subType(NotificationSubType.COMMENT_ADDED)
66+
.volunteerId(targetVolunteerId)
67+
.communityBoardId(communityComment.getCommunityBoardId())
68+
.build();
69+
70+
serverEventPublisher.publish(event);
71+
}
72+
73+
private UUID getTargetVolunteerId(CommunityComment communityComment, Long parentCommentId) {
74+
UUID targetVolunteerId;
75+
76+
if (parentCommentId == null) {
77+
targetVolunteerId = communityBoardRepository.findById(communityComment.getCommunityBoardId())
78+
.orElseThrow(EntityNotFoundException::new)
79+
.getWriterId();
80+
81+
return targetVolunteerId;
82+
}
83+
84+
targetVolunteerId = communityCommentRepository.findById(parentCommentId)
85+
.map(CommunityComment::getWriterId)
86+
.orElse(null);
87+
88+
return targetVolunteerId;
89+
}
5090
}

src/main/java/com/somemore/global/common/event/ServerEvent.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -24,8 +24,19 @@ protected ServerEvent(
2424
@JsonProperty(value = "subType", required = true) T subType,
2525
@JsonProperty(value = "createdAt", required = true) LocalDateTime createdAt
2626
) {
27+
validate(type, subType);
2728
this.type = type;
2829
this.subType = subType;
2930
this.createdAt = (createdAt == null) ? LocalDateTime.now() : createdAt;
3031
}
32+
33+
private void validate(ServerEventType type, T subType) {
34+
if (!type.getSubtype().isInstance(subType)) {
35+
throw new IllegalArgumentException(String.format(
36+
"잘못된 서브타입: %s는 %s와 일치하지 않습니다.",
37+
subType,
38+
type.getSubtype()
39+
));
40+
}
41+
}
3142
}

src/main/java/com/somemore/notification/converter/MessageConverter.java

Lines changed: 17 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,9 @@
11
package com.somemore.notification.converter;
22

33
import com.fasterxml.jackson.core.JsonProcessingException;
4-
import com.fasterxml.jackson.databind.JsonMappingException;
54
import com.fasterxml.jackson.databind.JsonNode;
65
import com.fasterxml.jackson.databind.ObjectMapper;
6+
import com.somemore.community.event.CommentAddedEvent;
77
import com.somemore.facade.event.VolunteerReviewRequestEvent;
88
import com.somemore.notification.domain.Notification;
99
import com.somemore.notification.domain.NotificationSubType;
@@ -29,6 +29,7 @@ public Notification from(String message) {
2929
case NOTE_BLAH_BLAH -> throw new UnsupportedOperationException("NOTE 알림 타입 처리 로직 미구현");
3030
case VOLUNTEER_REVIEW_REQUEST -> buildVolunteerReviewRequestNotification(message);
3131
case VOLUNTEER_APPLY_STATUS_CHANGE -> buildVolunteerApplyStatusChangeNotification(message);
32+
case COMMENT_ADDED -> buildCommentAddedNotification(message);
3233
};
3334
} catch (Exception e) {
3435
log.error(e.getMessage());
@@ -58,6 +59,17 @@ private Notification buildVolunteerApplyStatusChangeNotification(String message)
5859
.build();
5960
}
6061

62+
private Notification buildCommentAddedNotification(String message) throws JsonProcessingException {
63+
CommentAddedEvent event = objectMapper.readValue(message, CommentAddedEvent.class);
64+
65+
return Notification.builder()
66+
.receiverId(event.getVolunteerId())
67+
.title(createCommentAddedNotificationTitle())
68+
.type(NotificationSubType.COMMENT_ADDED)
69+
.relatedId(event.getCommunityBoardId())
70+
.build();
71+
}
72+
6173
private String createVolunteerReviewRequestNotificationTitle() {
6274
return "최근 활동하신 활동의 후기를 작성해 주세요!";
6375
}
@@ -72,4 +84,8 @@ private String createVolunteerApplyStatusChangeNotificationTitle(ApplyStatus new
7284
}
7385
};
7486
}
87+
88+
private String createCommentAddedNotificationTitle() {
89+
return "새로운 댓글이 작성되었습니다.";
90+
}
7591
}

src/main/java/com/somemore/notification/domain/NotificationSubType.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -8,7 +8,8 @@
88
public enum NotificationSubType {
99
NOTE_BLAH_BLAH("쪽지"),
1010
VOLUNTEER_REVIEW_REQUEST("봉사 후기 요청"),
11-
VOLUNTEER_APPLY_STATUS_CHANGE("신청 상태 변경")
11+
VOLUNTEER_APPLY_STATUS_CHANGE("신청 상태 변경"),
12+
COMMENT_ADDED("댓글 대댓글 추가")
1213
;
1314

1415
private final String description;

src/main/java/com/somemore/notification/dto/NotificationResponseDto.java

Lines changed: 4 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,8 +9,10 @@
99
import java.time.LocalDateTime;
1010

1111
@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
12-
@Schema(description = "알림 응답 전송 DTO")
12+
@Schema(description = "알림 응답 DTO")
1313
public record NotificationResponseDto(
14+
@Schema(description = "알림 ID", example = "1")
15+
Long id,
1416
@Schema(description = "알림 제목", example = "봉사 활동 신청이 승인되었습니다.")
1517
String title,
1618

@@ -25,6 +27,7 @@ public record NotificationResponseDto(
2527

2628
public static NotificationResponseDto from(Notification notification) {
2729
return new NotificationResponseDto(
30+
notification.getId(),
2831
notification.getTitle(),
2932
notification.getType(),
3033
notification.getRelatedId(),

0 commit comments

Comments
 (0)