Skip to content

Commit 9b736e0

Browse files
authored
Merge pull request #67 from classic-daramg/feature/notification
Feature/notification
2 parents efea525 + 39dc0ea commit 9b736e0

19 files changed

+1095
-0
lines changed

โ€Žsrc/main/java/com/daramg/server/comment/application/CommentService.javaโ€Ž

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,10 +8,13 @@
88
import com.daramg.server.common.application.EntityUtils;
99
import com.daramg.server.common.exception.BusinessException;
1010
import com.daramg.server.post.domain.Post;
11+
import com.daramg.server.notification.domain.NotificationType;
12+
import com.daramg.server.notification.event.NotificationEvent;
1113
import com.daramg.server.post.dto.CommentCreateDto;
1214
import com.daramg.server.post.dto.CommentReplyCreateDto;
1315
import com.daramg.server.user.domain.User;
1416
import lombok.RequiredArgsConstructor;
17+
import org.springframework.context.ApplicationEventPublisher;
1518
import org.springframework.stereotype.Service;
1619
import org.springframework.transaction.annotation.Transactional;
1720

@@ -23,6 +26,7 @@ public class CommentService {
2326
private final EntityUtils entityUtils;
2427
private final CommentRepository commentRepository;
2528
private final CommentLikeRepository commentLikeRepository;
29+
private final ApplicationEventPublisher eventPublisher;
2630

2731
public void createComment(Long postId, CommentCreateDto request, User user){
2832
Post post = entityUtils.getEntity(postId, Post.class);
@@ -39,6 +43,11 @@ public void createComment(Long postId, CommentCreateDto request, User user){
3943

4044
commentRepository.save(comment);
4145
post.incrementCommentCount();
46+
if (!post.getUser().getId().equals(user.getId())) {
47+
eventPublisher.publishEvent(new NotificationEvent(
48+
post.getUser(), user, post, NotificationType.COMMENT
49+
));
50+
}
4251
}
4352

4453
public void createReply(Long commentId, CommentReplyCreateDto request, User user){
@@ -60,6 +69,11 @@ public void createReply(Long commentId, CommentReplyCreateDto request, User user
6069

6170
commentRepository.save(reply);
6271
post.incrementCommentCount();
72+
if (!parentComment.getUser().getId().equals(user.getId())) {
73+
eventPublisher.publishEvent(new NotificationEvent(
74+
parentComment.getUser(), user, post, NotificationType.REPLY
75+
));
76+
}
6377
}
6478

6579
public CommentLikeResponseDto toggleCommentLike(Long commentId, User user){
@@ -79,6 +93,11 @@ public CommentLikeResponseDto toggleCommentLike(Long commentId, User user){
7993

8094
commentLikeRepository.save(CommentLike.of(comment, user));
8195
comment.incrementLikeCount();
96+
if (!comment.getUser().getId().equals(user.getId())) {
97+
eventPublisher.publishEvent(new NotificationEvent(
98+
comment.getUser(), user, comment.getPost(), NotificationType.COMMENT_LIKE
99+
));
100+
}
82101
return new CommentLikeResponseDto(true, comment.getLikeCount());
83102
}
84103

Lines changed: 42 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,42 @@
1+
package com.daramg.server.notification.application;
2+
3+
import com.daramg.server.common.dto.PageRequestDto;
4+
import com.daramg.server.common.dto.PageResponseDto;
5+
import com.daramg.server.common.util.PagingUtils;
6+
import com.daramg.server.notification.domain.Notification;
7+
import com.daramg.server.notification.dto.NotificationResponseDto;
8+
import com.daramg.server.notification.repository.NotificationQueryRepository;
9+
import com.daramg.server.notification.repository.NotificationRepository;
10+
import com.daramg.server.user.domain.User;
11+
import lombok.RequiredArgsConstructor;
12+
import org.springframework.stereotype.Service;
13+
import org.springframework.transaction.annotation.Transactional;
14+
15+
import java.util.List;
16+
17+
@Service
18+
@Transactional(readOnly = true)
19+
@RequiredArgsConstructor
20+
public class NotificationQueryService {
21+
22+
private final NotificationRepository notificationRepository;
23+
private final NotificationQueryRepository notificationQueryRepository;
24+
private final PagingUtils pagingUtils;
25+
26+
public PageResponseDto<NotificationResponseDto> getNotifications(User user, PageRequestDto request) {
27+
List<Notification> notifications = notificationQueryRepository
28+
.getNotificationsWithPaging(user.getId(), request);
29+
30+
return pagingUtils.createPageResponse(
31+
notifications,
32+
request.getValidatedSize(),
33+
NotificationResponseDto::from,
34+
Notification::getCreatedAt,
35+
Notification::getId
36+
);
37+
}
38+
39+
public long getUnreadCount(User user) {
40+
return notificationRepository.countByReceiverIdAndIsReadFalse(user.getId());
41+
}
42+
}
Lines changed: 41 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
package com.daramg.server.notification.application;
2+
3+
import com.daramg.server.common.application.EntityUtils;
4+
import com.daramg.server.common.exception.BusinessException;
5+
import com.daramg.server.notification.domain.Notification;
6+
import com.daramg.server.notification.repository.NotificationRepository;
7+
import com.daramg.server.user.domain.User;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.stereotype.Service;
10+
import org.springframework.transaction.annotation.Transactional;
11+
12+
@Service
13+
@Transactional
14+
@RequiredArgsConstructor
15+
public class NotificationService {
16+
17+
private final NotificationRepository notificationRepository;
18+
private final EntityUtils entityUtils;
19+
20+
public void markAsRead(Long notificationId, User user) {
21+
Notification notification = entityUtils.getEntity(notificationId, Notification.class);
22+
validateReceiver(notification, user);
23+
notification.markAsRead();
24+
}
25+
26+
public void markAllAsRead(User user) {
27+
notificationRepository.markAllAsReadByReceiverId(user.getId());
28+
}
29+
30+
public void delete(Long notificationId, User user) {
31+
Notification notification = entityUtils.getEntity(notificationId, Notification.class);
32+
validateReceiver(notification, user);
33+
notificationRepository.delete(notification);
34+
}
35+
36+
private void validateReceiver(Notification notification, User user) {
37+
if (!notification.getReceiver().getId().equals(user.getId())) {
38+
throw new BusinessException("๋ณธ์ธ์˜ ์•Œ๋ฆผ๋งŒ ์ฒ˜๋ฆฌํ•  ์ˆ˜ ์žˆ์Šต๋‹ˆ๋‹ค.");
39+
}
40+
}
41+
}
Lines changed: 54 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,54 @@
1+
package com.daramg.server.notification.domain;
2+
3+
import com.daramg.server.common.domain.BaseEntity;
4+
import com.daramg.server.post.domain.Post;
5+
import com.daramg.server.user.domain.User;
6+
import jakarta.persistence.*;
7+
import lombok.AccessLevel;
8+
import lombok.Getter;
9+
import lombok.NoArgsConstructor;
10+
import org.springframework.lang.NonNull;
11+
12+
@Entity
13+
@Getter
14+
@Table(name = "notifications")
15+
@NoArgsConstructor(access = AccessLevel.PROTECTED)
16+
public class Notification extends BaseEntity<Notification> {
17+
18+
@ManyToOne(fetch = FetchType.LAZY)
19+
@JoinColumn(name = "receiver_id", nullable = false)
20+
private User receiver;
21+
22+
@ManyToOne(fetch = FetchType.LAZY)
23+
@JoinColumn(name = "sender_id", nullable = false)
24+
private User sender;
25+
26+
@ManyToOne(fetch = FetchType.LAZY)
27+
@JoinColumn(name = "post_id", nullable = false)
28+
private Post post;
29+
30+
@Enumerated(EnumType.STRING)
31+
@Column(name = "type", nullable = false, length = 20)
32+
private NotificationType type;
33+
34+
@Column(name = "is_read", nullable = false)
35+
private boolean isRead = false;
36+
37+
private Notification(User receiver, User sender, Post post, NotificationType type) {
38+
this.receiver = receiver;
39+
this.sender = sender;
40+
this.post = post;
41+
this.type = type;
42+
}
43+
44+
public static Notification of(@NonNull User receiver,
45+
@NonNull User sender,
46+
@NonNull Post post,
47+
@NonNull NotificationType type) {
48+
return new Notification(receiver, sender, post, type);
49+
}
50+
51+
public void markAsRead() {
52+
this.isRead = true;
53+
}
54+
}
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.daramg.server.notification.domain;
2+
3+
public enum NotificationType {
4+
POST_LIKE,
5+
COMMENT_LIKE,
6+
COMMENT,
7+
REPLY
8+
}
Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,30 @@
1+
package com.daramg.server.notification.dto;
2+
3+
import com.daramg.server.notification.domain.Notification;
4+
import com.daramg.server.notification.domain.NotificationType;
5+
6+
import java.time.LocalDateTime;
7+
8+
public record NotificationResponseDto(
9+
Long id,
10+
String senderNickname,
11+
String senderProfileImage,
12+
Long postId,
13+
String postTitle,
14+
NotificationType type,
15+
boolean isRead,
16+
LocalDateTime createdAt
17+
) {
18+
public static NotificationResponseDto from(Notification notification) {
19+
return new NotificationResponseDto(
20+
notification.getId(),
21+
notification.getSender().getNickname(),
22+
notification.getSender().getProfileImage(),
23+
notification.getPost().getId(),
24+
notification.getPost().getTitle(),
25+
notification.getType(),
26+
notification.isRead(),
27+
notification.getCreatedAt()
28+
);
29+
}
30+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.daramg.server.notification.event;
2+
3+
import com.daramg.server.notification.domain.NotificationType;
4+
import com.daramg.server.post.domain.Post;
5+
import com.daramg.server.user.domain.User;
6+
7+
public record NotificationEvent(
8+
User receiver,
9+
User sender,
10+
Post post,
11+
NotificationType type
12+
) {
13+
}
Lines changed: 32 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,32 @@
1+
package com.daramg.server.notification.event;
2+
3+
import com.daramg.server.notification.domain.Notification;
4+
import com.daramg.server.notification.repository.NotificationRepository;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.stereotype.Component;
7+
import org.springframework.transaction.event.TransactionalEventListener;
8+
9+
import static org.springframework.transaction.event.TransactionPhase.BEFORE_COMMIT;
10+
11+
@Component
12+
@RequiredArgsConstructor
13+
public class NotificationEventListener {
14+
15+
private final NotificationRepository notificationRepository;
16+
17+
@TransactionalEventListener(phase = BEFORE_COMMIT)
18+
public void handleNotificationEvent(NotificationEvent event) {
19+
if (event.receiver().getId().equals(event.sender().getId())) {
20+
return;
21+
}
22+
23+
Notification notification = Notification.of(
24+
event.receiver(),
25+
event.sender(),
26+
event.post(),
27+
event.type()
28+
);
29+
30+
notificationRepository.save(notification);
31+
}
32+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.daramg.server.notification.presentation;
2+
3+
import com.daramg.server.notification.application.NotificationService;
4+
import com.daramg.server.user.domain.User;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.http.HttpStatus;
7+
import org.springframework.web.bind.annotation.*;
8+
9+
@RestController
10+
@RequiredArgsConstructor
11+
@RequestMapping("/notifications")
12+
public class NotificationController {
13+
14+
private final NotificationService notificationService;
15+
16+
@PatchMapping("/{notificationId}/read")
17+
@ResponseStatus(HttpStatus.OK)
18+
public void markAsRead(@PathVariable Long notificationId, User user) {
19+
notificationService.markAsRead(notificationId, user);
20+
}
21+
22+
@PatchMapping("/read-all")
23+
@ResponseStatus(HttpStatus.OK)
24+
public void markAllAsRead(User user) {
25+
notificationService.markAllAsRead(user);
26+
}
27+
28+
@DeleteMapping("/{notificationId}")
29+
@ResponseStatus(HttpStatus.NO_CONTENT)
30+
public void delete(@PathVariable Long notificationId, User user) {
31+
notificationService.delete(notificationId, user);
32+
}
33+
}
Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
package com.daramg.server.notification.presentation;
2+
3+
import com.daramg.server.common.dto.PageRequestDto;
4+
import com.daramg.server.common.dto.PageResponseDto;
5+
import com.daramg.server.notification.application.NotificationQueryService;
6+
import com.daramg.server.notification.dto.NotificationResponseDto;
7+
import com.daramg.server.user.domain.User;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.web.bind.annotation.GetMapping;
10+
import org.springframework.web.bind.annotation.ModelAttribute;
11+
import org.springframework.web.bind.annotation.RequestMapping;
12+
import org.springframework.web.bind.annotation.RestController;
13+
14+
@RestController
15+
@RequiredArgsConstructor
16+
@RequestMapping("/notifications")
17+
public class NotificationQueryController {
18+
19+
private final NotificationQueryService notificationQueryService;
20+
21+
@GetMapping
22+
public PageResponseDto<NotificationResponseDto> getNotifications(
23+
User user,
24+
@ModelAttribute PageRequestDto request
25+
) {
26+
return notificationQueryService.getNotifications(user, request);
27+
}
28+
29+
@GetMapping("/unread-count")
30+
public long getUnreadCount(User user) {
31+
return notificationQueryService.getUnreadCount(user);
32+
}
33+
}

0 commit comments

Comments
ย (0)