From bab17906290d03670e87ac07113d941585141337 Mon Sep 17 00:00:00 2001 From: Chanhae Lee Date: Sun, 24 May 2026 20:26:47 +0900 Subject: [PATCH] =?UTF-8?q?fix(notify):=20=EB=8C=80=EB=8C=93=EA=B8=80=20?= =?UTF-8?q?=EC=95=8C=EB=A6=BC=20=EB=B9=84=EB=B0=80=20=EC=B2=98=EB=A6=AC=20?= =?UTF-8?q?=EB=B0=8F=20=EC=B0=A8=EB=8B=A8=20=EA=B4=80=EA=B3=84=20=EC=88=98?= =?UTF-8?q?=EC=8B=A0=EC=9E=90=20=EC=98=A4=EB=A5=98=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 비밀 대댓글 내용이 열람 권한 없는 참여자에게 알림으로 노출되는 문제를 해결하기 위해 SubCommentCreatedEvent에 isSecret, parentSecret 필드 추가 후 CommentNotificationEventListener에서 부모 댓글 작성자 알림에 차단 관계 체크 추가, isSecret && !parentSecret이면 skip, 리포트 당사자가 참여자인 경우는 별도 발송 --- .../application/CommentCommandService.java | 4 +- .../event/SubCommentCreatedEvent.java | 2 + .../CommentNotificationEventListener.java | 66 ++++++++++++++----- 3 files changed, 56 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/devkor/ifive/nadab/domain/comment/application/CommentCommandService.java b/src/main/java/com/devkor/ifive/nadab/domain/comment/application/CommentCommandService.java index 179d5ea..07be458 100644 --- a/src/main/java/com/devkor/ifive/nadab/domain/comment/application/CommentCommandService.java +++ b/src/main/java/com/devkor/ifive/nadab/domain/comment/application/CommentCommandService.java @@ -87,7 +87,9 @@ public Long createSubComment(Long parentCommentId, Long authorId, String content parentCommentId, parentComment.getAuthor().getId(), reportOwnerId, - content + content, + finalIsSecret, + parentComment.isSecret() )); return subComment.getId(); diff --git a/src/main/java/com/devkor/ifive/nadab/domain/comment/application/event/SubCommentCreatedEvent.java b/src/main/java/com/devkor/ifive/nadab/domain/comment/application/event/SubCommentCreatedEvent.java index 2443ec8..e657a82 100644 --- a/src/main/java/com/devkor/ifive/nadab/domain/comment/application/event/SubCommentCreatedEvent.java +++ b/src/main/java/com/devkor/ifive/nadab/domain/comment/application/event/SubCommentCreatedEvent.java @@ -14,4 +14,6 @@ public class SubCommentCreatedEvent { private final Long parentCommentAuthorId; private final Long reportOwnerId; private final String content; + private final boolean secret; + private final boolean parentSecret; } \ No newline at end of file diff --git a/src/main/java/com/devkor/ifive/nadab/domain/notification/application/event/social/CommentNotificationEventListener.java b/src/main/java/com/devkor/ifive/nadab/domain/notification/application/event/social/CommentNotificationEventListener.java index 3bf1ea1..2f0bc38 100644 --- a/src/main/java/com/devkor/ifive/nadab/domain/notification/application/event/social/CommentNotificationEventListener.java +++ b/src/main/java/com/devkor/ifive/nadab/domain/notification/application/event/social/CommentNotificationEventListener.java @@ -3,6 +3,7 @@ import com.devkor.ifive.nadab.domain.comment.application.event.CommentCreatedEvent; import com.devkor.ifive.nadab.domain.comment.application.event.SubCommentCreatedEvent; import com.devkor.ifive.nadab.domain.comment.core.repository.CommentRepository; +import com.devkor.ifive.nadab.domain.moderation.core.repository.UserBlockRepository; import com.devkor.ifive.nadab.domain.notification.application.NotificationCommandService; import com.devkor.ifive.nadab.domain.notification.core.entity.NotificationType; import com.devkor.ifive.nadab.domain.user.core.entity.User; @@ -17,6 +18,7 @@ import java.util.List; import java.util.Map; +import java.util.Set; @Component @RequiredArgsConstructor @@ -25,6 +27,7 @@ public class CommentNotificationEventListener { private final UserRepository userRepository; private final CommentRepository commentRepository; + private final UserBlockRepository userBlockRepository; private final NotificationMessageFactory messageFactory; private final NotificationCommandService notificationCommandService; @@ -87,8 +90,12 @@ public void handleSubCommentCreated(SubCommentCreatedEvent event) { "commentContent", truncate(event.getContent()) ); - // 1. 부모 댓글 작성자 알림 (author 제외, 역할 무관 최우선) - if (!event.getAuthorId().equals(event.getParentCommentAuthorId())) { + Set blockedByAuthor = Set.copyOf( + userBlockRepository.findBlockedUserIdsBidirectional(event.getAuthorId())); + + // 1. 부모 댓글 작성자 알림 (author 제외, 역할 무관 최우선,차단 관계이면 skip) + if (!event.getAuthorId().equals(event.getParentCommentAuthorId()) + && !blockedByAuthor.contains(event.getParentCommentAuthorId())) { User parentCommentAuthor = userRepository.findById(event.getParentCommentAuthorId()).orElse(null); if (parentCommentAuthor == null || parentCommentAuthor.getDeletedAt() != null) { log.debug("Parent comment author not found or deleted, skip notification: parentCommentAuthorId={}", event.getParentCommentAuthorId()); @@ -141,20 +148,49 @@ public void handleSubCommentCreated(SubCommentCreatedEvent event) { } } - NotificationContent participantContent = messageFactory.createMessage( - NotificationType.REPLY_ON_PARTICIPATED_COMMENT, params); - for (Long participantId : participantIds) { - notificationCommandService.sendNotification( - participantId, - NotificationType.REPLY_ON_PARTICIPATED_COMMENT, - participantContent.title(), - participantContent.body(), - participantContent.inboxMessage(), - event.getDailyReportId().toString(), - String.format("COMMENT_%d_PARTICIPANT_%d", event.getSubCommentId(), participantId) - ); + // 4. 참여자 알림 + if (!participantIds.isEmpty()) { + if (!event.isSecret() || event.isParentSecret()) { + // 공개 대댓글 or 비밀 부모 아래 대댓글: 참여자들 열람 가능 — 차단 관계 제외 + List notifiableParticipantIds = participantIds.stream() + .filter(id -> !blockedByAuthor.contains(id)) + .toList(); + if (!notifiableParticipantIds.isEmpty()) { + NotificationContent participantContent = messageFactory.createMessage( + NotificationType.REPLY_ON_PARTICIPATED_COMMENT, params); + for (Long participantId : notifiableParticipantIds) { + notificationCommandService.sendNotification( + participantId, + NotificationType.REPLY_ON_PARTICIPATED_COMMENT, + participantContent.title(), + participantContent.body(), + participantContent.inboxMessage(), + event.getDailyReportId().toString(), + String.format("COMMENT_%d_PARTICIPANT_%d", event.getSubCommentId(), participantId) + ); + } + log.debug("Sub-comment notifications sent: subCommentId={}, participantCount={}", event.getSubCommentId(), notifiableParticipantIds.size()); + } + } else if (reportOwnerIsParticipant && !reportOwnerIsAuthor + && !blockedByAuthor.contains(event.getReportOwnerId())) { + // 공개 부모 댓글에 달린 비밀 대댓글 : 일반 참여자는 열람 불가하지만 리포트 당사자는 가능 + User reportOwner = userRepository.findById(event.getReportOwnerId()).orElse(null); + if (reportOwner != null && reportOwner.getDeletedAt() == null) { + NotificationContent participantContent = messageFactory.createMessage( + NotificationType.REPLY_ON_PARTICIPATED_COMMENT, params); + notificationCommandService.sendNotification( + event.getReportOwnerId(), + NotificationType.REPLY_ON_PARTICIPATED_COMMENT, + participantContent.title(), + participantContent.body(), + participantContent.inboxMessage(), + event.getDailyReportId().toString(), + String.format("COMMENT_%d_PARTICIPANT_%d", event.getSubCommentId(), event.getReportOwnerId()) + ); + log.debug("Sub-comment notification sent to report owner (participant): subCommentId={}", event.getSubCommentId()); + } + } } - log.debug("Sub-comment notifications sent: subCommentId={}, participantCount={}", event.getSubCommentId(), participantIds.size()); } catch (Exception e) { log.error("Failed to handle SubCommentCreatedEvent: subCommentId={}, error={}", event.getSubCommentId(), e.getMessage(), e);