Skip to content

Commit

Permalink
feat: 푸시 알림 추가
Browse files Browse the repository at this point in the history
수정
  • Loading branch information
jihoon-jang committed Oct 19, 2023
1 parent cb655bf commit 6ac8c79
Show file tree
Hide file tree
Showing 9 changed files with 181 additions and 7 deletions.
4 changes: 4 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ dependencies {
implementation("org.flywaydb:flyway-core")
implementation("org.flywaydb:flyway-mysql")

// FCM
implementation 'com.google.firebase:firebase-admin:9.2.0'
implementation("com.squareup.okhttp3:okhttp")

asciidoctorExtensions 'org.springframework.restdocs:spring-restdocs-asciidoctor'
testAnnotationProcessor 'org.projectlombok:lombok'
testImplementation("com.h2database:h2")
Expand Down
45 changes: 45 additions & 0 deletions src/main/java/com/polzzak/domain/notification/dto/FcmMessage.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.polzzak.domain.notification.dto;

import lombok.Getter;
import lombok.NoArgsConstructor;

@NoArgsConstructor
@Getter
public class FcmMessage {

private boolean validateOnly;
private Message message;
private String to;

public FcmMessage(boolean validateOnly, Message message, String to) {
this.validateOnly = validateOnly;
this.message = message;
this.to = to;
}

@NoArgsConstructor
@Getter
public static class Message {
private Notification notification;
private String token;

public Message(Notification notification, String token) {
this.notification = notification;
this.token = token;
}
}

@NoArgsConstructor
@Getter
public static class Notification {
private String title;
private String body;
private String image;

public Notification(String title, String body, String image) {
this.title = title;
this.body = body;
this.image = image;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,15 @@ public String getMessageWithParameter(final String parameter) {
return String.format(this.message, "<b>" + parameter + "</b>");
}

public String getParameterWithoutBold(final String parameter) {
if (this == STAMP_REQUEST || this == REWARD_REQUEST || this == STAMP_BOARD_COMPLETE || this == REWARDED
|| this == REWARD_REQUEST_AGAIN || this == REWARD_FAIL || this == CREATED_STAMP_BOARD
|| this == ISSUED_COUPON || this == REWARDED_REQUEST) {
return String.format(this.message, "<b>'" + parameter + "'</b>");
}
return String.format(this.message, parameter);
}

public String getLinkWithParameter(final String parameter) {
if (link == null) {
return null;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,10 +5,14 @@
import org.springframework.stereotype.Component;

import com.polzzak.domain.notification.dto.NotificationCreateEvent;
import com.polzzak.domain.notification.dto.NotificationDto;
import com.polzzak.domain.notification.dto.NotificationSettingDto;
import com.polzzak.domain.notification.entity.Notification;
import com.polzzak.domain.notification.entity.NotificationType;
import com.polzzak.domain.notification.service.NotificationService;
import com.polzzak.domain.user.entity.Member;
import com.polzzak.domain.user.service.UserService;
import com.polzzak.global.infra.firebase.FirebaseCloudMessageService;

import lombok.RequiredArgsConstructor;
import lombok.extern.log4j.Log4j2;
Expand All @@ -19,6 +23,8 @@
public class NotificationEventHandler {

private final NotificationService notificationService;
private final FirebaseCloudMessageService firebaseCloudMessageService;
private final UserService userService;

@Async
@EventListener
Expand Down Expand Up @@ -91,12 +97,22 @@ public void addNotification(NotificationCreateEvent event) {
}
}

notificationService.addNotification(event.senderId(), event.receiverId(), event.type(), event.data());
Notification notification = notificationService.addNotification(event.senderId(), event.receiverId(),
event.type(), event.data());
sendPushNotification(event.senderId(), notification);

log.info("[NotificationEvent] info. sender_id : {}, receiver_id : {}, type : {}, data : {}",
event.senderId(), event.receiverId(), event.type(), event.data());
} catch (Exception e) {
log.error("[NotificationEvent] error.", e);
}
}

private void sendPushNotification(long memberId, Notification notification) {
Member member = userService.findMemberByMemberId(memberId);
NotificationDto notificationDto = notificationService.getNotificationDto(member, notification, false);

firebaseCloudMessageService.sendPushNotification(member, notificationDto.title(), notificationDto.message(),
notificationDto.link());
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ public class NotificationService {
private final NotificationSettingRepository notificationSettingRepository;

@Transactional
public void addNotification(final Long senderId, final Long receiverId, final NotificationType type,
public Notification addNotification(final Long senderId, final Long receiverId, final NotificationType type,
final String data) {
Member sender = userService.findMemberByMemberId(senderId);
Member receiver = userService.findMemberByMemberId(receiverId);
Expand All @@ -53,6 +53,7 @@ public void addNotification(final Long senderId, final Long receiverId, final No
.data(data)
.build();
notificationRepository.save(notification);
return notification;
}

@Transactional
Expand Down Expand Up @@ -126,18 +127,26 @@ private NotificationResponse getNotificationResponse(final Long memberId, final
pageRequest);

List<NotificationDto> notificationDtoList = notifications.getContent().stream()
.map(notification -> getNotificationDto(member, notification))
.map(notification -> getNotificationDto(member, notification, true))
.toList();

return NotificationResponse.from(pageRequest, notificationDtoList, notifications.hasNext());
}

private NotificationDto getNotificationDto(final Member member, final Notification notification) {
public NotificationDto getNotificationDto(final Member member, final Notification notification,
final boolean isBold) {
Member sender = notification.getSender();
MemberDtoForNotification senderDto = sender == null ? null : MemberDtoForNotification.from(sender,
fileClient.getSignedUrl(sender.getProfileKey()));
String message = notification.getType()
.getMessageWithParameter(getMessageParameter(member.getId(), notification));
String message;
if (isBold) {
message = notification.getType()
.getMessageWithParameter(getMessageParameter(member.getId(), notification));
} else {
message = notification.getType()
.getParameterWithoutBold(getMessageParameter(member.getId(), notification));
}

String link = notification.getType().getLinkWithParameter(getLinkParameter(member.getId(), notification));

return NotificationDto.from(notification, message, link, senderDto);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package com.polzzak.domain.pushtoken.repository;

import java.util.List;

import org.springframework.data.jpa.repository.JpaRepository;

import com.polzzak.domain.pushtoken.model.PushToken;
import com.polzzak.domain.user.entity.Member;

public interface PushTokenRepository extends JpaRepository<PushToken, Long> {

List<PushToken> getPushTokensByMember(Member member);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.polzzak.domain.pushtoken.service;

import java.util.List;

import org.springframework.dao.DataIntegrityViolationException;
import org.springframework.stereotype.Service;

Expand All @@ -17,7 +19,7 @@ public class PushTokenService {
private final UserService userService;
private final PushTokenRepository pushTokenRepository;

public void addToken(Long memberId, String token) {
public void addToken(final Long memberId, final String token) {
Member member = userService.findMemberByMemberId(memberId);

PushToken pushToken = PushToken.createPushToken()
Expand All @@ -31,4 +33,8 @@ public void addToken(Long memberId, String token) {

}
}

public List<PushToken> getPushTokens(final Member member) {
return pushTokenRepository.getPushTokensByMember(member);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.polzzak.global.infra.firebase;

import java.io.FileInputStream;
import java.util.List;

import org.springframework.stereotype.Service;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.messaging.BatchResponse;
import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingException;
import com.google.firebase.messaging.MulticastMessage;
import com.google.firebase.messaging.Notification;
import com.polzzak.domain.pushtoken.model.PushToken;
import com.polzzak.domain.pushtoken.service.PushTokenService;
import com.polzzak.domain.user.entity.Member;

import lombok.RequiredArgsConstructor;

@Service
@RequiredArgsConstructor
public class FirebaseCloudMessageService {

private final PushTokenService pushTokenService;

public void sendPushNotification(Member member, String title, String body, String link) {
try {
FileInputStream serviceAccount = new FileInputStream(
"src/main/resources/firebase/firebase_service_key.json");

FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.build();

if (FirebaseApp.getApps().isEmpty()) {
FirebaseApp.initializeApp(options);
}

List<String> registrationTokens = pushTokenService.getPushTokens(member).stream()
.map(PushToken::getToken)
.toList();

MulticastMessage message = MulticastMessage.builder()
.setNotification(Notification.builder()
.setTitle(title)
.setBody(body)
.build())
.addAllTokens(registrationTokens)
.putData("link", link)
.build();
BatchResponse response = null;
try {
response = FirebaseMessaging.getInstance().sendEachForMulticast(message);
} catch (FirebaseMessagingException e) {
e.printStackTrace();
}
// See the BatchResponse reference documentation
// for the contents of response.
System.out.println(response.getSuccessCount() + " messages were sent successfully");
System.out.println(response);

} catch (Exception e) {

}
}
}
13 changes: 13 additions & 0 deletions src/main/resources/firebase/firebase_service_key.json
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
{
"type": "service_account",
"project_id": "polzzak-57648",
"private_key_id": "ead4311d449ac5facc6c507ec152a6d9f54a279d",
"private_key": "-----BEGIN PRIVATE KEY-----\nMIIEvgIBADANBgkqhkiG9w0BAQEFAASCBKgwggSkAgEAAoIBAQCuffOTpdr5Z23/\nn6c37jA+TkjDj8MZxBr67ovNVgKSUchidJe3A8VYS+Ck6tMaMr7/YPUu6OfkkHOS\n0VlW3UQwYRqj5Rk5kH6ByBxVH9ZI7Sh0FO9pH71dsHZXwVLam6UOKcyrrbJhwcfX\nXrnksdCi4LxhvIxTsn0z11Q3CXtf5diKXgIt+Ewgqk/sKWFmCLSjIATD6mMn5VTl\nD8+QyOCV/U6ec3CmXHj2RAJMm1FiIE0vHDaGwNtc6hha/igibK6OoQ5+lyrnvuMr\ngGS3o2zH5TOBfMs0FI9KrHaUgxfa0p9jG4Oze1ibyZhK+6hl1RSBSWuxy5iY77vp\n4fdrzzexAgMBAAECggEACaYdWgTJ3xDBHGmPraAWOtvJWkcQ2tPlSgr24BvpeH3d\nPtSDrzMeLovDmFsD4Wb8+NI7vKRUbmcufOfmsM77flFgT7/TbUN4O2T9bBeemdnD\naufddUq0BgJECQY/tqb0sZvOHZA1VQKKMnaigOr0Ro123VC30ckE82Ds3z4+/EZ5\n4+KKxVxXMiV3seQOHIdiMHI/ClrUTrr7S7h67UNJ6ZFzuP/JD1j7XENO9lM2haCL\ny3uxa7KEHDJWs1DU6O8FsXaxdb/XCMAYYTnH33SBtnUxgHNmdbv6IyZfTi/Wm+rY\nC8Vrua52T+8VRav1btVCVNQE6RpNWgWFGGq8pAy1oQKBgQDyDUNyfnfK/xZJAZ9N\nKMe7uBhrS95PAF33fHF6Kc70WPO9JLxWnXFNJPiT9bCRer7Vc9fkB388N43BnGX9\nYDR68EySd2GA8C2BloM6/pBULg7u4CxOrbcaWgqkY8oEktM7cgn5I2EVOGVwTfwP\n420ApXUS5zdUIGR/XsUROBf+hwKBgQC4jAyHNzGhczEALhbLTB+4gZqRO3J1z2uH\n4msAyyD5dU/Vrpa+i+HwG/wWELAzIRbFbwL8pEa4qtbD+ofDN3ytpn86KQZrwxK2\n0m89ajadwppeOh2z051TGCeU1cyECkv3iUPfZP+z9fERnNdAt+cly462zgaH+uJT\n2+/luP0uBwKBgQCJBhEkg4t1EyqecZioqWlIT1MjinNy7ZZEP+JNcdWCZci1TlKA\nBejZ7w/5UqB9+qqFU2rn34abpCdPbyYdZZTP87ClSYec4logfgAUKX+y58/0UltC\nvvxkooxbu1HlfOivQkN7EhgnVyG1jbAfnnNaZk/8P4AG07+QiymsMcEDiQKBgAyx\ntXrnlQZiAhDdGrxJNDVg1N0AldL8vYzPSkT3tAD0zNUJ+VyKCrSVeDWcWEJsGEDk\nbfQq6KJzPeqlJQmMm4rmVQIPKF3pQTRKLVSwJamcZTnuDXT9LWk11CMswbCjdK5G\nRuDq9ZvPYxGvFC9jdwbmhZ6VdWWNIFxcWJgYrXGpAoGBAOwyu9ycczHwhaiITVnH\nDmG7S2LCj8Jq3u3351c6dv/Lt8wuZ6u2FmTuMh8id9hD7u2TO9QzIF8+S10Rc/J8\nzb9GI3KLg67bCCrReZQ0B1L2cjKUObpx5om2icxYXTV1k3GyVg7RJwQ5NA8ncQSP\nvXysN6nV8rcLOSuK8iGsS9Je\n-----END PRIVATE KEY-----\n",
"client_email": "[email protected]",
"client_id": "101961288027838881996",
"auth_uri": "https://accounts.google.com/o/oauth2/auth",
"token_uri": "https://oauth2.googleapis.com/token",
"auth_provider_x509_cert_url": "https://www.googleapis.com/oauth2/v1/certs",
"client_x509_cert_url": "https://www.googleapis.com/robot/v1/metadata/x509/firebase-adminsdk-mzmu4%40polzzak-57648.iam.gserviceaccount.com",
"universe_domain": "googleapis.com"
}

0 comments on commit 6ac8c79

Please sign in to comment.