Skip to content

Commit

Permalink
Fix : DIG-177 FCM 웹 푸시 버그 수정
Browse files Browse the repository at this point in the history
- 알림을 차단하고, 허용했을 때 새로운 FCM토큰이 발급되는데 이때, 전 토큰값이 그대로 DB에 저장되어 FCM 웹푸시가 되지 않음
    - 새로운 토큰이 들어왔을 때, 전 토큰값을 삭제하고 새로운 토큰을 저장

리팩토링
- FcmToken, FCMAlarm 서비스 분리
    - 토큰 생성/수정/삭제 로직과 웹푸시 알람 보내는 메서드 분리
- FcmToken Entity 연관관계 수정
    - @OnetoOne -> @manytoone로 수정하여 User를 FK로 설정
    - 고유의 id값을 PK로 설정
- ScheduledService 내용 수정
    - 운동일지가 비었을 때, 비어있다는 알림 추가
- NotificationApiController 삭제, FcmTokenController 로 변경
  • Loading branch information
YEJINGO committed Nov 27, 2023
1 parent c48f04d commit ef8787d
Show file tree
Hide file tree
Showing 8 changed files with 149 additions and 88 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package com.ogjg.daitgym.alarm.controller.fcm;

import com.ogjg.daitgym.alarm.dto.FcmTokenRequestDto;
import com.ogjg.daitgym.alarm.service.FcmTokenService;
import com.ogjg.daitgym.common.exception.ErrorCode;
import com.ogjg.daitgym.common.response.ApiResponse;
import com.ogjg.daitgym.config.security.details.OAuth2JwtUserDetails;
import lombok.RequiredArgsConstructor;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/notification")
public class FcmTokenController {

private final FcmTokenService fcmTokenService;


@GetMapping("/token")
public ApiResponse<Boolean> getNotification(@AuthenticationPrincipal OAuth2JwtUserDetails oAuth2JwtUserDetails) {

return new ApiResponse<>(ErrorCode.SUCCESS, fcmTokenService.getNotification(oAuth2JwtUserDetails));
}

@PostMapping("/token")
public ApiResponse<Void> saveNotification(@RequestBody FcmTokenRequestDto fcmTokenRequestDto,
@AuthenticationPrincipal OAuth2JwtUserDetails oAuth2JwtUserDetails) {
fcmTokenService.saveNotification(fcmTokenRequestDto, oAuth2JwtUserDetails);
return new ApiResponse<>(ErrorCode.SUCCESS);
}

@PatchMapping("/token")
public ApiResponse<Void> updateNotification(@RequestBody FcmTokenRequestDto fcmTokenRequestDto,
@AuthenticationPrincipal OAuth2JwtUserDetails oAuth2JwtUserDetails) {
fcmTokenService.updateNotification(fcmTokenRequestDto, oAuth2JwtUserDetails);
return new ApiResponse<>(ErrorCode.SUCCESS);
}
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -11,4 +11,9 @@ public interface FcmTokenRepository extends JpaRepository<FcmToken, Long> {
boolean existsByUserAndToken(User user, String token);

Optional<FcmToken> findByUserAndToken(User user, String token);

Optional<FcmToken> findByUser(User user);

Optional<FcmToken> findByUserEmail(String email);
}

42 changes: 0 additions & 42 deletions src/main/java/com/ogjg/daitgym/alarm/service/FcmAlarmService.java
Original file line number Diff line number Diff line change
Expand Up @@ -4,22 +4,14 @@
import com.google.firebase.messaging.Message;
import com.google.firebase.messaging.WebpushConfig;
import com.google.firebase.messaging.WebpushNotification;
import com.ogjg.daitgym.alarm.dto.FcmTokenRequestDto;
import com.ogjg.daitgym.alarm.dto.NotificationRequestDto;
import com.ogjg.daitgym.alarm.repository.FcmTokenRepository;
import com.ogjg.daitgym.common.exception.exercise.NotFoundExercise;
import com.ogjg.daitgym.common.exception.fcmtoken.NotFoundFcmToken;
import com.ogjg.daitgym.common.exception.journal.NotFoundExerciseList;
import com.ogjg.daitgym.config.security.details.OAuth2JwtUserDetails;
import com.ogjg.daitgym.domain.FcmToken;
import com.ogjg.daitgym.domain.User;
import com.ogjg.daitgym.domain.exercise.Exercise;
import com.ogjg.daitgym.domain.journal.ExerciseJournal;
import com.ogjg.daitgym.domain.journal.ExerciseList;
import com.ogjg.daitgym.exercise.repository.ExerciseRepository;
import com.ogjg.daitgym.journal.repository.exerciselist.ExerciseListRepository;
import com.ogjg.daitgym.user.service.UserHelper;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
Expand All @@ -33,32 +25,8 @@
@RequiredArgsConstructor
public class FcmAlarmService {

private final UserHelper userHelper;
private final ExerciseRepository exerciseRepository;
private final FcmTokenRepository notificationRepository;
private final ExerciseListRepository exerciseListRepository;
private final FcmTokenRepository fcmTokenRepository;


/**
* FCM 토큰값 저장
*/
@Transactional
public void saveNotification(FcmTokenRequestDto fcmTokenRequestDto, OAuth2JwtUserDetails oAuth2JwtUserDetails) {
User user = userHelper.findUserByEmail(oAuth2JwtUserDetails.getEmail());

String token = fcmTokenRequestDto.getToken();

if (!fcmTokenRepository.existsByUserAndToken(user, token)) {
FcmToken notification = FcmToken.builder()
.token(token)
.user(user)
.build();
notificationRepository.save(notification);
} else {
log.info("이미 토큰이 존재합니다.");
}
}


public String alarmMessage(ExerciseJournal exerciseJournal) {
Expand Down Expand Up @@ -102,14 +70,4 @@ public void sendNotification(NotificationRequestDto requestDto) throws Execution
String response = FirebaseMessaging.getInstance().sendAsync(message).get();
log.info("Send message : " + response);
}


public void deleteFcmToken(OAuth2JwtUserDetails oAuth2JwtUserDetails, FcmTokenRequestDto fcmTokenRequestDto) {
User user = userHelper.findUserByEmail(oAuth2JwtUserDetails.getEmail());
String token = fcmTokenRequestDto.getToken();

FcmToken fcmToken = fcmTokenRepository.findByUserAndToken(user, token).orElseThrow(NotFoundFcmToken::new);
fcmTokenRepository.delete(fcmToken);
}

}
71 changes: 71 additions & 0 deletions src/main/java/com/ogjg/daitgym/alarm/service/FcmTokenService.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
package com.ogjg.daitgym.alarm.service;

import com.ogjg.daitgym.alarm.dto.FcmTokenRequestDto;
import com.ogjg.daitgym.alarm.repository.FcmTokenRepository;
import com.ogjg.daitgym.common.exception.fcmtoken.NotFoundFcmToken;
import com.ogjg.daitgym.common.exception.user.NotFoundUser;
import com.ogjg.daitgym.config.security.details.OAuth2JwtUserDetails;
import com.ogjg.daitgym.domain.FcmToken;
import com.ogjg.daitgym.domain.User;
import com.ogjg.daitgym.user.service.UserHelper;
import jakarta.transaction.Transactional;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;

@Slf4j
@Service
@RequiredArgsConstructor
public class FcmTokenService {

private final UserHelper userHelper;
private final FcmTokenRepository notificationRepository;
private final FcmTokenRepository fcmTokenRepository;


public boolean getNotification(OAuth2JwtUserDetails oAuth2JwtUserDetails) {

return fcmTokenRepository.findByUserEmail(oAuth2JwtUserDetails.getEmail()).isPresent();
}

@Transactional
public void updateNotification(FcmTokenRequestDto fcmTokenRequestDto, OAuth2JwtUserDetails oAuth2JwtUserDetails) {
User user = userHelper.findUserByEmail(oAuth2JwtUserDetails.getEmail());
String token = fcmTokenRequestDto.getToken();

FcmToken fcmToken = fcmTokenRepository.findByUser(user).orElseThrow(NotFoundUser::new);
fcmToken.update(token);
fcmTokenRepository.save(fcmToken);
}

@Transactional
public void saveNotification(FcmTokenRequestDto fcmTokenRequestDto, OAuth2JwtUserDetails oAuth2JwtUserDetails) {

try {
User user = userHelper.findUserByEmail(oAuth2JwtUserDetails.getEmail());
String token = fcmTokenRequestDto.getToken();

if (!fcmTokenRepository.existsByUserAndToken(user, token)) {
FcmToken notification = FcmToken.builder()
.token(token)
.user(user)
.build();
notificationRepository.save(notification);
log.info("토큰 저장 성공");
} else {
log.info("이미 토큰이 존재합니다.");
}
} catch (Exception e) {
log.error("트랜잭션 롤백 발생", e);
throw e;
}
}

public void deleteFcmToken(FcmTokenRequestDto fcmTokenRequestDto, OAuth2JwtUserDetails oAuth2JwtUserDetails) {
User user = userHelper.findUserByEmail(oAuth2JwtUserDetails.getEmail());
String token = fcmTokenRequestDto.getToken();

FcmToken fcmToken = fcmTokenRepository.findByUserAndToken(user, token).orElseThrow(NotFoundFcmToken::new);
fcmTokenRepository.delete(fcmToken);
}
}
36 changes: 24 additions & 12 deletions src/main/java/com/ogjg/daitgym/alarm/service/ScheduledService.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.ogjg.daitgym.alarm.dto.NotificationRequestDto;
import com.ogjg.daitgym.alarm.repository.FcmTokenRepository;
import com.ogjg.daitgym.comment.feedExerciseJournal.exception.NotFoundExerciseJournal;
import com.ogjg.daitgym.domain.FcmToken;
import com.ogjg.daitgym.domain.User;
import com.ogjg.daitgym.domain.journal.ExerciseJournal;
Expand All @@ -14,6 +13,7 @@

import java.time.LocalDate;
import java.util.List;
import java.util.Optional;
import java.util.concurrent.ExecutionException;

@Slf4j
Expand All @@ -27,31 +27,43 @@ public class ScheduledService {


/**
* 7시 30분에 알림 메세지 보내기
* 8시에 알림 메세지 보내기
*/
@Scheduled(cron = "*/10 * * * * *")
@Scheduled(cron = "0 0 8 * * *")
public void scheduledSend() throws ExecutionException, InterruptedException {

List<FcmToken> fcmTokens = fcmTokenRepository.findAll();

if (fcmTokens.isEmpty()) {
log.info("fcmToken이 없어, 작업을 하지 않습니다.");
log.info("FCM 토큰이 비어있어, 알림전송 실패");
return;
}

for (FcmToken fcmToken : fcmTokens) {
User user = fcmToken.getUser();
ExerciseJournal exerciseJournal = exerciseJournalRepository.findByUserAndJournalDate(user, LocalDate.now()).orElseThrow(NotFoundExerciseJournal::new);
Optional<ExerciseJournal> optionalExerciseJournal = exerciseJournalRepository.findByUserAndJournalDate(user, LocalDate.now());

String message = notificationService.alarmMessage(exerciseJournal);
if (optionalExerciseJournal.isPresent()) {
ExerciseJournal exerciseJournal = optionalExerciseJournal.get();
String message = notificationService.alarmMessage(exerciseJournal);

NotificationRequestDto notificationRequestDto = NotificationRequestDto.builder()
.title("[DaItGym 운동 알림]")
.message(message)
.token(fcmToken.getToken())
.build();
NotificationRequestDto notificationRequestDto = NotificationRequestDto.builder()
.title("[DaItGym 운동 알림]")
.message(message)
.token(fcmToken.getToken())
.build();

notificationService.sendNotification(notificationRequestDto);
notificationService.sendNotification(notificationRequestDto);

} else {
NotificationRequestDto notificationRequestDto = NotificationRequestDto.builder()
.title("[DaItGym 운동 알림]")
.message("오늘 운동일지가 비어있어요!")
.token(fcmToken.getToken())
.build();

notificationService.sendNotification(notificationRequestDto);
}
}
log.info("웹푸시 보냈어!");
}
Expand Down
6 changes: 5 additions & 1 deletion src/main/java/com/ogjg/daitgym/domain/FcmToken.java
Original file line number Diff line number Diff line change
Expand Up @@ -18,7 +18,7 @@ public class FcmToken {
@Column(name = "notification_id")
private Long id;

@OneToOne(fetch = FetchType.LAZY)
@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "email")
private User user;

Expand All @@ -30,4 +30,8 @@ public FcmToken(String token, User user) {
this.user = user;
}

public void update(String token) {
this.token = token;
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import com.fasterxml.jackson.core.JsonProcessingException;
import com.fasterxml.jackson.databind.ObjectMapper;
import com.ogjg.daitgym.alarm.dto.FcmTokenRequestDto;
import com.ogjg.daitgym.alarm.service.FcmAlarmService;
import com.ogjg.daitgym.alarm.service.FcmTokenService;
import com.ogjg.daitgym.common.exception.ErrorCode;
import com.ogjg.daitgym.common.response.ApiResponse;
import com.ogjg.daitgym.config.security.details.OAuth2JwtUserDetails;
Expand All @@ -29,7 +29,7 @@
public class UserController {

private final UserService userService;
private final FcmAlarmService fcmAlarmService;
private final FcmTokenService fcmTokenService;

private final ObjectMapper objectMapper;

Expand All @@ -38,10 +38,10 @@ public class UserController {
*/
@PostMapping("/logout")
public ApiResponse<?> logout(HttpServletResponse response,
@AuthenticationPrincipal OAuth2JwtUserDetails oAuth2JwtUserDetails,
@RequestBody FcmTokenRequestDto fcmTokenRequestDto) {
@RequestBody FcmTokenRequestDto fcmTokenRequestDto,
@AuthenticationPrincipal OAuth2JwtUserDetails oAuth2JwtUserDetails) {
response.setHeader("Set-Cookie", userService.getExpiredResponseCookie().toString());
fcmAlarmService.deleteFcmToken(oAuth2JwtUserDetails, fcmTokenRequestDto);
fcmTokenService.deleteFcmToken(fcmTokenRequestDto, oAuth2JwtUserDetails);
return new ApiResponse<>(ErrorCode.SUCCESS.changeMessage("로그아웃 성공"));
}

Expand Down

0 comments on commit ef8787d

Please sign in to comment.