Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
54 commits
Select commit Hold shift + click to select a range
bf9ed02
[REMOVE] 중복된 @Transactional 어노테이션 제거
Kim-TaeUk Jan 22, 2025
e2f9b42
[FEAT] 푸시 알림 수신 여부 조회를 위한 PushSettingGetResponse DTO 추가
Kim-TaeUk Jan 22, 2025
cf3af4a
[FEAT] 푸시 알림 수신 여부 조회를 위한 PushSettingGetResponse 엔드 포인트 추가
Kim-TaeUk Jan 22, 2025
665f0a6
[FEAT] User 엔티티에 푸시 알림 수신 여부 컬럼 추가
Kim-TaeUk Jan 22, 2025
6570f65
[FEAT] 푸시 알림 수신 여부 조회를 위한 PushSettingGetResponse 서비스 로직 추가
Kim-TaeUk Jan 22, 2025
599845c
[FIX] 의존성 주입 의도에 맞게 어노테이션 수정
Kim-TaeUk Jan 22, 2025
ba7f39d
[REMOVE] 멀티 디바이스 고려를 위한 User 엔티티에서 fcmToken 관련 삭제
Kim-TaeUk Jan 23, 2025
59e4b4f
[FEAT] 멀티 디바이스 고려를 위한 UserDevice 엔티티 추가
Kim-TaeUk Jan 23, 2025
5a209f4
[FIX] FCM token 등록 관련 멀티 디바이스 고려로 인한 request 수정
Kim-TaeUk Jan 23, 2025
d1e9e2d
[FEAT] UserDeviceRepository 추가
Kim-TaeUk Jan 23, 2025
f0ff942
[CHORE] UserService에 UserDeviceRepository bean 주입
Kim-TaeUk Jan 23, 2025
e3f1b4d
[FIX] fcm token을 업데이트 하거나, UserDevice를 새로 생성하도록 변경
Kim-TaeUk Jan 23, 2025
d498e3b
[FIX] 생성인 경우 200, 수정인 경우 204를 반환하도록 변경
Kim-TaeUk Jan 23, 2025
c17bc6e
[FEAT] UserDeviceRepository에 findByDeviceIdentifier 추가
Kim-TaeUk Jan 23, 2025
ef856e2
[FIX] 생성인 경우 201로 수정
Kim-TaeUk Jan 23, 2025
c60144c
[FEAT] UserDevice 생성자와 정적 팩터리 메서드 create 추가
Kim-TaeUk Jan 23, 2025
c6f1cde
[FEAT] UserDevice 엔티티 updateFcmToken 메서드 추가
Kim-TaeUk Jan 23, 2025
2708e40
[FIX] User 생성자 변경 - default true로 설정
Kim-TaeUk Jan 23, 2025
5430746
[FEAT] 푸시 알림 수신 여부를 위한 PushSettingRequest DTO 추가
Kim-TaeUk Jan 23, 2025
33c3b68
[FEAT] User 엔티티에 updatePushSetting 메서드 추가
Kim-TaeUk Jan 23, 2025
b180707
[FEAT] 푸시 알림 수신 여부 변경 controller 로직 추가
Kim-TaeUk Jan 23, 2025
9a546aa
[FEAT] 푸시 알림 수신 여부 변경 서비스 로직 추가
Kim-TaeUk Jan 23, 2025
41b2c42
[FEAT] User에 UserDevice 양방향 연관관계 추가
Kim-TaeUk Jan 23, 2025
7e8496f
[FIX] 멀티 디바이스 고려에 따른 pushMessage 메서드 변경
Kim-TaeUk Jan 23, 2025
ccc457f
[FEAT] NotificationTypeRepository 추가
Kim-TaeUk Jan 23, 2025
77a7a86
[CHORE] CommentService에 NotificationTypeRepository bean 주입
Kim-TaeUk Jan 23, 2025
ce55c43
[FEAT] 댓글 알림 생성 시 Notification 엔티티 생성 로직 추가
Kim-TaeUk Jan 23, 2025
2e9bc4d
[FIX] FCM 메시지 요청에 notificationId 포함하도록 수정
Kim-TaeUk Jan 23, 2025
9aab167
[REFACTOR] feedOwner 변수로 추출하여 중복 제거
Kim-TaeUk Jan 23, 2025
7bfa260
[REFACTOR] Notification 및 FCM 메시지 생성 시 중복 코드 변수로 추출
Kim-TaeUk Jan 23, 2025
b130e40
[CHORE] CommentService에 NotificationRepository bean 추가
Kim-TaeUk Jan 23, 2025
63fe5ac
[FEAT] Notification 엔티티 저장 로직 추가
Kim-TaeUk Jan 23, 2025
78a292d
[REFACTOR] 중복 코드 변수로 추출
Kim-TaeUk Jan 23, 2025
821b021
[REFACTOR] 댓글 작성자의 fcm token 대신 User 엔티티로 반환
Kim-TaeUk Jan 23, 2025
0988a7a
[FEAT] Notification 엔티티 생성
Kim-TaeUk Jan 23, 2025
d9714a6
[FEAT] Notification 엔티티 저장 로직 추가
Kim-TaeUk Jan 23, 2025
a2b0bd4
[FEAT] 푸시 보낼 FCM token 목록 생성
Kim-TaeUk Jan 23, 2025
0ec2d22
[FIX] 각각 다른 notificationId를 포함한 FCMMessageRequest list 생성
Kim-TaeUk Jan 23, 2025
49db01d
[FIX] forEach로 FCM message 전송
Kim-TaeUk Jan 23, 2025
adc6427
[FIX] 푸시 알림 설계 오류 수정
Kim-TaeUk Jan 23, 2025
615cca6
[REFACTOR] 중복 코드 변수로 추출
Kim-TaeUk Jan 23, 2025
4445334
[CHORE] FeedService에 NotificationRepository bean 추가
Kim-TaeUk Jan 23, 2025
c2208ac
[FEAT] Notification 엔티티 생성 및 저장 로직 추가
Kim-TaeUk Jan 23, 2025
16e6f3b
[CHORE] FeedService에 NotificationRepository bean 주입
Kim-TaeUk Jan 23, 2025
3838b55
[FIX] 잘못 매칭된 NotificationType 수정
Kim-TaeUk Jan 23, 2025
0d84e4e
[FIX] FCM 메시지 요청에 notificationId 포함하도록 수정
Kim-TaeUk Jan 23, 2025
fc0e77c
[REFACTOR] 중복 코드 변수로 추출
Kim-TaeUk Jan 23, 2025
8c3c007
[CHORE] PopularFeedService에 NotificationTypeRepository bean 추가
Kim-TaeUk Jan 23, 2025
1b822a4
[CHORE] PopularFeedService에 NotificationRepository bean 추가
Kim-TaeUk Jan 23, 2025
7c361ee
[FEAT] Notification 엔티티 생성 및 저장
Kim-TaeUk Jan 23, 2025
71c371a
[FIX] FCM 메시지 요청에 notificationId 포함하도록 수정
Kim-TaeUk Jan 23, 2025
d0c7bf4
[REFACTOR] 가독성을 위한 변수로 추출
Kim-TaeUk Jan 23, 2025
85cd2ec
[FIX] FCMMessageRequest에 notificationId 필드 추가
Kim-TaeUk Jan 23, 2025
2dcb563
[FIX] FCM message의 data property에 notificationId 추가
Kim-TaeUk Jan 23, 2025
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
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
Loading