Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

알림 작업 구조 개선 & 특정 알림 메시지 구체화 #747

Merged
merged 17 commits into from
Oct 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
17 commits
Select commit Hold shift + click to select a range
3c21e40
feat: FCM 알림 재시도시 재시도 가능 여부 판단을 별도의 객체로 분리
pricelees Oct 23, 2024
78f4c1f
feat: 비동기로 알림을 전송하는 별도의 객체 분리
pricelees Oct 23, 2024
c80d039
feat: 알림 전송 이벤트 객체 구현
pricelees Oct 23, 2024
c3b7aa1
feat: 알림 전송 이벤트 처리 객체 구현
pricelees Oct 23, 2024
7a04bb6
refactor: 구독 정보 필터의 파라미터 타입 수정
pricelees Oct 23, 2024
ed69d79
refactor: 구독 정보 필터를 가져올 때의 처리 로직 수정
pricelees Oct 23, 2024
4bf8b42
rename: NotificationEvent 클래스명 수정(->NotificationPayload)
pricelees Oct 23, 2024
6634ccf
refactor: 알림 저장 & 이벤트 발행 객체 생성 및 이에따른 NotificationService 삭제
pricelees Oct 23, 2024
244bc89
feat: 모임 패키지 안에서의 알림 전송(=이벤트 발행) 객체 추가
pricelees Oct 23, 2024
6a8577c
feat: 모임 패키지 안에서의 공통 이벤트 처리 객체 구현
pricelees Oct 23, 2024
63b5113
feat: 참여 이벤트 처리 객체 구현
pricelees Oct 23, 2024
e961496
feat: 댓글 이벤트 처리 객체 구현
pricelees Oct 23, 2024
2d2001c
feat: 모임 관련(모임 생성, 수정, 상태 변경) 이벤트 처리 객체 구현
pricelees Oct 23, 2024
b69bcf6
feat: 채팅 이벤트 처리 객체 구현
pricelees Oct 23, 2024
bff5b8c
refactor: FcmFailedResponse에 실패한 토큰이 없는지 확인하는 메서드 추가
pricelees Oct 23, 2024
62eecb8
refactor: 토큰 스케쥴러에 Transactional 적용
pricelees Oct 23, 2024
c2c2d0e
refactor: 비동기 테스트 추가
pricelees Oct 23, 2024
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 @@ -14,7 +14,7 @@
import mouda.backend.chat.domain.Chats;
import mouda.backend.chat.implement.ChatRoomFinder;
import mouda.backend.chat.implement.ChatWriter;
import mouda.backend.chat.implement.sender.ChatNotificationSender;
import mouda.backend.chat.implement.notification.ChatNotificationSender;
import mouda.backend.chat.presentation.request.ChatCreateRequest;
import mouda.backend.chat.presentation.request.DateTimeConfirmRequest;
import mouda.backend.chat.presentation.request.LastReadChatRequest;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package mouda.backend.chat.domain;

import lombok.Builder;
import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
@Builder
public class ChatNotificationEvent {

private final Long darakbangId;
private final ChatRoom chatRoom;
private final Chat appendedChat;
}
Original file line number Diff line number Diff line change
@@ -1,16 +1,20 @@
package mouda.backend.chat.implement.sender;
package mouda.backend.chat.implement.notification;

import java.util.List;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

import lombok.Getter;
import lombok.RequiredArgsConstructor;
import mouda.backend.bet.domain.Bet;
import mouda.backend.bet.implement.BetFinder;
import mouda.backend.chat.domain.Chat;
import mouda.backend.chat.domain.ChatNotificationEvent;
import mouda.backend.chat.domain.ChatRoom;
import mouda.backend.chat.domain.ChatRoomType;
import mouda.backend.chat.entity.ChatType;
Expand All @@ -20,82 +24,93 @@
import mouda.backend.darakbang.implement.DarakbangFinder;
import mouda.backend.moim.domain.Moim;
import mouda.backend.moim.implement.finder.MoimFinder;
import mouda.backend.notification.domain.NotificationEvent;
import mouda.backend.notification.domain.NotificationPayload;
import mouda.backend.notification.domain.NotificationType;
import mouda.backend.notification.domain.Recipient;
import mouda.backend.notification.implement.NotificationProcessor;

@Component
@EnableConfigurationProperties(UrlConfig.class)
@RequiredArgsConstructor
public class ChatNotificationSender {
public class ChatNotificationEventHandler {

private final UrlConfig urlConfig;
private final ChatRecipientFinder chatRecipientFinder;
private final ApplicationEventPublisher eventPublisher;
private final MoimFinder moimFinder;
private final BetFinder betFinder;
private final DarakbangFinder darakbangFinder;
private final ChatRecipientFinder chatRecipientFinder;
private final NotificationProcessor notificationProcessor;

@Async
@Transactional(readOnly = true, propagation = Propagation.REQUIRES_NEW)
@TransactionalEventListener(classes = ChatNotificationEvent.class, phase = TransactionPhase.AFTER_COMMIT)
public void sendChatNotification(
long darakbangId, ChatRoom chatRoom, Chat appendedChat
ChatNotificationEvent chatNotificationEvent
) {
long darakbangId = chatNotificationEvent.getDarakbangId();
ChatRoom chatRoom = chatNotificationEvent.getChatRoom();
Chat appendedChat = chatNotificationEvent.getAppendedChat();

ChatRoomType chatRoomType = chatRoom.getType();
long chatRoomId = chatRoom.getId();

if (chatRoomType == ChatRoomType.BET) {
Bet bet = betFinder.find(darakbangId, chatRoom.getTargetId());
sendBetNotification(bet, appendedChat, chatRoomId);
handleBetNotification(bet, appendedChat, chatRoomId);
return;
}

Moim moim = moimFinder.read(chatRoom.getTargetId(), darakbangId);
sendMoimNotification(moim, appendedChat, chatRoomId);
handleMoimNotification(moim, appendedChat, chatRoomId);
}

private void sendMoimNotification(
private void handleMoimNotification(
Moim moim, Chat chat, long chatRoomId
) {
List<Recipient> recipients = chatRecipientFinder.getMoimChatNotificationRecipients(moim.getId(),
chat.getAuthor());
long darakbangId = moim.getDarakbangId();

publishEvent(darakbangId, chatRoomId, moim.getTitle(), chat, recipients);
processNotification(darakbangId, chatRoomId, moim.getTitle(), chat, recipients);
}

private void sendBetNotification(Bet bet, Chat chat, long chatRoomId) {
private void handleBetNotification(Bet bet, Chat chat, long chatRoomId) {
List<Recipient> recipients = chatRecipientFinder.getBetChatNotificationRecipients(bet.getId(),
chat.getAuthor());
long darakbangId = bet.getDarakbangId();

publishEvent(darakbangId, chatRoomId, bet.getTitle(), chat, recipients);
processNotification(darakbangId, chatRoomId, bet.getTitle(), chat, recipients);
}

private void publishEvent(long darakbangId, long chatRoomId, String title, Chat chat, List<Recipient> recipients) {
private void processNotification(
long darakbangId, long chatRoomId, String title, Chat chat, List<Recipient> recipients
) {
Darakbang darakbang = darakbangFinder.findById(darakbangId);
ChatNotification chatNotification = ChatNotification.create(darakbang.getName(), title, chat);
ChatNotificationMessage chatNotificationMessage = ChatNotificationMessage.create(darakbang.getName(), title,
chat);

NotificationEvent notificationEvent = NotificationEvent.chatEvent(
chatNotification.getType(),
NotificationPayload payload = NotificationPayload.createChatPayload(
chatNotificationMessage.getType(),
darakbang.getName(),
chatNotification.getMessage(),
chatNotificationMessage.getMessage(),
urlConfig.getChatRoomUrl(darakbangId, chatRoomId),
recipients,
darakbangId,
chatRoomId
);

eventPublisher.publishEvent(notificationEvent);
notificationProcessor.process(payload);
}

@Getter
@RequiredArgsConstructor
static class ChatNotification {
static class ChatNotificationMessage {

private final String title;
private final NotificationType type;
private final String message;

public static ChatNotification create(String darakbangName, String title, Chat chat) {
public static ChatNotificationMessage create(String darakbangName, String title,
Chat chat) {
ChatType chatType = chat.getChatType();
String content = chat.getContent();

Expand All @@ -114,16 +129,19 @@ public static ChatNotification create(String darakbangName, String title, Chat c
return basicChat(title, message);
}

private static ChatNotification placeConfirmChat(String title, String message) {
return new ChatNotification(title, NotificationType.MOIM_PLACE_CONFIRMED, message);
private static ChatNotificationMessage placeConfirmChat(String title, String message) {
return new ChatNotificationMessage(title, NotificationType.MOIM_PLACE_CONFIRMED,
message);
}

private static ChatNotification dateTimeConfirmChat(String title, String message) {
return new ChatNotification(title, NotificationType.MOIM_TIME_CONFIRMED, message);
private static ChatNotificationMessage dateTimeConfirmChat(String title,
String message) {
return new ChatNotificationMessage(title, NotificationType.MOIM_TIME_CONFIRMED,
message);
}

private static ChatNotification basicChat(String title, String message) {
return new ChatNotification(title, NotificationType.NEW_CHAT, message);
private static ChatNotificationMessage basicChat(String title, String message) {
return new ChatNotificationMessage(title, NotificationType.NEW_CHAT, message);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package mouda.backend.chat.implement.notification;

import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

import lombok.RequiredArgsConstructor;
import mouda.backend.chat.domain.Chat;
import mouda.backend.chat.domain.ChatNotificationEvent;
import mouda.backend.chat.domain.ChatRoom;

@Component
@RequiredArgsConstructor
public class ChatNotificationSender {

private final ApplicationEventPublisher eventPublisher;

public void sendChatNotification(long darakbangId, ChatRoom chatRoom, Chat appendedChat) {
ChatNotificationEvent event = ChatNotificationEvent.builder()
.darakbangId(darakbangId)
.chatRoom(chatRoom)
.appendedChat(appendedChat)
.build();

eventPublisher.publishEvent(event);
}
}
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
package mouda.backend.chat.implement.sender;
package mouda.backend.chat.implement.notification;

import java.util.List;
import java.util.stream.Stream;
Expand Down Expand Up @@ -48,25 +48,4 @@ public List<Recipient> getNotificationRecipients(Stream<DarakbangMember> memberS
.build())
.toList();
}

// todo: 구버전 채팅 기능 삭제시 같이 지워주세요.
public List<Recipient> getMoimChatNotificationRecipients(long moimId, DarakbangMember sender) {
List<Chamyo> chamyos = chamyoRepository.findAllByMoimId(moimId);

Stream<DarakbangMember> darakbangMemberStream = chamyos.stream()
.map(Chamyo::getDarakbangMember);

return getNotificationRecipients(darakbangMemberStream, sender);
}

// todo: 구버전 채팅 기능 삭제시 같이 지워주세요.
public List<Recipient> getNotificationRecipients(Stream<DarakbangMember> memberStream, DarakbangMember sender) {
return memberStream
.filter(darakbangMember -> darakbangMember.isNotSameMemberWith(sender))
.map(darakbangMember -> Recipient.builder()
.memberId(darakbangMember.getMemberId())
.darakbangMemberId(darakbangMember.getId())
.build())
.toList();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,7 @@
import mouda.backend.moim.domain.MoimRole;
import mouda.backend.moim.implement.finder.ChamyoFinder;
import mouda.backend.moim.implement.finder.MoimFinder;
import mouda.backend.moim.implement.sender.ChamyoNotificationSender;
import mouda.backend.moim.implement.notificiation.MoimRelatedNotificationSender;
import mouda.backend.moim.implement.writer.ChamyoWriter;
import mouda.backend.moim.implement.writer.MoimWriter;
import mouda.backend.moim.presentation.response.chamyo.ChamyoFindAllResponses;
Expand All @@ -28,7 +28,7 @@ public class ChamyoService {
private final MoimWriter moimWriter;
private final ChamyoFinder chamyoFinder;
private final ChamyoWriter chamyoWriter;
private final ChamyoNotificationSender chamyoNotificationSender;
private final MoimRelatedNotificationSender notificationSender;

@Transactional(readOnly = true)
public MoimRoleFindResponse findMoimRole(Long darakbangId, Long moimId, DarakbangMember darakbangMember) {
Expand All @@ -51,14 +51,14 @@ public void chamyoMoim(Long darakbangId, Long moimId, DarakbangMember darakbangM
Chamyo chamyo = chamyoWriter.saveAsMoimee(moim, darakbangMember);
moimWriter.updateMoimStatusIfFull(moim);

chamyoNotificationSender.sendChamyoNotification(moimId, darakbangMember, NotificationType.NEW_MOIMEE_JOINED);
notificationSender.sendChamyoNotification(chamyo, NotificationType.NEW_MOIMEE_JOINED);
}

public void cancelChamyo(Long darakbangId, Long moimId, DarakbangMember darakbangMember) {
Moim moim = moimFinder.read(moimId, darakbangId);
Chamyo chamyo = chamyoFinder.read(moim, darakbangMember);
chamyoWriter.delete(chamyo);

chamyoNotificationSender.sendChamyoNotification(moimId, darakbangMember, NotificationType.MOIMEE_LEFT);
notificationSender.sendChamyoNotification(chamyo, NotificationType.MOIMEE_LEFT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,7 +8,7 @@
import mouda.backend.moim.domain.Comment;
import mouda.backend.moim.domain.Moim;
import mouda.backend.moim.implement.finder.MoimFinder;
import mouda.backend.moim.implement.sender.CommentNotificationSender;
import mouda.backend.moim.implement.notificiation.MoimRelatedNotificationSender;
import mouda.backend.moim.implement.writer.CommentWriter;
import mouda.backend.moim.presentation.request.comment.CommentCreateRequest;

Expand All @@ -19,14 +19,14 @@ public class CommentService {

private final MoimFinder moimFinder;
private final CommentWriter commentWriter;
private final CommentNotificationSender commentNotificationSender;
private final MoimRelatedNotificationSender notificationSender;

public void createComment(
Long darakbangId, Long moimId, DarakbangMember darakbangMember, CommentCreateRequest request
) {
Moim moim = moimFinder.read(moimId, darakbangId);
Comment comment = commentWriter.saveComment(moim, darakbangMember, request.parentId(), request.content());

commentNotificationSender.sendCommentNotification(comment, darakbangMember);
notificationSender.sendCommentNotification(comment, darakbangMember);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@
import mouda.backend.moim.domain.ParentComment;
import mouda.backend.moim.implement.finder.CommentFinder;
import mouda.backend.moim.implement.finder.MoimFinder;
import mouda.backend.moim.implement.sender.MoimNotificationSender;
import mouda.backend.moim.implement.notificiation.MoimRelatedNotificationSender;
import mouda.backend.moim.implement.writer.MoimWriter;
import mouda.backend.moim.presentation.request.moim.MoimCreateRequest;
import mouda.backend.moim.presentation.request.moim.MoimEditRequest;
Expand All @@ -32,8 +32,8 @@ public class MoimService {
private final MoimWriter moimWriter;
private final MoimFinder moimFinder;
private final CommentFinder commentFinder;
private final MoimNotificationSender moimNotificationSender;
private final ChatRoomFinder chatRoomFinder;
private final MoimRelatedNotificationSender notificationSender;

@Transactional(readOnly = true)
public MoimDetailsFindResponse findMoimDetails(long darakbangId, long moimId) {
Expand Down Expand Up @@ -76,29 +76,29 @@ public MoimFindAllResponses findZzimedMoim(DarakbangMember darakbangMember) {
public Moim createMoim(Long darakbangId, DarakbangMember darakbangMember, MoimCreateRequest moimCreateRequest) {
Moim moim = moimWriter.save(moimCreateRequest.toEntity(darakbangId), darakbangMember);

moimNotificationSender.sendMoimCreatedNotification(moim, darakbangMember, NotificationType.MOIM_CREATED);
notificationSender.sendMoimCreatedNotification(moim, darakbangMember, NotificationType.MOIM_CREATED);
return moim;
}

public void completeMoim(Long darakbangId, Long moimId, DarakbangMember darakbangMember) {
Moim moim = moimFinder.read(moimId, darakbangId);
moimWriter.completeMoim(moim, darakbangMember);

moimNotificationSender.sendMoimStatusChangedNotification(moim, NotificationType.MOIMING_COMPLETED);
notificationSender.sendMoimStatusChangeNotification(moim, NotificationType.MOIMING_COMPLETED);
}

public void cancelMoim(Long darakbangId, Long moimId, DarakbangMember darakbangMember) {
Moim moim = moimFinder.read(moimId, darakbangId);
moimWriter.cancelMoim(moim, darakbangMember);

moimNotificationSender.sendMoimStatusChangedNotification(moim, NotificationType.MOIM_CANCELLED);
notificationSender.sendMoimStatusChangeNotification(moim, NotificationType.MOIM_CANCELLED);
}

public void reopenMoim(Long darakbangId, Long moimId, DarakbangMember darakbangMember) {
Moim moim = moimFinder.read(moimId, darakbangId);
moimWriter.reopenMoim(moim, darakbangMember);

moimNotificationSender.sendMoimStatusChangedNotification(moim, NotificationType.MOINING_REOPENED);
notificationSender.sendMoimStatusChangeNotification(moim, NotificationType.MOINING_REOPENED);
}

public void editMoim(Long darakbangId, MoimEditRequest request, DarakbangMember darakbangMember) {
Expand All @@ -107,6 +107,6 @@ public void editMoim(Long darakbangId, MoimEditRequest request, DarakbangMember
moimWriter.updateMoim(moim, darakbangMember, request.title(), request.date(), request.time(), request.place(),
request.maxPeople(), request.description());

moimNotificationSender.sendMoimInfoModifiedNotification(moim, oldTitle, NotificationType.MOIM_MODIFIED);
notificationSender.sendMoimEditedNotification(moim, oldTitle, NotificationType.MOIM_MODIFIED);
}
}
Original file line number Diff line number Diff line change
@@ -1,23 +1,21 @@
package mouda.backend.moim.implement.sender;
package mouda.backend.moim.implement.notificiation;

import org.springframework.boot.context.properties.EnableConfigurationProperties;
import org.springframework.stereotype.Component;

import lombok.RequiredArgsConstructor;
import mouda.backend.common.config.UrlConfig;
import mouda.backend.notification.implement.NotificationProcessor;

@Component
@RequiredArgsConstructor
@EnableConfigurationProperties(UrlConfig.class)
public abstract class AbstractMoimNotificationSender {
public abstract class AbstractMoimRelatedNotificationEventHandler {

private final UrlConfig urlConfig;
protected final UrlConfig urlConfig;
protected final NotificationProcessor notificationProcessor;

protected String getMoimUrl(long darakbangId, long moimId) {
return urlConfig.getMoimUrl(darakbangId, moimId);
}

protected String getChatRoomUrl(long darakbangId, long moimId) {
return urlConfig.getChatRoomUrl(darakbangId, moimId);
}
}
Loading