Skip to content

Commit

Permalink
♻️ Refactor: FCM 알림 기능 리팩토링 (#96)
Browse files Browse the repository at this point in the history
* ♻️ Refactor: FCM 알림 기능 리팩토링

* ✨ Feature: 리마인더 기능 보완
  • Loading branch information
ahnsugyeong authored Jun 2, 2024
1 parent b8149f2 commit 422c4c5
Show file tree
Hide file tree
Showing 9 changed files with 122 additions and 12 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,7 @@
import zzangdol.user.business.UserFacade;
import zzangdol.user.domain.User;
import zzangdol.user.presentation.dto.request.PushNotificationRequest;
import zzangdol.user.presentation.dto.request.TestPushNotificationRequest;
import zzangdol.user.presentation.dto.request.UserInfoUpdateRequest;
import zzangdol.user.presentation.dto.response.UserInfoResponse;

Expand Down Expand Up @@ -59,13 +60,13 @@ public ResponseDto<Void> handlePushNotifications(@AuthUser User user, @RequestBo
}

@Operation(
summary = "테스트 푸시 알림 전송 🔑",
summary = "[테스트] 푸시 알림 전송",
description = "특정 사용자의 FCM 토큰으로 푸시 알림을 즉시 전송합니다."
)
@PostMapping("/send-test-notification")
public ResponseDto<Void> sendTestNotification(@RequestBody PushNotificationRequest request) {
public ResponseDto<Void> sendTestNotification(@RequestBody TestPushNotificationRequest request) {
try {
fcmService.sendNotification(request.getFcmToken(), "테스트 title", "테스트 body");
fcmService.sendNotification(request.getFcmToken(), request.getTitle(), request.getBody());
} catch (Exception e) {
e.printStackTrace();
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package zzangdol.user.presentation.dto.request;

import lombok.Getter;
import lombok.Setter;

@Getter
@Setter
public class TestPushNotificationRequest {

private String fcmToken;
private String title;
private String body;

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,12 @@
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import zzangdol.user.dao.querydsl.UserQueryRepository;
import zzangdol.user.domain.AuthProvider;
import zzangdol.user.domain.User;

@Repository
public interface UserRepository extends JpaRepository<User, Long> {
public interface UserRepository extends JpaRepository<User, Long>, UserQueryRepository {

Optional<User> findByAuthProviderAndEmail(AuthProvider authProvider, String email);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package zzangdol.user.dao.querydsl;

import java.util.List;
import zzangdol.user.domain.User;

public interface UserQueryRepository {

List<User> findUsersWithNotificationTime(int hour, int minute);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package zzangdol.user.dao.querydsl;

import static zzangdol.user.domain.QUser.user;

import com.querydsl.jpa.impl.JPAQueryFactory;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Repository;
import zzangdol.user.domain.User;

@RequiredArgsConstructor
@Repository
public class UserQueryRepositoryImpl implements UserQueryRepository {

private final JPAQueryFactory queryFactory;

@Override
public List<User> findUsersWithNotificationTime(int hour, int minute) {
return queryFactory.selectFrom(user)
.where(user.notificationTime.hour().eq(hour)
.and(user.notificationTime.minute().eq(minute)))
.fetch();
}

}
8 changes: 8 additions & 0 deletions moodoodle-domain/src/main/java/zzangdol/user/domain/User.java
Original file line number Diff line number Diff line change
@@ -1,15 +1,19 @@
package zzangdol.user.domain;

import jakarta.persistence.CascadeType;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.EnumType;
import jakarta.persistence.Enumerated;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.OneToMany;
import java.time.LocalTime;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
Expand All @@ -18,6 +22,7 @@
import org.springframework.security.core.authority.SimpleGrantedAuthority;
import org.springframework.security.core.userdetails.UserDetails;
import zzangdol.global.BaseTimeEntity;
import zzangdol.notification.domain.FcmToken;

@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
Expand All @@ -42,6 +47,9 @@ public class User extends BaseTimeEntity implements UserDetails {
@Enumerated(EnumType.STRING)
private Role role;

@OneToMany(mappedBy = "user", cascade = CascadeType.ALL, orphanRemoval = true)
private List<FcmToken> fcmTokens = new ArrayList<>();

@Builder
public User(String email, String password, String nickname, AuthProvider authProvider, Role role,
LocalTime notificationTime) {
Expand Down
1 change: 1 addition & 0 deletions moodoodle-notification/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@ dependencies {
implementation 'org.springframework.boot:spring-boot-starter'
testImplementation 'org.springframework.boot:spring-boot-starter-test'
implementation 'org.springframework.boot:spring-boot-starter-web'
implementation 'org.springframework.boot:spring-boot-starter-security'

// fcm
implementation 'com.google.firebase:firebase-admin:8.0.0'
Expand Down
11 changes: 8 additions & 3 deletions moodoodle-notification/src/main/java/zzangdol/FCMService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.Notification;
import java.util.List;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -17,14 +18,18 @@ public class FCMService {
private final FcmTokenRepository fcmTokenRepository;

public void sendNotification(String token, String title, String body) throws Exception {
Notification notification = Notification.builder()
.setTitle(title)
.setBody(body)
.build();

Message message = Message.builder()
.putData("title", title)
.putData("body", body)
.setNotification(notification)
.setToken(token)
.build();

String response = FirebaseMessaging.getInstance().send(message);
System.out.println("Successfully sent message: " + response);
log.info("Successfully sent message: " + response);
}

public void sendNotificationToAllUsers(String title, String body) throws Exception {
Expand Down
Original file line number Diff line number Diff line change
@@ -1,23 +1,68 @@
package zzangdol;

import java.time.LocalTime;
import java.util.List;
import java.util.Random;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Component;
import zzangdol.notification.domain.FcmToken;
import zzangdol.user.dao.UserRepository;
import zzangdol.user.domain.User;

@RequiredArgsConstructor
@Component
public class NotificationScheduler {

private final FCMService fcmService;
private final UserRepository userRepository;

@Scheduled(cron = "0 0 21 * * ?")
private static final List<String> TITLES = List.of(
"무두들: 오늘의 일기 시간이에요!",
"무두들: 일기 쓸 시간입니다",
"무두들: 오늘 하루 어땠나요?",
"무두들: 일기 작성 시간!",
"무두들: 오늘의 생각을 기록해보세요",
"무두들: 하루를 마무리하며 일기를 써보세요",
"무두들: 당신의 하루를 기록하세요",
"무두들: 일기 쓰는 시간을 잊지 마세요",
"무두들: 오늘의 일기를 써보세요",
"무두들: 잠시 시간을 내어 일기를 써보세요"
);

private static final List<String> BODIES = List.of(
"무두들에서 오늘 하루 있었던 일들을 일기에 적어보세요.",
"무두들에 오늘의 감정과 경험을 남겨보세요.",
"무두들에서 일기를 쓰며 하루를 정리해보세요.",
"무두들에 오늘 하루를 돌아보며 일기를 작성해보세요.",
"무두들에서 일기를 통해 하루를 마무리해보세요.",
"무두들에 오늘 느낀 감정과 생각을 기록해보세요.",
"무두들에서 일기를 쓰며 하루를 정리해보는 건 어떨까요?",
"무두들에 오늘의 소중한 순간들을 남겨보세요.",
"무두들에서 하루를 마무리하며 일기를 써보세요.",
"무두들에 오늘 하루를 기록하며 내일을 준비해보세요."
);

@Scheduled(cron = "0,30 * * * * ?") // 30분 단위로 실행
public void scheduleDailyNotification() {
try {
String title = "Daily Reminder";
String body = "This is your daily reminder!";
fcmService.sendNotificationToAllUsers(title, body);
LocalTime now = LocalTime.now();
int currentHour = now.getHour();
int currentMinute = now.getMinute();

List<User> users = userRepository.findUsersWithNotificationTime(currentHour, currentMinute);

for (User user : users) {
Random random = new Random();
String title = TITLES.get(random.nextInt(TITLES.size()));
String body = BODIES.get(random.nextInt(BODIES.size()));
List<FcmToken> tokens = user.getFcmTokens();
for (FcmToken token : tokens) {
fcmService.sendNotification(token.getToken(), title, body);
}
}
} catch (Exception e) {
e.printStackTrace();
}
}
}
}

0 comments on commit 422c4c5

Please sign in to comment.