Skip to content

Commit

Permalink
feat: FCM 기능 추가 (#260)
Browse files Browse the repository at this point in the history
* feat: FCM Config 추가

* feat: FCM Info 추가 및 토큰 갱신, 알림 허용 여부 API 추가

* fix: spotlessApply

* fix: PostConstruct javax -> jakarta

* feat: Notification gitkeep 추가

* fix: 메서드 명 변경

* fix: @kdomo 리뷰 반영

* fix: description 수정

* fix: appAlarm default 값 true로 변경

* fix: return 형 void로 수정

* fix: spotlessApply

* fix: throw 에러 출력 변경

* fix: 필요없는 코드 삭제

* fix: slf4j info -> error
  • Loading branch information
char-yb authored Jan 31, 2024
1 parent c4c7703 commit 019fda2
Show file tree
Hide file tree
Showing 14 changed files with 173 additions and 0 deletions.
3 changes: 3 additions & 0 deletions build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -67,6 +67,9 @@ dependencies {
// AWS
implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE'

// Firebase-admin
implementation 'com.google.firebase:firebase-admin:9.2.0'

// Test
testImplementation 'org.springframework.boot:spring-boot-starter-test'
testCompileOnly 'org.projectlombok:lombok'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import com.depromeet.domain.member.application.MemberService;
import com.depromeet.domain.member.dto.request.NicknameCheckRequest;
import com.depromeet.domain.member.dto.request.NicknameUpdateRequest;
import com.depromeet.domain.member.dto.request.UpdateFcmTokenRequest;
import com.depromeet.domain.member.dto.response.MemberFindOneResponse;
import com.depromeet.domain.member.dto.response.MemberSearchResponse;
import com.depromeet.domain.member.dto.response.MemberSocialInfoResponse;
Expand Down Expand Up @@ -79,4 +80,19 @@ public ResponseEntity<Void> memberNicknameUpdate(
memberService.updateMemberNickname(reqest);
return ResponseEntity.ok().build();
}

@Operation(summary = "토글 여부 변경", description = "기존 토글 값을 변경합니다.")
@PatchMapping("/alarm")
public ResponseEntity<Void> memberToggleAppAlarmStateUpdate() {
memberService.toggleAppAlarm();
return ResponseEntity.ok().build();
}

@Operation(summary = "FCM 토큰 갱신", description = "FCM 토큰을 갱신합니다.")
@PatchMapping("/fcm-token")
public ResponseEntity<Void> memberFcmTokenUpdate(
@Valid @RequestBody UpdateFcmTokenRequest updateFcmTokenRequest) {
memberService.updateFcmToken(updateFcmTokenRequest);
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import com.depromeet.domain.member.domain.Profile;
import com.depromeet.domain.member.dto.request.NicknameCheckRequest;
import com.depromeet.domain.member.dto.request.NicknameUpdateRequest;
import com.depromeet.domain.member.dto.request.UpdateFcmTokenRequest;
import com.depromeet.domain.member.dto.response.MemberFindOneResponse;
import com.depromeet.domain.member.dto.response.MemberSearchResponse;
import com.depromeet.domain.member.dto.response.MemberSocialInfoResponse;
Expand Down Expand Up @@ -163,4 +164,14 @@ private ImageFileExtension getImageFileExtension(Profile profile) {
}
return imageFileExtension;
}

public void toggleAppAlarm() {
final Member currentMember = memberUtil.getCurrentMember();
currentMember.toggleAppAlarmState(currentMember.getFcmInfo());
}

public void updateFcmToken(UpdateFcmTokenRequest updateFcmTokenRequest) {
final Member currentMember = memberUtil.getCurrentMember();
currentMember.updateFcmToken(currentMember.getFcmInfo(), updateFcmTokenRequest.fcmToken());
}
}
42 changes: 42 additions & 0 deletions src/main/java/com/depromeet/domain/member/domain/FcmInfo.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
package com.depromeet.domain.member.domain;

import jakarta.persistence.Embeddable;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Embeddable
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class FcmInfo {

private String fcmToken;
private Boolean appAlarm;

@Builder(access = AccessLevel.PRIVATE)
private FcmInfo(String fcmToken, Boolean appAlarm) {
this.fcmToken = fcmToken;
this.appAlarm = appAlarm;
}

public static FcmInfo createFcmInfo() {
return FcmInfo.builder().fcmToken("").appAlarm(true).build();
}

public static FcmInfo toggleAlarm(FcmInfo fcmState) {
return new FcmInfo(fcmState.getFcmToken(), !fcmState.getAppAlarm());
}

public static FcmInfo disableAlarm(FcmInfo fcmInfo) {
return new FcmInfo(fcmInfo.getFcmToken(), false);
}

public static FcmInfo deleteToken(FcmInfo fcmInfo) {
return new FcmInfo("", fcmInfo.getAppAlarm());
}

public static FcmInfo updateToken(FcmInfo fcmState, String fcmToken) {
return new FcmInfo(fcmToken, fcmState.getAppAlarm());
}
}
14 changes: 14 additions & 0 deletions src/main/java/com/depromeet/domain/member/domain/Member.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,6 +36,8 @@ public class Member extends BaseTimeEntity {

@Embedded private OauthInfo oauthInfo;

@Embedded private FcmInfo fcmInfo;

@Enumerated(EnumType.STRING)
private MemberStatus status;

Expand All @@ -58,6 +60,7 @@ public class Member extends BaseTimeEntity {
private Member(
Profile profile,
OauthInfo oauthInfo,
FcmInfo fcmInfo,
MemberStatus status,
MemberRole role,
MemberVisibility visibility,
Expand All @@ -66,6 +69,7 @@ private Member(
String password) {
this.profile = profile;
this.oauthInfo = oauthInfo;
this.fcmInfo = fcmInfo;
this.status = status;
this.role = role;
this.visibility = visibility;
Expand All @@ -78,6 +82,7 @@ public static Member createNormalMember(OauthInfo oauthInfo, String nickname) {
return Member.builder()
.profile(Profile.createProfile(nickname, null))
.oauthInfo(oauthInfo)
.fcmInfo(FcmInfo.createFcmInfo())
.status(MemberStatus.NORMAL)
.role(MemberRole.USER)
.visibility(MemberVisibility.PUBLIC)
Expand Down Expand Up @@ -108,6 +113,15 @@ public void withdrawal() {
throw new CustomException(ErrorCode.MEMBER_ALREADY_DELETED);
}
this.status = MemberStatus.DELETED;
this.fcmInfo = FcmInfo.disableAlarm(FcmInfo.createFcmInfo());
}

public void toggleAppAlarmState(FcmInfo fcmState) {
fcmInfo = FcmInfo.toggleAlarm(fcmState);
}

public void updateFcmToken(FcmInfo fcmState, String fcmToken) {
fcmInfo = FcmInfo.updateToken(fcmState, fcmToken);
}

public void updateNickname(String nickname) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
package com.depromeet.domain.member.dto.request;

import io.swagger.v3.oas.annotations.media.Schema;

public record UpdateFcmTokenRequest(
@Schema(description = "FCM 토큰", defaultValue = "fcm-token-value") String fcmToken) {}
Empty file.
Empty file.
Empty file.
Empty file.
Empty file.
38 changes: 38 additions & 0 deletions src/main/java/com/depromeet/global/config/fcm/FcmConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.depromeet.global.config.fcm;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import jakarta.annotation.PostConstruct;
import java.io.ByteArrayInputStream;
import java.nio.charset.StandardCharsets;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Configuration;

@Configuration
@Slf4j
public class FcmConfig {

@Value("${fcm.certification}")
private String fcmCertification;

@PostConstruct
public void init() {
try {
if (FirebaseApp.getApps().isEmpty()) {
FirebaseOptions options =
new FirebaseOptions.Builder()
.setCredentials(
GoogleCredentials.fromStream(
new ByteArrayInputStream(
fcmCertification.getBytes(
StandardCharsets.UTF_8))))
.build();
FirebaseApp.initializeApp(options);
}
} catch (Exception e) {
log.error("FCM initializing Exception: {}", e.getStackTrace()[0]);
}
}
}
40 changes: 40 additions & 0 deletions src/main/java/com/depromeet/global/config/fcm/FcmService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,40 @@
package com.depromeet.global.config.fcm;

import com.google.api.core.ApiFuture;
import com.google.firebase.messaging.*;
import java.util.List;
import org.springframework.stereotype.Service;

@Service
public class FcmService {

/**
* 참고: https://firebase.google.com/support/release-notes/admin/java 위 레퍼런스에 의거하여
* sendMulticastAsync 는 Deprecated 되어 sendEachForMulticastAsync
*
* @param tokenList: 푸시 토큰 리스트
* @param title: 알림 제목
* @param content: 알림 내용
* @return ApiFuture<BatchResponse>
*/
public ApiFuture<BatchResponse> sendGroupMessageAsync(
List<String> tokenList, String title, String content) {
MulticastMessage multicast =
MulticastMessage.builder()
.addAllTokens(tokenList)
.setNotification(
Notification.builder().setTitle(title).setBody(content).build())
.build();
return FirebaseMessaging.getInstance().sendEachForMulticastAsync(multicast);
}

public ApiFuture<String> sendMessageSync(String token, String title, String content) {
Message message =
Message.builder()
.setToken(token)
.setNotification(
Notification.builder().setTitle(title).setBody(content).build())
.build();
return FirebaseMessaging.getInstance().sendAsync(message);
}
}
3 changes: 3 additions & 0 deletions src/main/resources/application.yml
Original file line number Diff line number Diff line change
Expand Up @@ -30,3 +30,6 @@ springdoc:
logging:
level:
com.depromeet.domain.*.api.*: debug

fcm:
certification: ${FCM_CERTIFICATION:}

0 comments on commit 019fda2

Please sign in to comment.