Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

[Feat] 알림 목록 조회 / 알림 읽음 처리 기능 개발 #282

Merged
merged 5 commits into from
Apr 9, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
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
@@ -0,0 +1,46 @@
package ccc.keeweapi.controller.api.notification;

import static ccc.keewecore.consts.KeeweConsts.LONG_MAX_STRING;

import ccc.keeweapi.dto.ApiResponse;
import ccc.keeweapi.dto.notification.NotificationResponse;
import ccc.keeweapi.dto.notification.PaginateNotificationResponse;
import ccc.keeweapi.service.notification.command.NotificationCommandApiService;
import ccc.keeweapi.service.notification.query.NotificationQueryApiService;
import ccc.keewedomain.persistence.domain.notification.enums.NotificationCategory;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/v1/notification")
@RequiredArgsConstructor
public class NotificationController {
private final NotificationQueryApiService notificationQueryApiService;
private final NotificationCommandApiService notificationCommandApiService;

@GetMapping
public ApiResponse<PaginateNotificationResponse> paginateNotifications(
@RequestParam(required = false, defaultValue = LONG_MAX_STRING) Long cursor,
@RequestParam Long limit
) {
//return ApiResponse.ok(notificationQueryApiService.paginateNotifications(CursorPageable.of(cursor, limit)));
return ApiResponse.ok(
PaginateNotificationResponse.of(
10L, List.of(
NotificationResponse.of(3L, "내 인사이트에 \n누군가 댓글 남김", "유승훈님이 댓글을 남겼어요.", NotificationCategory.COMMENT, "3", false),
NotificationResponse.of(4L, "초보 기록가", "꾸준함이 중요하죠. 초보 기록가!", NotificationCategory.TITLE, "6", true)
))
);
}

@PatchMapping("/{notificationId}/read")
public ApiResponse<NotificationResponse> markAsReadToNotification(@PathVariable("notificationId") Long notificationId) {
return ApiResponse.ok(notificationCommandApiService.markAsRead(notificationId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package ccc.keeweapi.dto.notification;

import ccc.keewedomain.persistence.domain.notification.enums.NotificationCategory;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor(staticName = "of")
public class NotificationResponse {
private Long id;
private String title;
private String contents;
private NotificationCategory category;
private String referenceId;
private Boolean read;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ccc.keeweapi.dto.notification;

import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor(staticName = "of")
public class PaginateNotificationResponse {
private Long nextCursor;
private List<NotificationResponse> notifications;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ccc.keeweapi.service.notification;

import ccc.keeweapi.dto.notification.NotificationResponse;
import ccc.keewedomain.persistence.domain.insight.Comment;
import ccc.keewedomain.persistence.domain.notification.Notification;
import ccc.keewedomain.persistence.domain.notification.enums.NotificationCategory;
import ccc.keewedomain.persistence.domain.notification.enums.NotificationContents;
import ccc.keewedomain.service.insight.CommentDomainService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@RequiredArgsConstructor
@Component
public class CommentNotificationProcessor implements NotificationProcessor {
private final CommentDomainService commentDomainService;

@Override
public NotificationCategory getCategory() {
return NotificationCategory.COMMENT;
}

@Override
public NotificationResponse process(Notification notification) {
String commentId = notification.getReferenceId();
Comment comment = commentDomainService.getByIdOrElseThrow(Long.parseLong(commentId));
NotificationContents contents = notification.getContents();
return NotificationResponse.of(
notification.getId(),
notification.getContents().getTitle(),
String.format(contents.getContents(), comment.getWriter().getNickname()), // note. {UserName}님이 댓글을 남겼어요.
contents.getCategory(),
notification.getReferenceId(),
notification.isRead()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ccc.keeweapi.service.notification;

import ccc.keeweapi.dto.notification.NotificationResponse;
import ccc.keewedomain.persistence.domain.insight.Comment;
import ccc.keewedomain.persistence.domain.notification.Notification;
import ccc.keewedomain.persistence.domain.notification.enums.NotificationCategory;
import ccc.keewedomain.persistence.domain.notification.enums.NotificationContents;
import ccc.keewedomain.service.insight.CommentDomainService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@RequiredArgsConstructor
@Component
public class CommentReplyNotificationProcessor implements NotificationProcessor {
private final CommentDomainService commentDomainService;

@Override
public NotificationCategory getCategory() {
return NotificationCategory.COMMENT_REPLY;
}

@Override
public NotificationResponse process(Notification notification) {
String commentReplyId = notification.getReferenceId();
Comment comment = commentDomainService.getByIdOrElseThrow(Long.parseLong(commentReplyId));
NotificationContents contents = notification.getContents();
return NotificationResponse.of(
notification.getId(),
notification.getContents().getTitle(),
String.format(contents.getContents(), comment.getWriter().getNickname()), // note. {UserName}님이 답글을 남겼어요.
contents.getCategory(),
notification.getReferenceId(),
notification.isRead()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package ccc.keeweapi.service.notification;


import ccc.keeweapi.dto.notification.NotificationResponse;
import ccc.keewedomain.persistence.domain.notification.Notification;
import ccc.keewedomain.persistence.domain.notification.enums.NotificationCategory;

public interface NotificationProcessor {
NotificationCategory getCategory();
NotificationResponse process(Notification notification);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
package ccc.keeweapi.service.notification;

import ccc.keeweapi.dto.notification.NotificationResponse;
import ccc.keewedomain.persistence.domain.insight.Reaction;
import ccc.keewedomain.persistence.domain.notification.Notification;
import ccc.keewedomain.persistence.domain.notification.enums.NotificationCategory;
import ccc.keewedomain.persistence.domain.notification.enums.NotificationContents;
import ccc.keewedomain.persistence.domain.user.User;
import ccc.keewedomain.service.insight.ReactionDomainService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@RequiredArgsConstructor
@Component
public class ReactionNotificationProcessor implements NotificationProcessor {
private final ReactionDomainService reactionDomainService;

@Override
public NotificationCategory getCategory() {
return NotificationCategory.REACTION;
}

@Override
public NotificationResponse process(Notification notification) {
String reactionId = notification.getReferenceId();

Reaction reaction = reactionDomainService.getByIdOrElseThrow(Long.parseLong(reactionId));
User user = reaction.getReactor();
NotificationContents contents = notification.getContents();
return NotificationResponse.of(
notification.getId(),
contents.getTitle(), // note. 내 인사이트에 누군가 반응 남김
String.format(contents.getContents(), user.getNickname()), // note. {UserName}님이 반응을 남겼어요.
contents.getCategory(),
notification.getReferenceId(),
notification.isRead()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
package ccc.keeweapi.service.notification;

import ccc.keeweapi.dto.notification.NotificationResponse;
import ccc.keewedomain.persistence.domain.notification.Notification;
import ccc.keewedomain.persistence.domain.notification.enums.NotificationCategory;
import ccc.keewedomain.persistence.domain.notification.enums.NotificationContents;
import org.springframework.stereotype.Component;

@Component
public class TitleNotificationProcessor implements NotificationProcessor {
@Override
public NotificationCategory getCategory() {
return NotificationCategory.TITLE;
}

@Override
public NotificationResponse process(Notification notification) {
NotificationContents contents = notification.getContents();
return NotificationResponse.of(
notification.getId(),
contents.getTitle(),
contents.getContents(),
contents.getCategory(),
notification.getReferenceId(),
notification.isRead()
);
}
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package ccc.keeweapi.service.notification.command;

import ccc.keeweapi.dto.notification.NotificationResponse;
import ccc.keeweapi.service.notification.NotificationProcessor;
import ccc.keeweapi.utils.SecurityUtil;
import ccc.keewedomain.persistence.domain.notification.Notification;
import ccc.keewedomain.persistence.domain.notification.enums.NotificationCategory;
import ccc.keewedomain.service.notification.command.NotificationCommandDomainService;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@Service
public class NotificationCommandApiService {
private final NotificationCommandDomainService notificationCommandDomainService;
private final Map<NotificationCategory, NotificationProcessor> notificationProcessors;

public NotificationCommandApiService(
NotificationCommandDomainService notificationCommandDomainService,
List<NotificationProcessor> notificationProcessors
) {
Youhoseong marked this conversation as resolved.
Show resolved Hide resolved
this.notificationCommandDomainService = notificationCommandDomainService;
this.notificationProcessors = notificationProcessors.stream()
.collect(Collectors.toMap(NotificationProcessor::getCategory, notificationProcessor -> notificationProcessor));
}

@Transactional
public NotificationResponse markAsRead(Long notificationId) {
Notification notification = notificationCommandDomainService.getByIdWithUserAssert(notificationId, SecurityUtil.getUserId());
Notification readMarkedNotification = notificationCommandDomainService.save(notification.markAsRead());
return notificationProcessors.get(readMarkedNotification.getContents().getCategory())
.process(readMarkedNotification);
}
}
Empty file.
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
package ccc.keeweapi.service.notification.query;

import ccc.keeweapi.dto.notification.NotificationResponse;
import ccc.keeweapi.dto.notification.PaginateNotificationResponse;
import ccc.keeweapi.service.notification.NotificationProcessor;
import ccc.keeweapi.utils.SecurityUtil;
import ccc.keewecore.utils.ListUtils;
import ccc.keewedomain.persistence.domain.notification.enums.NotificationCategory;
import ccc.keewedomain.persistence.repository.utils.CursorPageable;
import ccc.keewedomain.service.notification.query.NotificationQueryDomainService;
import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;
import lombok.extern.slf4j.Slf4j;
import org.springframework.stereotype.Service;
import org.springframework.util.Assert;

@Service
@Slf4j
public class NotificationQueryApiService {
private final NotificationQueryDomainService notificationQueryDomainService;
private final Map<NotificationCategory, NotificationProcessor> notificationProcessors;

public NotificationQueryApiService(
NotificationQueryDomainService notificationQueryDomainService,
List<NotificationProcessor> notificationProcessors
) {
this.notificationQueryDomainService = notificationQueryDomainService;
this.notificationProcessors = notificationProcessors.stream()
.collect(Collectors.toMap(NotificationProcessor::getCategory, notificationProcessor -> notificationProcessor));
}

public PaginateNotificationResponse paginateNotifications(CursorPageable<Long> cPage) {
List<NotificationResponse> notificationResponses = notificationQueryDomainService.paginateNotifications(cPage, SecurityUtil.getUser()).stream()
.map(notification -> {
NotificationProcessor notificationProcessor = notificationProcessors.get(notification.getContents().getCategory());
Assert.notNull(notificationProcessor);
return notificationProcessor.process(notification);
})
.collect(Collectors.toList());

if(notificationResponses.size() >= cPage.getLimit()) {
NotificationResponse lastResponse = ListUtils.getLast(notificationResponses);
return PaginateNotificationResponse.of(lastResponse.getId(), notificationResponses);
} else {
return PaginateNotificationResponse.of(null, notificationResponses);
}
}
}
Loading