From 67cde016131bb094e19c6fa40a67de7954d05cd4 Mon Sep 17 00:00:00 2001 From: realisshomyang Date: Wed, 31 Jan 2024 16:42:34 +0900 Subject: [PATCH 1/5] =?UTF-8?q?[feat]:=20=ED=91=B8=EC=8B=9C=EC=95=8C?= =?UTF-8?q?=EB=9E=8C=20=EA=B4=80=EB=A0=A8=20firebaseConfig=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .gitignore | 1 + build.gradle | 2 + .../onnoff/onnoff/config/FirebaseConfig.java | 43 +++++++++++++++++++ 3 files changed, 46 insertions(+) create mode 100644 src/main/java/com/onnoff/onnoff/config/FirebaseConfig.java diff --git a/.gitignore b/.gitignore index 93b290e..904e84b 100644 --- a/.gitignore +++ b/.gitignore @@ -315,4 +315,5 @@ gradle-app.setting # Java heap dump *.hprof +**/resources/* # End of https://www.toptal.com/developers/gitignore/api/macos,intellij,intellij+iml,intellij+all,gradle,java diff --git a/build.gradle b/build.gradle index ddefedb..08ad8c5 100644 --- a/build.gradle +++ b/build.gradle @@ -37,6 +37,8 @@ dependencies { implementation 'io.jsonwebtoken:jjwt-api:0.12.3' implementation 'io.jsonwebtoken:jjwt-jackson:0.12.3' runtimeOnly 'io.jsonwebtoken:jjwt-impl:0.12.3' + //FCM + implementation 'com.google.firebase:firebase-admin:9.2.0' } tasks.named('bootBuildImage') { diff --git a/src/main/java/com/onnoff/onnoff/config/FirebaseConfig.java b/src/main/java/com/onnoff/onnoff/config/FirebaseConfig.java new file mode 100644 index 0000000..70a9fc0 --- /dev/null +++ b/src/main/java/com/onnoff/onnoff/config/FirebaseConfig.java @@ -0,0 +1,43 @@ +package com.onnoff.onnoff.config; + +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import jakarta.annotation.PostConstruct; +import lombok.extern.slf4j.Slf4j; +import org.slf4j.Logger; +import org.slf4j.LoggerFactory; +import org.springframework.beans.factory.annotation.Value; +import org.springframework.context.annotation.Configuration; +import org.springframework.core.io.ClassPathResource; +import java.io.FileNotFoundException; +import java.io.IOException; +import java.io.InputStream; + +@Configuration +@Slf4j +public class FirebaseConfig { + private final Logger logger = LoggerFactory.getLogger(FirebaseConfig.class); + @Value("${fcm.firebase-sdk-path}") // your firebase sdk path + private String firebaseSdkPath; + @Value("${fcm.project-id}") + private String projectId; + @PostConstruct + public void initialize() { + try { + ClassPathResource resource = new ClassPathResource(firebaseSdkPath); + InputStream serviceAccount = resource.getInputStream(); + FirebaseOptions options = FirebaseOptions.builder() + .setCredentials(GoogleCredentials.fromStream(serviceAccount)) + .build(); + if (FirebaseApp.getApps().isEmpty()) { + FirebaseApp.initializeApp(options); + log.info("Successfully firebase application initialized!"); + } + } catch (FileNotFoundException e) { + logger.error("Firebase ServiceAccountKey FileNotFoundException" + e.getMessage()); + } catch (IOException e) { + logger.error("FirebaseOptions IOException" + e.getMessage()); + } + } +} From 41cbc613362a7a45d647e4fbe295092e9785d70b Mon Sep 17 00:00:00 2001 From: realisshomyang Date: Wed, 31 Jan 2024 16:43:55 +0900 Subject: [PATCH 2/5] =?UTF-8?q?[feat]:=20=ED=91=B8=EC=8B=9C=EC=95=8C?= =?UTF-8?q?=EB=9E=8C=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20api=20=EC=9E=91?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/push/controller/FcmController.java | 21 ++++++++ .../onnoff/domain/push/dto/FcmServiceDto.java | 24 +++++++++ .../domain/push/dto/NotificationMessage.java | 35 +++++++++++++ .../domain/push/dto/NotificationType.java | 5 ++ .../service/FcmSendNotificationService.java | 27 ++++++++++ .../domain/push/service/FcmService.java | 52 +++++++++++++++++++ 6 files changed, 164 insertions(+) create mode 100644 src/main/java/com/onnoff/onnoff/domain/push/controller/FcmController.java create mode 100644 src/main/java/com/onnoff/onnoff/domain/push/dto/FcmServiceDto.java create mode 100644 src/main/java/com/onnoff/onnoff/domain/push/dto/NotificationMessage.java create mode 100644 src/main/java/com/onnoff/onnoff/domain/push/dto/NotificationType.java create mode 100644 src/main/java/com/onnoff/onnoff/domain/push/service/FcmSendNotificationService.java create mode 100644 src/main/java/com/onnoff/onnoff/domain/push/service/FcmService.java diff --git a/src/main/java/com/onnoff/onnoff/domain/push/controller/FcmController.java b/src/main/java/com/onnoff/onnoff/domain/push/controller/FcmController.java new file mode 100644 index 0000000..d4fa68c --- /dev/null +++ b/src/main/java/com/onnoff/onnoff/domain/push/controller/FcmController.java @@ -0,0 +1,21 @@ +package com.onnoff.onnoff.domain.push.controller; + +import com.onnoff.onnoff.domain.push.service.FcmSendNotificationService; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@Slf4j +public class FcmController { + private final FcmSendNotificationService fcmSendNotificationService; + + @GetMapping("/message/test") + public ResponseEntity pushTest(){ + fcmSendNotificationService.sendTestNotification(); + return ResponseEntity.ok("good"); + } +} diff --git a/src/main/java/com/onnoff/onnoff/domain/push/dto/FcmServiceDto.java b/src/main/java/com/onnoff/onnoff/domain/push/dto/FcmServiceDto.java new file mode 100644 index 0000000..bb8ed27 --- /dev/null +++ b/src/main/java/com/onnoff/onnoff/domain/push/dto/FcmServiceDto.java @@ -0,0 +1,24 @@ +package com.onnoff.onnoff.domain.push.dto; + +import lombok.Getter; +import lombok.NoArgsConstructor; + +@Getter +@NoArgsConstructor +public class FcmServiceDto { + private String username; + private Long contentId; + private NotificationType type; + private String title; + private String content; + + public static FcmServiceDto of(String username, Long contentId, NotificationType type, String title, String content){ + FcmServiceDto dto = new FcmServiceDto(); + dto.username = username; + dto.contentId = contentId; + dto.type = type; + dto.title = title; + dto.content = content; + return dto; + } +} diff --git a/src/main/java/com/onnoff/onnoff/domain/push/dto/NotificationMessage.java b/src/main/java/com/onnoff/onnoff/domain/push/dto/NotificationMessage.java new file mode 100644 index 0000000..e1a987d --- /dev/null +++ b/src/main/java/com/onnoff/onnoff/domain/push/dto/NotificationMessage.java @@ -0,0 +1,35 @@ +package com.onnoff.onnoff.domain.push.dto; + +import com.google.firebase.messaging.Notification; +import com.onnoff.onnoff.domain.user.User; +import lombok.Builder; + +@Builder +public record NotificationMessage( + String title, + String message, + NotificationType type +) { + public static NotificationMessage toGoHomeNotification(User user) { + return NotificationMessage.builder() + .title(user.getName() + "님, 이제 퇴근하실 시간이에요.") + .message("") + .type(NotificationType.NOTIFY) + .build(); + } + public static NotificationMessage toGoHomeNotificationTest() { + return NotificationMessage.builder() + .title("예진" + "님, 이제 퇴근하실 시간이에요.") + .message("") + .type(NotificationType.NOTIFY) + .build(); + } + + + public Notification toNotification() { + return Notification.builder() + .setTitle(title) + .setBody(message) + .build(); + } +} diff --git a/src/main/java/com/onnoff/onnoff/domain/push/dto/NotificationType.java b/src/main/java/com/onnoff/onnoff/domain/push/dto/NotificationType.java new file mode 100644 index 0000000..87be077 --- /dev/null +++ b/src/main/java/com/onnoff/onnoff/domain/push/dto/NotificationType.java @@ -0,0 +1,5 @@ +package com.onnoff.onnoff.domain.push.dto; + +public enum NotificationType { + NOTIFY +} diff --git a/src/main/java/com/onnoff/onnoff/domain/push/service/FcmSendNotificationService.java b/src/main/java/com/onnoff/onnoff/domain/push/service/FcmSendNotificationService.java new file mode 100644 index 0000000..d33d776 --- /dev/null +++ b/src/main/java/com/onnoff/onnoff/domain/push/service/FcmSendNotificationService.java @@ -0,0 +1,27 @@ +package com.onnoff.onnoff.domain.push.service; + +import com.google.firebase.messaging.Message; +import com.onnoff.onnoff.domain.push.dto.NotificationMessage; +import com.onnoff.onnoff.domain.user.repository.UserRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.scheduling.annotation.Scheduled; +import org.springframework.stereotype.Service; + +@Service +@RequiredArgsConstructor +public class FcmSendNotificationService { + private final FcmService fcmService; + private final UserRepository userRepository; + public void sendTestNotification() { + NotificationMessage notificationMessage = + NotificationMessage.toGoHomeNotificationTest(); + final Message message = + fcmService.createMessage("testtoken", notificationMessage); + fcmService.send(message); + } + @Scheduled(cron = "0 * * * * *", zone = "Asia/Seoul") + public void sendGoHomeNotificationByMinute() { + // Your logic to send the notification every minute + } + +} diff --git a/src/main/java/com/onnoff/onnoff/domain/push/service/FcmService.java b/src/main/java/com/onnoff/onnoff/domain/push/service/FcmService.java new file mode 100644 index 0000000..db1fa29 --- /dev/null +++ b/src/main/java/com/onnoff/onnoff/domain/push/service/FcmService.java @@ -0,0 +1,52 @@ +package com.onnoff.onnoff.domain.push.service; + +import com.google.firebase.messaging.*; +import com.onnoff.onnoff.domain.push.dto.NotificationMessage; +import lombok.Builder; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Service +@Builder +@RequiredArgsConstructor +public class FcmService { + public void send(Message message) { + FirebaseMessaging.getInstance() + .sendAsync(message); + } + public Message createMessage(String deviceToken, NotificationMessage notificationMessage) { + return Message.builder() + .setToken(deviceToken) + .setNotification(notificationMessage.toNotification()) + .putData("title", notificationMessage.title()) + .putData("body", notificationMessage.message()) + .putData("type", notificationMessage.type().name()) + .build(); + } + + //다음 행동 유도가 필요할 때 path를 쓴다..! + public Message createMessage(String deviceToken, NotificationMessage notificationMessage, String path) { + + Notification notification = notificationMessage.toNotification(); + return Message.builder() + .setToken(deviceToken) + .setNotification(notification) + .setApnsConfig( + ApnsConfig.builder() + .setAps( + Aps.builder() + .setSound("default") + .build() + ) + .build() + ) + .putData("title", notificationMessage.title()) + .putData("body", notificationMessage.message()) + .putData("type", notificationMessage.type().name()) + .putData("path", path) + .putData("sound", "default") + .build(); + } +} From d9b3af82f43990693c06d444ddb3996e88934e50 Mon Sep 17 00:00:00 2001 From: realisshomyang Date: Wed, 31 Jan 2024 16:57:08 +0900 Subject: [PATCH 3/5] =?UTF-8?q?[feat]:=20=ED=91=B8=EC=8B=9C=EC=95=8C?= =?UTF-8?q?=EB=9E=8C=20=EC=88=98=EC=8B=A0=20=EC=8B=9C=EA=B0=84=20=EC=BB=AC?= =?UTF-8?q?=EB=9F=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/onnoff/onnoff/domain/user/User.java | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/src/main/java/com/onnoff/onnoff/domain/user/User.java b/src/main/java/com/onnoff/onnoff/domain/user/User.java index 7dc6203..2212361 100644 --- a/src/main/java/com/onnoff/onnoff/domain/user/User.java +++ b/src/main/java/com/onnoff/onnoff/domain/user/User.java @@ -12,7 +12,9 @@ import lombok.*; import org.hibernate.annotations.DynamicInsert; +import java.sql.Time; import java.time.LocalDateTime; +import java.time.LocalTime; import java.util.ArrayList; import java.util.List; @@ -67,6 +69,9 @@ public class User extends BaseEntity { private String fcmToken; + @Column(nullable = true) + private LocalTime pushNotificationTime; + @Enumerated(EnumType.STRING) private SocialType socialType; From bb4897311b589818fd96a7f2b7ca100b8e8386c3 Mon Sep 17 00:00:00 2001 From: realisshomyang Date: Wed, 31 Jan 2024 17:44:13 +0900 Subject: [PATCH 4/5] =?UTF-8?q?[feat]:=20=ED=91=B8=EC=8B=9C=EC=95=8C?= =?UTF-8?q?=EB=9E=8C=20=EB=B6=84=20=EB=8B=A8=EC=9C=84=20=EC=A0=84=EC=86=A1?= =?UTF-8?q?=20=EA=B8=B0=EB=8A=A5=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/onnoff/onnoff/OnnoffApplication.java | 2 ++ .../onnoff/onnoff/auth/config/WebConfig.java | 2 +- .../onnoff/auth/jwt/filter/JwtAuthFilter.java | 2 +- .../service/FcmSendNotificationService.java | 20 +++++++++++++++++-- .../user/repository/UserRepository.java | 6 ++++++ 5 files changed, 28 insertions(+), 4 deletions(-) diff --git a/src/main/java/com/onnoff/onnoff/OnnoffApplication.java b/src/main/java/com/onnoff/onnoff/OnnoffApplication.java index 062589f..d0d6e99 100644 --- a/src/main/java/com/onnoff/onnoff/OnnoffApplication.java +++ b/src/main/java/com/onnoff/onnoff/OnnoffApplication.java @@ -5,10 +5,12 @@ import org.springframework.boot.autoconfigure.SpringBootApplication; import org.springframework.cloud.openfeign.EnableFeignClients; import org.springframework.data.jpa.repository.config.EnableJpaAuditing; +import org.springframework.scheduling.annotation.EnableScheduling; @EnableFeignClients(defaultConfiguration = FeignConfig.class) @SpringBootApplication @EnableJpaAuditing +@EnableScheduling public class OnnoffApplication { public static void main(String[] args) { diff --git a/src/main/java/com/onnoff/onnoff/auth/config/WebConfig.java b/src/main/java/com/onnoff/onnoff/auth/config/WebConfig.java index 75b5624..e9167bd 100644 --- a/src/main/java/com/onnoff/onnoff/auth/config/WebConfig.java +++ b/src/main/java/com/onnoff/onnoff/auth/config/WebConfig.java @@ -39,6 +39,6 @@ public void addInterceptors(InterceptorRegistry registry) { registry.addInterceptor(new UserInterceptor(userService, jwtUtil)) .addPathPatterns("/**") // 스프링 경로는 /*와 /**이 다름 - .excludePathPatterns("/swagger-ui/**", "/v3/api-docs/**", "/oauth2/**", "/on/**"); + .excludePathPatterns("/swagger-ui/**", "/v3/api-docs/**", "/oauth2/**", "/on/**", "/message/**"); } } diff --git a/src/main/java/com/onnoff/onnoff/auth/jwt/filter/JwtAuthFilter.java b/src/main/java/com/onnoff/onnoff/auth/jwt/filter/JwtAuthFilter.java index e6c8d0d..15ffb4f 100644 --- a/src/main/java/com/onnoff/onnoff/auth/jwt/filter/JwtAuthFilter.java +++ b/src/main/java/com/onnoff/onnoff/auth/jwt/filter/JwtAuthFilter.java @@ -25,7 +25,7 @@ @RequiredArgsConstructor public class JwtAuthFilter extends OncePerRequestFilter { private final JwtTokenProvider jwtTokenProvider; - private final static String[] ignorePrefix = {"/swagger-ui", "/v3/api-docs", "/oauth2", "/on"}; + private final static String[] ignorePrefix = {"/swagger-ui", "/v3/api-docs", "/oauth2", "/on","/message"}; @Override protected void doFilterInternal(HttpServletRequest request, HttpServletResponse response, FilterChain filterChain) throws ServletException, IOException { log.info("url ={}", request.getRequestURI()); diff --git a/src/main/java/com/onnoff/onnoff/domain/push/service/FcmSendNotificationService.java b/src/main/java/com/onnoff/onnoff/domain/push/service/FcmSendNotificationService.java index d33d776..e202d43 100644 --- a/src/main/java/com/onnoff/onnoff/domain/push/service/FcmSendNotificationService.java +++ b/src/main/java/com/onnoff/onnoff/domain/push/service/FcmSendNotificationService.java @@ -2,12 +2,19 @@ import com.google.firebase.messaging.Message; import com.onnoff.onnoff.domain.push.dto.NotificationMessage; +import com.onnoff.onnoff.domain.user.User; import com.onnoff.onnoff.domain.user.repository.UserRepository; import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; import org.springframework.scheduling.annotation.Scheduled; import org.springframework.stereotype.Service; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; + @Service +@Slf4j @RequiredArgsConstructor public class FcmSendNotificationService { private final FcmService fcmService; @@ -17,11 +24,20 @@ public void sendTestNotification() { NotificationMessage.toGoHomeNotificationTest(); final Message message = fcmService.createMessage("testtoken", notificationMessage); - fcmService.send(message); + fcmService.send(message); } + //1분 단위로 푸시알람 보내는 함수 @Scheduled(cron = "0 * * * * *", zone = "Asia/Seoul") public void sendGoHomeNotificationByMinute() { - // Your logic to send the notification every minute + LocalDateTime currentTime = LocalDateTime.now(); + LocalTime startTime = currentTime.minusSeconds(30).toLocalTime(); + LocalTime endTime = currentTime.plusSeconds(30).toLocalTime(); + List userList = userRepository.findByPushNotificationTimeBetween(startTime, endTime); + for (User user : userList) { + NotificationMessage notificationMessage = NotificationMessage.toGoHomeNotification(user); + Message message = fcmService.createMessage(user.getFcmToken(), notificationMessage); + fcmService.send(message); + } } } diff --git a/src/main/java/com/onnoff/onnoff/domain/user/repository/UserRepository.java b/src/main/java/com/onnoff/onnoff/domain/user/repository/UserRepository.java index 09246a5..f67e3ab 100644 --- a/src/main/java/com/onnoff/onnoff/domain/user/repository/UserRepository.java +++ b/src/main/java/com/onnoff/onnoff/domain/user/repository/UserRepository.java @@ -3,8 +3,14 @@ import com.onnoff.onnoff.domain.user.User; import org.springframework.data.jpa.repository.JpaRepository; +import java.time.LocalDateTime; +import java.time.LocalTime; +import java.util.List; import java.util.Optional; public interface UserRepository extends JpaRepository { Optional findByOauthId(Long oauthId); + + List findByPushNotificationTimeBetween(LocalTime startTime, LocalTime endTime); + } From 856d4d67b1af146f53b73d3a2e4cfa835ce2fdf6 Mon Sep 17 00:00:00 2001 From: realisshomyang Date: Thu, 1 Feb 2024 20:52:23 +0900 Subject: [PATCH 5/5] =?UTF-8?q?[fix]:=20=EC=99=80=EC=9D=B4=EC=96=B4=20?= =?UTF-8?q?=ED=94=84=EB=A0=88=EC=9E=84=EC=97=90=20=EB=A7=9E=EA=B2=8C=20?= =?UTF-8?q?=ED=91=B8=EC=8B=9C=20=EC=95=8C=EB=9E=8C=20=EB=A9=94=EC=8B=9C?= =?UTF-8?q?=EC=A7=80=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/onnoff/onnoff/domain/push/dto/NotificationMessage.java | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/main/java/com/onnoff/onnoff/domain/push/dto/NotificationMessage.java b/src/main/java/com/onnoff/onnoff/domain/push/dto/NotificationMessage.java index e1a987d..c5e43bc 100644 --- a/src/main/java/com/onnoff/onnoff/domain/push/dto/NotificationMessage.java +++ b/src/main/java/com/onnoff/onnoff/domain/push/dto/NotificationMessage.java @@ -12,7 +12,7 @@ public record NotificationMessage( ) { public static NotificationMessage toGoHomeNotification(User user) { return NotificationMessage.builder() - .title(user.getName() + "님, 이제 퇴근하실 시간이에요.") + .title(user.getNickname() + "님, 이제 퇴근하실 시간이에요.") .message("") .type(NotificationType.NOTIFY) .build();