Skip to content

Commit

Permalink
Merge pull request #66 from kakao-tech-campus-2nd-step3/feature/65-no…
Browse files Browse the repository at this point in the history
…tification-fcm-consistency

feat: 알림 table에 저장될 데이터 및 FCM 알림 통일
  • Loading branch information
peeerr authored Oct 23, 2024
2 parents 939d685 + 9e05755 commit aae5e7e
Show file tree
Hide file tree
Showing 8 changed files with 114 additions and 81 deletions.
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.potatocake.everymoment.constant;

import lombok.Getter;
import lombok.RequiredArgsConstructor;

@Getter
@RequiredArgsConstructor
public enum NotificationType {

COMMENT("새로운 댓글", "%s님이 회원님의 일기에 댓글을 남겼습니다."),
LIKE("새로운 좋아요", "%s님이 회원님의 일기를 좋아합니다."),
FRIEND_REQUEST("새로운 친구 요청", "%s님이 친구 요청을 보냈습니다."),
FRIEND_ACCEPT("친구 요청 수락", "%s님이 친구 요청을 수락했습니다."),
MOOD_CHECK("새로운 장소", "현재 %s에 머무르고 있어요! 지금 기분은 어떠신가요?");

private final String title;
private final String messageFormat;

public String formatMessage(String... args) {
return String.format(messageFormat, (Object[]) args);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -50,17 +50,17 @@ public class DiaryController {
@Operation(summary = "자동 일기 작성", description = "자동으로 일기를 작성합니다.")
@ApiResponse(responseCode = "200", description = "자동 일기 작성 성공", content = @Content(schema = @Schema(implementation = NotificationResponse.class)))
@PostMapping("/auto")
public ResponseEntity<SuccessResponse<NotificationResponse>> createDiaryAuto(
public ResponseEntity<SuccessResponse> createDiaryAuto(
@Parameter(description = "인증된 사용자 정보", hidden = true)
@AuthenticationPrincipal MemberDetails memberDetails,
@Parameter(description = "자동 일기 작성 정보", required = true)
@RequestBody DiaryAutoCreateRequest diaryAutoCreateRequest) {
Long memberId = memberDetails.getId();

NotificationResponse response = diaryService.createDiaryAuto(memberId, diaryAutoCreateRequest);
diaryService.createDiaryAuto(memberId, diaryAutoCreateRequest);

return ResponseEntity.ok()
.body(SuccessResponse.ok(response));
.body(SuccessResponse.ok());
}

@Operation(summary = "수기 일기 작성", description = "수동으로 일기를 작성합니다.")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
package com.potatocake.everymoment.service;

import com.potatocake.everymoment.constant.NotificationType;
import com.potatocake.everymoment.dto.request.CommentRequest;
import com.potatocake.everymoment.dto.request.FcmNotificationRequest;
import com.potatocake.everymoment.dto.response.CommentFriendResponse;
import com.potatocake.everymoment.dto.response.CommentResponse;
import com.potatocake.everymoment.dto.response.CommentsResponse;
Expand Down Expand Up @@ -31,7 +31,7 @@ public class CommentService {
private final CommentRepository commentRepository;
private final DiaryRepository diaryRepository;
private final MemberRepository memberRepository;
private final FcmService fcmService;
private final NotificationService notificationService;

// 댓글 목록 조회
public CommentsResponse getComments(Long diaryId, int key, int size) {
Expand All @@ -52,7 +52,7 @@ public CommentsResponse getComments(Long diaryId, int key, int size) {

// 댓글 작성
public void createComment(Long memberId, Long diaryId, CommentRequest commentRequest) {
Member currentMember = memberRepository.findById(memberId)
Member member = memberRepository.findById(memberId)
.orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND));

Diary diary = diaryRepository.findById(diaryId)
Expand All @@ -64,20 +64,20 @@ public void createComment(Long memberId, Long diaryId, CommentRequest commentReq

Comment comment = Comment.builder()
.content(commentRequest.getContent())
.member(currentMember)
.member(member)
.diary(diary)
.build();

commentRepository.save(comment);

// 다이어리 작성자에게 FCM 알림 전송 (자신의 댓글에는 알림 안보냄)
// 자신의 게시글이 아닐 경우에만 알림 발송
if (!diary.getMember().getId().equals(memberId)) {
fcmService.sendNotification(diary.getMember().getId(), FcmNotificationRequest.builder()
.title("새로운 댓글")
.body(currentMember.getNickname() + "님이 회원님의 일기에 댓글을 남겼습니다.")
.type("COMMENT")
.targetId(diary.getId())
.build());
notificationService.createAndSendNotification(
diary.getMember().getId(),
NotificationType.COMMENT,
diaryId,
member.getNickname()
);
}
}

Expand Down
36 changes: 9 additions & 27 deletions src/main/java/com/potatocake/everymoment/service/DiaryService.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.potatocake.everymoment.service;

import com.potatocake.everymoment.constant.NotificationType;
import com.potatocake.everymoment.dto.LocationPoint;
import com.potatocake.everymoment.dto.request.CategoryRequest;
import com.potatocake.everymoment.dto.request.DiaryAutoCreateRequest;
Expand All @@ -9,21 +10,18 @@
import com.potatocake.everymoment.dto.response.MyDiariesResponse;
import com.potatocake.everymoment.dto.response.MyDiaryResponse;
import com.potatocake.everymoment.dto.response.MyDiarySimpleResponse;
import com.potatocake.everymoment.dto.response.NotificationResponse;
import com.potatocake.everymoment.dto.response.ThumbnailResponse;
import com.potatocake.everymoment.entity.Diary;
import com.potatocake.everymoment.entity.DiaryCategory;
import com.potatocake.everymoment.entity.File;
import com.potatocake.everymoment.entity.Member;
import com.potatocake.everymoment.entity.Notification;
import com.potatocake.everymoment.exception.ErrorCode;
import com.potatocake.everymoment.exception.GlobalException;
import com.potatocake.everymoment.repository.CategoryRepository;
import com.potatocake.everymoment.repository.DiaryCategoryRepository;
import com.potatocake.everymoment.repository.DiaryRepository;
import com.potatocake.everymoment.repository.FileRepository;
import com.potatocake.everymoment.repository.MemberRepository;
import com.potatocake.everymoment.repository.NotificationRepository;
import java.util.List;
import java.util.Objects;
import java.util.stream.Collectors;
Expand All @@ -44,14 +42,14 @@ public class DiaryService {

private final DiaryRepository diaryRepository;
private final DiaryCategoryRepository diaryCategoryRepository;
private final NotificationRepository notificationRepository;
private final MemberRepository memberRepository;
private final CategoryRepository categoryRepository;
private final FileRepository fileRepository;
private final GeometryFactory geometryFactory;
private final NotificationService notificationService;

// 자동 일기 저장 (LocationPoint, Name, Adress 만 저장)
public NotificationResponse createDiaryAuto(Long memberId, DiaryAutoCreateRequest diaryAutoCreateRequest) {
public void createDiaryAuto(Long memberId, DiaryAutoCreateRequest diaryAutoCreateRequest) {
// member 가져옴
Member currentMember = memberRepository.findById(memberId)
.orElseThrow(() -> new GlobalException(ErrorCode.MEMBER_NOT_FOUND));
Expand All @@ -70,28 +68,12 @@ public NotificationResponse createDiaryAuto(Long memberId, DiaryAutoCreateReques

Diary savedDiary = diaryRepository.save(diary);

//알람 만듦
String content = "현재 " + savedDiary.getLocationName() + "에 머무르고 있어요! 지금 기분은 어떠신가요?";

Notification notification = Notification.builder()
.member(currentMember)
.content(content)
.type("MOOD_CHECK")
.targetId(savedDiary.getId())
.build();

//알람 저장
Notification savedNotification = notificationRepository.save(notification);

//알람 DTO
NotificationResponse notificationResponse = NotificationResponse.builder()
.content(savedNotification.getContent())
.type(savedNotification.getType())
.targetId(savedNotification.getTargetId())
.createAt(savedNotification.getCreateAt())
.build();

return notificationResponse;
notificationService.createAndSendNotification(
memberId,
NotificationType.MOOD_CHECK,
savedDiary.getId(),
savedDiary.getLocationName()
);
}

// 수동 일기 작성
Expand Down
12 changes: 4 additions & 8 deletions src/main/java/com/potatocake/everymoment/service/FcmService.java
Original file line number Diff line number Diff line change
Expand Up @@ -36,7 +36,8 @@ public void sendNotification(Long targetMemberId, FcmNotificationRequest request
List<DeviceToken> deviceTokens = deviceTokenRepository.findAllByMemberId(targetMemberId);

if (deviceTokens.isEmpty()) {
throw new GlobalException(ErrorCode.FCM_TOKEN_NOT_FOUND);
log.warn("FCM 토큰이 존재하지 않는 사용자입니다. memberId: {}", targetMemberId);
return;
}

List<Message> messages = deviceTokens.stream()
Expand All @@ -55,8 +56,7 @@ public void sendNotification(Long targetMemberId, FcmNotificationRequest request
BatchResponse response = firebaseMessaging.sendEach(messages);
handleBatchResponse(response, deviceTokens);
} catch (FirebaseMessagingException e) {
log.error("FCM 메시지 전송 실패 : {}", e.getMessage(), e);
throw new GlobalException(ErrorCode.FCM_MESSAGE_SEND_FAILED);
log.error("FCM 메시지 전송 실패. targetMemberId: {}, error: {}", targetMemberId, e.getMessage());
}
}

Expand All @@ -83,13 +83,9 @@ private void handleBatchResponse(BatchResponse response, List<DeviceToken> devic
}

if (!tokensToDelete.isEmpty()) {
log.info("유효하지 않은 FCM 토큰 {} 개를 삭제합니다", tokensToDelete.size());
deviceTokenRepository.deleteAll(tokensToDelete);
log.info("유효하지 않은 FCM 토큰 {} 개를 삭제했습니다", tokensToDelete.size());
}

log.info("FCM 일괄 전송 결과 - 성공: {}, 실패: {}",
response.getSuccessCount(),
response.getFailureCount());
}

private boolean shouldDeleteToken(MessagingErrorCode errorCode) {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,7 @@
import static java.util.function.Function.identity;
import static org.springframework.data.domain.Sort.Direction.DESC;

import com.potatocake.everymoment.dto.request.FcmNotificationRequest;
import com.potatocake.everymoment.constant.NotificationType;
import com.potatocake.everymoment.dto.response.FriendRequestPageRequest;
import com.potatocake.everymoment.dto.response.FriendRequestResponse;
import com.potatocake.everymoment.entity.Friend;
Expand Down Expand Up @@ -36,7 +36,7 @@ public class FriendRequestService {
private final MemberRepository memberRepository;
private final FriendRepository friendRepository;
private final PagingUtil pagingUtil;
private final FcmService fcmService;
private final NotificationService notificationService;

@Transactional(readOnly = true)
public FriendRequestPageRequest getFriendRequests(Long key, int size, Long memberId) {
Expand Down Expand Up @@ -76,12 +76,12 @@ public void sendFriendRequest(Long senderId, Long receiverId) {
friendRequestRepository.delete(oppositeRequest.get());

// 상대방에게 친구 수락 알림 발송
fcmService.sendNotification(receiverId, FcmNotificationRequest.builder()
.title("친구 요청 수락")
.body(sender.getNickname() + "님이 친구 요청을 수락했습니다.")
.type("FRIEND_ACCEPT")
.targetId(sender.getId())
.build());
notificationService.createAndSendNotification(
receiverId,
NotificationType.FRIEND_ACCEPT,
sender.getId(),
sender.getNickname()
);
} else {
// 상대방이 보낸 요청이 없다면 새로운 친구 요청 생성
FriendRequest friendRequest = friendRequestRepository.save(FriendRequest.builder()
Expand All @@ -90,12 +90,12 @@ public void sendFriendRequest(Long senderId, Long receiverId) {
.build());

// 상대방에게 친구 요청 알림 발송
fcmService.sendNotification(receiverId, FcmNotificationRequest.builder()
.title("새로운 친구 요청")
.body(sender.getNickname() + "님이 친구 요청을 보냈습니다.")
.type("FRIEND_REQUEST")
.targetId(friendRequest.getId())
.build());
notificationService.createAndSendNotification(
receiverId,
NotificationType.FRIEND_REQUEST,
friendRequest.getId(),
sender.getNickname()
);
}
}

Expand All @@ -111,12 +111,12 @@ public void acceptFriendRequest(Long requestId, Long memberId) {
friendRequestRepository.delete(friendRequest);

// 알림 발송
fcmService.sendNotification(friendRequest.getSender().getId(), FcmNotificationRequest.builder()
.title("친구 요청 수락")
.body(friendRequest.getReceiver().getNickname() + "님이 친구 요청을 수락했습니다.")
.type("FRIEND_ACCEPT")
.targetId(friendRequest.getReceiver().getId())
.build());
notificationService.createAndSendNotification(
friendRequest.getSender().getId(),
NotificationType.FRIEND_ACCEPT,
friendRequest.getReceiver().getId(),
friendRequest.getReceiver().getNickname()
);
}

public void rejectFriendRequest(Long requestId, Long memberId) {
Expand Down
17 changes: 7 additions & 10 deletions src/main/java/com/potatocake/everymoment/service/LikeService.java
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.potatocake.everymoment.service;

import com.potatocake.everymoment.dto.request.FcmNotificationRequest;
import com.potatocake.everymoment.constant.NotificationType;
import com.potatocake.everymoment.dto.response.LikeCountResponse;
import com.potatocake.everymoment.entity.Diary;
import com.potatocake.everymoment.entity.Like;
Expand All @@ -23,7 +23,7 @@ public class LikeService {
private final LikeRepository likeRepository;
private final DiaryRepository diaryRepository;
private final MemberRepository memberRepository;
private final FcmService fcmService;
private final NotificationService notificationService;

@Transactional(readOnly = true)
public LikeCountResponse getLikeCount(Long diaryId) {
Expand Down Expand Up @@ -63,14 +63,11 @@ public void toggleLike(Long memberId, Long diaryId) {
likeRepository.save(likeEntity);

if (!diary.getMember().getId().equals(memberId)) {
// 좋아요를 눌렀을 때만 (취소 제외), 그리고 자신의 게시물이 아닐 때만 알림 발송
fcmService.sendNotification(diary.getMember().getId(),
FcmNotificationRequest.builder()
.title("새로운 좋아요")
.body(member.getNickname() + "님이 회원님의 일기를 좋아합니다.")
.type("LIKE")
.targetId(diary.getId())
.build()
notificationService.createAndSendNotification(
diary.getMember().getId(),
NotificationType.LIKE,
diaryId,
member.getNickname()
);
}
}
Expand Down
Loading

0 comments on commit aae5e7e

Please sign in to comment.