Skip to content

Commit 4f228ba

Browse files
Feat/214 (#222)
2 parents 9c871c2 + c933633 commit 4f228ba

30 files changed

Lines changed: 946 additions & 3 deletions

src/docs/asciidoc/index.adoc

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -109,4 +109,6 @@ include::votes.adoc[]
109109

110110
include::comments.adoc[]
111111

112-
include::comment-likes.adoc[]
112+
include::comment-likes.adoc[]
113+
114+
include::notifications.adoc[]
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
[[알림-API]]
2+
== 알림 API
3+
4+
[[알림-조회]]
5+
=== `GET` 알림 조회
6+
7+
operation::comment-controller-test/find-notifications[snippets='http-request,curl-request,path-parameters,request-headers,query-parameters,http-response,response-fields']

src/main/java/com/chooz/commentLike/application/CommentLikeCommandService.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -3,28 +3,40 @@
33
import com.chooz.commentLike.domain.CommentLike;
44
import com.chooz.commentLike.domain.CommentLikeRepository;
55
import com.chooz.commentLike.presentation.dto.CommentLikeIdResponse;
6+
import com.chooz.common.event.EventPublisher;
67
import com.chooz.common.exception.BadRequestException;
78
import com.chooz.common.exception.ErrorCode;
9+
import com.chooz.notification.domain.event.CommentLikedEvent;
810
import lombok.RequiredArgsConstructor;
911
import lombok.extern.slf4j.Slf4j;
1012
import org.springframework.stereotype.Service;
1113
import org.springframework.transaction.annotation.Transactional;
1214

15+
import java.time.LocalDateTime;
16+
1317
@Service
1418
@Transactional(readOnly = true)
1519
@RequiredArgsConstructor
1620
@Slf4j
1721
public class CommentLikeCommandService {
1822

1923
private final CommentLikeRepository commentLikeRepository;
24+
private final EventPublisher eventPublisher;
2025

2126
public CommentLikeIdResponse createCommentLike(Long commentId, Long userId) {
2227
if(commentLikeRepository.existsByCommentIdAndUserId(commentId, userId)){
2328
throw new BadRequestException(ErrorCode.COMMENT_LIKE_NOT_FOUND);
2429
}
30+
CommentLike commentLike = commentLikeRepository.save(CommentLike.create(commentId, userId));
2531

32+
eventPublisher.publish(new CommentLikedEvent(
33+
commentId,
34+
commentLike.getId(),
35+
userId,
36+
LocalDateTime.now()
37+
));
2638
return new CommentLikeIdResponse(
27-
commentLikeRepository.save(CommentLike.create(commentId, userId)).getId(),
39+
commentLike.getId(),
2840
commentLikeRepository.countByCommentId(commentId)
2941
);
3042
}
Lines changed: 34 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,34 @@
1+
package com.chooz.notification.application;
2+
3+
import com.chooz.notification.application.dto.CommentLikedContent;
4+
import com.chooz.notification.domain.Notification;
5+
import com.chooz.notification.domain.TargetType;
6+
import com.chooz.notification.domain.event.CommentLikedEvent;
7+
import lombok.RequiredArgsConstructor;
8+
import org.springframework.stereotype.Component;
9+
import org.springframework.transaction.event.TransactionPhase;
10+
import org.springframework.transaction.event.TransactionalEventListener;
11+
12+
@Component
13+
@RequiredArgsConstructor
14+
public class CommentLikeNotificationListener {
15+
16+
private final NotificationCommandService notificationCommandService;
17+
private final NotificationContentAssembler notificationContentAssembler;
18+
19+
@TransactionalEventListener(phase = TransactionPhase.AFTER_COMMIT)
20+
public void onCommentLiked(CommentLikedEvent e) {
21+
CommentLikedContent commentLikedContent = notificationContentAssembler.forCommentLiked(e.commentId(), e.likerId());
22+
Notification.create(
23+
commentLikedContent.getCommentAuthorId(),
24+
commentLikedContent.getCommentAuthorName(),
25+
e.likerId(),
26+
commentLikedContent.getActorName(),
27+
commentLikedContent.getActorProfileImageUrl(),
28+
e.commentId(),
29+
TargetType.COMMENT,
30+
commentLikedContent.getTargetThumbnailUrl(),
31+
e.eventAt()
32+
).ifPresent(notificationCommandService::create);
33+
}
34+
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package com.chooz.notification.application;
2+
3+
import com.chooz.notification.domain.Notification;
4+
import com.chooz.notification.domain.NotificationRepository;
5+
import lombok.RequiredArgsConstructor;
6+
import org.springframework.stereotype.Service;
7+
import org.springframework.transaction.annotation.Propagation;
8+
import org.springframework.transaction.annotation.Transactional;
9+
10+
@Service
11+
@RequiredArgsConstructor
12+
public class NotificationCommandService {
13+
14+
private final NotificationRepository notificationRepository;
15+
16+
@Transactional(propagation = Propagation.REQUIRES_NEW)
17+
public Notification create(Notification notification) {
18+
return notificationRepository.save(notification);
19+
}
20+
}
Lines changed: 49 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
1+
package com.chooz.notification.application;
2+
3+
import com.chooz.common.exception.BadRequestException;
4+
import com.chooz.common.exception.ErrorCode;
5+
import com.chooz.notification.application.dto.CommentLikedContent;
6+
import com.chooz.notification.application.dto.TargetPostDto;
7+
import com.chooz.notification.application.dto.TargetUserDto;
8+
import com.chooz.notification.domain.NotificationQueryRepository;
9+
import lombok.RequiredArgsConstructor;
10+
import org.springframework.stereotype.Service;
11+
12+
@Service
13+
@RequiredArgsConstructor
14+
public class NotificationContentAssembler {
15+
16+
private final NotificationQueryRepository notificationQueryDslRepository;
17+
18+
public CommentLikedContent forCommentLiked(Long commentId, Long likerId) {
19+
TargetUserDto targetUserDto = notificationQueryDslRepository.getUser(likerId)
20+
.orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND));
21+
TargetUserDto commentAuthorDto = notificationQueryDslRepository.getUserByCommentId(commentId)
22+
.orElseThrow(() -> new BadRequestException(ErrorCode.USER_NOT_FOUND));
23+
TargetPostDto targetPostDto = notificationQueryDslRepository.getPost(commentId)
24+
.orElseThrow(() -> new BadRequestException(ErrorCode.POST_NOT_FOUND));
25+
26+
return new CommentLikedContent(
27+
targetUserDto.nickname(),
28+
targetUserDto.profileUrl(),
29+
targetPostDto.imageUrl(),
30+
commentAuthorDto.id(),
31+
commentAuthorDto.nickname()
32+
);
33+
}
34+
35+
// public NotificationContent forVoteClosed(Long postId) {
36+
// String title = postPort.getPostTitle(postId).orElse("투표 마감");
37+
// String body = "참여한 투표가 마감되었어요.";
38+
// String thumbnail = postPort.getPostThumbnailUrl(postId).orElse(null);
39+
// return new NotificationContent(title, body, thumbnail);
40+
// }
41+
//
42+
// public NotificationContent forPostParticipated(Long postId, Long voterId) {
43+
// String title = postPort.getPostTitle(postId).orElse("새로운 참여");
44+
// String voter = userPort.getDisplayName(voterId).orElse("누군가");
45+
// String body = voter + "님이 내 투표에 참여했어요.";
46+
// String thumbnail = userPort.getAvatarUrl(voterId).orElse(null);
47+
// return new NotificationContent(title, body, thumbnail);
48+
// }
49+
}
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package com.chooz.notification.application;
2+
3+
import com.chooz.common.dto.CursorBasePaginatedResponse;
4+
import com.chooz.notification.application.dto.NotificationDto;
5+
import com.chooz.notification.domain.Notification;
6+
import com.chooz.notification.domain.NotificationQueryRepository;
7+
import com.chooz.notification.presentation.dto.NotificationResponse;
8+
import lombok.RequiredArgsConstructor;
9+
import org.springframework.data.domain.PageRequest;
10+
import org.springframework.data.domain.Slice;
11+
import org.springframework.stereotype.Service;
12+
import org.springframework.transaction.annotation.Transactional;
13+
14+
@Service
15+
@Transactional(readOnly = true)
16+
@RequiredArgsConstructor
17+
public class NotificationQueryService {
18+
19+
private final NotificationQueryRepository notificationQueryRepository;
20+
21+
// public CursorBasePaginatedResponse<NotificationResponse> findNotifications(Long userId, Long cursor, int size) {
22+
// Slice<Notification> notificationSlice = notificationQueryRepository.findNotifications(userId, cursor, PageRequest.ofSize(size));
23+
// return CursorBasePaginatedResponse.of(notificationSlice.map(NotificationResponse::of));
24+
// }
25+
public CursorBasePaginatedResponse<NotificationResponse> findNotifications(Long userId, Long cursor, int size) {
26+
Slice<NotificationDto> notificationSlice = notificationQueryRepository.findNotifications(userId, cursor, PageRequest.ofSize(size));
27+
return CursorBasePaginatedResponse.of(notificationSlice.map(NotificationResponse::of));
28+
}
29+
}
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package com.chooz.notification.application.dto;
2+
3+
import lombok.Getter;
4+
5+
@Getter
6+
public class CommentLikedContent extends NotificationContent {
7+
8+
private final Long commentAuthorId;
9+
private final String commentAuthorName;
10+
11+
public CommentLikedContent(
12+
String actorName,
13+
String actorProfileImageUrl,
14+
String targetThumbnailUrl,
15+
Long commentAuthorId,
16+
String commentAuthorName
17+
) {
18+
super(actorName, targetThumbnailUrl, actorProfileImageUrl);
19+
this.commentAuthorId = commentAuthorId;
20+
this.commentAuthorName = commentAuthorName;
21+
}
22+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.chooz.notification.application.dto;
2+
3+
import lombok.Getter;
4+
import lombok.RequiredArgsConstructor;
5+
6+
@Getter
7+
@RequiredArgsConstructor
8+
public abstract class NotificationContent {
9+
private final String actorName;
10+
private final String actorProfileImageUrl;
11+
private final String targetThumbnailUrl;
12+
13+
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.chooz.notification.application.dto;
2+
3+
4+
import com.chooz.notification.domain.TargetType;
5+
import com.querydsl.core.annotations.QueryProjection;
6+
7+
import java.time.LocalDateTime;
8+
9+
@QueryProjection
10+
public record NotificationDto(
11+
Long id,
12+
Long postId,
13+
Long receiverId,
14+
String receiverNickname,
15+
Long actorId,
16+
String actorNickname,
17+
String actorProfileUrl,
18+
Long targetId,
19+
TargetType targetType,
20+
String targetImageUrl,
21+
boolean isRead,
22+
LocalDateTime eventAt
23+
) {}

0 commit comments

Comments
 (0)