Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import org.devkor.apu.saerok_server.domain.freeboard.api.dto.request.UpdateFreeBoardPostCommentRequest;
import org.devkor.apu.saerok_server.domain.freeboard.api.dto.response.CreateFreeBoardPostCommentResponse;
import org.devkor.apu.saerok_server.domain.freeboard.api.dto.response.UpdateFreeBoardPostCommentResponse;
import org.devkor.apu.saerok_server.domain.freeboard.application.event.FreeBoardNotificationEvent;
import org.devkor.apu.saerok_server.domain.freeboard.core.entity.FreeBoardPost;
import org.devkor.apu.saerok_server.domain.freeboard.core.entity.FreeBoardPostComment;
import org.devkor.apu.saerok_server.domain.freeboard.core.repository.FreeBoardPostCommentRepository;
Expand All @@ -14,6 +15,7 @@
import org.devkor.apu.saerok_server.domain.user.core.repository.UserRepository;
import org.devkor.apu.saerok_server.global.shared.exception.ForbiddenException;
import org.devkor.apu.saerok_server.global.shared.exception.NotFoundException;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

@Service
Expand All @@ -24,6 +26,7 @@ public class FreeBoardPostCommentCommandService {
private final FreeBoardPostCommentRepository commentRepository;
private final FreeBoardPostRepository postRepository;
private final UserRepository userRepository;
private final ApplicationEventPublisher eventPublisher;

/* 댓글 작성 */
public CreateFreeBoardPostCommentResponse createComment(Long userId, Long postId,
Expand All @@ -35,8 +38,9 @@ public CreateFreeBoardPostCommentResponse createComment(Long userId, Long postId
.orElseThrow(() -> new NotFoundException("존재하지 않는 게시글 id예요"));

FreeBoardPostComment comment;
FreeBoardPostComment parentComment = null;
if (req.parentId() != null) {
FreeBoardPostComment parentComment = commentRepository.findById(req.parentId())
parentComment = commentRepository.findById(req.parentId())
.orElseThrow(() -> new NotFoundException("존재하지 않는 댓글 id예요"));

if (!parentComment.getPost().getId().equals(postId)) {
Expand All @@ -55,6 +59,15 @@ public CreateFreeBoardPostCommentResponse createComment(Long userId, Long postId
}

commentRepository.save(comment);

eventPublisher.publishEvent(new FreeBoardNotificationEvent.CommentCreated(
userId, user.getNickname(),
postId, post.getUser().getId(),
parentComment != null ? parentComment.getId() : null,
parentComment != null ? parentComment.getUser().getId() : null,
req.content()
));

return new CreateFreeBoardPostCommentResponse(comment.getId());
}

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.devkor.apu.saerok_server.domain.freeboard.application.event;

public sealed interface FreeBoardNotificationEvent {

record CommentCreated(
Long actorId, String actorNickname,
Long postId, Long postOwnerId,
Long parentCommentId, Long parentCommentOwnerId,
String commentContent
) implements FreeBoardNotificationEvent {}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
package org.devkor.apu.saerok_server.domain.freeboard.application.event;

import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.devkor.apu.saerok_server.domain.notification.application.facade.NotifyActionDsl;
import org.devkor.apu.saerok_server.domain.notification.application.model.dsl.ActionKind;
import org.devkor.apu.saerok_server.domain.notification.application.model.dsl.Actor;
import org.devkor.apu.saerok_server.domain.notification.application.model.dsl.Target;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

@Slf4j
@Component
@RequiredArgsConstructor
public class FreeBoardNotificationWorker {

private final NotifyActionDsl notifyAction;

@Async("pushNotificationExecutor")
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
public void handle(FreeBoardNotificationEvent.CommentCreated event) {
try {
Actor actor = Actor.of(event.actorId(), event.actorNickname());

if (event.parentCommentId() != null) {
// 대댓글: 원댓글 작성자에게 REPLY 알림
if (!event.parentCommentOwnerId().equals(event.actorId())) {
notifyAction
.by(actor)
.on(Target.freeBoardComment(event.parentCommentId()))
.did(ActionKind.REPLY)
.comment(event.commentContent())
.to(event.parentCommentOwnerId());
}
// 게시글 소유자에게 COMMENT 알림 (원댓글 작성자와 다른 경우에만)
if (!event.postOwnerId().equals(event.actorId())
&& !event.postOwnerId().equals(event.parentCommentOwnerId())) {
notifyAction
.by(actor)
.on(Target.freeBoardPost(event.postId()))
.did(ActionKind.COMMENT)
.comment(event.commentContent())
.to(event.postOwnerId());
}
} else {
// 원댓글: 게시글 소유자에게 COMMENT 알림
if (!event.postOwnerId().equals(event.actorId())) {
notifyAction
.by(actor)
.on(Target.freeBoardPost(event.postId()))
.did(ActionKind.COMMENT)
.comment(event.commentContent())
.to(event.postOwnerId());
}
}
} catch (Exception e) {
log.error("Failed to send freeboard comment notification: postId={}, actorId={}",
event.postId(), event.actorId(), e);
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -18,12 +18,16 @@ public class DelegatingTargetMetadataAdapter implements TargetMetadataPort {

private final CollectionTargetMetadataAdapter collectionAdapter;
private final CommentTargetMetadataAdapter commentAdapter;
private final FreeBoardPostTargetMetadataAdapter freeBoardPostAdapter;
private final FreeBoardCommentTargetMetadataAdapter freeBoardCommentAdapter;

@Override
public Map<String, Object> enrich(Target target, Map<String, Object> baseExtras) {
return switch (target.type()) {
case COLLECTION -> collectionAdapter.enrich(target, baseExtras);
case COMMENT -> commentAdapter.enrich(target, baseExtras);
case FREE_BOARD_POST -> freeBoardPostAdapter.enrich(target, baseExtras);
case FREE_BOARD_COMMENT -> freeBoardCommentAdapter.enrich(target, baseExtras);
};
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package org.devkor.apu.saerok_server.domain.notification.application.adapter;

import lombok.RequiredArgsConstructor;
import org.devkor.apu.saerok_server.domain.freeboard.core.repository.FreeBoardPostCommentRepository;
import org.devkor.apu.saerok_server.domain.notification.application.model.dsl.Target;
import org.devkor.apu.saerok_server.domain.notification.application.model.dsl.TargetType;
import org.devkor.apu.saerok_server.domain.notification.application.port.TargetMetadataPort;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
* TargetType.FREE_BOARD_COMMENT 전용 메타데이터 어댑터.<br>
* - extras.freeBoardCommentId<br>
* - extras.freeBoardPostId (댓글이 속한 자유게시판 글)
*/
@Component
@RequiredArgsConstructor
public class FreeBoardCommentTargetMetadataAdapter implements TargetMetadataPort {

private final FreeBoardPostCommentRepository commentRepository;

@Override
public Map<String, Object> enrich(Target target, Map<String, Object> baseExtras) {
if (target.type() != TargetType.FREE_BOARD_COMMENT) {
return baseExtras != null ? baseExtras : Map.of();
}

Map<String, Object> extras = baseExtras != null ? new HashMap<>(baseExtras) : new HashMap<>();
extras.put("freeBoardCommentId", target.id());

commentRepository.findById(target.id())
.ifPresent(comment -> extras.put("freeBoardPostId", comment.getPost().getId()));

return extras;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package org.devkor.apu.saerok_server.domain.notification.application.adapter;

import lombok.RequiredArgsConstructor;
import org.devkor.apu.saerok_server.domain.notification.application.model.dsl.Target;
import org.devkor.apu.saerok_server.domain.notification.application.model.dsl.TargetType;
import org.devkor.apu.saerok_server.domain.notification.application.port.TargetMetadataPort;
import org.springframework.stereotype.Component;

import java.util.HashMap;
import java.util.Map;

/**
* TargetType.FREE_BOARD_POST 전용 메타데이터 어댑터.<br>
* - extras.freeBoardPostId
*/
@Component
@RequiredArgsConstructor
public class FreeBoardPostTargetMetadataAdapter implements TargetMetadataPort {

@Override
public Map<String, Object> enrich(Target target, Map<String, Object> baseExtras) {
if (target.type() != TargetType.FREE_BOARD_POST) {
return baseExtras != null ? baseExtras : Map.of();
}

Map<String, Object> extras = baseExtras != null ? new HashMap<>(baseExtras) : new HashMap<>();
extras.put("freeBoardPostId", target.id());
return extras;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -103,6 +103,8 @@ public void to(Long recipientId) {
var notificationSubject = switch (target.type()) {
case COLLECTION -> NotificationSubject.COLLECTION;
case COMMENT -> NotificationSubject.COMMENT;
case FREE_BOARD_POST -> NotificationSubject.FREE_BOARD_POST;
case FREE_BOARD_COMMENT -> NotificationSubject.FREE_BOARD_COMMENT;
};

var notificationAction = switch (action) {
Expand All @@ -112,9 +114,15 @@ public void to(Long recipientId) {
case SUGGEST_BIRD_ID -> NotificationAction.SUGGEST_BIRD_ID;
};

Long relatedId = target.type() == TargetType.COMMENT && extras.containsKey("collectionId")
? (Long) extras.get("collectionId")
: target.id();
Long relatedId = switch (target.type()) {
case COMMENT -> extras.containsKey("collectionId")
? (Long) extras.get("collectionId")
: target.id();
case FREE_BOARD_COMMENT -> extras.containsKey("freeBoardPostId")
? (Long) extras.get("freeBoardPostId")
: target.id();
default -> target.id();
};

publisher.push(
new ActionNotificationPayload(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
public record Target(TargetType type, Long id) {
public static Target collection(Long id) { return new Target(TargetType.COLLECTION, id); }
public static Target comment(Long id) { return new Target(TargetType.COMMENT, id); }
public static Target freeBoardPost(Long id) { return new Target(TargetType.FREE_BOARD_POST, id); }
public static Target freeBoardComment(Long id) { return new Target(TargetType.FREE_BOARD_COMMENT, id); }
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,4 +3,6 @@
public enum TargetType {
COLLECTION,
COMMENT,
FREE_BOARD_POST,
FREE_BOARD_COMMENT,
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,8 @@
package org.devkor.apu.saerok_server.domain.notification.core.entity;

public enum NotificationSubject {
COLLECTION, // "내 컬렉션에 대한 활동"
COMMENT, // "내 댓글에 대한 활동"
COLLECTION, // "내 컬렉션에 대한 활동"
COMMENT, // "내 컬렉션 댓글에 대한 활동"
FREE_BOARD_POST, // "내 자유게시판 글에 대한 활동"
FREE_BOARD_COMMENT, // "내 자유게시판 댓글에 대한 활동"
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,8 @@ public enum NotificationType {
COMMENTED_ON_COLLECTION,
REPLIED_TO_COMMENT,
SUGGESTED_BIRD_ID_ON_COLLECTION,
COMMENTED_ON_FREE_BOARD_POST,
REPLIED_TO_FREE_BOARD_COMMENT,

// ---- System Notification Types ----
SYSTEM_PUBLISHED_ANNOUNCEMENT,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -24,6 +24,14 @@ public static NotificationType from(NotificationSubject subject, NotificationAct
case REPLY -> NotificationType.REPLIED_TO_COMMENT;
case LIKE, COMMENT, SUGGEST_BIRD_ID -> throw new IllegalArgumentException(action + " action is not supported for COMMENT subject");
};
case FREE_BOARD_POST -> switch (action) {
case COMMENT -> NotificationType.COMMENTED_ON_FREE_BOARD_POST;
case LIKE, REPLY, SUGGEST_BIRD_ID -> throw new IllegalArgumentException(action + " action is not supported for FREE_BOARD_POST subject");
};
case FREE_BOARD_COMMENT -> switch (action) {
case REPLY -> NotificationType.REPLIED_TO_FREE_BOARD_COMMENT;
case LIKE, COMMENT, SUGGEST_BIRD_ID -> throw new IllegalArgumentException(action + " action is not supported for FREE_BOARD_COMMENT subject");
};
};
}
}
10 changes: 10 additions & 0 deletions src/main/resources/config/notification-messages.yml
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,16 @@ notification-messages:
push-body: "두근두근! 새로운 의견이 공유되었어요. 확인해볼까요?"
batch-push-title: "동정 의견 공유"
batch-push-body: "{count}개의 새로운 의견이 공유되었어요. 확인해볼까요?"
COMMENTED_ON_FREE_BOARD_POST:
push-title: "{actorName}"
push-body: "나의 게시글에 댓글을 남겼어요. \"{comment}\""
batch-push-title: "{actorName} 외 {othersCount}명"
batch-push-body: "나의 게시글에 댓글을 남겼어요."
REPLIED_TO_FREE_BOARD_COMMENT:
push-title: "{actorName}"
push-body: "나의 댓글에 답글을 남겼어요. \"{comment}\""
batch-push-title: "{actorName} 외 {othersCount}명"
batch-push-body: "나의 댓글에 답글을 남겼어요."
SYSTEM_PUBLISHED_ANNOUNCEMENT:
push-title: "{title}"
push-body: "{body}"
Expand Down
Loading
Loading