Skip to content
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
2 changes: 2 additions & 0 deletions src/main/java/com/mos/backend/BackendApplication.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,8 +2,10 @@

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;
import org.springframework.data.web.config.EnableSpringDataWebSupport;

@SpringBootApplication
@EnableSpringDataWebSupport(pageSerializationMode = EnableSpringDataWebSupport.PageSerializationMode.VIA_DTO)
public class BackendApplication {

public static void main(String[] args) {
Expand Down
1 change: 1 addition & 0 deletions src/main/java/com/mos/backend/common/config/WebConfig.java
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.mos.backend.common.argumentresolvers.pageable.CustomPageableArgumentResolver;
import org.springframework.context.annotation.Configuration;
import org.springframework.data.web.config.EnableSpringDataWebSupport;
import org.springframework.web.method.support.HandlerMethodArgumentResolver;
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,20 @@

import com.mos.backend.common.event.EventType;
import com.mos.backend.common.infrastructure.EntityFacade;
import com.mos.backend.notifications.application.dto.NotificationListResponseDto;
import com.mos.backend.notifications.application.dto.NotificationResponseDto;
import com.mos.backend.notifications.application.dto.NotificationUnreadCountDto;
import com.mos.backend.notifications.entity.NotificationLog;
import com.mos.backend.notifications.entity.NotificationReadStatus;
import com.mos.backend.notifications.infrastructure.notificationlog.NotificationLogRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.security.access.prepost.PostAuthorize;
import org.springframework.security.access.prepost.PreAuthorize;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
Expand All @@ -23,9 +29,48 @@ public void create (Long recipientId, EventType eventType, String title, String
notificationLogRepository.save(NotificationLog.create(entityFacade.getUser(recipientId), eventType, title, content));
}

/**
* 알림 상세 조회하며 읽음 체크
* @param notificationLogId 읽을 알림의 아이디
*/
@Transactional
public void read(Long notificationLogId) {
@PostAuthorize("#returnObject.recipientId == authentication.principal")
public NotificationResponseDto read(Long notificationLogId) {
NotificationLog notificationLog = entityFacade.getNotificationLog(notificationLogId);
notificationLog.read();
return new NotificationResponseDto(notificationLog);
}

/**
* 사용자가 읽지 않은 알림 수 조회
* @param userId 읽지 않은 알림의 수를 조회할 사용자 아이디
*/
@Transactional
@PreAuthorize("hasRole('ADMIN') or authentication.principal == #userId")
public NotificationUnreadCountDto getUnreadCount(Long userId) {
Integer unreadCount = notificationLogRepository.getUnreadCount(userId);
return new NotificationUnreadCountDto(unreadCount);
}

/**
* 전체 알림 조회
* @param pageable
* @param userId 조회할 유저 아이디
* @param status 읽음 상태 필터링
*/
@Transactional
@PreAuthorize("hasRole('ADMIN') or authentication.principal == #userId")
public NotificationListResponseDto getNotifications(
Pageable pageable,
Long userId,
NotificationReadStatus status
) {
Page<NotificationResponseDto> notifications = notificationLogRepository.getNotifications(pageable, userId, status);
return new NotificationListResponseDto(
notifications.getTotalElements(),
notifications.getNumber(),
notifications.getTotalPages(),
notifications.getContent()
);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package com.mos.backend.notifications.application.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.util.List;

@Getter
@AllArgsConstructor
public class NotificationListResponseDto {
private long totalNotifications;
private int currentPage;
private int totalPages;
private List<NotificationResponseDto> notifications;
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,45 @@
package com.mos.backend.notifications.application.dto;

import com.mos.backend.common.event.EventType;
import com.mos.backend.notifications.entity.NotificationLog;
import lombok.Getter;
import lombok.NoArgsConstructor;

import java.time.LocalDateTime;

@Getter
@NoArgsConstructor
public class NotificationResponseDto {
private Long notificationId;

private Long recipientId;

private EventType type;

private String title;

private String content;

private boolean isRead;
private LocalDateTime createdAt;

public NotificationResponseDto(Long notificationId, Long recipientId, EventType type, String title, String content, boolean isRead, LocalDateTime createdAt) {
this.notificationId = notificationId;
this.recipientId = recipientId;
this.type = type;
this.title = title;
this.content = content;
this.isRead = isRead;
this.createdAt = createdAt;
}

public NotificationResponseDto(NotificationLog notificationLog) {
this.notificationId = notificationLog.getId();
this.recipientId = notificationLog.getRecipient().getId();
this.type = notificationLog.getType();
this.title = notificationLog.getTitle();
this.content = notificationLog.getContent();
this.isRead = notificationLog.isRead();
this.createdAt = notificationLog.getCreatedAt();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.mos.backend.notifications.application.dto;

import lombok.AllArgsConstructor;
import lombok.Getter;

@Getter
@AllArgsConstructor
public class NotificationUnreadCountDto {
private Integer unreadCount;
}
Original file line number Diff line number Diff line change
Expand Up @@ -39,7 +39,7 @@ public class NotificationLog extends BaseTimeEntity {
@Column(nullable = true)
private String content;

@Column
@Column(nullable = false, name = "is_read")
private boolean isRead = false;

public static NotificationLog create(User recipient, EventType type,String title, String content) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
package com.mos.backend.notifications.entity;

public enum NotificationReadStatus {
READ, UNREAD, ALL
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,4 +4,6 @@
import org.springframework.data.jpa.repository.JpaRepository;

public interface NotificationLogJpaRepository extends JpaRepository<NotificationLog, Long>{

Integer countNotificationLogByRecipientIdAndIsReadIsFalse(Long userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,70 @@
package com.mos.backend.notifications.infrastructure.notificationlog;

import com.mos.backend.common.utils.QueryDslSortUtil;
import com.mos.backend.notifications.application.dto.NotificationResponseDto;
import com.mos.backend.notifications.entity.NotificationLog;
import com.mos.backend.notifications.entity.NotificationReadStatus;
import com.querydsl.core.types.OrderSpecifier;
import com.querydsl.core.types.Projections;
import com.querydsl.core.types.dsl.BooleanExpression;
import com.querydsl.jpa.JPQLQuery;
import com.querydsl.jpa.impl.JPAQueryFactory;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.support.PageableExecutionUtils;
import org.springframework.stereotype.Repository;

import java.util.List;

import static com.mos.backend.notifications.entity.QNotificationLog.notificationLog;

@Repository
@RequiredArgsConstructor
public class NotificationLogQueryDslRepository {
private final JPAQueryFactory queryFactory;

public Page<NotificationResponseDto> getNotificationLogs(Pageable pageable, Long userId, NotificationReadStatus readStatus) {
List<NotificationResponseDto> content = queryFactory
.select(Projections.constructor(NotificationResponseDto.class,
notificationLog.id,
notificationLog.recipient.id,
notificationLog.type,
notificationLog.title,
notificationLog.content,
notificationLog.isRead,
notificationLog.createdAt
))
.from(notificationLog)
.where(
notificationLog.recipient.id.eq(userId),
readStatusEq(readStatus)
)
.offset(pageable.getOffset())
.limit(pageable.getPageSize())
.orderBy(getOrderSpecifiers(pageable))
.fetch();

JPQLQuery<NotificationLog> countQuery = queryFactory
.select(notificationLog)
.from(notificationLog)
.where(
notificationLog.recipient.id.eq(userId),
readStatusEq(readStatus)
);
return PageableExecutionUtils.getPage(content, pageable, countQuery::fetchCount);
}


private BooleanExpression readStatusEq(NotificationReadStatus readStatus) {
return switch (readStatus) {
case NotificationReadStatus.READ -> notificationLog.isRead.isTrue();
case NotificationReadStatus.UNREAD -> notificationLog.isRead.isFalse();
default -> null;
};
}

private static OrderSpecifier[] getOrderSpecifiers(Pageable pageable) {
return QueryDslSortUtil.toOrderSpecifiers(pageable.getSort(), NotificationLog.class).toArray(new OrderSpecifier[0]);
}
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,19 @@
package com.mos.backend.notifications.infrastructure.notificationlog;

import com.mos.backend.notifications.application.dto.NotificationResponseDto;
import com.mos.backend.notifications.entity.NotificationLog;
import com.mos.backend.notifications.entity.NotificationReadStatus;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;

import java.util.Optional;

public interface NotificationLogRepository {
void save(NotificationLog notificationLog);

Optional<NotificationLog> findById(Long notificationId);

Integer getUnreadCount(Long userId);

Page<NotificationResponseDto> getNotifications(Pageable pageable, Long userId, NotificationReadStatus readStatus);
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,11 @@
package com.mos.backend.notifications.infrastructure.notificationlog;

import com.mos.backend.notifications.application.dto.NotificationResponseDto;
import com.mos.backend.notifications.entity.NotificationLog;
import com.mos.backend.notifications.entity.NotificationReadStatus;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.stereotype.Repository;

import java.util.Optional;
Expand All @@ -10,6 +14,7 @@
@Repository
public class NotificationLogRepositoryImpl implements NotificationLogRepository{
private final NotificationLogJpaRepository notificationLogJpaRepository;
private final NotificationLogQueryDslRepository notificationLogQueryDslRepository;

@Override
public void save(NotificationLog notificationLog) {
Expand All @@ -20,4 +25,14 @@ public void save(NotificationLog notificationLog) {
public Optional<NotificationLog> findById(Long notificationId) {
return notificationLogJpaRepository.findById(notificationId);
}

@Override
public Integer getUnreadCount(Long userId) {
return notificationLogJpaRepository.countNotificationLogByRecipientIdAndIsReadIsFalse(userId);
}

@Override
public Page<NotificationResponseDto> getNotifications(Pageable pageable, Long userId, NotificationReadStatus readStatus) {
return notificationLogQueryDslRepository.getNotificationLogs(pageable, userId, readStatus);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.mos.backend.notifications.presentation.controller.api;

import com.mos.backend.notifications.application.NotificationLogService;
import com.mos.backend.notifications.application.dto.NotificationListResponseDto;
import com.mos.backend.notifications.application.dto.NotificationResponseDto;
import com.mos.backend.notifications.application.dto.NotificationUnreadCountDto;
import com.mos.backend.notifications.entity.NotificationReadStatus;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.http.HttpStatus;
import org.springframework.security.core.annotation.AuthenticationPrincipal;
import org.springframework.web.bind.annotation.*;

@RequiredArgsConstructor
@RestController
public class NotificationLogController {
private final NotificationLogService notificationLogService;

/**
* 전체 알림 조회
* @param pageable
* @param userId 조회할 유저 아이디
* @param readStatus 읽음 상태 필터링
*/
@GetMapping("/notifications")
@ResponseStatus(HttpStatus.OK)
public NotificationListResponseDto getNotifications(
@AuthenticationPrincipal Long userId,
@RequestParam(required = false, defaultValue = "ALL") NotificationReadStatus readStatus,
Pageable pageable
) {
return notificationLogService.getNotifications(pageable, userId, readStatus);
}

/**
* 알림 상세 조회하며 읽음 체크
* @param notificationId 읽을 알림의 아이디
*/
@PostMapping("/notifications/{notificationId}")
@ResponseStatus(HttpStatus.OK)
public NotificationResponseDto read(
@PathVariable Long notificationId
) {
return notificationLogService.read(notificationId);
}

/**
* 사용자가 읽지 않은 알림 수 조회
* @param userId
*/
@GetMapping("/notifications/unread")
public NotificationUnreadCountDto getUnreadCount(
@AuthenticationPrincipal Long userId
) {
return notificationLogService.getUnreadCount(userId);
}
}
Loading