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/145] 알림 관련 API 구현 #150

Merged
merged 7 commits into from
Aug 11, 2024
Original file line number Diff line number Diff line change
Expand Up @@ -137,11 +137,14 @@ public enum ErrorStatus implements BaseErrorCode {
NOT_STAR_FRIEND(HttpStatus.BAD_REQUEST, "FRIEND410", "즐겨찾기 되어 있는 친구가 아닙니다."),

// 알림 관련 에러
NOTIFICATION_TYPE_NOT_FOUND(HttpStatus.NOT_FOUND, "NOTI401", "잘못된 알림 타입입니다."),
NOTIFICATION_TYPE_NOT_FOUND(HttpStatus.NOT_FOUND, "NOTI401", "해당 알림 타입 데이터를 찾을 수 없습니다."),
NOTIFICATION_METHOD_BAD_REQUEST(HttpStatus.BAD_REQUEST, "NOTI402", "알림 생성 메소드 호출이 잘못되었습니다."),
INVALID_NOTIFICATION_TYPE(HttpStatus.BAD_REQUEST, "NOTI403",
"잘못된 알림 조회 타입입니다. general과 friend 중 하나를 입력하세요."),
NOTIFICATION_NOT_FOUND(HttpStatus.NOT_FOUND, "NOTI404", "해당 알림 내역을 찾을 수 없습니다."),

// SOCKET 서버 API 호출 에러
SOCKET_API_RESPONSE_ERR(HttpStatus.INTERNAL_SERVER_ERROR, "SOCKET501",
SOCKET_API_RESPONSE_ERROR(HttpStatus.INTERNAL_SERVER_ERROR, "SOCKET501",
"socket서버 api 요청에 실패했습니다.");

private final HttpStatus httpStatus;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,98 @@
package com.gamegoo.controller.notification;

import com.gamegoo.apiPayload.ApiResponse;
import com.gamegoo.converter.NotificationConverter;
import com.gamegoo.domain.notification.Notification;
import com.gamegoo.dto.notification.NotificationResponse;
import com.gamegoo.dto.notification.NotificationResponse.notificationReadDTO;
import com.gamegoo.service.notification.NotificationService;
import com.gamegoo.util.JWTUtil;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.Parameters;
import io.swagger.v3.oas.annotations.tags.Tag;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Slice;
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
@RequiredArgsConstructor
@Slf4j
@RequestMapping("/v1/notification")
@Tag(name = "Notification", description = "Notification 관련 API")
public class NotificationController {

private final NotificationService notificationService;

@Operation(summary = "알림 목록 조회 API", description = "알림 팝업 화면에서 알림 목록을 조회하는 API 입니다.")
@Parameters({
@Parameter(name = "cursor", description = "페이징을 위한 커서, Long 타입 notificationId를 보내주세요. 보내지 않으면 가장 최근 알림 10개를 조회합니다."),
@Parameter(name = "type", description = "알림 타입 조회 필터, general 또는 friend를 입력해주세요.")
})
@GetMapping
public ApiResponse<NotificationResponse.cursorNotificationListDTO> getNotificationList(
@RequestParam(name = "type") String type,
@RequestParam(name = "cursor", required = false) Long cursor
) {

Long memberId = JWTUtil.getCurrentUserId();

Slice<Notification> notifications = notificationService.getNotificationListByCursor(
memberId, type, cursor);

return ApiResponse.onSuccess(
NotificationConverter.toCursorNotificationListDTO(notifications));
}

@Operation(summary = "알림 전체 목록 조회 API", description = "알림 전체보기 화면에서 알림 목록을 조회하는 API 입니다.")
@Parameter(name = "page", description = "페이지 번호, 1 이상의 숫자를 입력해 주세요.")
@GetMapping("/total")
public ApiResponse<NotificationResponse.pageNotificationListDTO> getTotalNotificationList(
@RequestParam(name = "page") Integer page
) {
Long memberId = JWTUtil.getCurrentUserId();

Page<Notification> notifications = notificationService.getNotificationListByPage(
memberId, page - 1);

return ApiResponse.onSuccess(
NotificationConverter.toPageNotificationListDTO(notifications)
);
}

@Operation(summary = "알림 읽음 처리 API", description = "특정 알림을 읽음 처리하는 API 입니다.")
@Parameter(name = "notificationId", description = "읽음 처리할 알림의 id 입니다.")
@PatchMapping("/{notificationId}")
public ApiResponse<NotificationResponse.notificationReadDTO> getTotalNotificationList(
@PathVariable(name = "notificationId") Long notificationId
) {
Long memberId = JWTUtil.getCurrentUserId();

Notification notification = notificationService.readNotification(memberId, notificationId);

NotificationResponse.notificationReadDTO result = notificationReadDTO.builder()
.notificationId(notification.getId())
.message("알림 읽음 처리 성공")
.build();

return ApiResponse.onSuccess(result);
}

@Operation(summary = "안읽은 알림 개수 조회 API", description = "해당 회원의 안읽은 알림의 개수를 조회하는 API 입니다.")
@GetMapping("/unread/count")
public ApiResponse<Long> getUnreadNotificationCount(
) {
Long memberId = JWTUtil.getCurrentUserId();

return ApiResponse.onSuccess(notificationService.countUnreadNotification(memberId));
}


}
67 changes: 67 additions & 0 deletions src/main/java/com/gamegoo/converter/NotificationConverter.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,67 @@
package com.gamegoo.converter;

import com.gamegoo.domain.notification.Notification;
import com.gamegoo.dto.notification.NotificationResponse;
import com.gamegoo.dto.notification.NotificationResponse.notificationDTO;
import java.util.List;
import java.util.stream.Collectors;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Slice;

public class NotificationConverter {

public static NotificationResponse.cursorNotificationListDTO toCursorNotificationListDTO(
Slice<Notification> notifications) {
List<notificationDTO> notificationDTOList = notifications.stream()
.map(NotificationConverter::toNotificationDTO).collect(Collectors.toList());

return NotificationResponse.cursorNotificationListDTO.builder()
.notificationDTOList(notificationDTOList)
.list_size(notificationDTOList.size())
.has_next(notifications.hasNext())
.next_cursor(notifications.hasNext() ? notifications.getContent().get(9).getId()
: null) // next cursor를 현재 notificationList의 가장 마지막 요소의 id로 주기
.build();
}

public static NotificationResponse.pageNotificationListDTO toPageNotificationListDTO(
Page<Notification> notifications) {
List<notificationDTO> notificationDTOList = notifications.stream()
.map(NotificationConverter::toNotificationDTO).collect(Collectors.toList());

return NotificationResponse.pageNotificationListDTO.builder()
.notificationDTOList(notificationDTOList)
.listSize(notificationDTOList.size())
.totalPage(notifications.getTotalPages())
.totalElements(notifications.getTotalElements())
.isFirst(notifications.isFirst())
.isLast(notifications.isLast())
.build();
}

public static NotificationResponse.notificationDTO toNotificationDTO(
Notification notification) {

String pageUrl = null;

if (notification.getNotificationType().getSourceUrl() != null) {
StringBuilder urlBuilder = new StringBuilder(
notification.getNotificationType().getSourceUrl());

if (notification.getSourceId() != null) {
urlBuilder.append(notification.getSourceId());
}

pageUrl = urlBuilder.toString();
}

return NotificationResponse.notificationDTO.builder()
.notificationId(notification.getId())
.notificationType(notification.getNotificationType().getId().intValue())
.content(notification.getContent())
.pageUrl(pageUrl)
.read(notification.isRead())
.createdAt(notification.getCreatedAt().withNano(0))
.build();
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,7 @@
package com.gamegoo.domain.notification;

import com.gamegoo.domain.member.Member;
import com.gamegoo.domain.common.BaseDateTimeEntity;

import com.gamegoo.domain.member.Member;
import javax.persistence.Column;
import javax.persistence.Entity;
import javax.persistence.FetchType;
Expand All @@ -11,7 +10,6 @@
import javax.persistence.Id;
import javax.persistence.JoinColumn;
import javax.persistence.ManyToOne;

import lombok.AccessLevel;
import lombok.AllArgsConstructor;
import lombok.Builder;
Expand Down Expand Up @@ -55,4 +53,8 @@ public void setMember(Member member) {
this.member.getNotificationList().add(this);
}

public void updateIsRead(boolean isRead) {
this.isRead = isRead;
}

}
15 changes: 13 additions & 2 deletions src/main/java/com/gamegoo/dto/board/BoardResponse.java
Original file line number Diff line number Diff line change
@@ -1,18 +1,23 @@
package com.gamegoo.dto.board;

import com.gamegoo.domain.member.Tier;
import lombok.*;

import java.time.LocalDateTime;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;
import lombok.Setter;

public class BoardResponse {

@Getter
@Builder
@Setter
@NoArgsConstructor
@AllArgsConstructor
public static class boardInsertResponseDTO {

Long boardId;
Long memberId;
Integer profileImage;
Expand All @@ -35,6 +40,7 @@ public static class boardInsertResponseDTO {
@NoArgsConstructor
@AllArgsConstructor
public static class boardUpdateResponseDTO {

Long boardId;
Long memberId;
Integer profileImage;
Expand All @@ -56,6 +62,7 @@ public static class boardUpdateResponseDTO {
@NoArgsConstructor
@AllArgsConstructor
public static class boardListResponseDTO {

Long boardId;
Long memberId;
Integer profileImage;
Expand All @@ -78,6 +85,7 @@ public static class boardListResponseDTO {
@NoArgsConstructor
@AllArgsConstructor
public static class boardByIdResponseDTO {

Long boardId;
Long memberId;
LocalDateTime createdAt;
Expand All @@ -104,10 +112,12 @@ public static class boardByIdResponseDTO {
@NoArgsConstructor
@AllArgsConstructor
public static class boardByIdResponseForMemberDTO {

Long boardId;
Long memberId;
Boolean isBlocked;
Boolean isFriend;
Long friendRequestMemberId;
LocalDateTime createdAt;
Integer profileImage;
String gameName;
Expand All @@ -132,6 +142,7 @@ public static class boardByIdResponseForMemberDTO {
@NoArgsConstructor
@AllArgsConstructor
public static class myBoardListResponseDTO {

Long boardId;
Long memberId;
Integer profileImage;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,62 @@
package com.gamegoo.dto.notification;

import java.time.LocalDateTime;
import java.util.List;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

public class NotificationResponse {

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class cursorNotificationListDTO {

List<notificationDTO> notificationDTOList;
Integer list_size;
Boolean has_next;
Long next_cursor;
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class pageNotificationListDTO {

List<notificationDTO> notificationDTOList;
Integer listSize;
Integer totalPage;
Long totalElements;
Boolean isFirst;
Boolean isLast;
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class notificationDTO {

Long notificationId;
int notificationType;
String content;
String pageUrl;
boolean read;
LocalDateTime createdAt;
}

@Builder
@Getter
@NoArgsConstructor
@AllArgsConstructor
public static class notificationReadDTO {

Long notificationId;
String message;
}

}
Original file line number Diff line number Diff line change
@@ -1,8 +1,14 @@
package com.gamegoo.repository.notification;

import com.gamegoo.domain.member.Member;
import com.gamegoo.domain.notification.Notification;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;

public interface NotificationRepository extends JpaRepository<Notification, Long> {
public interface NotificationRepository extends JpaRepository<Notification, Long>,
NotificationRepositoryCustom {

Page<Notification> findNotificationsByMember(Member member, Pageable pageable);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.gamegoo.repository.notification;

import com.gamegoo.domain.notification.Notification;
import org.springframework.data.domain.Slice;

public interface NotificationRepositoryCustom {

Slice<Notification> findNotificationsByCursorAndType(Long memberId, String type, Long cursor);
}
Loading
Loading