Skip to content

Commit 77497f6

Browse files
Merge pull request #80 from Donut-DONationUTile/feature/fcm
Feature/fcm
2 parents 862ddba + 22b73f7 commit 77497f6

17 files changed

+340
-30
lines changed

Donut-Server-yml

build.gradle

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -57,6 +57,8 @@ dependencies {
5757
implementation 'io.jsonwebtoken:jjwt-jackson:0.11.5'
5858
//WebClient
5959
implementation group: 'org.springframework.boot', name: 'spring-boot-starter-webflux'
60+
// firebase
61+
implementation group: 'com.google.firebase', name: 'firebase-admin', version: '8.1.0'
6062

6163
}
6264

Lines changed: 93 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,93 @@
1+
package zero.eight.donut.config.firebase;
2+
3+
import com.google.firebase.messaging.FirebaseMessaging;
4+
import com.google.firebase.messaging.FirebaseMessagingException;
5+
import com.google.firebase.messaging.Message;
6+
import com.google.firebase.messaging.Notification;
7+
import lombok.RequiredArgsConstructor;
8+
import lombok.extern.slf4j.Slf4j;
9+
import zero.eight.donut.config.jwt.AuthUtils;
10+
import zero.eight.donut.domain.FcmToken;
11+
import zero.eight.donut.domain.Giver;
12+
import zero.eight.donut.domain.Receiver;
13+
import zero.eight.donut.dto.auth.Role;
14+
import zero.eight.donut.dto.fcm.FcmMemberDto;
15+
import zero.eight.donut.exception.Error;
16+
import zero.eight.donut.repository.FcmTokenRepository;
17+
import zero.eight.donut.repository.GiverRepository;
18+
import zero.eight.donut.repository.ReceiverRepository;
19+
20+
import java.util.Optional;
21+
22+
@Slf4j
23+
@RequiredArgsConstructor
24+
public class FcmUtils {
25+
26+
private final AuthUtils authUtils;
27+
private final GiverRepository giverRepository;
28+
private final ReceiverRepository receiverRepository;
29+
private final FcmTokenRepository fcmTokenRepository;
30+
private final FirebaseMessaging firebaseMessaging;
31+
32+
33+
34+
public FcmMemberDto getMemberDto() throws Exception {
35+
if (authUtils.getCurrentUserRole().equals(Role.ROLE_GIVER)) {
36+
Giver giver = giverRepository.findByEmail(authUtils.getCurrentUserEmail()).orElseThrow(
37+
() -> new Exception(Error.USERNAME_NOT_FOUND_EXCEPTION.getMessage())
38+
);
39+
return FcmMemberDto.builder()
40+
.id(giver.getId())
41+
.role(Role.ROLE_GIVER)
42+
.build();
43+
}
44+
else {
45+
Optional<Receiver> optionalReceiver = receiverRepository.findByName(authUtils.getCurrentUserEmail());
46+
if (optionalReceiver.isEmpty()) {
47+
throw new Exception(Error.USERNAME_NOT_FOUND_EXCEPTION.getMessage());
48+
}
49+
50+
return FcmMemberDto.builder()
51+
.id(optionalReceiver.get().getId())
52+
.role(Role.ROLE_RECEIVER)
53+
.build();
54+
}
55+
}
56+
57+
// FCM 메세지 전송
58+
public String sendMessage(Long memberId, String title, String body) throws FirebaseMessagingException {
59+
// 알림 수신자의 FCM 토큰 조회
60+
String fcmToken = getFcmToken(memberId);
61+
// FCM 메세지 생성
62+
Message message = makeMessage(fcmToken, title, body);
63+
// FCM 발신
64+
return firebaseMessaging.send(message);
65+
}
66+
67+
public String getFcmToken(Long memberId) {
68+
Optional<FcmToken> fcmToken = fcmTokenRepository.findByMemberId(memberId);
69+
70+
if (fcmToken.isEmpty()) {
71+
try {
72+
throw new Exception(Error.FCM_TOKEN_NOT_FOUND_EXCEPTION.getMessage());
73+
} catch (Exception e) {
74+
throw new RuntimeException(e);
75+
}
76+
}
77+
78+
return fcmToken.get().getToken();
79+
}
80+
81+
public Message makeMessage(String targetToken, String title, String body) {
82+
// Notification 객체는 모바일 환경에서 제목과 본문을 표시
83+
Notification notification = Notification.builder()
84+
.setTitle(title)
85+
.setBody(body)
86+
.build();
87+
88+
return Message.builder()
89+
.setNotification(notification)
90+
.setToken(targetToken)
91+
.build();
92+
}
93+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package zero.eight.donut.config.firebase;
2+
3+
import com.google.auth.oauth2.GoogleCredentials;
4+
import com.google.firebase.FirebaseApp;
5+
import com.google.firebase.FirebaseOptions;
6+
import org.springframework.context.annotation.Configuration;
7+
8+
import javax.annotation.PostConstruct;
9+
import java.io.FileInputStream;
10+
import java.io.FileNotFoundException;
11+
12+
@Configuration
13+
public class FirebaseConfig {
14+
15+
@PostConstruct
16+
public void init() {
17+
try {
18+
FileInputStream serviceAccount =
19+
new FileInputStream("src/main/resources/serviceAccountKey.json");
20+
FirebaseOptions options = new FirebaseOptions.Builder()
21+
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
22+
.build();
23+
FirebaseApp.initializeApp(options);
24+
} catch (Exception e) {
25+
e.printStackTrace();
26+
}
27+
}
28+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package zero.eight.donut.controller;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.springframework.web.bind.annotation.PostMapping;
5+
import org.springframework.web.bind.annotation.RequestBody;
6+
import org.springframework.web.bind.annotation.RequestMapping;
7+
import org.springframework.web.bind.annotation.RestController;
8+
import zero.eight.donut.common.response.ApiResponse;
9+
import zero.eight.donut.dto.fcm.FcmTokenRequestDto;
10+
import zero.eight.donut.service.FcmService;
11+
12+
@RequiredArgsConstructor
13+
@RequestMapping("/api/fcm")
14+
@RestController
15+
public class FcmController {
16+
17+
private final FcmService fcmService;
18+
19+
@PostMapping("/token")
20+
public ApiResponse<?> createFcmToken(@RequestBody FcmTokenRequestDto requestDto) throws Exception {
21+
return fcmService.registerFcmToken(requestDto);
22+
}
23+
}
Lines changed: 38 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,38 @@
1+
package zero.eight.donut.domain;
2+
3+
import jakarta.persistence.*;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
import zero.eight.donut.dto.auth.Role;
8+
9+
@Getter
10+
@NoArgsConstructor
11+
@Entity
12+
public class FcmToken {
13+
14+
@Id
15+
@GeneratedValue(strategy = GenerationType.IDENTITY)
16+
private Long id;
17+
18+
@Column(nullable = false)
19+
private String token;
20+
21+
@Column(nullable = false, unique = true)
22+
private Long memberId;
23+
24+
@Column(nullable = false)
25+
private Role role;
26+
27+
@Builder // 롬복의 @Builder 애너테이션 적용
28+
public FcmToken(String token, Long memberId, Role role) {
29+
this.token = token;
30+
this.memberId = memberId;
31+
this.role = role;
32+
}
33+
34+
public void updateToken(String token, Role role) {
35+
this.token = token;
36+
this.role = role;
37+
}
38+
}
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package zero.eight.donut.dto.fcm;
2+
3+
import lombok.Builder;
4+
import lombok.Getter;
5+
import zero.eight.donut.dto.auth.Role;
6+
7+
@Getter
8+
@Builder
9+
public class FcmMemberDto {
10+
private Long id;
11+
private Role role;
12+
}
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
package zero.eight.donut.dto.fcm;
2+
3+
import lombok.Getter;
4+
import lombok.RequiredArgsConstructor;
5+
6+
@RequiredArgsConstructor
7+
@Getter
8+
public class FcmTokenRequestDto {
9+
private final String token;
10+
}

src/main/java/zero/eight/donut/exception/Error.java

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,11 +13,15 @@ public enum Error {
1313
ERROR(HttpStatus.BAD_REQUEST, "Request processing failed"),
1414

1515
// 400 BAD REQUEST
16+
CONVERT_JSON_EXCEPTION(HttpStatus.BAD_REQUEST, "Failed to convert json object to JSON"),
17+
FIREBASE_CONNECT_EXCEPTION(HttpStatus.BAD_REQUEST, "Firebase connect failed"),
18+
GOOGLE_REQUEST_TOKEN_EXCEPTION(HttpStatus.BAD_REQUEST, "google oauth error"),
1619
INSUFFICIENT_BALANCE_EXCEPTION(HttpStatus.BAD_REQUEST, "Insufficient balance"),
1720
INSUFFICIENT_DONATION_EXCEPTION(HttpStatus.BAD_REQUEST, "Insufficient donation amount"),
1821
INVALID_JSON_INPUT_EXCEPTION(HttpStatus.BAD_REQUEST, "입력 형식이 맞지 않습니다."),
1922

2023
// 401 UNAUTHORIZED
24+
USERNAME_NOT_FOUND_EXCEPTION(HttpStatus.BAD_REQUEST, "Username not found"),
2125
INVALID_ID_PASSWORD_EXCEPTION(HttpStatus.UNAUTHORIZED, "ID/password error"),
2226
INVALID_GOOGLE_TOKEN_EXCEPTION(HttpStatus.UNAUTHORIZED, "Invalid Google Token"),
2327
INVALID_JWT_EXCEPTION(HttpStatus.UNAUTHORIZED, "Invalid JWT"),
@@ -32,6 +36,7 @@ public enum Error {
3236

3337

3438
// 404 NOT FOUND
39+
FCM_TOKEN_NOT_FOUND_EXCEPTION(HttpStatus.NOT_FOUND, "FCM_token not found"),
3540
GIFT_NOT_FOUND_EXCEPTION(HttpStatus.NOT_FOUND, "That gift does not exist"),
3641
GIFTBOX_NOT_FOUND_EXCEPTION(HttpStatus.NOT_FOUND, "That giftbox does not exist"),
3742
BENEFIT_NOT_FOUND_EXCEPTION(HttpStatus.NOT_FOUND, "Benefit info does not exist"),

src/main/java/zero/eight/donut/exception/Success.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,7 @@ public enum Success {
3030
GET_HISTORY_RECEIVER_BENEFIT_SUCCESS(HttpStatus.OK, " Success in getting benefit history list"),
3131

3232
//201 CREATED SUCCESS
33+
CREATE_FCM_TOKEN_SUCCESS(HttpStatus.CREATED, "Successfully update fcmToken"),
3334
SEND_MESSAGE_SUCCESS(HttpStatus.CREATED, "Send a message successfully"),
3435
UPLOAD_GIFT_SUCCESS(HttpStatus.CREATED, "Successfully upload gifticon"),
3536
ASSIGN_BENEFIT_SUCCESS(HttpStatus.CREATED, "Successfully assigned benefits"),

0 commit comments

Comments
 (0)