Skip to content

Commit

Permalink
[MERGE] Merge pull request #284 from Team-WSS/fix/#281
Browse files Browse the repository at this point in the history
[FIX] 누락된 푸시알림 구현 추가
  • Loading branch information
ChaeAg authored Jan 24, 2025
2 parents cad1a38 + 2dcb563 commit 29feb73
Show file tree
Hide file tree
Showing 15 changed files with 308 additions and 48 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,8 @@
import org.springframework.web.bind.annotation.RestController;
import org.websoso.WSSServer.domain.User;
import org.websoso.WSSServer.dto.feed.UserFeedsGetResponse;
import org.websoso.WSSServer.dto.notification.PushSettingGetResponse;
import org.websoso.WSSServer.dto.notification.PushSettingRequest;
import org.websoso.WSSServer.dto.user.EditMyInfoRequest;
import org.websoso.WSSServer.dto.user.EditProfileStatusRequest;
import org.websoso.WSSServer.dto.user.FCMTokenRequest;
Expand Down Expand Up @@ -215,9 +217,26 @@ public ResponseEntity<UserIdAndNicknameResponse> getUserIdAndNicknameAndGender(P
public ResponseEntity<Void> registerFCMToken(Principal principal,
@Valid @RequestBody FCMTokenRequest fcmTokenRequest) {
User user = userService.getUserOrException(Long.valueOf(principal.getName()));
userService.registerFCMToken(user, fcmTokenRequest.fcmToken());
return userService.registerFCMToken(user, fcmTokenRequest)
? ResponseEntity.status(CREATED).build()
: ResponseEntity.status(NO_CONTENT).build();
}

@GetMapping("/push-settings")
public ResponseEntity<PushSettingGetResponse> getPushSettingValue(Principal principal) {
User user = userService.getUserOrException(Long.valueOf(principal.getName()));
return ResponseEntity
.status(OK)
.body(userService.getPushSettingValue(user));
}

@PostMapping("/push-settings")
public ResponseEntity<Void> registerPushSetting(Principal principal,
@Valid @RequestBody PushSettingRequest pushSettingRequest) {
User user = userService.getUserOrException(Long.valueOf(principal.getName()));
userService.registerPushSetting(user, pushSettingRequest.isPushEnabled());
return ResponseEntity
.status(NO_CONTENT)
.build();
}
}
14 changes: 9 additions & 5 deletions src/main/java/org/websoso/WSSServer/domain/User.java
Original file line number Diff line number Diff line change
Expand Up @@ -50,9 +50,6 @@ public class User extends BaseEntity {
@Column(nullable = false)
private String socialId;

@Column
private String fcmToken;

@Column(columnDefinition = "varchar(10)", nullable = false)
private String nickname;
//TODO 일부 특수문자 제외, 앞뒤 공백 불가능
Expand All @@ -77,6 +74,9 @@ public class User extends BaseEntity {
@Column(columnDefinition = "Boolean default true", nullable = false)
private Boolean isProfilePublic;

@Column(columnDefinition = "Boolean default true", nullable = false)
private Boolean isPushEnabled;

@Enumerated(EnumType.STRING)
@Column(nullable = false)
@ColumnDefault("'USER'")
Expand All @@ -94,6 +94,9 @@ public class User extends BaseEntity {
@OneToMany(mappedBy = "user", cascade = ALL, orphanRemoval = true)
private List<ReportedComment> reportedComments = new ArrayList<>();

@OneToMany(mappedBy = "user", cascade = ALL, orphanRemoval = true)
private List<UserDevice> userDevices = new ArrayList<>();

public void updateProfileStatus(Boolean profileStatus) {
this.isProfilePublic = profileStatus;
}
Expand Down Expand Up @@ -126,6 +129,7 @@ private User(String socialId, String nickname, String email) {
this.birth = Year.now();
this.avatarId = 1;
this.isProfilePublic = true;
this.isPushEnabled = true;
this.role = USER;
this.socialId = socialId;
this.nickname = nickname;
Expand All @@ -141,7 +145,7 @@ public void editMyInfo(EditMyInfoRequest editMyInfoRequest) {
this.birth = Year.of(editMyInfoRequest.birth());
}

public void updateFCMToken(String fcmToken) {
this.fcmToken = fcmToken;
public void updatePushSetting(boolean isPushEnabled) {
this.isPushEnabled = isPushEnabled;
}
}
48 changes: 48 additions & 0 deletions src/main/java/org/websoso/WSSServer/domain/UserDevice.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
package org.websoso.WSSServer.domain;

import jakarta.persistence.Column;
import jakarta.persistence.Entity;
import jakarta.persistence.FetchType;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;
import jakarta.persistence.JoinColumn;
import jakarta.persistence.ManyToOne;
import lombok.AccessLevel;
import lombok.Getter;
import lombok.NoArgsConstructor;

@Entity
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
public class UserDevice {

@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
@Column(nullable = false)
private Long userDeviceId;

@Column
private String fcmToken;

@Column(nullable = false)
private String deviceIdentifier;

@ManyToOne(fetch = FetchType.LAZY)
@JoinColumn(name = "user_id", nullable = false)
private User user;

private UserDevice(String fcmToken, String deviceIdentifier, User user) {
this.fcmToken = fcmToken;
this.deviceIdentifier = deviceIdentifier;
this.user = user;
}

public static UserDevice create(String fcmToken, String deviceIdentifier, User user) {
return new UserDevice(fcmToken, deviceIdentifier, user);
}

public void updateFcmToken(String fcmToken) {
this.fcmToken = fcmToken;
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package org.websoso.WSSServer.dto.notification;

public record PushSettingGetResponse(
Boolean isPushEnabled
) {

public static PushSettingGetResponse of(Boolean isPushEnabled) {
return new PushSettingGetResponse(isPushEnabled);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package org.websoso.WSSServer.dto.notification;

import jakarta.validation.constraints.NotNull;

public record PushSettingRequest(
@NotNull(message = "푸시 알림 설정 값은 null일 수 없습니다.")
Boolean isPushEnabled
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,9 @@

public record FCMTokenRequest(
@NotBlank(message = "FCM Token 값은 null 이거나, 공백일 수 없습니다.")
String fcmToken
String fcmToken,

@NotBlank(message = "디바이스 식별자 값 null 이거나, 공백일 수 없습니다.")
String deviceIdentifier
) {
}
Original file line number Diff line number Diff line change
Expand Up @@ -47,6 +47,7 @@ private Message createMessage(String targetFCMToken, FCMMessageRequest fcmMessag
.putData("body", fcmMessageRequest.body())
.putData("feedId", fcmMessageRequest.feedId())
.putData("view", fcmMessageRequest.view())
.putData("notificationId", fcmMessageRequest.notificationId())
.setApnsConfig(apnsConfig)
.build();
}
Expand Down Expand Up @@ -77,6 +78,7 @@ private MulticastMessage createMulticastMessage(List<String> targetFCMTokens, FC
.putData("body", fcmMessageRequest.body())
.putData("feedId", fcmMessageRequest.feedId())
.putData("view", fcmMessageRequest.view())
.putData("notificationId", fcmMessageRequest.notificationId())
.setApnsConfig(apnsConfig)
.build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -4,10 +4,11 @@ public record FCMMessageRequest(
String title,
String body,
String feedId,
String view
String view,
String notificationId
) {

public static FCMMessageRequest of(String title, String body, String feedId, String view) {
return new FCMMessageRequest(title, body, feedId, view);
public static FCMMessageRequest of(String title, String body, String feedId, String view, String notificationId) {
return new FCMMessageRequest(title, body, feedId, view, notificationId);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package org.websoso.WSSServer.repository;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.websoso.WSSServer.domain.NotificationType;

@Repository
public interface NotificationTypeRepository extends JpaRepository<NotificationType, Long> {

NotificationType findByNotificationTypeName(String notificationTypeName);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package org.websoso.WSSServer.repository;

import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.websoso.WSSServer.domain.UserDevice;

@Repository
public interface UserDeviceRepository extends JpaRepository<UserDevice, Long> {

Optional<UserDevice> findByDeviceIdentifier(String deviceIdentifier);
}
93 changes: 73 additions & 20 deletions src/main/java/org/websoso/WSSServer/service/CommentService.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,8 +15,11 @@
import org.springframework.transaction.annotation.Transactional;
import org.websoso.WSSServer.domain.Comment;
import org.websoso.WSSServer.domain.Feed;
import org.websoso.WSSServer.domain.Notification;
import org.websoso.WSSServer.domain.NotificationType;
import org.websoso.WSSServer.domain.Novel;
import org.websoso.WSSServer.domain.User;
import org.websoso.WSSServer.domain.UserDevice;
import org.websoso.WSSServer.domain.common.DiscordWebhookMessage;
import org.websoso.WSSServer.domain.common.ReportedType;
import org.websoso.WSSServer.dto.comment.CommentGetResponse;
Expand All @@ -26,6 +29,8 @@
import org.websoso.WSSServer.notification.FCMService;
import org.websoso.WSSServer.notification.dto.FCMMessageRequest;
import org.websoso.WSSServer.repository.CommentRepository;
import org.websoso.WSSServer.repository.NotificationRepository;
import org.websoso.WSSServer.repository.NotificationTypeRepository;

@Service
@RequiredArgsConstructor
Expand All @@ -40,6 +45,8 @@ public class CommentService {
private final MessageService messageService;
private final FCMService fcmService;
private final NovelService novelService;
private final NotificationTypeRepository notificationTypeRepository;
private final NotificationRepository notificationRepository;

public void createComment(User user, Feed feed, String commentContent) {
commentRepository.save(Comment.create(user.getUserId(), feed, commentContent));
Expand All @@ -48,18 +55,43 @@ public void createComment(User user, Feed feed, String commentContent) {
}

private void sendCommentPushMessageToFeedOwner(User user, Feed feed) {
if (isUserCommentOwner(user, feed.getUser())) {
User feedOwner = feed.getUser();
if (isUserCommentOwner(user, feedOwner)) {
return;
}

NotificationType notificationTypeComment = notificationTypeRepository.findByNotificationTypeName("댓글");

String notificationTitle = createNotificationTitle(feed);
String notificationBody = String.format("%s님이 내 수다글에 댓글을 남겼어요", user.getNickname());
Long feedId = feed.getFeedId();

Notification notification = Notification.create(
notificationTitle,
notificationBody,
null,
feedOwner.getUserId(),
feedId,
notificationTypeComment
);
notificationRepository.save(notification);

FCMMessageRequest fcmMessageRequest = FCMMessageRequest.of(
createNotificationTitle(feed),
String.format("%s님이 내 수다글에 댓글을 남겼어요", user.getNickname()),
String.valueOf(feed.getFeedId()),
"feedDetail"
notificationTitle,
notificationBody,
String.valueOf(feedId),
"feedDetail",
String.valueOf(notification.getNotificationId())
);
fcmService.sendPushMessage(
feed.getUser().getFcmToken(),

List<String> targetFCMTokens = feedOwner
.getUserDevices()
.stream()
.map(UserDevice::getFcmToken)
.toList();

fcmService.sendMulticastPushMessage(
targetFCMTokens,
fcmMessageRequest
);
}
Expand All @@ -76,29 +108,50 @@ private String createNotificationTitle(Feed feed) {
}

private void sendCommentPushMessageToCommenters(User user, Feed feed) {
List<String> commentersUserId = feed.getComments()
List<User> commenters = feed.getComments()
.stream()
.map(Comment::getUserId)
.filter(userId -> !userId.equals(user.getUserId()))
.distinct()
.map(userService::getUserOrException)
.map(User::getFcmToken)
.toList();

if (commentersUserId.isEmpty()) {
if (commenters.isEmpty()) {
return;
}

FCMMessageRequest fcmMessageRequest = FCMMessageRequest.of(
createNotificationTitle(feed),
"내가 댓글 단 수다글에 또 다른 댓글이 달렸어요.",
String.valueOf(feed.getFeedId()),
"feedDetail"
);
fcmService.sendMulticastPushMessage(
commentersUserId,
fcmMessageRequest
);
NotificationType notificationTypeComment = notificationTypeRepository.findByNotificationTypeName("댓글");

String notificationTitle = createNotificationTitle(feed);
String notificationBody = "내가 댓글 단 수다글에 또 다른 댓글이 달렸어요.";
Long feedId = feed.getFeedId();

commenters.forEach(commenter -> {
Notification notification = Notification.create(
notificationTitle,
notificationBody,
null,
commenter.getUserId(),
feedId,
notificationTypeComment
);
notificationRepository.save(notification);

List<String> targetFCMTokens = commenter.getUserDevices()
.stream()
.map(UserDevice::getFcmToken)
.distinct()
.toList();

FCMMessageRequest fcmMessageRequest = FCMMessageRequest.of(
notificationTitle,
notificationBody,
String.valueOf(feedId),
"feedDetail",
String.valueOf(notification.getNotificationId())
);
fcmService.sendMulticastPushMessage(targetFCMTokens, fcmMessageRequest);
});
}

public void updateComment(Long userId, Feed feed, Long commentId, String commentContent) {
Expand Down
Loading

0 comments on commit 29feb73

Please sign in to comment.