diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/api/event/EventPublisher.java b/src/main/java/com/wakeUpTogetUp/togetUp/api/event/EventPublisher.java new file mode 100644 index 00000000..85aed68b --- /dev/null +++ b/src/main/java/com/wakeUpTogetUp/togetUp/api/event/EventPublisher.java @@ -0,0 +1,19 @@ +package com.wakeUpTogetUp.togetUp.api.event; + +import org.springframework.context.ApplicationEventPublisher; + +public class EventPublisher { + + private static ApplicationEventPublisher publisher; + + public static void setPublisher(ApplicationEventPublisher publisher) { + EventPublisher.publisher = publisher; + } + + public static void raise(Object event) { + if (publisher != null) { + publisher.publishEvent(event); + } + } + +} diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/NotificationEventHandler.java b/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/NotificationEventHandler.java new file mode 100644 index 00000000..88b327c2 --- /dev/null +++ b/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/NotificationEventHandler.java @@ -0,0 +1,21 @@ +package com.wakeUpTogetUp.togetUp.api.notification; + +import com.wakeUpTogetUp.togetUp.api.notification.vo.NotificationSendEvent; +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(NotificationSendEvent.class) + public void handleSendPushEvent(NotificationSendEvent event) { + fcmService.sendMessageToTokens(event); + } +} diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/PushLogService.java b/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/NotificationLogService.java similarity index 82% rename from src/main/java/com/wakeUpTogetUp/togetUp/api/notification/PushLogService.java rename to src/main/java/com/wakeUpTogetUp/togetUp/api/notification/NotificationLogService.java index 843e1cb6..c6f02f6c 100644 --- a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/PushLogService.java +++ b/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/NotificationLogService.java @@ -7,6 +7,6 @@ @Service @RequiredArgsConstructor -public class PushLogService { +public class NotificationLogService { } diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/NotificationProvider.java b/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/NotificationProvider.java deleted file mode 100644 index c66f439b..00000000 --- a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/NotificationProvider.java +++ /dev/null @@ -1,11 +0,0 @@ -package com.wakeUpTogetUp.togetUp.api.notification; - -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; - -@Service -@RequiredArgsConstructor -public class NotificationProvider { - private final NotificationRepository notificationRepository; - -} diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/NotificationService.java b/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/NotificationService.java index 4a2bf4c7..405cd5ee 100644 --- a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/NotificationService.java +++ b/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/NotificationService.java @@ -1,7 +1,8 @@ package com.wakeUpTogetUp.togetUp.api.notification; -import com.wakeUpTogetUp.togetUp.api.notification.vo.NotificationSendVo; -import com.wakeUpTogetUp.togetUp.api.notification.vo.RoomMissionLogNotificationVo; +import com.wakeUpTogetUp.togetUp.api.event.EventPublisher; +import com.wakeUpTogetUp.togetUp.api.notification.vo.NotificationSendEvent; +import com.wakeUpTogetUp.togetUp.api.notification.vo.RoomMissionLogNotificationEvent; import com.wakeUpTogetUp.togetUp.api.room.RoomRepository; import com.wakeUpTogetUp.togetUp.api.room.model.Room; import com.wakeUpTogetUp.togetUp.api.room.model.RoomUser; @@ -11,10 +12,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; @@ -28,14 +31,13 @@ public class NotificationService { private final FcmTokenRepository fcmTokenRepository; 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()) - ) - ); + EventPublisher.raise(new NotificationSendEvent( + fcmTokenRepository.findAllByUser_IdIn(userService.getAgreedNotiUsersIds()), + title, + body, + Map.of(DataKeyType.LINK.getKey(), DataValueType.HOME.toString()) + )); + } public void sendNotificationToUsersInRoom(Integer alarmId, Integer missionCompleteUserId) { @@ -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) - ) - ); + EventPublisher.raise(new RoomMissionLogNotificationEvent( + user, + room, + fcmTokenRepository.findAllByUser_IdIn(userIdsInRoom) + )); } } diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/dto/response/PushNotificationRes.java b/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/dto/response/PushNotificationRes.java deleted file mode 100644 index 7707fe14..00000000 --- a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/dto/response/PushNotificationRes.java +++ /dev/null @@ -1,26 +0,0 @@ -package com.wakeUpTogetUp.togetUp.api.notification.dto.response; - -import lombok.*; - -@Getter -@Setter -@NoArgsConstructor -@Builder -public class PushNotificationRes { - private String message; - private String category; - private String response; - private String topic; - - public PushNotificationRes(String message, String category, String response) { - this.message = message; - this.category = category; - this.response = response; - } - public PushNotificationRes(String message, String category, String response, String topic) { - this.message = message; - this.category = category; - this.response = response; - this.topic = topic; - } -} \ No newline at end of file diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/entity/Notification.java b/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/entity/Notification.java index 0ab50e64..ee681358 100644 --- a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/entity/Notification.java +++ b/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/entity/Notification.java @@ -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; @@ -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; } diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/vo/NotificationSendVo.java b/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/vo/NotificationSendEvent.java similarity index 76% rename from src/main/java/com/wakeUpTogetUp/togetUp/api/notification/vo/NotificationSendVo.java rename to src/main/java/com/wakeUpTogetUp/togetUp/api/notification/vo/NotificationSendEvent.java index 38becf9c..29fda766 100644 --- a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/vo/NotificationSendVo.java +++ b/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/vo/NotificationSendEvent.java @@ -8,13 +8,13 @@ @ToString @Getter -public class NotificationSendVo { +public class NotificationSendEvent { private final List fcmTokens; private final String title; private final String body; private final Map dataMap; - public NotificationSendVo(List fcmTokens, String title, String body, Map dataMap) { + public NotificationSendEvent(List fcmTokens, String title, String body, Map dataMap) { this.fcmTokens = fcmTokens; this.title = title; this.body = body; diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/vo/RoomMissionLogNotificationVo.java b/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/vo/RoomMissionLogNotificationEvent.java similarity index 83% rename from src/main/java/com/wakeUpTogetUp/togetUp/api/notification/vo/RoomMissionLogNotificationVo.java rename to src/main/java/com/wakeUpTogetUp/togetUp/api/notification/vo/RoomMissionLogNotificationEvent.java index 973fe7f8..d8e7711a 100644 --- a/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/vo/RoomMissionLogNotificationVo.java +++ b/src/main/java/com/wakeUpTogetUp/togetUp/api/notification/vo/RoomMissionLogNotificationEvent.java @@ -7,16 +7,15 @@ import com.wakeUpTogetUp.togetUp.api.users.fcmToken.FcmToken; import com.wakeUpTogetUp.togetUp.api.users.model.User; -import java.text.DecimalFormat; import java.util.HashMap; import java.util.List; import java.util.Map; -public class RoomMissionLogNotificationVo extends NotificationSendVo { +public class RoomMissionLogNotificationEvent extends NotificationSendEvent { private static final String title = "%s님이 미션을 완료했어요!"; private static final String body = "%s 그룹"; - public RoomMissionLogNotificationVo(User user, Room room, List fcmTokens) { + public RoomMissionLogNotificationEvent(User user, Room room, List fcmTokens) { super(fcmTokens, String.format(title, user.getName()), String.format(body, room.getName()), createDataMap(room.getId())); } diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/api/users/UserEventHandler.java b/src/main/java/com/wakeUpTogetUp/togetUp/api/users/UserEventHandler.java new file mode 100644 index 00000000..1270bef1 --- /dev/null +++ b/src/main/java/com/wakeUpTogetUp/togetUp/api/users/UserEventHandler.java @@ -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()); + } + +} + diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/api/users/UserService.java b/src/main/java/com/wakeUpTogetUp/togetUp/api/users/UserService.java index 936d4c42..95c44d19 100644 --- a/src/main/java/com/wakeUpTogetUp/togetUp/api/users/UserService.java +++ b/src/main/java/com/wakeUpTogetUp/togetUp/api/users/UserService.java @@ -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; @@ -68,6 +70,11 @@ private void saveFcmToken(User user, String fcmTokenValue) { fcmTokenRepository.save(fcmToken); } + @Transactional + public void deleteFcmTokens(List fcmTokenValues) { + fcmTokenRepository.deleteByValueIn(fcmTokenValues); + } + public void updateAgreePush(Integer userId, boolean agreePush) { User user = findExistingUser(userRepository, userId); user.changeAgreePush(agreePush); diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/api/users/fcmToken/FcmTokenDeleteEvent.java b/src/main/java/com/wakeUpTogetUp/togetUp/api/users/fcmToken/FcmTokenDeleteEvent.java new file mode 100644 index 00000000..0a7e4116 --- /dev/null +++ b/src/main/java/com/wakeUpTogetUp/togetUp/api/users/fcmToken/FcmTokenDeleteEvent.java @@ -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 fcmTokens; +} diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/api/users/fcmToken/FcmTokenRepository.java b/src/main/java/com/wakeUpTogetUp/togetUp/api/users/fcmToken/FcmTokenRepository.java index a82cf9ff..b36fb531 100644 --- a/src/main/java/com/wakeUpTogetUp/togetUp/api/users/fcmToken/FcmTokenRepository.java +++ b/src/main/java/com/wakeUpTogetUp/togetUp/api/users/fcmToken/FcmTokenRepository.java @@ -10,7 +10,8 @@ public interface FcmTokenRepository extends JpaRepository { List findAllByUser_IdIn(List userIds); - void deleteAllByValueIn(List values); + + void deleteByValueIn(List fcmTokenValues); boolean existsByValue(String fcmTokenValue); } diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/api/users/model/User.java b/src/main/java/com/wakeUpTogetUp/togetUp/api/users/model/User.java index 257a5cb1..34a26f0f 100644 --- a/src/main/java/com/wakeUpTogetUp/togetUp/api/users/model/User.java +++ b/src/main/java/com/wakeUpTogetUp/togetUp/api/users/model/User.java @@ -80,6 +80,7 @@ public class User { @Column(name = "is_deleted") private boolean isDeleted; + @JsonIgnore @OneToMany(mappedBy = "user") private List fcmToken = new ArrayList<>(); diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/config/EventsConfig.java b/src/main/java/com/wakeUpTogetUp/togetUp/config/EventsConfig.java new file mode 100644 index 00000000..ad3d0a3d --- /dev/null +++ b/src/main/java/com/wakeUpTogetUp/togetUp/config/EventsConfig.java @@ -0,0 +1,19 @@ +package com.wakeUpTogetUp.togetUp.config; + +import com.wakeUpTogetUp.togetUp.api.event.EventPublisher; +import org.springframework.beans.factory.InitializingBean; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.context.ApplicationContext; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; +@Configuration +public class EventsConfig { + @Autowired + private ApplicationContext applicationContext; + + @Bean + public InitializingBean eventsInitializer(ApplicationEventPublisher eventPublisher) { + return () -> EventPublisher.setPublisher(eventPublisher); + } +} diff --git a/src/main/java/com/wakeUpTogetUp/togetUp/infra/google/fcm/FcmService.java b/src/main/java/com/wakeUpTogetUp/togetUp/infra/fcm/FcmService.java similarity index 53% rename from src/main/java/com/wakeUpTogetUp/togetUp/infra/google/fcm/FcmService.java rename to src/main/java/com/wakeUpTogetUp/togetUp/infra/fcm/FcmService.java index 6beaba2d..49e273cf 100644 --- a/src/main/java/com/wakeUpTogetUp/togetUp/infra/google/fcm/FcmService.java +++ b/src/main/java/com/wakeUpTogetUp/togetUp/infra/fcm/FcmService.java @@ -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.event.EventPublisher; +import com.wakeUpTogetUp.togetUp.api.notification.vo.NotificationSendEvent; 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; - public void sendMessageToTokens(NotificationSendVo notificationSendVo) { - // message 만들기 - MulticastMessage message = getPreconfiguredMessageToTokens(notificationSendVo); - ApiFuture response = sendAndGetResponse(message); + public void sendMessageToTokens(NotificationSendEvent notificationSendEvent) { - deleteInvalidToken(response, notificationSendVo.getFcmTokens().stream() + MulticastMessage message = getPreconfiguredMessageToTokens(notificationSendEvent); + ApiFuture response = sendAndGetResponse(message); + deleteInvalidToken(response, notificationSendEvent.getFcmTokens().stream() .map(FcmToken::getValue) .collect(Collectors.toList())); } + public void deleteInvalidToken(ApiFuture response, List deviceTokens) { try { if (response.get().getFailureCount() > 0) { List failedDeviceTokens = new ArrayList<>(); List 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.raise(new FcmTokenDeleteEvent(failedDeviceTokens)); } } catch (InterruptedException e) { throw new RuntimeException(e); @@ -51,34 +69,33 @@ public void deleteInvalidToken(ApiFuture response, List d } private ApiFuture sendAndGetResponse(MulticastMessage message) { - ApiFuture response = FirebaseMessaging.getInstance().sendMulticastAsync(message); + ApiFuture response = firebaseMessaging.sendMulticastAsync(message); return response; } - // token 여러 개 - private MulticastMessage getPreconfiguredMessageToTokens(NotificationSendVo notificationSendVo) { - return getPreconfiguredMulticastMessageBuilder(notificationSendVo).addAllTokens(notificationSendVo.getFcmTokens().stream() + private MulticastMessage getPreconfiguredMessageToTokens(NotificationSendEvent notificationSendEvent) { + return getPreconfiguredMulticastMessageBuilder(notificationSendEvent).addAllTokens(notificationSendEvent.getFcmTokens().stream() .map(FcmToken::getValue) .collect(Collectors.toList())) .build(); } - private Message.Builder getPreconfiguredMessageBuilder(NotificationSendVo notificationSendVo) { + private Message.Builder getPreconfiguredMessageBuilder(NotificationSendEvent notificationSendEvent) { return Message.builder() .setNotification(Notification.builder() - .setTitle(notificationSendVo.getTitle()) - .setBody(notificationSendVo.getBody()) + .setTitle(notificationSendEvent.getTitle()) + .setBody(notificationSendEvent.getBody()) .build()); } - private MulticastMessage.Builder getPreconfiguredMulticastMessageBuilder(NotificationSendVo notificationSendVo) { + private MulticastMessage.Builder getPreconfiguredMulticastMessageBuilder(NotificationSendEvent notificationSendEvent) { return MulticastMessage.builder() .setNotification(Notification.builder() - .setTitle(notificationSendVo.getTitle()) - .setBody(notificationSendVo.getBody()) + .setTitle(notificationSendEvent.getTitle()) + .setBody(notificationSendEvent.getBody()) .build()) - .putAllData(notificationSendVo.getDataMap()); + .putAllData(notificationSendEvent.getDataMap()); } }