From 06fba58456951ef4eee477ac0a75423d67178474 Mon Sep 17 00:00:00 2001 From: okod Date: Sat, 1 Jun 2024 20:07:16 +0900 Subject: [PATCH 1/8] feat: Add Webpush Domain --- .../vom/spring/domain/webpush/Webpush.java | 38 +++++++++++++ .../domain/webpush/WebpushController.java | 29 ++++++++++ .../vom/spring/domain/webpush/WebpushDto.java | 39 ++++++++++++++ .../domain/webpush/WebpushRepository.java | 28 ++++++++++ .../spring/domain/webpush/WebpushService.java | 54 +++++++++++++++++++ 5 files changed, 188 insertions(+) create mode 100644 src/main/java/vom/spring/domain/webpush/Webpush.java create mode 100644 src/main/java/vom/spring/domain/webpush/WebpushController.java create mode 100644 src/main/java/vom/spring/domain/webpush/WebpushDto.java create mode 100644 src/main/java/vom/spring/domain/webpush/WebpushRepository.java create mode 100644 src/main/java/vom/spring/domain/webpush/WebpushService.java diff --git a/src/main/java/vom/spring/domain/webpush/Webpush.java b/src/main/java/vom/spring/domain/webpush/Webpush.java new file mode 100644 index 0000000..48dab44 --- /dev/null +++ b/src/main/java/vom/spring/domain/webpush/Webpush.java @@ -0,0 +1,38 @@ +package vom.spring.domain.webpush; + +import jakarta.persistence.*; +import lombok.AllArgsConstructor; +import lombok.Builder; +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; + +@Getter +@Builder +@NoArgsConstructor +@AllArgsConstructor +@Entity +public class Webpush { + + @Id @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + + @Column(nullable = false) + private LocalDateTime createdAt; + + @ManyToOne + @JoinColumn(referencedColumnName = "id", name = "from_member_id", nullable = false) + private Member fromMember; + + @ManyToOne + @JoinColumn(referencedColumnName = "id", name = "to_member_id", nullable = false) + private Member toMember; + +// @OneToOne +// @JoinColumn(referencedColumnName = "id", name = "webcam_id", nullable = false) +// private Webcam webcam; +} diff --git a/src/main/java/vom/spring/domain/webpush/WebpushController.java b/src/main/java/vom/spring/domain/webpush/WebpushController.java new file mode 100644 index 0000000..47d9b9f --- /dev/null +++ b/src/main/java/vom/spring/domain/webpush/WebpushController.java @@ -0,0 +1,29 @@ +package vom.spring.domain.webpush; + +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import vom.spring.domain.touchpoint.TouchpointDto; +import vom.spring.domain.touchpoint.TouchpointService; + +import java.util.List; + +@Controller +public class WebpushController { + private WebpushService webpushService; + @Autowired + public WebpushController(WebpushService webpushService) { + this.webpushService = webpushService; + } + + @GetMapping(value = "/api/webpush/{member-id}") + public ResponseEntity> getWebpushes( + @PathVariable("member-id") Long memberId + ) { + return new ResponseEntity<>(webpushService.getWebpushes(memberId), HttpStatus.OK); + } + +} diff --git a/src/main/java/vom/spring/domain/webpush/WebpushDto.java b/src/main/java/vom/spring/domain/webpush/WebpushDto.java new file mode 100644 index 0000000..fdc617b --- /dev/null +++ b/src/main/java/vom/spring/domain/webpush/WebpushDto.java @@ -0,0 +1,39 @@ +package vom.spring.domain.webpush; + +import java.time.LocalDateTime; + +public class WebpushDto { + private Long fromMemberId; + private LocalDateTime createdAt; +// private Long webcamId; + + public WebpushDto(Long fromMemberId, LocalDateTime createdAt) { + this.fromMemberId = fromMemberId; + this.createdAt = createdAt; +// this.webcamId = webcamId; + } + + public Long getFromMemberId() { + return fromMemberId; + } + + public void setFromMemberId(Long fromMemberId) { + this.fromMemberId = fromMemberId; + } + + public LocalDateTime getCreatedAt() { + return createdAt; + } + + public void setCreatedAt(LocalDateTime createdAt) { + this.createdAt = createdAt; + } + +// public Long getWebcamId() { +// return webcamId; +// } +// +// public void setFromMemberProfileImgUrl(String fromMemberProfileUrl) { +// this.webcamId = webcamId; +// } +} diff --git a/src/main/java/vom/spring/domain/webpush/WebpushRepository.java b/src/main/java/vom/spring/domain/webpush/WebpushRepository.java new file mode 100644 index 0000000..a0733a9 --- /dev/null +++ b/src/main/java/vom/spring/domain/webpush/WebpushRepository.java @@ -0,0 +1,28 @@ +package vom.spring.domain.webpush; + +import jakarta.persistence.EntityManager; +import jakarta.persistence.PersistenceContext; +import org.springframework.stereotype.Repository; +import vom.spring.domain.touchpoint.Touchpoint; +import vom.spring.domain.touchpoint.TouchpointDto; + +import java.util.List; + +@Repository +public class WebpushRepository { + @PersistenceContext + private EntityManager em; + + public void save(Touchpoint touchpoint) { + em.persist(touchpoint); + } + + public List findFromMemberIdsByToMemberId(Long toMemberId) { + return em.createQuery( + "select new WebpushDto(w.fromMember.id, w.createdAt) " + + "from Webpush w where w.toMember.id = :toMemberId", + WebpushDto.class) + .setParameter("toMemberId", toMemberId) + .getResultList(); + } +} diff --git a/src/main/java/vom/spring/domain/webpush/WebpushService.java b/src/main/java/vom/spring/domain/webpush/WebpushService.java new file mode 100644 index 0000000..65e3399 --- /dev/null +++ b/src/main/java/vom/spring/domain/webpush/WebpushService.java @@ -0,0 +1,54 @@ +package vom.spring.domain.webpush; + +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.touchpoint.Touchpoint; +import vom.spring.domain.touchpoint.TouchpointDto; +import vom.spring.domain.touchpoint.TouchpointRepository; + +import java.time.LocalDateTime; +import java.util.List; + +@Service +public class WebpushService { + + private final WebpushRepository webpushRepository; + private final MemberRepository memberRepository; +// private final WebcamRepository webcamRepository; + + @Autowired + public WebpushService(WebpushRepository webpushRepository, MemberRepository memberRepository) { + this.webpushRepository = webpushRepository; + this.memberRepository = memberRepository; + } + /** + * 웹푸쉬 생성 + */ + @Transactional + public void createWebpush(Long from_member_id, Long to_member_id, Long webcam_id) { + + Member fromMember = memberRepository.findById(from_member_id).get(); + Member toMember = memberRepository.findById(to_member_id).get(); + + webpushRepository.save( + Touchpoint.builder() + .createdAt(LocalDateTime.now()) + .fromMember(fromMember) + .toMember(toMember) + .build() + ); + } + + /** + * 웹푸쉬 조회 + */ + @Transactional + public List getWebpushes(Long member_id) { + return webpushRepository.findFromMemberIdsByToMemberId(member_id); + } + + +} From 847d896103e06ab9c007c1735ede10e374bc445b Mon Sep 17 00:00:00 2001 From: okod Date: Sun, 2 Jun 2024 16:53:50 +0900 Subject: [PATCH 2/8] feat: Add Webpush Service & Controller --- .../domain/webpush/WebpushController.java | 27 +++++++- .../spring/domain/webpush/WebpushService.java | 62 +++++++++++++------ 2 files changed, 68 insertions(+), 21 deletions(-) diff --git a/src/main/java/vom/spring/domain/webpush/WebpushController.java b/src/main/java/vom/spring/domain/webpush/WebpushController.java index 47d9b9f..a6fca39 100644 --- a/src/main/java/vom/spring/domain/webpush/WebpushController.java +++ b/src/main/java/vom/spring/domain/webpush/WebpushController.java @@ -1,16 +1,22 @@ package vom.spring.domain.webpush; +import lombok.Getter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; -import vom.spring.domain.touchpoint.TouchpointDto; -import vom.spring.domain.touchpoint.TouchpointService; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.beans.factory.annotation.Autowired; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; import java.util.List; +@RestController @Controller public class WebpushController { private WebpushService webpushService; @@ -26,4 +32,21 @@ 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/WebpushService.java b/src/main/java/vom/spring/domain/webpush/WebpushService.java index 65e3399..46416fd 100644 --- a/src/main/java/vom/spring/domain/webpush/WebpushService.java +++ b/src/main/java/vom/spring/domain/webpush/WebpushService.java @@ -3,14 +3,21 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; +import org.springframework.web.client.HttpClientErrorException; import vom.spring.domain.member.domain.Member; import vom.spring.domain.member.repository.MemberRepository; import vom.spring.domain.touchpoint.Touchpoint; -import vom.spring.domain.touchpoint.TouchpointDto; -import vom.spring.domain.touchpoint.TouchpointRepository; +import org.springframework.http.HttpEntity; +import org.springframework.http.HttpHeaders; +import org.springframework.http.HttpMethod; +import org.springframework.http.ResponseEntity; +import org.springframework.stereotype.Service; +import org.springframework.web.client.RestTemplate; import java.time.LocalDateTime; import java.util.List; +import java.util.HashMap; +import java.util.Map; @Service public class WebpushService { @@ -24,23 +31,6 @@ public WebpushService(WebpushRepository webpushRepository, MemberRepository memb this.webpushRepository = webpushRepository; this.memberRepository = memberRepository; } - /** - * 웹푸쉬 생성 - */ - @Transactional - public void createWebpush(Long from_member_id, Long to_member_id, Long webcam_id) { - - Member fromMember = memberRepository.findById(from_member_id).get(); - Member toMember = memberRepository.findById(to_member_id).get(); - - webpushRepository.save( - Touchpoint.builder() - .createdAt(LocalDateTime.now()) - .fromMember(fromMember) - .toMember(toMember) - .build() - ); - } /** * 웹푸쉬 조회 @@ -50,5 +40,39 @@ public List getWebpushes(Long member_id) { return webpushRepository.findFromMemberIdsByToMemberId(member_id); } + /** + * 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()); +// } +// } + } From 9951ba4c6b9356380fd352468814e4af9c2314ba Mon Sep 17 00:00:00 2001 From: okod Date: Sun, 2 Jun 2024 16:57:37 +0900 Subject: [PATCH 3/8] feat: Add Getter annotation --- .../vom/spring/domain/webpush/WebpushDto.java | 27 +++---------------- 1 file changed, 3 insertions(+), 24 deletions(-) diff --git a/src/main/java/vom/spring/domain/webpush/WebpushDto.java b/src/main/java/vom/spring/domain/webpush/WebpushDto.java index fdc617b..fa0ab38 100644 --- a/src/main/java/vom/spring/domain/webpush/WebpushDto.java +++ b/src/main/java/vom/spring/domain/webpush/WebpushDto.java @@ -1,7 +1,10 @@ package vom.spring.domain.webpush; +import lombok.Getter; + import java.time.LocalDateTime; +@Getter public class WebpushDto { private Long fromMemberId; private LocalDateTime createdAt; @@ -12,28 +15,4 @@ public WebpushDto(Long fromMemberId, LocalDateTime createdAt) { this.createdAt = createdAt; // this.webcamId = webcamId; } - - public Long getFromMemberId() { - return fromMemberId; - } - - public void setFromMemberId(Long fromMemberId) { - this.fromMemberId = fromMemberId; - } - - public LocalDateTime getCreatedAt() { - return createdAt; - } - - public void setCreatedAt(LocalDateTime createdAt) { - this.createdAt = createdAt; - } - -// public Long getWebcamId() { -// return webcamId; -// } -// -// public void setFromMemberProfileImgUrl(String fromMemberProfileUrl) { -// this.webcamId = webcamId; -// } } From 0f00ab7576663c02267e662194b46fc123adab78 Mon Sep 17 00:00:00 2001 From: okod Date: Sun, 2 Jun 2024 16:59:20 +0900 Subject: [PATCH 4/8] fix: Modify WebpushRepository save parameter --- .../java/vom/spring/domain/webpush/WebpushRepository.java | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/main/java/vom/spring/domain/webpush/WebpushRepository.java b/src/main/java/vom/spring/domain/webpush/WebpushRepository.java index a0733a9..6c7e624 100644 --- a/src/main/java/vom/spring/domain/webpush/WebpushRepository.java +++ b/src/main/java/vom/spring/domain/webpush/WebpushRepository.java @@ -13,8 +13,8 @@ public class WebpushRepository { @PersistenceContext private EntityManager em; - public void save(Touchpoint touchpoint) { - em.persist(touchpoint); + public void save(Webpush webpush) { + em.persist(webpush); } public List findFromMemberIdsByToMemberId(Long toMemberId) { From 362040053717d3fd543ce0d802acc10e35ae488b Mon Sep 17 00:00:00 2001 From: okod Date: Sun, 2 Jun 2024 17:09:00 +0900 Subject: [PATCH 5/8] refactor: Update Webpush domain folder structure --- .../{ => controller}/WebpushController.java | 10 +++------- .../domain/webpush/{ => domain}/Webpush.java | 2 +- .../domain/webpush/{ => dto}/WebpushDto.java | 2 +- .../{ => repository}/WebpushRepository.java | 6 +++--- .../webpush/{ => service}/WebpushService.java | 16 +++------------- 5 files changed, 11 insertions(+), 25 deletions(-) rename src/main/java/vom/spring/domain/webpush/{ => controller}/WebpushController.java (79%) rename src/main/java/vom/spring/domain/webpush/{ => domain}/Webpush.java (95%) rename src/main/java/vom/spring/domain/webpush/{ => dto}/WebpushDto.java (90%) rename src/main/java/vom/spring/domain/webpush/{ => repository}/WebpushRepository.java (83%) rename src/main/java/vom/spring/domain/webpush/{ => service}/WebpushService.java (81%) diff --git a/src/main/java/vom/spring/domain/webpush/WebpushController.java b/src/main/java/vom/spring/domain/webpush/controller/WebpushController.java similarity index 79% rename from src/main/java/vom/spring/domain/webpush/WebpushController.java rename to src/main/java/vom/spring/domain/webpush/controller/WebpushController.java index a6fca39..6960409 100644 --- a/src/main/java/vom/spring/domain/webpush/WebpushController.java +++ b/src/main/java/vom/spring/domain/webpush/controller/WebpushController.java @@ -1,18 +1,14 @@ -package vom.spring.domain.webpush; +package vom.spring.domain.webpush.controller; -import lombok.Getter; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; import org.springframework.stereotype.Controller; import org.springframework.web.bind.annotation.GetMapping; import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.beans.factory.annotation.Autowired; -import org.springframework.web.bind.annotation.PostMapping; -import org.springframework.web.bind.annotation.RequestBody; -import org.springframework.web.bind.annotation.RequestMapping; import org.springframework.web.bind.annotation.RestController; +import vom.spring.domain.webpush.dto.WebpushDto; +import vom.spring.domain.webpush.service.WebpushService; import java.util.List; diff --git a/src/main/java/vom/spring/domain/webpush/Webpush.java b/src/main/java/vom/spring/domain/webpush/domain/Webpush.java similarity index 95% rename from src/main/java/vom/spring/domain/webpush/Webpush.java rename to src/main/java/vom/spring/domain/webpush/domain/Webpush.java index 48dab44..617579b 100644 --- a/src/main/java/vom/spring/domain/webpush/Webpush.java +++ b/src/main/java/vom/spring/domain/webpush/domain/Webpush.java @@ -1,4 +1,4 @@ -package vom.spring.domain.webpush; +package vom.spring.domain.webpush.domain; import jakarta.persistence.*; import lombok.AllArgsConstructor; diff --git a/src/main/java/vom/spring/domain/webpush/WebpushDto.java b/src/main/java/vom/spring/domain/webpush/dto/WebpushDto.java similarity index 90% rename from src/main/java/vom/spring/domain/webpush/WebpushDto.java rename to src/main/java/vom/spring/domain/webpush/dto/WebpushDto.java index fa0ab38..cf54d66 100644 --- a/src/main/java/vom/spring/domain/webpush/WebpushDto.java +++ b/src/main/java/vom/spring/domain/webpush/dto/WebpushDto.java @@ -1,4 +1,4 @@ -package vom.spring.domain.webpush; +package vom.spring.domain.webpush.dto; import lombok.Getter; diff --git a/src/main/java/vom/spring/domain/webpush/WebpushRepository.java b/src/main/java/vom/spring/domain/webpush/repository/WebpushRepository.java similarity index 83% rename from src/main/java/vom/spring/domain/webpush/WebpushRepository.java rename to src/main/java/vom/spring/domain/webpush/repository/WebpushRepository.java index 6c7e624..9e7a849 100644 --- a/src/main/java/vom/spring/domain/webpush/WebpushRepository.java +++ b/src/main/java/vom/spring/domain/webpush/repository/WebpushRepository.java @@ -1,10 +1,10 @@ -package vom.spring.domain.webpush; +package vom.spring.domain.webpush.repository; import jakarta.persistence.EntityManager; import jakarta.persistence.PersistenceContext; import org.springframework.stereotype.Repository; -import vom.spring.domain.touchpoint.Touchpoint; -import vom.spring.domain.touchpoint.TouchpointDto; +import vom.spring.domain.webpush.domain.Webpush; +import vom.spring.domain.webpush.dto.WebpushDto; import java.util.List; diff --git a/src/main/java/vom/spring/domain/webpush/WebpushService.java b/src/main/java/vom/spring/domain/webpush/service/WebpushService.java similarity index 81% rename from src/main/java/vom/spring/domain/webpush/WebpushService.java rename to src/main/java/vom/spring/domain/webpush/service/WebpushService.java index 46416fd..5bcaf7d 100644 --- a/src/main/java/vom/spring/domain/webpush/WebpushService.java +++ b/src/main/java/vom/spring/domain/webpush/service/WebpushService.java @@ -1,23 +1,13 @@ -package vom.spring.domain.webpush; +package vom.spring.domain.webpush.service; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; -import org.springframework.web.client.HttpClientErrorException; -import vom.spring.domain.member.domain.Member; import vom.spring.domain.member.repository.MemberRepository; -import vom.spring.domain.touchpoint.Touchpoint; -import org.springframework.http.HttpEntity; -import org.springframework.http.HttpHeaders; -import org.springframework.http.HttpMethod; -import org.springframework.http.ResponseEntity; -import org.springframework.stereotype.Service; -import org.springframework.web.client.RestTemplate; +import vom.spring.domain.webpush.dto.WebpushDto; +import vom.spring.domain.webpush.repository.WebpushRepository; -import java.time.LocalDateTime; import java.util.List; -import java.util.HashMap; -import java.util.Map; @Service public class WebpushService { From f8c3116037aa08b6feb913eb9aa3ddf0138cce7d Mon Sep 17 00:00:00 2001 From: okod Date: Wed, 5 Jun 2024 05:13:57 +0900 Subject: [PATCH 6/8] feat: FCM push message API --- build.gradle | 3 + .../domain/webpush/ApiResponseWrapper.java | 46 ++++++++++ .../spring/domain/webpush/FcmController.java | 37 ++++++++ .../spring/domain/webpush/FcmMessageDto.java | 29 +++++++ .../vom/spring/domain/webpush/FcmSendDto.java | 21 +++++ .../vom/spring/domain/webpush/FcmService.java | 12 +++ .../spring/domain/webpush/FcmServiceImpl.java | 84 +++++++++++++++++++ .../spring/domain/webpush/SuccessCode.java | 23 +++++ 8 files changed, 255 insertions(+) create mode 100644 src/main/java/vom/spring/domain/webpush/ApiResponseWrapper.java create mode 100644 src/main/java/vom/spring/domain/webpush/FcmController.java create mode 100644 src/main/java/vom/spring/domain/webpush/FcmMessageDto.java create mode 100644 src/main/java/vom/spring/domain/webpush/FcmSendDto.java create mode 100644 src/main/java/vom/spring/domain/webpush/FcmService.java create mode 100644 src/main/java/vom/spring/domain/webpush/FcmServiceImpl.java create mode 100644 src/main/java/vom/spring/domain/webpush/SuccessCode.java diff --git a/build.gradle b/build.gradle index 789c2e1..cef1b75 100644 --- a/build.gradle +++ b/build.gradle @@ -29,6 +29,9 @@ dependencies { implementation 'org.springframework.boot:spring-boot-starter-data-jpa' implementation 'org.springframework.boot:spring-boot-starter-security' implementation 'org.springframework.boot:spring-boot-starter-web' + implementation 'com.google.firebase:firebase-admin:9.2.0' // Google Firebase Admin + implementation 'com.fasterxml.jackson.core:jackson-core:2.16.1' // Jackson Data Bind + implementation 'org.springframework.boot:spring-boot-starter-json' //webPush implementation 'org.springframework.boot:spring-boot-starter-websocket' //webrtc implementation 'org.springframework.cloud:spring-cloud-starter-aws:2.2.6.RELEASE' compileOnly 'org.projectlombok:lombok' diff --git a/src/main/java/vom/spring/domain/webpush/ApiResponseWrapper.java b/src/main/java/vom/spring/domain/webpush/ApiResponseWrapper.java new file mode 100644 index 0000000..ce768a9 --- /dev/null +++ b/src/main/java/vom/spring/domain/webpush/ApiResponseWrapper.java @@ -0,0 +1,46 @@ +package vom.spring.domain.webpush; + +import lombok.Getter; + +@Getter +public class ApiResponseWrapper { + private int resultCode; + private String resultMsg; + private T result; + + // Builder 클래스를 사용하여 객체를 더 쉽게 생성할 수 있도록 합니다 + public static Builder builder() { + return new Builder<>(); + } + + public static class Builder { + private int resultCode; + private String resultMsg; + private T result; + + public Builder resultCode(int resultCode) { + this.resultCode = resultCode; + return this; + } + + public Builder resultMsg(String resultMsg) { + this.resultMsg = resultMsg; + return this; + } + + public Builder result(T result) { + this.result = result; + return this; + } + + public ApiResponseWrapper build() { + ApiResponseWrapper response = new ApiResponseWrapper<>(); + response.resultCode = this.resultCode; + response.resultMsg = this.resultMsg; + response.result = this.result; + return response; + } + } + + // Getter 및 Setter 메소드는 필요에 따라 추가합니다. +} diff --git a/src/main/java/vom/spring/domain/webpush/FcmController.java b/src/main/java/vom/spring/domain/webpush/FcmController.java new file mode 100644 index 0000000..14daa5e --- /dev/null +++ b/src/main/java/vom/spring/domain/webpush/FcmController.java @@ -0,0 +1,37 @@ +package vom.spring.domain.webpush; + +import lombok.extern.slf4j.Slf4j; +import org.springframework.http.HttpStatus; +import org.springframework.http.ResponseEntity; +import org.springframework.validation.annotation.Validated; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +import java.io.IOException; + +@Slf4j +@RestController +@RequestMapping("/api/v1/fcm") +public class FcmController { + private final FcmService fcmService; + + public FcmController(FcmService fcmService) { + this.fcmService = fcmService; + } + + @PostMapping("/send") + public ResponseEntity> pushMessage(@RequestBody @Validated FcmSendDto fcmSendDto) throws IOException, IOException { + log.debug("[+] 푸시 메시지를 전송합니다. "); + int result = fcmService.sendMessageTo(fcmSendDto); + + 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/FcmMessageDto.java b/src/main/java/vom/spring/domain/webpush/FcmMessageDto.java new file mode 100644 index 0000000..ba57327 --- /dev/null +++ b/src/main/java/vom/spring/domain/webpush/FcmMessageDto.java @@ -0,0 +1,29 @@ +package vom.spring.domain.webpush; + +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class FcmMessageDto { + private boolean validateOnly; + private FcmMessageDto.Message message; + + @Builder + @AllArgsConstructor + @Getter + public static class Message { + private FcmMessageDto.Notification notification; + private String token; + } + + @Builder + @AllArgsConstructor + @Getter + public static class Notification { + private String title; + private String body; + private String image; + } +} diff --git a/src/main/java/vom/spring/domain/webpush/FcmSendDto.java b/src/main/java/vom/spring/domain/webpush/FcmSendDto.java new file mode 100644 index 0000000..3c2fc27 --- /dev/null +++ b/src/main/java/vom/spring/domain/webpush/FcmSendDto.java @@ -0,0 +1,21 @@ +package vom.spring.domain.webpush; + +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/FcmService.java b/src/main/java/vom/spring/domain/webpush/FcmService.java new file mode 100644 index 0000000..78637c4 --- /dev/null +++ b/src/main/java/vom/spring/domain/webpush/FcmService.java @@ -0,0 +1,12 @@ +package vom.spring.domain.webpush; + +import org.springframework.stereotype.Service; + +import java.io.IOException; + +@Service +public class FcmService { + int sendMessageTo(FcmSendDto fcmSendDto) throws IOException { + return 0; + } +} diff --git a/src/main/java/vom/spring/domain/webpush/FcmServiceImpl.java b/src/main/java/vom/spring/domain/webpush/FcmServiceImpl.java new file mode 100644 index 0000000..f1c7738 --- /dev/null +++ b/src/main/java/vom/spring/domain/webpush/FcmServiceImpl.java @@ -0,0 +1,84 @@ +package vom.spring.domain.webpush; + +import com.fasterxml.jackson.core.JsonProcessingException; +import com.fasterxml.jackson.databind.ObjectMapper; +import com.google.auth.oauth2.GoogleCredentials; +import org.springframework.core.io.ClassPathResource; +import org.springframework.http.*; +import org.springframework.web.client.RestTemplate; + +import java.io.IOException; +import java.util.List; + +public class FcmServiceImpl extends FcmService { + /** + * 푸시 메시지 처리를 수행하는 비즈니스 로직 + * + * @param fcmSendDto 모바일에서 전달받은 Object + * @return 성공(1), 실패(0) + */ + @Override + public int sendMessageTo(FcmSendDto fcmSendDto) throws IOException { + + String message = makeMessage(fcmSendDto); + RestTemplate restTemplate = new RestTemplate(); + + HttpHeaders headers = new HttpHeaders(); +// headers.setContentType(MediaType.APPLICATION_JSON); + headers.set("Content-Type", "application/json"); + headers.set("Authorization", "Bearer " + getAccessToken()); + + 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 = restTemplate.exchange(API_URL, HttpMethod.POST, entity, String.class); + + System.out.println(response.getStatusCode()); + + return response.getStatusCode() == HttpStatus.OK ? 1 : 0; + } + + /** + * Firebase Admin SDK의 비공개 키를 참조하여 Bearer 토큰을 발급 받습니다. + * + * playground에서 발급받은 토큰 + * + * @return Bearer token + */ + private String getAccessToken() throws IOException { + String firebaseConfigPath = "firebase/vomvom-fd09b-firebase-adminsdk-ghtjs-0070b39a4e.json"; + + GoogleCredentials googleCredentials = GoogleCredentials + .fromStream(new ClassPathResource(firebaseConfigPath).getInputStream()) + .createScoped(List.of("https://www.googleapis.com/auth/cloud-platform")); + + googleCredentials.refreshIfExpired(); + + System.out.println(googleCredentials.getAccessToken()); + + return googleCredentials.getAccessToken().getTokenValue(); + } + + /** + * FCM 전송 정보를 기반으로 메시지를 구성합니다. (Object -> String) + * + * @param fcmSendDto FcmSendDto + * @return String + */ + private String makeMessage(FcmSendDto fcmSendDto) throws JsonProcessingException { + + ObjectMapper om = new ObjectMapper(); + FcmMessageDto fcmMessageDto = FcmMessageDto.builder() + .message(FcmMessageDto.Message.builder() + .token(fcmSendDto.getToken()) + .notification(FcmMessageDto.Notification.builder() + .title(fcmSendDto.getTitle()) + .body(fcmSendDto.getBody()) + .image(null) + .build() + ).build()).validateOnly(false).build(); + + return om.writeValueAsString(fcmMessageDto); + } +} diff --git a/src/main/java/vom/spring/domain/webpush/SuccessCode.java b/src/main/java/vom/spring/domain/webpush/SuccessCode.java new file mode 100644 index 0000000..60dbdd1 --- /dev/null +++ b/src/main/java/vom/spring/domain/webpush/SuccessCode.java @@ -0,0 +1,23 @@ +package vom.spring.domain.webpush; + +public enum SuccessCode { + SELECT_SUCCESS(200, "데이터 조회 성공"), + UPDATE_SUCCESS(200, "데이터 업데이트 성공"), + CREATE_SUCCESS(201, "데이터 생성 성공"); + + private final int status; + private final String message; + + SuccessCode(int status, String message) { + this.status = status; + this.message = message; + } + + public int getStatus() { + return status; + } + + public String getMessage() { + return message; + } +} From f102e9e1304b71b2c1e9f1ea7ca358b5e46004b3 Mon Sep 17 00:00:00 2001 From: okod Date: Wed, 5 Jun 2024 05:17:45 +0900 Subject: [PATCH 7/8] chore: .gitignore - firebase admin sdk --- .gitignore | 3 +++ 1 file changed, 3 insertions(+) diff --git a/.gitignore b/.gitignore index 19c38ab..fe8d388 100644 --- a/.gitignore +++ b/.gitignore @@ -44,3 +44,6 @@ src/main/**/application-dev.yml # 배포 관련 수정 appspec.yml + +### Firebase Admin SDK ### +src/main/resources/firebase \ No newline at end of file From af357b5296c443fafe88c485e227082986b25e5c Mon Sep 17 00:00:00 2001 From: okod Date: Fri, 7 Jun 2024 02:08:00 +0900 Subject: [PATCH 8/8] fix: Modify createScoped --- src/main/java/vom/spring/domain/webpush/FcmServiceImpl.java | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/main/java/vom/spring/domain/webpush/FcmServiceImpl.java b/src/main/java/vom/spring/domain/webpush/FcmServiceImpl.java index f1c7738..8dca721 100644 --- a/src/main/java/vom/spring/domain/webpush/FcmServiceImpl.java +++ b/src/main/java/vom/spring/domain/webpush/FcmServiceImpl.java @@ -24,7 +24,6 @@ public int sendMessageTo(FcmSendDto fcmSendDto) throws IOException { RestTemplate restTemplate = new RestTemplate(); HttpHeaders headers = new HttpHeaders(); -// headers.setContentType(MediaType.APPLICATION_JSON); headers.set("Content-Type", "application/json"); headers.set("Authorization", "Bearer " + getAccessToken()); @@ -32,9 +31,8 @@ public int sendMessageTo(FcmSendDto fcmSendDto) throws IOException { String API_URL = "https://fcm.googleapis.com/v1/projects/vomvom-fd09b/messages:send"; // String API_URL = "https://fcm.googleapis.com/fcm/send"; - ResponseEntity 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); return response.getStatusCode() == HttpStatus.OK ? 1 : 0; } @@ -51,7 +49,7 @@ private String getAccessToken() throws IOException { GoogleCredentials googleCredentials = GoogleCredentials .fromStream(new ClassPathResource(firebaseConfigPath).getInputStream()) - .createScoped(List.of("https://www.googleapis.com/auth/cloud-platform")); + .createScoped(List.of("https://www.googleapis.com/auth/cloud-platform", "https://www.googleapis.com/auth/firebase.messaging")); googleCredentials.refreshIfExpired();