Skip to content

Commit

Permalink
Merge pull request #85 from Donut-DONationUTile/feature/history/receiver
Browse files Browse the repository at this point in the history
Feat: connect fem
  • Loading branch information
Kang1221 authored Apr 30, 2024
2 parents 6a864bd + fffaba9 commit 390ae82
Show file tree
Hide file tree
Showing 10 changed files with 299 additions and 7 deletions.
10 changes: 5 additions & 5 deletions .gitignore
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
FirebaseConfig.*
FcmUtils.*
FirebaseConfig.*
FcmController.*
FcmService.*
#FirebaseConfig.*
#FcmUtils.*
#FirebaseConfig.*
#FcmController.*
#FcmService.*

HELP.md
.gradle
Expand Down
2 changes: 1 addition & 1 deletion Donut-Server-yml
95 changes: 95 additions & 0 deletions src/main/java/zero/eight/donut/config/firebase/FcmUtils.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,95 @@
package zero.eight.donut.config.firebase;

import com.google.firebase.messaging.FirebaseMessaging;
import com.google.firebase.messaging.FirebaseMessagingException;
import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.Notification;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Component;
import zero.eight.donut.config.jwt.AuthUtils;
import zero.eight.donut.domain.FcmToken;
import zero.eight.donut.domain.Giver;
import zero.eight.donut.domain.Receiver;
import zero.eight.donut.dto.auth.Role;
import zero.eight.donut.dto.fcm.FcmMemberDto;
import zero.eight.donut.exception.Error;
import zero.eight.donut.repository.FcmTokenRepository;
import zero.eight.donut.repository.GiverRepository;
import zero.eight.donut.repository.ReceiverRepository;

import java.util.Optional;

@Slf4j
@RequiredArgsConstructor
@Component
public class FcmUtils {

private final AuthUtils authUtils;
private final GiverRepository giverRepository;
private final ReceiverRepository receiverRepository;
private final FcmTokenRepository fcmTokenRepository;
private final FirebaseMessaging firebaseMessaging;



public FcmMemberDto getMemberDto() throws Exception {
if (authUtils.getCurrentUserRole().equals(Role.ROLE_GIVER)) {
Giver giver = giverRepository.findByEmail(authUtils.getCurrentUserEmail()).orElseThrow(
() -> new Exception(Error.USERNAME_NOT_FOUND_EXCEPTION.getMessage())
);
return FcmMemberDto.builder()
.id(giver.getId())
.role(Role.ROLE_GIVER)
.build();
}
else {
Optional<Receiver> optionalReceiver = receiverRepository.findByName(authUtils.getCurrentUserEmail());
if (optionalReceiver.isEmpty()) {
throw new Exception(Error.USERNAME_NOT_FOUND_EXCEPTION.getMessage());
}

return FcmMemberDto.builder()
.id(optionalReceiver.get().getId())
.role(Role.ROLE_RECEIVER)
.build();
}
}

// FCM 메세지 전송
public String sendMessage(Long memberId, String title, String body) throws FirebaseMessagingException {
// 알림 수신자의 FCM 토큰 조회
String fcmToken = getFcmToken(memberId);
// FCM 메세지 생성
Message message = makeMessage(fcmToken, title, body);
// FCM 발신
return firebaseMessaging.send(message);
}

public String getFcmToken(Long memberId) {
Optional<FcmToken> fcmToken = fcmTokenRepository.findByMemberId(memberId);

if (fcmToken.isEmpty()) {
try {
throw new Exception(Error.FCM_TOKEN_NOT_FOUND_EXCEPTION.getMessage());
} catch (Exception e) {
throw new RuntimeException(e);
}
}

return fcmToken.get().getToken();
}

public Message makeMessage(String targetToken, String title, String body) {
// Notification 객체는 모바일 환경에서 제목과 본문을 표시
Notification notification = Notification.builder()
.setTitle(title)
.setBody(body)
.build();

return Message.builder()
.setNotification(notification)
.setToken(targetToken)
.build();
}
}
49 changes: 49 additions & 0 deletions src/main/java/zero/eight/donut/config/firebase/FirebaseConfig.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package zero.eight.donut.config.firebase;

import com.google.auth.oauth2.GoogleCredentials;
import com.google.firebase.FirebaseApp;
import com.google.firebase.FirebaseOptions;
import com.google.firebase.messaging.FirebaseMessaging;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;

import javax.annotation.PostConstruct;
import java.io.FileInputStream;

@Configuration
public class FirebaseConfig {

@PostConstruct
public void init() {
try {
FileInputStream serviceAccount =
new FileInputStream("src/main/resources/serviceAccountKey.json");
FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.build();
FirebaseApp.initializeApp(options);
} catch (Exception e) {
e.printStackTrace();
}
}


@Bean
public FirebaseMessaging firebaseMessaging() {
try {
FileInputStream serviceAccount =
new FileInputStream("src/main/resources/serviceAccountKey.json");

FirebaseOptions options = new FirebaseOptions.Builder()
.setCredentials(GoogleCredentials.fromStream(serviceAccount))
.build();

if (FirebaseApp.getApps().isEmpty()) { // FirebaseApp의 중복 초기화 방지.
FirebaseApp.initializeApp(options);
}
return FirebaseMessaging.getInstance();
} catch (Exception e) {
throw new IllegalStateException("Firebase Messaging 서비스를 초기화하는 동안 문제가 발생했습니다.", e);
}
}
}
38 changes: 38 additions & 0 deletions src/main/java/zero/eight/donut/domain/FcmToken.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package zero.eight.donut.domain;

import jakarta.persistence.*;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import zero.eight.donut.dto.auth.Role;

@Getter
@NoArgsConstructor
@Entity
public class FcmToken {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;

@Column(nullable = false)
private String token;

@Column(nullable = false, unique = true)
private Long memberId;

@Column(nullable = false)
private Role role;

@Builder // 롬복의 @Builder 애너테이션 적용
public FcmToken(String token, Long memberId, Role role) {
this.token = token;
this.memberId = memberId;
this.role = role;
}

public void updateToken(String token, Role role) {
this.token = token;
this.role = role;
}
}
12 changes: 12 additions & 0 deletions src/main/java/zero/eight/donut/dto/fcm/FcmMemberDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package zero.eight.donut.dto.fcm;

import lombok.Builder;
import lombok.Getter;
import zero.eight.donut.dto.auth.Role;

@Getter
@Builder
public class FcmMemberDto {
private Long id;
private Role role;
}
10 changes: 10 additions & 0 deletions src/main/java/zero/eight/donut/dto/fcm/FcmTokenRequestDto.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package zero.eight.donut.dto.fcm;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@RequiredArgsConstructor
@Getter
public class FcmTokenRequestDto {
private final String token;
}
10 changes: 10 additions & 0 deletions src/main/java/zero/eight/donut/repository/FcmTokenRepository.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package zero.eight.donut.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import zero.eight.donut.domain.FcmToken;

import java.util.Optional;

public interface FcmTokenRepository extends JpaRepository<FcmToken, Long> {
Optional<FcmToken> findByMemberId(Long memberId);
}
4 changes: 3 additions & 1 deletion src/main/java/zero/eight/donut/service/DonationService.java
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import zero.eight.donut.common.response.ApiResponse;
import zero.eight.donut.config.firebase.FcmUtils;
import zero.eight.donut.domain.Gift;
import zero.eight.donut.dto.donation.DonateGiftRequestDto;
import zero.eight.donut.dto.donation.GiftboxRequestDto;
Expand All @@ -21,7 +22,7 @@
@RequiredArgsConstructor
@Service
public class DonationService {

private final FcmUtils fcmUtils;
private final SerialDonationService donationService;
private final GiftRepository giftRepository;

Expand All @@ -33,6 +34,7 @@ public void autoDonate() throws FirebaseMessagingException {
for (Gift gift : giftList) {
gift.updateStatus("UNUSED");
giftRepository.save(gift);
fcmUtils.sendMessage(gift.getGiver().getId(), "wallet: D-30", "Your item" + gift.getProduct() + "is donated now!");
}
}

Expand Down
76 changes: 76 additions & 0 deletions src/main/java/zero/eight/donut/service/FcmService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,76 @@
package zero.eight.donut.service;

import com.google.firebase.messaging.FirebaseMessagingException;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.scheduling.annotation.Scheduled;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import zero.eight.donut.common.response.ApiResponse;
import zero.eight.donut.config.firebase.FcmUtils;
import zero.eight.donut.domain.FcmToken;
import zero.eight.donut.domain.Gift;
import zero.eight.donut.domain.Giftbox;
import zero.eight.donut.domain.Receiver;
import zero.eight.donut.dto.fcm.FcmMemberDto;
import zero.eight.donut.dto.fcm.FcmTokenRequestDto;
import zero.eight.donut.exception.Success;
import zero.eight.donut.repository.FcmTokenRepository;
import zero.eight.donut.repository.GiftRepository;
import zero.eight.donut.repository.GiftboxRepository;

import java.time.LocalDateTime;
import java.util.List;

@RequiredArgsConstructor
@Service
public class FcmService {
private final FcmUtils fcmUtils;
private final FcmTokenRepository fcmTokenRepository;
private final GiftRepository giftRepository;
private final GiftboxRepository giftboxRepository;

public ApiResponse<?> registerFcmToken(FcmTokenRequestDto requestDto) throws Exception {
final String token = requestDto.getToken();
FcmMemberDto member = fcmUtils.getMemberDto();

fcmTokenRepository.findByMemberId(member.getId())
.ifPresentOrElse(
it -> it.updateToken(token, member.getRole()),
() -> fcmTokenRepository.save(FcmToken.builder()
.token(token)
.memberId(member.getId())
.build())
);

return ApiResponse.success(Success.CREATE_FCM_TOKEN_SUCCESS);
}

// 사용 기한 D-37 push 알림 (기부자)
@Async
@Transactional
@Scheduled(cron = "0 0 0 * * *")
public void imminentWallet() throws FirebaseMessagingException {
List<Gift> giftList = giftRepository.findAllByNotAssignedAndStoredAndAutoDonation(LocalDateTime.now().plusDays(37));
for (Gift gift : giftList) {
// 기프티콘의 giverId로 FCM 전송
fcmUtils.sendMessage(gift.getGiver().getId(), "wallet: D-37", "Your item" + gift.getProduct() + "is expiring soon! It will be automatically donated.");
}
}

// 사용 기한 D-7 push 알림 (수혜자)
@Async
@Transactional
@Scheduled(cron = "0 0 0 * * *")
public void immminentGift() throws FirebaseMessagingException {
// 7일 뒤 만료되는 꾸러미 찾기
List<Giftbox> giftboxList = giftboxRepository.findAllByIsAvailableAndDueDate(LocalDateTime.now().plusDays(7));
for (Giftbox giftbox : giftboxList) {
// 수혜자 찾기
Receiver receiver = giftbox.getReceiver();

// 조회된 수혜자에게 FCM 전송
fcmUtils.sendMessage(receiver.getId(), "giftbox: D-7", "Your gift box is expiring soon! You can use it at" + giftbox.getStore() + ".");
}
}
}

0 comments on commit 390ae82

Please sign in to comment.