-
Notifications
You must be signed in to change notification settings - Fork 2
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Merge pull request #117 from PawWithU/feat/114-firebase-setting
[Feature] Firebase 설정 및 푸시 알림 구현
- Loading branch information
Showing
14 changed files
with
417 additions
and
2 deletions.
There are no files selected for viewing
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
55 changes: 55 additions & 0 deletions
55
src/main/java/com/pawwithu/connectdog/domain/fcm/controller/FcmController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,55 @@ | ||
package com.pawwithu.connectdog.domain.fcm.controller; | ||
|
||
import com.pawwithu.connectdog.domain.fcm.dto.request.IntermediaryFcmRequest; | ||
import com.pawwithu.connectdog.domain.fcm.dto.request.VolunteerFcmRequest; | ||
import com.pawwithu.connectdog.domain.fcm.service.FcmService; | ||
import com.pawwithu.connectdog.error.dto.ErrorResponse; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import io.swagger.v3.oas.annotations.media.Content; | ||
import io.swagger.v3.oas.annotations.media.Schema; | ||
import io.swagger.v3.oas.annotations.responses.ApiResponse; | ||
import io.swagger.v3.oas.annotations.tags.Tag; | ||
import jakarta.validation.Valid; | ||
import lombok.RequiredArgsConstructor; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.security.core.annotation.AuthenticationPrincipal; | ||
import org.springframework.security.core.userdetails.UserDetails; | ||
import org.springframework.web.bind.annotation.PostMapping; | ||
import org.springframework.web.bind.annotation.RequestBody; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
@Tag(name = "Fcm", description = "Fcm API") | ||
@RestController | ||
@RequiredArgsConstructor | ||
public class FcmController { | ||
|
||
private final FcmService fcmService; | ||
|
||
@Operation(summary = "이동봉사자 - FCM 토큰 저장", description = "이동봉사자의 FCM 토큰을 저장합니다.", | ||
responses = {@ApiResponse(responseCode = "200", description = "FCM 토큰 저장 성공") | ||
, @ApiResponse(responseCode = "400" | ||
, description = "V1, fcm 토큰은 필수 입력 값입니다. \t\n M1, 해당 이동봉사자를 찾을 수 없습니다." | ||
, content = @Content(schema = @Schema(implementation = ErrorResponse.class))) | ||
}) | ||
@PostMapping("/volunteers/fcm") | ||
public ResponseEntity<Void> saveVolunteerFcm(@AuthenticationPrincipal UserDetails loginUser, | ||
@Valid @RequestBody VolunteerFcmRequest request) { | ||
|
||
fcmService.saveVolunteerFcm(loginUser.getUsername(), request); | ||
return ResponseEntity.noContent().build(); | ||
} | ||
|
||
@Operation(summary = "이동봉사 중개 - FCM 토큰 저장", description = "이동봉사 중개의 FCM 토큰을 저장합니다.", | ||
responses = {@ApiResponse(responseCode = "200", description = "FCM 토큰 저장 성공") | ||
, @ApiResponse(responseCode = "400" | ||
, description = "V1, fcm 토큰은 필수 입력 값입니다. \t\n M2, 해당 이동봉사 중개를 찾을 수 없습니다." | ||
, content = @Content(schema = @Schema(implementation = ErrorResponse.class))) | ||
}) | ||
@PostMapping("/intermediaries/fcm") | ||
public ResponseEntity<Void> saveIntermediaryFcm(@AuthenticationPrincipal UserDetails loginUser, | ||
@Valid @RequestBody IntermediaryFcmRequest request) { | ||
|
||
fcmService.saveIntermediaryFcm(loginUser.getUsername(), request); | ||
return ResponseEntity.noContent().build(); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
src/main/java/com/pawwithu/connectdog/domain/fcm/dto/request/FcmMessage.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.pawwithu.connectdog.domain.fcm.dto.request; | ||
|
||
import lombok.AllArgsConstructor; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
|
||
@Builder | ||
@AllArgsConstructor | ||
@Getter | ||
public class FcmMessage { | ||
|
||
private boolean validateOnly; | ||
private Message message; | ||
|
||
@Builder | ||
@AllArgsConstructor | ||
@Getter | ||
public static class Message { | ||
private Notification notification; | ||
private String token; | ||
|
||
} | ||
|
||
@Builder | ||
@AllArgsConstructor | ||
@Getter | ||
public static class Notification { | ||
private String title; | ||
private String body; | ||
} | ||
|
||
} |
16 changes: 16 additions & 0 deletions
16
src/main/java/com/pawwithu/connectdog/domain/fcm/dto/request/IntermediaryFcmRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.pawwithu.connectdog.domain.fcm.dto.request; | ||
|
||
import com.pawwithu.connectdog.domain.fcm.entity.IntermediaryFcm; | ||
import com.pawwithu.connectdog.domain.intermediary.entity.Intermediary; | ||
import jakarta.validation.constraints.NotBlank; | ||
|
||
public record IntermediaryFcmRequest(@NotBlank(message = "fcm 토큰은 필수 입력 값입니다.") | ||
String fcmToken) { | ||
|
||
public static IntermediaryFcm IntermediaryToEntity(Intermediary intermediary, IntermediaryFcmRequest request) { | ||
return IntermediaryFcm.builder() | ||
.intermediary(intermediary) | ||
.fcmToken(request.fcmToken) | ||
.build(); | ||
} | ||
} |
16 changes: 16 additions & 0 deletions
16
src/main/java/com/pawwithu/connectdog/domain/fcm/dto/request/VolunteerFcmRequest.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,16 @@ | ||
package com.pawwithu.connectdog.domain.fcm.dto.request; | ||
|
||
import com.pawwithu.connectdog.domain.fcm.entity.VolunteerFcm; | ||
import com.pawwithu.connectdog.domain.volunteer.entity.Volunteer; | ||
import jakarta.validation.constraints.NotBlank; | ||
|
||
public record VolunteerFcmRequest(@NotBlank(message = "fcm 토큰은 필수 입력 값입니다.") | ||
String fcmToken) { | ||
|
||
public static VolunteerFcm volunteerToEntity(Volunteer volunteer, VolunteerFcmRequest request) { | ||
return VolunteerFcm.builder() | ||
.volunteer(volunteer) | ||
.fcmToken(request.fcmToken) | ||
.build(); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
src/main/java/com/pawwithu/connectdog/domain/fcm/entity/IntermediaryFcm.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.pawwithu.connectdog.domain.fcm.entity; | ||
|
||
import com.pawwithu.connectdog.domain.intermediary.entity.Intermediary; | ||
import jakarta.persistence.*; | ||
import lombok.AccessLevel; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@Entity | ||
public class IntermediaryFcm { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@Column(nullable = false) | ||
private String fcmToken; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(name = "intermediary_id", nullable = false) | ||
private Intermediary intermediary; | ||
|
||
@Builder | ||
public IntermediaryFcm(String fcmToken, Intermediary intermediary) { | ||
this.fcmToken = fcmToken; | ||
this.intermediary = intermediary; | ||
} | ||
|
||
} |
32 changes: 32 additions & 0 deletions
32
src/main/java/com/pawwithu/connectdog/domain/fcm/entity/VolunteerFcm.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.pawwithu.connectdog.domain.fcm.entity; | ||
|
||
import com.pawwithu.connectdog.domain.volunteer.entity.Volunteer; | ||
import jakarta.persistence.*; | ||
import lombok.AccessLevel; | ||
import lombok.Builder; | ||
import lombok.Getter; | ||
import lombok.NoArgsConstructor; | ||
|
||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@Entity | ||
public class VolunteerFcm { | ||
|
||
@Id | ||
@GeneratedValue(strategy = GenerationType.IDENTITY) | ||
private Long id; | ||
|
||
@Column(nullable = false) | ||
private String fcmToken; | ||
|
||
@ManyToOne(fetch = FetchType.LAZY) | ||
@JoinColumn(name = "volunteer_id", nullable = false) | ||
private Volunteer volunteer; | ||
|
||
@Builder | ||
public VolunteerFcm(String fcmToken, Volunteer volunteer) { | ||
this.fcmToken = fcmToken; | ||
this.volunteer = volunteer; | ||
} | ||
|
||
} |
7 changes: 7 additions & 0 deletions
7
src/main/java/com/pawwithu/connectdog/domain/fcm/repository/IntermediaryFcmRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.pawwithu.connectdog.domain.fcm.repository; | ||
|
||
import com.pawwithu.connectdog.domain.fcm.entity.IntermediaryFcm; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface IntermediaryFcmRepository extends JpaRepository<IntermediaryFcm, Long> { | ||
} |
7 changes: 7 additions & 0 deletions
7
src/main/java/com/pawwithu/connectdog/domain/fcm/repository/VolunteerFcmRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,7 @@ | ||
package com.pawwithu.connectdog.domain.fcm.repository; | ||
|
||
import com.pawwithu.connectdog.domain.fcm.entity.VolunteerFcm; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface VolunteerFcmRepository extends JpaRepository<VolunteerFcm, Long> { | ||
} |
124 changes: 124 additions & 0 deletions
124
src/main/java/com/pawwithu/connectdog/domain/fcm/service/FcmService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,124 @@ | ||
package com.pawwithu.connectdog.domain.fcm.service; | ||
|
||
import com.fasterxml.jackson.core.JsonProcessingException; | ||
import com.fasterxml.jackson.databind.ObjectMapper; | ||
import com.google.auth.oauth2.GoogleCredentials; | ||
import com.google.common.net.HttpHeaders; | ||
import com.pawwithu.connectdog.domain.fcm.dto.request.FcmMessage; | ||
import com.pawwithu.connectdog.domain.fcm.dto.request.IntermediaryFcmRequest; | ||
import com.pawwithu.connectdog.domain.fcm.dto.request.VolunteerFcmRequest; | ||
import com.pawwithu.connectdog.domain.fcm.entity.IntermediaryFcm; | ||
import com.pawwithu.connectdog.domain.fcm.entity.VolunteerFcm; | ||
import com.pawwithu.connectdog.domain.fcm.repository.IntermediaryFcmRepository; | ||
import com.pawwithu.connectdog.domain.fcm.repository.VolunteerFcmRepository; | ||
import com.pawwithu.connectdog.domain.intermediary.entity.Intermediary; | ||
import com.pawwithu.connectdog.domain.intermediary.repository.IntermediaryRepository; | ||
import com.pawwithu.connectdog.domain.volunteer.entity.Volunteer; | ||
import com.pawwithu.connectdog.domain.volunteer.repository.VolunteerRepository; | ||
import com.pawwithu.connectdog.error.exception.custom.BadRequestException; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import okhttp3.*; | ||
import org.springframework.beans.factory.annotation.Value; | ||
import org.springframework.core.io.ClassPathResource; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
import java.io.IOException; | ||
import java.util.Arrays; | ||
|
||
import static com.pawwithu.connectdog.error.ErrorCode.INTERMEDIARY_NOT_FOUND; | ||
import static com.pawwithu.connectdog.error.ErrorCode.VOLUNTEER_NOT_FOUND; | ||
|
||
@Slf4j | ||
@Service | ||
@Transactional | ||
@RequiredArgsConstructor | ||
public class FcmService { | ||
|
||
@Value("${fcm.config.path}") | ||
private String FIREBASE_CONFIG_PATH; | ||
|
||
@Value("${fcm.api.url}") | ||
private String FIREBASE_API_URL; | ||
|
||
private final ObjectMapper objectMapper; | ||
private final VolunteerRepository volunteerRepository; | ||
private final VolunteerFcmRepository volunteerFcmRepository; | ||
private final IntermediaryRepository intermediaryRepository; | ||
private final IntermediaryFcmRepository intermediaryFcmRepository; | ||
|
||
private String getAccessToken() throws IOException { | ||
|
||
// firebase로 부터 access token을 가져온다. | ||
GoogleCredentials googleCredentials = GoogleCredentials | ||
.fromStream(new ClassPathResource(FIREBASE_CONFIG_PATH).getInputStream()) | ||
.createScoped(Arrays.asList("https://www.googleapis.com/auth/cloud-platform")); | ||
|
||
googleCredentials.refreshIfExpired(); | ||
return googleCredentials.getAccessToken().getTokenValue(); | ||
|
||
} | ||
|
||
/** | ||
* makeMessage : 알림 파라미터들을 FCM이 요구하는 body 형태로 가공한다. | ||
* @param targetToken : firebase token | ||
* @param title : 알림 제목 | ||
* @param body : 알림 내용 | ||
* @return | ||
* */ | ||
public String makeMessage(String targetToken, String title, String body) throws JsonProcessingException { | ||
|
||
FcmMessage fcmMessage = FcmMessage.builder() | ||
.message( | ||
FcmMessage.Message.builder() | ||
.token(targetToken) | ||
.notification( | ||
FcmMessage.Notification.builder() | ||
.title(title) | ||
.body(body) | ||
.build()) | ||
.build() | ||
) | ||
.validateOnly(false) | ||
.build(); | ||
|
||
return objectMapper.writeValueAsString(fcmMessage); | ||
|
||
} | ||
|
||
/** | ||
* 알림 푸쉬를 보내는 역할을 하는 메서드 | ||
* @param targetToken : 푸쉬 알림을 받을 클라이언트 앱의 식별 토큰 | ||
* */ | ||
public void sendMessageTo(String targetToken, String title, String body) throws IOException { | ||
|
||
String message = makeMessage(targetToken, title, body); | ||
|
||
OkHttpClient client = new OkHttpClient(); | ||
RequestBody requestBody = RequestBody.create(message, MediaType.get("application/json; charset=utf-8")); | ||
|
||
Request request = new Request.Builder() | ||
.url(FIREBASE_API_URL) | ||
.post(requestBody) | ||
.addHeader(HttpHeaders.AUTHORIZATION, "Bearer "+getAccessToken()) | ||
.addHeader(HttpHeaders.CONTENT_TYPE, "application/json; UTF-8") | ||
.build(); | ||
|
||
Response response = client.newCall(request).execute(); | ||
log.info(response.body().string()); | ||
return; | ||
} | ||
|
||
public void saveVolunteerFcm(String email, VolunteerFcmRequest request) { | ||
Volunteer volunteer = volunteerRepository.findByEmail(email).orElseThrow(() -> new BadRequestException(VOLUNTEER_NOT_FOUND)); | ||
VolunteerFcm volunteerFcm = VolunteerFcmRequest.volunteerToEntity(volunteer, request); | ||
volunteerFcmRepository.save(volunteerFcm); | ||
} | ||
|
||
public void saveIntermediaryFcm(String email, IntermediaryFcmRequest request) { | ||
Intermediary intermediary = intermediaryRepository.findByEmail(email).orElseThrow(() -> new BadRequestException(INTERMEDIARY_NOT_FOUND)); | ||
IntermediaryFcm intermediaryFcm = IntermediaryFcmRequest.IntermediaryToEntity(intermediary, request); | ||
intermediaryFcmRepository.save(intermediaryFcm); | ||
} | ||
} |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Oops, something went wrong.