diff --git a/.gitignore b/.gitignore index 71845e2..f9aa69d 100644 --- a/.gitignore +++ b/.gitignore @@ -37,4 +37,4 @@ out/ .vscode/ ### yml ### -src/main/resources/*.yml \ No newline at end of file +src/main/resources/* \ No newline at end of file diff --git a/build.gradle b/build.gradle index bb1a937..49a97f2 100644 --- a/build.gradle +++ b/build.gradle @@ -63,6 +63,8 @@ dependencies { // Test testImplementation 'org.projectlombok:lombok' testAnnotationProcessor 'org.projectlombok:lombok' + testImplementation 'org.springframework.boot:spring-boot-starter-test' + testImplementation 'org.mockito:mockito-core:4.6.1' // JSON implementation 'com.googlecode.json-simple:json-simple:1.1.1' @@ -75,6 +77,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-webflux' implementation 'jakarta.annotation:jakarta.annotation-api:2.0.0' + + //gradle + implementation 'com.google.firebase:firebase-admin:8.0.1' } tasks.named('test') { diff --git a/src/main/java/com/hana/api/auth/filter/CustomerAuthenticationFilter.java b/src/main/java/com/hana/api/auth/filter/CustomerAuthenticationFilter.java index dbd26e6..7dec386 100644 --- a/src/main/java/com/hana/api/auth/filter/CustomerAuthenticationFilter.java +++ b/src/main/java/com/hana/api/auth/filter/CustomerAuthenticationFilter.java @@ -24,6 +24,7 @@ public class CustomerAuthenticationFilter extends OncePerRequestFilter { private final JwtTokenProvider tokenProvider; private final List EXCLUDE_URL = List.of("/**", "/api/v1/auth", "/api/v1/customer/signup", "/favicon", + "/api/v1/send-notification", "/register-token","/api/send-notification", "/api/v1/sms", "/swagger", "/v3", "/api/v1/developer", "/api/ocr","/api/gpt/generate-image","/api/gpt/chat"); @Override diff --git a/src/main/java/com/hana/api/firebase/controller/TokenController.java b/src/main/java/com/hana/api/firebase/controller/TokenController.java new file mode 100644 index 0000000..7c6b83f --- /dev/null +++ b/src/main/java/com/hana/api/firebase/controller/TokenController.java @@ -0,0 +1,45 @@ +package com.hana.api.firebase.controller; + +import com.hana.api.auth.Auth; +import com.hana.api.user.entity.User; +import com.hana.api.user.service.UserService; +import com.hana.common.config.BaseResponse; +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.responses.ApiResponses; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.web.bind.annotation.*; + +import java.util.*; + +@RestController +@Slf4j +@RequiredArgsConstructor +@RequestMapping("/register-token") +public class TokenController { + final private UserService userService; + + //사용자의 token을 이용해 push하기 위해 받아옴 user db에 auth를 이용해 저장하기 + @Operation(summary = "토큰 주입") + @ApiResponses({ + @ApiResponse(responseCode = "1000", description = "수정성공", content = @Content(schema = @Schema(implementation = BaseResponse.SuccessResult.class))), + @ApiResponse(responseCode = "5000", description = "수정실패", content = @Content(schema = @Schema(implementation = BaseResponse.ErrorResult.class))), + }) + @PostMapping + public BaseResponse.SuccessResult registerToken(@RequestBody Map request, @Auth String usercode) { + String token = request.get("token"); + log.info("++++++++++++++"); + log.info(token); + UUID code = UUID.fromString(usercode); + User user = userService.findbyUser(code); + if(user != null){ + user.setDeviceId(token); + } + System.out.println("Token registered: " + token); + + return BaseResponse.success(userService.save(user).getUserCode()); + } +} diff --git a/src/main/java/com/hana/api/firebase/service/NotificationService.java b/src/main/java/com/hana/api/firebase/service/NotificationService.java new file mode 100644 index 0000000..a468b91 --- /dev/null +++ b/src/main/java/com/hana/api/firebase/service/NotificationService.java @@ -0,0 +1,27 @@ +package com.hana.api.firebase.service; + +import com.google.firebase.messaging.FirebaseMessaging; +import com.google.firebase.messaging.Message; +import com.google.firebase.messaging.Notification; +import org.springframework.stereotype.Service; + +@Service +public class NotificationService { + + public void sendNotification(String token, String title, String body) { + try { + Message message = Message.builder() + .setToken(token) + .putData("title", title) + .putData("body", body) + .build(); + + String response = FirebaseMessaging.getInstance().send(message); + System.out.println("Successfully sent message: " + response); + + } catch (Exception e) { + System.err.println("Error sending FCM message: " + e.getMessage()); + e.printStackTrace(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/hana/api/firebase/service/PushNotificationService.java b/src/main/java/com/hana/api/firebase/service/PushNotificationService.java new file mode 100644 index 0000000..2f04d61 --- /dev/null +++ b/src/main/java/com/hana/api/firebase/service/PushNotificationService.java @@ -0,0 +1,21 @@ +package com.hana.api.firebase.service; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; + +@Service +public class PushNotificationService { + + private static final String EXPO_PUSH_URL = "https://exp.host/--/api/v2/push/send"; + + public void sendNotificationToDevice(String token, String title, String message) { + sendPushNotification(token, title, message); + } + //해당 service 이용해서 영어만!!! 넣어서 보내면 제대로 push 감 token은 user에서 deviedId 테이블 사용 + //title은 큰 글씨 message는 작은 글씨 + private void sendPushNotification(String token, String title, String message) { + RestTemplate restTemplate = new RestTemplate(); + String payload = String.format("{\"to\":\"%s\",\"sound\":\"default\",\"title\":\"%s\",\"body\":\"%s\"}", token, title, message); + restTemplate.postForEntity(EXPO_PUSH_URL, payload, String.class); + } +} \ No newline at end of file diff --git a/src/main/java/com/hana/api/user/entity/User.java b/src/main/java/com/hana/api/user/entity/User.java index 67b2bce..5ecf9a1 100644 --- a/src/main/java/com/hana/api/user/entity/User.java +++ b/src/main/java/com/hana/api/user/entity/User.java @@ -19,6 +19,7 @@ @Builder @ToString @Getter + public class User extends BaseEntity { @Id @Column(columnDefinition = "BINARY(16)", name = "user_code") @@ -63,4 +64,8 @@ public class User extends BaseEntity { @OneToMany(mappedBy = "user", fetch = FetchType.LAZY, cascade = CascadeType.REMOVE) @ToString.Exclude private List accounts; + + public void setDeviceId(String id){ + this.deviceId = UUID.fromString(id); + } } diff --git a/src/main/java/com/hana/api/user/repository/CustomerRepository.java b/src/main/java/com/hana/api/user/repository/CustomerRepository.java index b4cd384..affa674 100644 --- a/src/main/java/com/hana/api/user/repository/CustomerRepository.java +++ b/src/main/java/com/hana/api/user/repository/CustomerRepository.java @@ -20,5 +20,5 @@ public interface CustomerRepository extends JpaRepository { "FROM user " ) List findCustomerContact(); - Optional findByUserCode(UUID userCode); + Optional findByUserCode(UUID userCode); } diff --git a/src/main/java/com/hana/api/user/service/UserService.java b/src/main/java/com/hana/api/user/service/UserService.java index 631b441..4cc0349 100644 --- a/src/main/java/com/hana/api/user/service/UserService.java +++ b/src/main/java/com/hana/api/user/service/UserService.java @@ -2,6 +2,7 @@ import java.util.List; +import java.util.Optional; import java.util.UUID; import com.hana.api.user.dto.CustomerContactDto; @@ -32,12 +33,34 @@ public UUID save(SignUpRequest signUpRequest) { throw new BaseException(BaseResponseStatus.DUPLICATE_CUSTOMER); } } + @Transactional + public User save(User user) { + try { + return customerRepository.save(user); + } catch (DataIntegrityViolationException e) { + throw new BaseException(BaseResponseStatus.DUPLICATE_CUSTOMER); + } + } + + + public MyInfoResponse findByUserCode(UUID userCode) { return new MyInfoResponse((User) customerRepository.findByUserCode(userCode).orElseThrow(() -> new BaseException(BaseResponseStatus.INVALID_USER_JWT))); } + + + public Optional findUser(UUID userCode) { + return customerRepository.findByUserCode(userCode); + } + + public User findbyUser(UUID userCode) { + return customerRepository.findByUserCode(userCode).orElseThrow(() -> + new BaseException(BaseResponseStatus.INVALID_USER_JWT)); + } + public List getCustomerContact() { return customerRepository.findCustomerContact(); } diff --git a/src/main/java/com/hana/common/config/FirebaseConfig.java b/src/main/java/com/hana/common/config/FirebaseConfig.java new file mode 100644 index 0000000..97ac134 --- /dev/null +++ b/src/main/java/com/hana/common/config/FirebaseConfig.java @@ -0,0 +1,36 @@ +package com.hana.common.config; + + +import com.google.auth.oauth2.GoogleCredentials; +import com.google.firebase.FirebaseApp; +import com.google.firebase.FirebaseOptions; +import org.springframework.context.annotation.Bean; +import org.springframework.context.annotation.Configuration; + +import java.io.FileInputStream; +import java.io.IOException; +import java.io.InputStream; + +@Configuration +public class FirebaseConfig { + + @Bean + public FirebaseApp firebaseApp() throws IOException { + // 서비스 계정 키 파일 경로 + String serviceAccountPath = "src/main/resources/firebase-service-account.json"; +// 서비스 계정 키 파일을 읽기 위한 InputStream + FileInputStream serviceAccount = new FileInputStream(serviceAccountPath); + + // FirebaseOptions를 사용하여 FirebaseApp 초기화 + FirebaseOptions options = new FirebaseOptions.Builder() + .setCredentials(GoogleCredentials.fromStream(serviceAccount)) + .build(); + + // FirebaseApp이 이미 초기화되어 있는지 확인하고, 초기화되지 않은 경우에만 초기화 + if (FirebaseApp.getApps().isEmpty()) { + return FirebaseApp.initializeApp(options); + } else { + return FirebaseApp.getInstance(); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/hana/common/job/InsertDailyChallengeTasklet.java b/src/main/java/com/hana/common/job/InsertDailyChallengeTasklet.java index 86c130f..49f5f36 100644 --- a/src/main/java/com/hana/common/job/InsertDailyChallengeTasklet.java +++ b/src/main/java/com/hana/common/job/InsertDailyChallengeTasklet.java @@ -1,6 +1,7 @@ package com.hana.common.job; import com.hana.api.challenge.service.ChallengeRecordService; +import com.hana.api.firebase.service.PushNotificationService; import lombok.RequiredArgsConstructor; import org.springframework.batch.core.StepContribution; import org.springframework.batch.core.repository.JobRepository; @@ -15,9 +16,10 @@ public class InsertDailyChallengeTasklet implements Tasklet { private final ChallengeRecordService challengeRecordService; + private final PushNotificationService pushNotificationService; @Override public RepeatStatus execute(StepContribution contribution, ChunkContext chunkContext) throws Exception { - challengeRecordService.insertDailyChallengeRecord(); + pushNotificationService.sendNotificationToDevice("d","d","d"); return RepeatStatus.FINISHED; } }