diff --git a/src/main/java/vom/spring/domain/member/repository/MemberRepository.java b/src/main/java/vom/spring/domain/member/repository/MemberRepository.java index 0f1fd43..fd47d31 100644 --- a/src/main/java/vom/spring/domain/member/repository/MemberRepository.java +++ b/src/main/java/vom/spring/domain/member/repository/MemberRepository.java @@ -1,14 +1,22 @@ package vom.spring.domain.member.repository; import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import org.springframework.data.repository.query.Param; +import org.springframework.stereotype.Repository; import vom.spring.domain.member.domain.Member; import java.util.Optional; +@Repository public interface MemberRepository extends JpaRepository { Optional findByEmail(String email); Optional findByNickname(String nickname); boolean existsByEmail(String email); boolean existsByNickname(String nickname); Optional findById(Long id); + + // ID로 닉네임 조회 + @Query("SELECT m.nickname FROM Member m WHERE m.id = :id") + String findNicknameById(@Param("id") Long id); } diff --git a/src/main/java/vom/spring/domain/webcam/service/WebcamServiceImpl.java b/src/main/java/vom/spring/domain/webcam/service/WebcamServiceImpl.java index f9077e5..f927425 100644 --- a/src/main/java/vom/spring/domain/webcam/service/WebcamServiceImpl.java +++ b/src/main/java/vom/spring/domain/webcam/service/WebcamServiceImpl.java @@ -1,6 +1,7 @@ package vom.spring.domain.webcam.service; import lombok.RequiredArgsConstructor; +import org.springframework.beans.factory.annotation.Autowired; import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -19,6 +20,7 @@ import java.time.LocalDateTime; import java.util.List; import java.util.Optional; +import vom.spring.domain.webpush.service.WebpushService; @Service @RequiredArgsConstructor @@ -28,6 +30,9 @@ public class WebcamServiceImpl implements WebcamServcie{ private final MemberWebcamRepository memberWebcamRepository; private final WebpushRepository webpushRepository; + @Autowired + private WebpushService webpushService; + /** * 화상채팅 방 생성 */ @@ -45,13 +50,7 @@ public WebcamResponseDto.CreateWebcamDto createWebcamRoom(WebcamRequestDto.Creat MemberWebcam toMemberWebcam = MemberWebcam.builder().member(toMember).webcam(newWebcam).build(); memberWebcamRepository.save(fromMemberWebcam); memberWebcamRepository.save(toMemberWebcam); - webpushRepository.save( - Webpush.builder() - .createdAt(LocalDateTime.now()) - .fromMember(fromMember) - .toMember(toMember) - .webcam(newWebcam) - .build()); + webpushService.createWebpush(fromMember, toMember, newWebcam); return WebcamResponseDto.CreateWebcamDto.builder().webcamId(newWebcam.getId()).build(); } @@ -65,6 +64,8 @@ public void deleteWebcamRoom(WebcamRequestDto.DeleteWebcamDto request) { Member fromMember = memberRepository.findByEmail(email).orElseThrow(() -> new RuntimeException("존재하지 않은 유저입니다")); //해당 방 찾기 Webcam webcam = webcamRepository.findById(request.getRoomId()).orElseThrow(() -> new IllegalArgumentException("존재하지 않은 방입니다")); + //해당 방 관련 웹 푸시 알림 삭제 + webpushService.deleteWebpushByWebcamId(webcam.getId()); //해당 방 관련 연관관계 삭제 memberWebcamRepository.deleteByWebcam(webcam); //해당 방 삭제 diff --git a/src/main/java/vom/spring/domain/webpush/controller/FcmController.java b/src/main/java/vom/spring/domain/webpush/controller/FcmController.java index 6ba8e8f..ea4a70a 100644 --- a/src/main/java/vom/spring/domain/webpush/controller/FcmController.java +++ b/src/main/java/vom/spring/domain/webpush/controller/FcmController.java @@ -48,4 +48,23 @@ public ResponseEntity> pushMessage( return new ResponseEntity<>(arw, HttpStatus.OK); } + + // 웹 푸시 메세지 내용 테스트용 + @PostMapping("/api/v3/fcm/send/{member_id}") + public ResponseEntity> sendPushMessage( + @PathVariable("member_id") Long memberId + ) throws IOException { + log.debug("[+] 푸시 메시지를 전송합니다. "); + + int result = fcmService.sendMessageTo(memberId); + + ApiResponseWrapper arw = ApiResponseWrapper + .builder() + .result(result) + .resultCode(SuccessCode.SELECT_SUCCESS.getStatus()) + .resultMsg(SuccessCode.SELECT_SUCCESS.getMessage()) + .build(); + + return new ResponseEntity<>(arw, HttpStatus.OK); + } } diff --git a/src/main/java/vom/spring/domain/webpush/controller/WebpushController.java b/src/main/java/vom/spring/domain/webpush/controller/WebpushController.java index 6960409..ddd76dc 100644 --- a/src/main/java/vom/spring/domain/webpush/controller/WebpushController.java +++ b/src/main/java/vom/spring/domain/webpush/controller/WebpushController.java @@ -27,22 +27,4 @@ public ResponseEntity> getWebpushes( ) { return new ResponseEntity<>(webpushService.getWebpushes(memberId), HttpStatus.OK); } - -// @PostMapping("/api/webpush/send") -// public String sendNotification( -// @RequestBody NotificationRequest request) -// { -// webpushService.sendNotification(request.getTargetToken(), request.getTitle(), request.getBody()); -// return "Notification sent successfully!"; -// } -// -// @Getter -// public static class NotificationRequest { -// private String targetToken; -// private String title; -// private String body; -// -// // Getters and Setters -// } - } diff --git a/src/main/java/vom/spring/domain/webpush/domain/Webpush.java b/src/main/java/vom/spring/domain/webpush/domain/Webpush.java index f8ad756..1bf9208 100644 --- a/src/main/java/vom/spring/domain/webpush/domain/Webpush.java +++ b/src/main/java/vom/spring/domain/webpush/domain/Webpush.java @@ -6,7 +6,6 @@ import lombok.Getter; import lombok.NoArgsConstructor; import vom.spring.domain.member.domain.Member; -import vom.spring.domain.webcam.domain.Status; import vom.spring.domain.webcam.domain.Webcam; import java.time.LocalDateTime; diff --git a/src/main/java/vom/spring/domain/webpush/dto/FcmMessageDto.java b/src/main/java/vom/spring/domain/webpush/dto/FcmMessageDto.java index a93d0c2..ef7fe8d 100644 --- a/src/main/java/vom/spring/domain/webpush/dto/FcmMessageDto.java +++ b/src/main/java/vom/spring/domain/webpush/dto/FcmMessageDto.java @@ -7,7 +7,7 @@ @Getter @Builder public class FcmMessageDto { - private boolean validateOnly; + private FcmMessageDto.Message message; @Builder diff --git a/src/main/java/vom/spring/domain/webpush/dto/FcmSendDto.java b/src/main/java/vom/spring/domain/webpush/dto/FcmSendDto.java deleted file mode 100644 index f807994..0000000 --- a/src/main/java/vom/spring/domain/webpush/dto/FcmSendDto.java +++ /dev/null @@ -1,21 +0,0 @@ -package vom.spring.domain.webpush.dto; - -import lombok.*; - -@Getter -@ToString -@NoArgsConstructor(access = AccessLevel.PROTECTED) -public class FcmSendDto { - private String token; - - private String title; - - private String body; - - @Builder(toBuilder = true) - public FcmSendDto(String token, String title, String body) { - this.token = token; - this.title = title; - this.body = body; - } -} diff --git a/src/main/java/vom/spring/domain/webpush/dto/WebpushDto.java b/src/main/java/vom/spring/domain/webpush/dto/WebpushDto.java index 5452dae..06ed157 100644 --- a/src/main/java/vom/spring/domain/webpush/dto/WebpushDto.java +++ b/src/main/java/vom/spring/domain/webpush/dto/WebpushDto.java @@ -3,15 +3,16 @@ import lombok.Getter; import java.time.LocalDateTime; +import lombok.Setter; -@Getter +@Getter @Setter public class WebpushDto { - private Long fromMemberId; + private String fromMemberNickname; private LocalDateTime createdAt; private Long webcamId; - public WebpushDto(Long fromMemberId, LocalDateTime createdAt, Long webcamId) { - this.fromMemberId = fromMemberId; + public WebpushDto(String fromMemberNickname, LocalDateTime createdAt, Long webcamId) { + this.fromMemberNickname = fromMemberNickname; this.createdAt = createdAt; this.webcamId = webcamId; } diff --git a/src/main/java/vom/spring/domain/webpush/repository/WebpushRepository.java b/src/main/java/vom/spring/domain/webpush/repository/WebpushRepository.java index 0bb1d2d..6cd1500 100644 --- a/src/main/java/vom/spring/domain/webpush/repository/WebpushRepository.java +++ b/src/main/java/vom/spring/domain/webpush/repository/WebpushRepository.java @@ -19,10 +19,25 @@ public void save(Webpush webpush) { public List findByToMemberId(Long toMemberId) { return em.createQuery( - "select new WebpushDto(w.fromMember.id, w.createdAt, w.webcam.id) " + + "select new WebpushDto(w.fromMember.nickname, w.createdAt, w.webcam.id) " + "from Webpush w where w.toMember.id = :toMemberId", WebpushDto.class) .setParameter("toMemberId", toMemberId) .getResultList(); } + + public Webpush findByWebcamId(Long webcamId) { + return em.createQuery("SELECT w FROM Webpush w WHERE w.webcam.id = :webcamId", Webpush.class) + .setParameter("webcamId", webcamId) + .getSingleResult(); + } + + public void delete(Webpush webpush) { + if (em.contains(webpush)) { + em.remove(webpush); + } else { + Webpush mergedWebpush = em.merge(webpush); // 비영속 상태라면 병합 후 삭제 + em.remove(mergedWebpush); + } + } } diff --git a/src/main/java/vom/spring/domain/webpush/service/FcmService.java b/src/main/java/vom/spring/domain/webpush/service/FcmService.java index 36d2259..1b9ec55 100644 --- a/src/main/java/vom/spring/domain/webpush/service/FcmService.java +++ b/src/main/java/vom/spring/domain/webpush/service/FcmService.java @@ -3,9 +3,11 @@ import com.fasterxml.jackson.core.JsonProcessingException; import com.fasterxml.jackson.databind.ObjectMapper; import com.google.auth.oauth2.GoogleCredentials; +import java.util.NoSuchElementException; import lombok.extern.slf4j.Slf4j; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.core.io.ClassPathResource; +import org.springframework.dao.EmptyResultDataAccessException; import org.springframework.http.*; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -55,48 +57,42 @@ public void setFcmToken(String fcmToken, Long member_id) { } /** - * 푸시 메시지 처리를 수행하는 비즈니스 로직 + * 푸시 메시지 전송 * * @return 성공(1), 실패(0) */ public int sendMessageTo(Long memberId) throws IOException { + try { + Fcm fcm = fcmRepository.findByMember_id(memberId); - Fcm fcm = fcmRepository.findByMember_id(memberId); + if (fcm == null) { + throw new NoSuchElementException("해당 멤버가 알림 설정을 하지 않았습니다."); + } - if (fcm == null) { -// log.error("Fcm token not found for memberId: " + memberId); -// throw new IllegalArgumentException("Fcm token not found for memberId: " + memberId); - System.out.println("해당 멤버가 알림 설정을 하지 않았습니다."); - return 0; - } + String message = makeMessage(fcm); - String message = makeMessage(fcm); - RestTemplate restTemplate = new RestTemplate(); + RestTemplate restTemplate = new RestTemplate(); - HttpHeaders headers = new HttpHeaders(); - headers.setContentType(MediaType.APPLICATION_JSON); - headers.set("Authorization", "Bearer " + getAccessToken()); + HttpHeaders headers = new HttpHeaders(); + headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Authorization", "Bearer " + getAccessToken()); - HttpEntity entity = new HttpEntity<>(message, headers); + HttpEntity entity = new HttpEntity<>(message, headers); - String API_URL = "https://fcm.googleapis.com/v1/projects/vomvom-fd09b/messages:send"; -// String API_URL = "https://fcm.googleapis.com/fcm/send"; - ResponseEntity response = null; + String API_URL = "https://fcm.googleapis.com/v1/projects/vomvom-fd09b/messages:send"; - try { - response = restTemplate.exchange(API_URL, HttpMethod.POST, entity, String.class); - System.out.println(response.getStatusCode()); + ResponseEntity response = restTemplate.exchange(API_URL, HttpMethod.POST, entity, String.class); + System.out.println("Response: " + response.getBody()); + System.out.println("Status Code: " + response.getStatusCode()); return response.getStatusCode() == HttpStatus.OK ? 1 : 0; } catch (Exception e) { - log.error("[-] FCM 전송 오류 :: " + e.getMessage()); + System.err.println("[-] FCM 전송 오류 :: " + e.getMessage()); return 0; } } /** - * Firebase Admin SDK의 비공개 키를 참조하여 Bearer 토큰을 발급 받습니다. - * - * playground에서 발급받은 토큰 + * Firebase Admin SDK의 비공개 키를 참조하여 Bearer 토큰 발급 * * @return Bearer token */ @@ -106,7 +102,8 @@ private String getAccessToken() throws IOException { GoogleCredentials googleCredentials = GoogleCredentials .fromStream(new ClassPathResource(firebaseConfigPath).getInputStream()) - .createScoped(List.of("https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/firebase.messaging")); + .createScoped(List.of("https://www.googleapis.com/auth/cloud-platform", + "https://www.googleapis.com/auth/firebase.messaging")); googleCredentials.refreshIfExpired(); @@ -116,7 +113,7 @@ private String getAccessToken() throws IOException { } /** - * FCM 전송 정보를 기반으로 메시지를 구성합니다. + * FCM 내용 생성 * * @return String */ @@ -124,17 +121,16 @@ private String makeMessage(Fcm fcm) throws JsonProcessingException { ObjectMapper om = new ObjectMapper(); - System.out.println(fcm.getFcmToken()); FcmMessageDto fcmMessageDto = FcmMessageDto.builder() .message(FcmMessageDto.Message.builder() .token(fcm.getFcmToken()) .notification(FcmMessageDto.Notification.builder() .title("VOM") - .body("화상 채팅 요청 알림\n클릭하여 접속!") - .image(null) + .body("화상 채팅 요청 알림 클릭하여 접속!") + .image("https://cdn-icons-png.flaticon.com/512/1762/1762755.png") .build() - ).build()).validateOnly(false).build(); + ).build()).build(); return om.writeValueAsString(fcmMessageDto); } -} +} \ No newline at end of file diff --git a/src/main/java/vom/spring/domain/webpush/service/WebpushService.java b/src/main/java/vom/spring/domain/webpush/service/WebpushService.java index 7aca517..606b81e 100644 --- a/src/main/java/vom/spring/domain/webpush/service/WebpushService.java +++ b/src/main/java/vom/spring/domain/webpush/service/WebpushService.java @@ -1,9 +1,13 @@ package vom.spring.domain.webpush.service; +import java.io.IOException; +import java.time.LocalDateTime; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; +import vom.spring.domain.member.domain.Member; import vom.spring.domain.member.repository.MemberRepository; +import vom.spring.domain.webcam.domain.Webcam; +import vom.spring.domain.webpush.domain.Webpush; import vom.spring.domain.webpush.dto.WebpushDto; import vom.spring.domain.webpush.repository.WebpushRepository; @@ -13,55 +17,40 @@ public class WebpushService { private final WebpushRepository webpushRepository; - private final MemberRepository memberRepository; -// private final WebcamRepository webcamRepository; + + @Autowired + private FcmService fcmService; @Autowired public WebpushService(WebpushRepository webpushRepository, MemberRepository memberRepository) { this.webpushRepository = webpushRepository; - this.memberRepository = memberRepository; } - /** - * 웹푸쉬 조회 - */ public List getWebpushes(Long toMemberId) { return webpushRepository.findByToMemberId(toMemberId); } - /** - * FCM - */ -// private static final String FCM_API_URL = "https://fcm.googleapis.com/fcm/send"; -// private static final String SERVER_KEY = ""; // Firebase Console에서 확인한 서버 키 -// -// public void sendNotification(String targetToken, String title, String body) { -// RestTemplate restTemplate = new RestTemplate(); -// -// HttpHeaders headers = new HttpHeaders(); -// headers.set("Authorization", "key=" + SERVER_KEY); -// headers.set("Content-Type", "application/json"); -// -// Map notification = new HashMap<>(); -// notification.put("title", title); -// notification.put("body", body); -// -// Map message = new HashMap<>(); -// message.put("to", targetToken); -// message.put("notification", notification); -// -// HttpEntity> entity = new HttpEntity<>(message, headers); - -// ResponseEntity response = restTemplate.exchange(FCM_API_URL, HttpMethod.POST, entity, String.class); -// System.out.println("FCM Response: " + response.getBody()); - -// try { -// ResponseEntity response = restTemplate.postForEntity(FCM_API_URL, entity, String.class); -// System.out.println("Response: " + response.getBody()); -// } catch (HttpClientErrorException e) { -// System.err.println("Error: " + e.getStatusCode() + " - " + e.getResponseBodyAsString()); -// } -// } - + public void createWebpush(Member fromMember, Member toMember, Webcam webcam) { + System.out.println("Enter createWebpush"); + webpushRepository.save( + Webpush.builder() + .createdAt(LocalDateTime.now()) + .fromMember(fromMember) + .toMember(toMember) + .webcam(webcam) + .build()); + try { + fcmService.sendMessageTo(toMember.getId()); + } catch (IOException e) { + System.err.println("FCM 메시지 전송 중 오류 발생: " + e.getMessage()); + } + System.out.println("Finish createWebpush"); + } + public void deleteWebpushByWebcamId(Long webcamId) { + Webpush webpush = webpushRepository.findByWebcamId(webcamId); + if (webpush != null) { + webpushRepository.delete(webpush); + } + } }