Skip to content
This repository was archived by the owner on Oct 20, 2024. It is now read-only.

Commit 56b016e

Browse files
feat: ✨ user 엔터티 fcmToken 컬럼 추가, 로그인 시 토큰 최신화 구현 (#64)
* Initial commit * Feat/#1 oauth2login (#3) * feat: User 엔터티 생성 * feat: jwt 버전 11->12, JWTUtil 생성 * feat: JWTFilter(JwtAuthenticationFilter) 등록 * feat: kakao 로그인 구현 * docs: swagger 태그(Authorization) 추가 (#5) * feat: User 엔터티 생성 * feat: jwt 버전 11->12, JWTUtil 생성 * feat: JWTFilter(JwtAuthenticationFilter) 등록 * feat: kakao 로그인 구현 * docs: swagger 태그(Authorization) 추가 * feat: accesstoken 테스트를 위한 test login 생성 (#9) * feat: User 엔티티에 상속 (#12) * feat: BaseEntity 생성 * feat: User 엔티티에 상속 * feat: 일기 생성 기능 구현 (#14) * feat: accesstoken 테스트를 위한 test login 생성 * feat: 일기 생성 기능 구현 * hotfix: ci 에러 수정 (#16) * feat: accesstoken 테스트를 위한 test login 생성 * feat: 일기 생성 기능 구현 * hotfix: ci 에러 수정 * fix: OIDC 카카오 로그인 nullPointerException 해결 * feat: 닉네임 설정 기능 구현 (#21) * feat: 일기 수정 기능 구현 (#25) * feat: 일기에 감정 컬럼 추가 * feat: 일기 수정 기능 구현 * feat: 일기 삭제 기능 구현 (#27) * feat: 일기에 감정 컬럼 추가 * feat: 일기 수정 기능 구현 * feat: 일기 삭제 기능 구현 * feat: 일기 감정 분석 기능 구현 (#31) * feat: 감정 저장 기능 구현 (#33) * feat: 일기 감정 분석 기능 구현 * feat: 감정 저장 기능 구현 * fix: 🐛 감정 저장 안되던 오류 수정 (#35) * feat: 일기 감정 분석 기능 구현 * feat: 감정 저장 기능 구현 * fix: 🐛 감정 저장 안되던 오류 수정 * hotfix: 🚑 서버 꺼짐 현상 해결 (#37) * feat: 일기 감정 분석 기능 구현 * feat: 감정 저장 기능 구현 * fix: 🐛 감정 저장 안되던 오류 수정 * hotfix: 🚑 서버 꺼짐 현상 해결 * feat: ✨ 홈 화면 조회 기능 구현 (#41) * feat: ✨ 회원가입 완료 여부 필드 추가 (#44) * feat: ✨ 일기 상세 조회 구현 (#47) * feat: ✨ 기간 별 감정 통계 조회 기능 구현 (#50) * feat: ✨ 일기 내용 검색 기능 구현 (#52) * feat: ✨ 감정 별 일기 조회 (#54) * feat: ✨ 월 별 일기 조회 기능 구현 (#59) * ci: ⚡ workflow 수정 (#61) * ci: ⚡ workflow 수정 * ci: ⚡ workflow 수정 * feat: ✨ user 엔터티 fcmToken 컬럼 추가, 로그인 시 토큰 최신화 구현 (#63) * ci: ⚡ workflow 수정 * ci: ⚡ workflow 수정 * feat: ✨ fcm 토큰 알림 기능 구현 * feat: ✨ user 엔터티 fcmToken 컬럼 추가, 로그인 시 토큰 최신화 구현
1 parent 8e0294b commit 56b016e

File tree

9 files changed

+164
-1
lines changed

9 files changed

+164
-1
lines changed

build.gradle

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -50,6 +50,11 @@ dependencies {
5050
implementation 'com.auth0:java-jwt:3.18.2'
5151
implementation 'com.auth0:jwks-rsa:0.20.0'
5252

53+
//fcm
54+
implementation 'com.google.firebase:firebase-admin:6.8.1'
55+
implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.2.2'
56+
implementation 'com.google.guava:guava:30.0-jre'
57+
5358
//s3 bucket
5459
implementation platform('com.amazonaws:aws-java-sdk-bom:1.11.1000')
5560
implementation 'com.amazonaws:aws-java-sdk-s3'

src/main/java/com/aidiary/domain/auth/application/AuthService.java

Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -65,4 +65,11 @@ public NicknameRes updateNickname(UserPrincipal userPrincipal, String nickname)
6565

6666
return nicknameRes;
6767
}
68+
69+
@Transactional
70+
public void updateFcmToken(Long id, String fcmToken) {
71+
User user = userRepository.findById(id)
72+
.orElseThrow(EntityNotFoundException::new);
73+
user.updateFcmToken(fcmToken);
74+
}
6875
}

src/main/java/com/aidiary/domain/auth/dto/IdTokenReq.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
@Builder
77
public record IdTokenReq(
88
String idToken,
9-
String provider
9+
String provider,
10+
String fcmToken
1011
) {
1112
}

src/main/java/com/aidiary/domain/auth/presentation/AuthController.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -62,6 +62,8 @@ public ResponseCustom<AuthRes> login(@RequestBody IdTokenReq idTokenReq) {
6262
User user = userRepository.findByProviderId(providerId)
6363
.orElseThrow(EntityNotFoundException::new);
6464

65+
authService.updateFcmToken(user.getId(), idTokenReq.fcmToken());
66+
6567
AuthRes authRes = AuthRes.builder()
6668
.accessToken(accessToken)
6769
.isRegistered(user.isRegistered())
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
package com.aidiary.domain.notification.application;
2+
3+
import com.aidiary.domain.notification.dto.FcmMessage;
4+
import com.fasterxml.jackson.core.JsonParseException;
5+
import com.fasterxml.jackson.core.JsonProcessingException;
6+
import com.fasterxml.jackson.databind.ObjectMapper;
7+
import com.google.auth.oauth2.GoogleCredentials;
8+
import lombok.RequiredArgsConstructor;
9+
import okhttp3.*;
10+
import org.springframework.core.io.ClassPathResource;
11+
import org.springframework.http.HttpHeaders;
12+
import org.springframework.stereotype.Service;
13+
import org.springframework.transaction.annotation.Transactional;
14+
15+
import java.io.IOException;
16+
import java.util.List;
17+
18+
@Service
19+
@RequiredArgsConstructor
20+
@Transactional
21+
public class FcmService {
22+
23+
private final String API_URL = "https://fcm.googleapis.com/v1/projects/" +
24+
"nabi-ffce7/messages:send";
25+
private final ObjectMapper objectMapper;
26+
27+
@Transactional
28+
public void sendMessageTo(String targetToken, String title, String body) throws IOException {
29+
String message = makeMessage(targetToken, title, body);
30+
31+
OkHttpClient client = new OkHttpClient();
32+
RequestBody requestBody = RequestBody.create(message,
33+
MediaType.get("application/json; charset=utf-8"));
34+
Request request = new Request.Builder()
35+
.url(API_URL)
36+
.post(requestBody)
37+
.addHeader(HttpHeaders.AUTHORIZATION, "Bearer " + getAccessToken())
38+
.addHeader(HttpHeaders.CONTENT_TYPE, "application/json; UTF-8")
39+
.build();
40+
41+
Response response = client.newCall(request).execute();
42+
43+
System.out.println(response.body().string());
44+
}
45+
46+
private String makeMessage(String targetToken, String title, String body) throws JsonParseException, JsonProcessingException {
47+
FcmMessage fcmMessage = FcmMessage.builder()
48+
.message(FcmMessage.Msg.builder()
49+
.token(targetToken)
50+
.notification(FcmMessage.Notification.builder()
51+
.title(title)
52+
.body(body)
53+
.image(null)
54+
.build()
55+
).build()).validateOnly(false).build();
56+
57+
return objectMapper.writeValueAsString(fcmMessage);
58+
}
59+
60+
private String getAccessToken() throws IOException {
61+
String firebaseConfigPath = "firebase/firebase_service_key.json";
62+
63+
GoogleCredentials googleCredentials = GoogleCredentials
64+
.fromStream(new ClassPathResource(firebaseConfigPath).getInputStream())
65+
.createScoped(List.of("https://www.googleapis.com/auth/cloud-platform"));
66+
67+
googleCredentials.refreshIfExpired();
68+
return googleCredentials.getAccessToken().getTokenValue();
69+
}
70+
71+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package com.aidiary.domain.notification.dto;
2+
3+
import lombok.AllArgsConstructor;
4+
import lombok.Builder;
5+
import lombok.Getter;
6+
7+
@Builder
8+
public record FcmMessage(
9+
boolean validateOnly,
10+
Msg message
11+
) {
12+
@Builder
13+
@AllArgsConstructor
14+
@Getter
15+
public static class Msg {
16+
private Notification notification;
17+
private String token;
18+
}
19+
20+
@Builder
21+
@AllArgsConstructor
22+
@Getter
23+
public static class Notification {
24+
private String title;
25+
private String body;
26+
private String image;
27+
}
28+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.aidiary.domain.notification.dto;
2+
3+
public record FcmReq(
4+
String targetToken,
5+
String title,
6+
String body
7+
) {
8+
}
Lines changed: 35 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,35 @@
1+
package com.aidiary.domain.notification.presentation;
2+
3+
import com.aidiary.domain.notification.application.FcmService;
4+
import com.aidiary.domain.notification.dto.FcmReq;
5+
import com.aidiary.global.payload.ResponseCustom;
6+
import lombok.RequiredArgsConstructor;
7+
import org.springframework.web.bind.annotation.PostMapping;
8+
import org.springframework.web.bind.annotation.RequestBody;
9+
import org.springframework.web.bind.annotation.RequestMapping;
10+
import org.springframework.web.bind.annotation.RestController;
11+
12+
import java.io.IOException;
13+
14+
@RestController
15+
@RequiredArgsConstructor
16+
@RequestMapping("/fcm")
17+
public class NotificationController {
18+
19+
private final FcmService fcmService;
20+
21+
22+
@PostMapping
23+
public ResponseCustom<?> pushMessage(@RequestBody FcmReq fcmReq) throws IOException {
24+
System.out.println(fcmReq.targetToken() + " "
25+
+ fcmReq.title() + " " + fcmReq.body());
26+
27+
fcmService.sendMessageTo(
28+
fcmReq.targetToken(),
29+
fcmReq.title(),
30+
fcmReq.body()
31+
);
32+
33+
return ResponseCustom.OK("Message is pushed");
34+
}
35+
}

src/main/java/com/aidiary/domain/user/domain/User.java

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,8 @@ public class User extends BaseEntity {
3030

3131
private boolean isRegistered;
3232

33+
private String fcmToken;
34+
3335

3436
@Builder
3537
public User(String nickname, String username, String email, String role, String provider, String providerId) {
@@ -49,4 +51,8 @@ public void updateNickname(String nickname) {
4951
public void updateIsRegistered() {
5052
this.isRegistered = true;
5153
}
54+
55+
public void updateFcmToken(String fcmToken) {
56+
this.fcmToken = fcmToken;
57+
}
5258
}

0 commit comments

Comments
 (0)