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

#106 [RESTRUCTURING] : 이벤트를 활용한 fcm push 알림 구현 #122

Merged
merged 8 commits into from
Apr 22, 2024
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package com.wakeUpTogetUp.togetUp.api.notification;

import com.wakeUpTogetUp.togetUp.api.notification.vo.NotificationSendVo;
import com.wakeUpTogetUp.togetUp.infra.fcm.FcmService;
import lombok.RequiredArgsConstructor;
import org.springframework.context.event.EventListener;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;


@Component
@RequiredArgsConstructor
public class NotificationEventHandler {
private final FcmService fcmService;

@Async()
@EventListener(NotificationSendVo.class)
public void handleSendPushEvent(NotificationSendVo event) {
fcmService.sendMessageToTokens(event);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,17 @@
package com.wakeUpTogetUp.togetUp.api.notification;

import com.wakeUpTogetUp.togetUp.api.notification.vo.NotificationSendVo;
import lombok.RequiredArgsConstructor;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class NotificationEventPublisher {
private final ApplicationEventPublisher eventPublisher;

public void publishNotificationSendEvent(NotificationSendVo notificationSendVo) {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P5: 해당 메서드의 인수에서 ~event 클래스를 받지 않는데 특별히 이렇게 구현하신 이유가 있을까요?

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

어떤 것이 궁금하신가요??
NotificationSendVo의 이름을 NotificationSendEvent로 하지 않은 이유가 궁금하신지
아니면 NotificationEventPublisher의 구현이 궁금하신지 알려주시면 좋을 것 같습니다!

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

eventPublisher는 event를 인수로 받는다고 알고 있는데 따로 event를 만들지 않고 Vo를 사용하신 이유 (재사용인지는 모르겠습니다)가 궁금합니다.

Copy link
Contributor Author

@hye-on hye-on Apr 21, 2024

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

내부 구현에 들어가면 오브젝트를 인자로 받을 수 있습니다! 원래 이름이 NotificationSendVo이였는데 변경할 생각을 못했습니다. NotificationSendEvent으로 변경해도 좋을 것 같네요!

	void publishEvent(Object event);

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 내부에서는 Object를 받지만 해당 클래스를 vo로 사용할 것인지 event로 사용할것인지 명확하게 하기 위해 클래스 이름을 변경하는 것이 좋아보입니다.

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

네 수정하겠습니다 :)

eventPublisher.publishEvent(notificationSendVo);
}
}

Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,6 @@
@Service

@RequiredArgsConstructor
public class PushLogService {
public class NotificationLogService {

}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,10 +11,12 @@
import com.wakeUpTogetUp.togetUp.api.users.model.User;
import com.wakeUpTogetUp.togetUp.common.Status;
import com.wakeUpTogetUp.togetUp.exception.BaseException;
import com.wakeUpTogetUp.togetUp.infra.google.fcm.FcmService;
import com.wakeUpTogetUp.togetUp.infra.fcm.FcmService;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;

Expand All @@ -26,16 +28,16 @@ public class NotificationService {
private final RoomRepository roomRepository;
private final UserRepository userRepository;
private final FcmTokenRepository fcmTokenRepository;
private final NotificationEventPublisher notificationEventPublisher;

public void sendNotificationToAllUsers(String title, String body) {
fcmService.sendMessageToTokens(
new NotificationSendVo(
fcmTokenRepository.findAllByUser_IdIn(userService.getAgreedNotiUsersIds()),
title,
body,
Map.of(DataKeyType.LINK.getKey(), DataValueType.HOME.toString())
)
);
notificationEventPublisher.publishNotificationSendEvent(new NotificationSendVo(
fcmTokenRepository.findAllByUser_IdIn(userService.getAgreedNotiUsersIds()),
title,
body,
Map.of(DataKeyType.LINK.getKey(), DataValueType.HOME.toString())
));

}

public void sendNotificationToUsersInRoom(Integer alarmId, Integer missionCompleteUserId) {
Expand All @@ -51,13 +53,11 @@ public void sendNotificationToUsersInRoom(Integer alarmId, Integer missionComple
.collect(Collectors.toList());

if (userIdsInRoom.size() > 0) {
fcmService.sendMessageToTokens(
new RoomMissionLogNotificationVo(
user,
room,
fcmTokenRepository.findAllByUser_IdIn(userIdsInRoom)
)
);
notificationEventPublisher.publishNotificationSendEvent(new RoomMissionLogNotificationVo(
user,
room,
fcmTokenRepository.findAllByUser_IdIn(userIdsInRoom)
));
}
}

Expand Down

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -19,17 +19,9 @@
public class Notification {


@Builder
public Notification(String title, String content, Room room, FcmToken fcmToken) {
this.title = title;
this.content = content;
this.room = room;
this.fcmToken = fcmToken;
}

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column( columnDefinition = "INT UNSIGNED")
@Column(columnDefinition = "INT UNSIGNED")
private Integer id;

private String title;
Expand All @@ -40,10 +32,10 @@ public Notification(String title, String content, Room room, FcmToken fcmToken)
private String createdAt = TimeFormatter.timestampFormat(new Timestamp(System.currentTimeMillis()));

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "roomId",columnDefinition = "INT UNSIGNED")
@JoinColumn(name = "roomId", columnDefinition = "INT UNSIGNED")
private Room room;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "fcmTokenId",columnDefinition = "INT UNSIGNED")
@JoinColumn(name = "fcmTokenId", columnDefinition = "INT UNSIGNED")
private FcmToken fcmToken;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.wakeUpTogetUp.togetUp.api.users;


import com.wakeUpTogetUp.togetUp.api.users.fcmToken.FcmTokenDeleteEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.event.EventListener;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;


@Slf4j
@Component
@Transactional
@RequiredArgsConstructor
public class UserEventHandler {

private final UserService userService;

@EventListener(FcmTokenDeleteEvent.class)
public void handlePushTokenDeleteEvent(FcmTokenDeleteEvent event) {
userService.deleteFcmTokens(event.getFcmTokens());
}

}

Original file line number Diff line number Diff line change
Expand Up @@ -12,9 +12,11 @@
import com.wakeUpTogetUp.togetUp.api.users.vo.UserProgressResult;
import com.wakeUpTogetUp.togetUp.common.Status;
import com.wakeUpTogetUp.togetUp.exception.BaseException;

import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;

import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand Down Expand Up @@ -68,6 +70,11 @@ private void saveFcmToken(User user, String fcmTokenValue) {
fcmTokenRepository.save(fcmToken);
}

@Transactional
public void deleteFcmTokens(List<String> fcmTokenValues) {
fcmTokenRepository.deleteByValueIn(fcmTokenValues);
}

public void updateAgreePush(Integer userId, boolean agreePush) {
User user = findExistingUser(userRepository, userId);
user.changeAgreePush(agreePush);
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.wakeUpTogetUp.togetUp.api.users.fcmToken;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

import java.util.List;

@Getter
@RequiredArgsConstructor
public class FcmTokenDeleteEvent {
private final List<String> fcmTokens;
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,8 @@
public interface FcmTokenRepository extends JpaRepository<FcmToken, Integer> {
List<FcmToken> findAllByUser_IdIn(List<Integer> userIds);

void deleteAllByValueIn(List<String> values);

void deleteByValueIn(List<String> fcmTokenValues);

boolean existsByValue(String fcmTokenValue);
}
Original file line number Diff line number Diff line change
Expand Up @@ -80,6 +80,7 @@ public class User {
@Column(name = "is_deleted")
private boolean isDeleted;

@JsonIgnore
@OneToMany(mappedBy = "user")
private List<FcmToken> fcmToken = new ArrayList<>();

Expand Down
Original file line number Diff line number Diff line change
@@ -1,47 +1,65 @@
package com.wakeUpTogetUp.togetUp.infra.google.fcm;
package com.wakeUpTogetUp.togetUp.infra.fcm;

import com.google.api.core.ApiFuture;
import com.google.firebase.messaging.*;
import com.wakeUpTogetUp.togetUp.api.notification.vo.NotificationSendVo;
import com.wakeUpTogetUp.togetUp.api.users.fcmToken.FcmToken;
import com.wakeUpTogetUp.togetUp.api.users.fcmToken.FcmTokenRepository;
import com.wakeUpTogetUp.togetUp.api.users.fcmToken.FcmTokenDeleteEvent;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.context.ApplicationEventPublisher;
import org.springframework.stereotype.Service;

import java.util.ArrayList;
import java.util.List;
import java.util.concurrent.ExecutionException;
import java.util.stream.Collectors;
import java.util.stream.IntStream;

@Slf4j
@Service
@RequiredArgsConstructor
public class FcmService {
private final FirebaseMessaging firebaseMessaging;
private final FcmTokenRepository fcmTokenRepository;
private final ApplicationEventPublisher eventPublisher;


public void sendMessageToTokens(NotificationSendVo notificationSendVo) {
// message 만들기

MulticastMessage message = getPreconfiguredMessageToTokens(notificationSendVo);
ApiFuture<BatchResponse> response = sendAndGetResponse(message);

deleteInvalidToken(response, notificationSendVo.getFcmTokens().stream()
.map(FcmToken::getValue)
.collect(Collectors.toList()));
}


public void deleteInvalidToken(ApiFuture<BatchResponse> response, List<String> deviceTokens) {

try {
if (response.get().getFailureCount() > 0) {
List<String> failedDeviceTokens = new ArrayList<>();
List<SendResponse> responses = response.get().getResponses();

IntStream.range(0, responses.size())
.filter(idx -> !responses.get(idx).isSuccessful())
.forEach(idx -> failedDeviceTokens.add(deviceTokens.get(idx)));
.forEach(idx -> {

String token = deviceTokens.get(idx);
MessagingErrorCode errorCode = responses.get(idx).getException().getMessagingErrorCode();
String errorMessage = responses.get(idx).getException().getMessage();

if (errorCode == MessagingErrorCode.UNREGISTERED)
failedDeviceTokens.add(deviceTokens.get(idx));

log.warn("[PushException][{}]{} {}",
token,
errorCode,
errorMessage);


});

fcmTokenRepository.deleteAllByValueIn(failedDeviceTokens);
eventPublisher.publishEvent(new FcmTokenDeleteEvent(failedDeviceTokens));
}
} catch (InterruptedException e) {
throw new RuntimeException(e);
Expand All @@ -51,12 +69,11 @@ public void deleteInvalidToken(ApiFuture<BatchResponse> response, List<String> d
}

private ApiFuture<BatchResponse> sendAndGetResponse(MulticastMessage message) {
ApiFuture<BatchResponse> response = FirebaseMessaging.getInstance().sendMulticastAsync(message);
ApiFuture<BatchResponse> response = firebaseMessaging.sendMulticastAsync(message);
return response;
}


// token 여러 개
private MulticastMessage getPreconfiguredMessageToTokens(NotificationSendVo notificationSendVo) {
return getPreconfiguredMulticastMessageBuilder(notificationSendVo).addAllTokens(notificationSendVo.getFcmTokens().stream()
.map(FcmToken::getValue)
Expand Down
Loading