From f71c20fe6a6ebae38cbcd6caba98610537eb18e1 Mon Sep 17 00:00:00 2001 From: kyeong-hyeok Date: Mon, 20 Nov 2023 22:17:07 +0900 Subject: [PATCH 1/4] =?UTF-8?q?chore:=20build.gradle=20=EC=9D=98=EC=A1=B4?= =?UTF-8?q?=EC=84=B1=20=EB=B2=84=EC=A0=84=20=EC=97=85=EB=8D=B0=EC=9D=B4?= =?UTF-8?q?=ED=8A=B8=20(#129)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- build.gradle | 1 + 1 file changed, 1 insertion(+) diff --git a/build.gradle b/build.gradle index b06b39ca..12c5f9d5 100644 --- a/build.gradle +++ b/build.gradle @@ -66,6 +66,7 @@ dependencies { // firebase implementation 'com.google.firebase:firebase-admin:6.8.1' implementation group: 'com.squareup.okhttp3', name: 'okhttp', version: '4.2.2' + implementation 'com.squareup.okio:okio:2.7.0' } // Querydsl 설정부 From bc7b835f417a29f823e3aa7fd62dc4e93da52fd0 Mon Sep 17 00:00:00 2001 From: kyeong-hyeok Date: Mon, 20 Nov 2023 22:17:54 +0900 Subject: [PATCH 2/4] =?UTF-8?q?chore:=20SecurityConfig=20url=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20(#129)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- src/main/java/com/pawwithu/connectdog/config/SecurityConfig.java | 1 + 1 file changed, 1 insertion(+) diff --git a/src/main/java/com/pawwithu/connectdog/config/SecurityConfig.java b/src/main/java/com/pawwithu/connectdog/config/SecurityConfig.java index 0e166951..939f4ece 100644 --- a/src/main/java/com/pawwithu/connectdog/config/SecurityConfig.java +++ b/src/main/java/com/pawwithu/connectdog/config/SecurityConfig.java @@ -77,6 +77,7 @@ public SecurityFilterChain filterChain(HttpSecurity http, HandlerMappingIntrospe .requestMatchers(mvcMatcherBuilder.pattern("/swagger-resources/**")).permitAll() .requestMatchers(mvcMatcherBuilder.pattern("/v3/api-docs/**")).permitAll() .requestMatchers(mvcMatcherBuilder.pattern("/volunteers/nickname/isDuplicated")).permitAll() + .requestMatchers(mvcMatcherBuilder.pattern("/fcm-test")).permitAll() .anyRequest().authenticated()) .addFilterAfter(customVolunteerAuthFilter(), LogoutFilter.class) .addFilterAfter(customIntermediaryAuthFilter(), LogoutFilter.class) From 231f10b8d8539e85ec7bd9739b81385f9f03239b Mon Sep 17 00:00:00 2001 From: kyeong-hyeok Date: Mon, 20 Nov 2023 22:19:07 +0900 Subject: [PATCH 3/4] =?UTF-8?q?feat:=20Enum=20=ED=83=80=EC=9E=85=20Notific?= =?UTF-8?q?ationMessage=20=EC=B6=94=EA=B0=80=20(#129)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/fcm/dto/NotificationMessage.java | 22 +++++++++++++++++++ 1 file changed, 22 insertions(+) create mode 100644 src/main/java/com/pawwithu/connectdog/domain/fcm/dto/NotificationMessage.java diff --git a/src/main/java/com/pawwithu/connectdog/domain/fcm/dto/NotificationMessage.java b/src/main/java/com/pawwithu/connectdog/domain/fcm/dto/NotificationMessage.java new file mode 100644 index 00000000..122b1abf --- /dev/null +++ b/src/main/java/com/pawwithu/connectdog/domain/fcm/dto/NotificationMessage.java @@ -0,0 +1,22 @@ +package com.pawwithu.connectdog.domain.fcm.dto; + +import lombok.Getter; +import lombok.RequiredArgsConstructor; + +@Getter +@RequiredArgsConstructor +public enum NotificationMessage { + APPLICATION("", "님이 이동봉사를 신청하셨어요. 지금 확인해 보세요!"); + + private final String title; + private final String body; + + public String getTitleWithLoc(String departureLoc, String arrivalLoc) { + return departureLoc + "→" + arrivalLoc; + } + + public String getBodyWithName(String name) { + return name + body; + } + +} \ No newline at end of file From f7a4eb9082d59c5cb0056231b14284bdb048140e Mon Sep 17 00:00:00 2001 From: kyeong-hyeok Date: Mon, 20 Nov 2023 22:20:37 +0900 Subject: [PATCH 4/4] =?UTF-8?q?feat:=20FCM=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20API=20=EA=B5=AC=ED=98=84=20(#129)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../domain/fcm/controller/FcmController.java | 16 +++++++ .../fcm/dto/{request => }/FcmMessage.java | 2 +- .../fcm/dto/request/FcmTokenRequest.java | 7 +++ .../domain/fcm/service/FcmService.java | 48 +++++++++++-------- .../pawwithu/connectdog/error/ErrorCode.java | 4 +- 5 files changed, 55 insertions(+), 22 deletions(-) rename src/main/java/com/pawwithu/connectdog/domain/fcm/dto/{request => }/FcmMessage.java (90%) create mode 100644 src/main/java/com/pawwithu/connectdog/domain/fcm/dto/request/FcmTokenRequest.java diff --git a/src/main/java/com/pawwithu/connectdog/domain/fcm/controller/FcmController.java b/src/main/java/com/pawwithu/connectdog/domain/fcm/controller/FcmController.java index 165ec44a..8bbe854b 100644 --- a/src/main/java/com/pawwithu/connectdog/domain/fcm/controller/FcmController.java +++ b/src/main/java/com/pawwithu/connectdog/domain/fcm/controller/FcmController.java @@ -1,5 +1,6 @@ package com.pawwithu.connectdog.domain.fcm.controller; +import com.pawwithu.connectdog.domain.fcm.dto.request.FcmTokenRequest; 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; @@ -18,6 +19,8 @@ import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RestController; +import static com.pawwithu.connectdog.domain.fcm.dto.NotificationMessage.APPLICATION; + @Tag(name = "Fcm", description = "Fcm API") @RestController @RequiredArgsConstructor @@ -52,4 +55,17 @@ public ResponseEntity saveIntermediaryFcm(@AuthenticationPrincipal UserDet fcmService.saveIntermediaryFcm(loginUser.getUsername(), request); return ResponseEntity.noContent().build(); } + + @Operation(summary = "FCM 토큰 테스트", description = "FCM 토큰을 테스트 합니다.", + responses = {@ApiResponse(responseCode = "200", description = "FCM 토큰 테스트 성공") + , @ApiResponse(responseCode = "400" + , description = "V1, fcm 토큰은 필수 입력 값입니다." + , content = @Content(schema = @Schema(implementation = ErrorResponse.class))) + }) + @PostMapping("/fcm-test") + public ResponseEntity testFcmToken(@Valid @RequestBody FcmTokenRequest request) { + fcmService.sendMessageTo(request.fcmToken(), APPLICATION.getTitleWithLoc("서울 강남구", "서울 도봉구"), APPLICATION.getBodyWithName("포윗유")); + return ResponseEntity.noContent().build(); + } + } diff --git a/src/main/java/com/pawwithu/connectdog/domain/fcm/dto/request/FcmMessage.java b/src/main/java/com/pawwithu/connectdog/domain/fcm/dto/FcmMessage.java similarity index 90% rename from src/main/java/com/pawwithu/connectdog/domain/fcm/dto/request/FcmMessage.java rename to src/main/java/com/pawwithu/connectdog/domain/fcm/dto/FcmMessage.java index 6343f670..096ffc50 100644 --- a/src/main/java/com/pawwithu/connectdog/domain/fcm/dto/request/FcmMessage.java +++ b/src/main/java/com/pawwithu/connectdog/domain/fcm/dto/FcmMessage.java @@ -1,4 +1,4 @@ -package com.pawwithu.connectdog.domain.fcm.dto.request; +package com.pawwithu.connectdog.domain.fcm.dto; import lombok.AllArgsConstructor; import lombok.Builder; diff --git a/src/main/java/com/pawwithu/connectdog/domain/fcm/dto/request/FcmTokenRequest.java b/src/main/java/com/pawwithu/connectdog/domain/fcm/dto/request/FcmTokenRequest.java new file mode 100644 index 00000000..8651fc46 --- /dev/null +++ b/src/main/java/com/pawwithu/connectdog/domain/fcm/dto/request/FcmTokenRequest.java @@ -0,0 +1,7 @@ +package com.pawwithu.connectdog.domain.fcm.dto.request; + +import jakarta.validation.constraints.NotBlank; + +public record FcmTokenRequest(@NotBlank(message = "fcm 토큰은 필수 입력 값입니다.") + String fcmToken) { +} diff --git a/src/main/java/com/pawwithu/connectdog/domain/fcm/service/FcmService.java b/src/main/java/com/pawwithu/connectdog/domain/fcm/service/FcmService.java index 97c8ca21..97d13640 100644 --- a/src/main/java/com/pawwithu/connectdog/domain/fcm/service/FcmService.java +++ b/src/main/java/com/pawwithu/connectdog/domain/fcm/service/FcmService.java @@ -4,7 +4,7 @@ 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.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; @@ -27,12 +27,10 @@ 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; +import static com.pawwithu.connectdog.error.ErrorCode.*; @Slf4j @Service -@Transactional @RequiredArgsConstructor public class FcmService { @@ -91,31 +89,41 @@ public String makeMessage(String targetToken, String title, String body) throws * 알림 푸쉬를 보내는 역할을 하는 메서드 * @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()); + public void sendMessageTo(String targetToken, String title, String body) { + + try { + 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(); + if (!response.isSuccessful()) { + log.error("FCM 푸시 알람 전송이 실패했습니다. 응답 코드: {}\n{}", response.code(), response.body().string()); + } + } + catch (Exception e) { + log.error("Fcm 푸시 알람을 전송하는 도중에 에러가 발생했습니다. {}", e.getMessage()); + throw new BadRequestException(NOTIFICATION_SEND_ERROR); + } return; } + @Transactional 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); } + @Transactional public void saveIntermediaryFcm(String email, IntermediaryFcmRequest request) { Intermediary intermediary = intermediaryRepository.findByEmail(email).orElseThrow(() -> new BadRequestException(INTERMEDIARY_NOT_FOUND)); IntermediaryFcm intermediaryFcm = IntermediaryFcmRequest.IntermediaryToEntity(intermediary, request); diff --git a/src/main/java/com/pawwithu/connectdog/error/ErrorCode.java b/src/main/java/com/pawwithu/connectdog/error/ErrorCode.java index 014d3615..67c903ad 100644 --- a/src/main/java/com/pawwithu/connectdog/error/ErrorCode.java +++ b/src/main/java/com/pawwithu/connectdog/error/ErrorCode.java @@ -40,7 +40,9 @@ public enum ErrorCode { APPLICATION_NOT_FOUND("AP2", "해당 신청 내역을 찾을 수 없습니다."), REVIEW_NOT_FOUND("R1", "해당 후기를 찾을 수 없습니다."), - DOG_STATUS_NOT_FOUND("D1", "해당 근황을 찾을 수 없습니다."); + DOG_STATUS_NOT_FOUND("D1", "해당 근황을 찾을 수 없습니다."), + + NOTIFICATION_SEND_ERROR("N1", "알림 전송을 실패했습니다."); private final String code;