diff --git a/src/main/java/clap/server/adapter/inbound/web/dto/common/SliceResponse.java b/src/main/java/clap/server/adapter/inbound/web/dto/common/SliceResponse.java new file mode 100644 index 00000000..3cffcec0 --- /dev/null +++ b/src/main/java/clap/server/adapter/inbound/web/dto/common/SliceResponse.java @@ -0,0 +1,13 @@ +package clap.server.adapter.inbound.web.dto.common; + + +import java.util.List; + +public record SliceResponse ( + List content, + int currentPage, + int size, + boolean isFirst, + boolean isLast +) { +} diff --git a/src/main/java/clap/server/adapter/inbound/web/notification/FindNotificationController.java b/src/main/java/clap/server/adapter/inbound/web/notification/FindNotificationController.java index 0af8e4c3..245648ec 100644 --- a/src/main/java/clap/server/adapter/inbound/web/notification/FindNotificationController.java +++ b/src/main/java/clap/server/adapter/inbound/web/notification/FindNotificationController.java @@ -1,6 +1,7 @@ package clap.server.adapter.inbound.web.notification; import clap.server.adapter.inbound.security.SecurityUserDetails; +import clap.server.adapter.inbound.web.dto.common.SliceResponse; import clap.server.adapter.inbound.web.dto.notification.FindNotificationListResponse; import clap.server.application.port.inbound.notification.FindNotificationListUsecase; import clap.server.common.annotation.architecture.WebAdapter; @@ -9,7 +10,6 @@ import io.swagger.v3.oas.annotations.Parameters; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.http.ResponseEntity; @@ -34,7 +34,7 @@ public class FindNotificationController { @Parameter(name = "size", description = "조회할 목록 페이지 당 개수", example = "5", required = false) }) @GetMapping - public ResponseEntity> findNotificationList( + public ResponseEntity> findNotificationList( @AuthenticationPrincipal SecurityUserDetails securityUserDetails, @RequestParam(defaultValue = "0") int page, @RequestParam(defaultValue = "5") int size) { diff --git a/src/main/java/clap/server/adapter/inbound/web/notification/ManagementNotificationController.java b/src/main/java/clap/server/adapter/inbound/web/notification/ManagementNotificationController.java index d4d37dc1..7e41862c 100644 --- a/src/main/java/clap/server/adapter/inbound/web/notification/ManagementNotificationController.java +++ b/src/main/java/clap/server/adapter/inbound/web/notification/ManagementNotificationController.java @@ -1,5 +1,6 @@ package clap.server.adapter.inbound.web.notification; +import clap.server.adapter.inbound.security.SecurityUserDetails; import clap.server.application.port.inbound.notification.UpdateNotificationUsecase; import clap.server.common.annotation.architecture.WebAdapter; import io.swagger.v3.oas.annotations.Operation; @@ -7,6 +8,7 @@ import io.swagger.v3.oas.annotations.enums.ParameterIn; import io.swagger.v3.oas.annotations.tags.Tag; import lombok.RequiredArgsConstructor; +import org.springframework.security.core.annotation.AuthenticationPrincipal; import org.springframework.web.bind.annotation.PatchMapping; import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.RequestMapping; @@ -27,4 +29,10 @@ public class ManagementNotificationController { public void updateNotificationIsRead(@PathVariable Long notificationId) { updateNotificationUsecase.updateNotification(notificationId); } + + @Operation(summary = "알림 목록에서 전체 읽음 버튼을 눌렀을 때 전체 읽음 처리") + @PatchMapping + public void updateAllNotificationIsRead(@AuthenticationPrincipal SecurityUserDetails userInfo) { + updateNotificationUsecase.updateAllNotification(userInfo.getUserId()); + } } diff --git a/src/main/java/clap/server/adapter/outbound/persistense/NotificationPersistenceAdapter.java b/src/main/java/clap/server/adapter/outbound/persistense/NotificationPersistenceAdapter.java index 5e94aa6c..b7330bd0 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/NotificationPersistenceAdapter.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/NotificationPersistenceAdapter.java @@ -1,5 +1,6 @@ package clap.server.adapter.outbound.persistense; +import clap.server.adapter.inbound.web.dto.common.SliceResponse; import clap.server.adapter.inbound.web.dto.notification.FindNotificationListResponse; import clap.server.adapter.outbound.persistense.mapper.NotificationPersistenceMapper; import clap.server.adapter.outbound.persistense.repository.notification.NotificationRepository; @@ -9,11 +10,12 @@ import clap.server.common.annotation.architecture.PersistenceAdapter; import clap.server.domain.model.notification.Notification; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import java.util.List; import java.util.Optional; +import java.util.stream.Collectors; @PersistenceAdapter @RequiredArgsConstructor @@ -30,10 +32,21 @@ public Optional findById(Long notificationId) { } @Override - public Page findAllByReceiverId(Long receiverId, Pageable pageable) { - Page notificationList = notificationRepository.findAllByReceiver_MemberId(receiverId, pageable) + public SliceResponse findAllByReceiverId(Long receiverId, Pageable pageable) { + Slice notificationList = notificationRepository + .findAllByReceiver_MemberIdOrderByCreatedAtDesc(receiverId, pageable) .map(notificationPersistenceMapper::toDomain); - return notificationList.map(NotificationMapper::toFindNoticeListResponse); + + return NotificationMapper.toSliceOfFindNoticeListResponse( + notificationList.map(NotificationMapper::toFindNoticeListResponse) + ); + } + + @Override + public List findNotificationsByMemberId(Long memberId) { + return notificationRepository.findAllByReceiver_MemberId(memberId) + .stream().map(notificationPersistenceMapper::toDomain) + .collect(Collectors.toList()); } @Override diff --git a/src/main/java/clap/server/adapter/outbound/persistense/repository/notification/NotificationRepository.java b/src/main/java/clap/server/adapter/outbound/persistense/repository/notification/NotificationRepository.java index 68e99315..0239cd44 100644 --- a/src/main/java/clap/server/adapter/outbound/persistense/repository/notification/NotificationRepository.java +++ b/src/main/java/clap/server/adapter/outbound/persistense/repository/notification/NotificationRepository.java @@ -1,14 +1,18 @@ package clap.server.adapter.outbound.persistense.repository.notification; import clap.server.adapter.outbound.persistense.entity.notification.NotificationEntity; -import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; +import org.springframework.data.domain.Slice; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Repository; +import java.util.List; + @Repository public interface NotificationRepository extends JpaRepository { - Page findAllByReceiver_MemberId(Long receiverId, Pageable pageable); + Slice findAllByReceiver_MemberIdOrderByCreatedAtDesc(Long receiverId, Pageable pageable); + + List findAllByReceiver_MemberId(Long memberId); } \ No newline at end of file diff --git a/src/main/java/clap/server/application/mapper/NotificationMapper.java b/src/main/java/clap/server/application/mapper/NotificationMapper.java index fb793d2c..d53d71b4 100644 --- a/src/main/java/clap/server/application/mapper/NotificationMapper.java +++ b/src/main/java/clap/server/application/mapper/NotificationMapper.java @@ -1,7 +1,9 @@ package clap.server.application.mapper; +import clap.server.adapter.inbound.web.dto.common.SliceResponse; import clap.server.adapter.inbound.web.dto.notification.FindNotificationListResponse; import clap.server.domain.model.notification.Notification; +import org.springframework.data.domain.Slice; public class NotificationMapper { private NotificationMapper() {throw new IllegalArgumentException();} @@ -17,4 +19,14 @@ public static FindNotificationListResponse toFindNoticeListResponse(Notification notification.getCreatedAt() ); } + + public static SliceResponse toSliceOfFindNoticeListResponse(Slice slice) { + return new SliceResponse<>( + slice.getContent(), + slice.getNumber(), + slice.getSize(), + slice.isFirst(), + slice.isLast() + ); + } } diff --git a/src/main/java/clap/server/application/port/inbound/notification/FindNotificationListUsecase.java b/src/main/java/clap/server/application/port/inbound/notification/FindNotificationListUsecase.java index 26196d1b..4b6f96ac 100644 --- a/src/main/java/clap/server/application/port/inbound/notification/FindNotificationListUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/notification/FindNotificationListUsecase.java @@ -1,9 +1,9 @@ package clap.server.application.port.inbound.notification; +import clap.server.adapter.inbound.web.dto.common.SliceResponse; import clap.server.adapter.inbound.web.dto.notification.FindNotificationListResponse; -import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; public interface FindNotificationListUsecase { - Page findNotificationList(Long receiverId, Pageable pageable); + SliceResponse findNotificationList(Long receiverId, Pageable pageable); } diff --git a/src/main/java/clap/server/application/port/inbound/notification/UpdateNotificationUsecase.java b/src/main/java/clap/server/application/port/inbound/notification/UpdateNotificationUsecase.java index 91c124fe..858b2cb4 100644 --- a/src/main/java/clap/server/application/port/inbound/notification/UpdateNotificationUsecase.java +++ b/src/main/java/clap/server/application/port/inbound/notification/UpdateNotificationUsecase.java @@ -2,4 +2,6 @@ public interface UpdateNotificationUsecase { void updateNotification(Long notificationId); + + void updateAllNotification(Long memberId); } diff --git a/src/main/java/clap/server/application/port/outbound/notification/LoadNotificationPort.java b/src/main/java/clap/server/application/port/outbound/notification/LoadNotificationPort.java index 3704ee67..8954c40d 100644 --- a/src/main/java/clap/server/application/port/outbound/notification/LoadNotificationPort.java +++ b/src/main/java/clap/server/application/port/outbound/notification/LoadNotificationPort.java @@ -1,11 +1,9 @@ package clap.server.application.port.outbound.notification; +import clap.server.adapter.inbound.web.dto.common.SliceResponse; import clap.server.adapter.inbound.web.dto.notification.FindNotificationListResponse; -import clap.server.adapter.outbound.persistense.entity.notification.NotificationEntity; import clap.server.domain.model.notification.Notification; -import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; - import java.util.List; import java.util.Optional; @@ -14,5 +12,7 @@ public interface LoadNotificationPort { Optional findById(Long notificationId); - Page findAllByReceiverId(Long receiverId, Pageable pageable); + SliceResponse findAllByReceiverId(Long receiverId, Pageable pageable); + + List findNotificationsByMemberId(Long memberId); } \ No newline at end of file diff --git a/src/main/java/clap/server/application/service/notification/FindNotificationListService.java b/src/main/java/clap/server/application/service/notification/FindNotificationListService.java index 85902515..5b352e10 100644 --- a/src/main/java/clap/server/application/service/notification/FindNotificationListService.java +++ b/src/main/java/clap/server/application/service/notification/FindNotificationListService.java @@ -1,11 +1,11 @@ package clap.server.application.service.notification; +import clap.server.adapter.inbound.web.dto.common.SliceResponse; import clap.server.adapter.inbound.web.dto.notification.FindNotificationListResponse; import clap.server.application.port.inbound.notification.FindNotificationListUsecase; import clap.server.application.port.outbound.notification.LoadNotificationPort; import clap.server.common.annotation.architecture.ApplicationService; import lombok.RequiredArgsConstructor; -import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; import org.springframework.transaction.annotation.Transactional; @@ -18,7 +18,7 @@ public class FindNotificationListService implements FindNotificationListUsecase @Override - public Page findNotificationList(Long receiverId, Pageable pageable) { + public SliceResponse findNotificationList(Long receiverId, Pageable pageable) { return loadNotificationPort.findAllByReceiverId(receiverId, pageable); } } diff --git a/src/main/java/clap/server/application/service/notification/ReadNotificationService.java b/src/main/java/clap/server/application/service/notification/ReadNotificationService.java index ed4ff10b..80e27a7e 100644 --- a/src/main/java/clap/server/application/service/notification/ReadNotificationService.java +++ b/src/main/java/clap/server/application/service/notification/ReadNotificationService.java @@ -10,6 +10,8 @@ import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; +import java.util.List; + @ApplicationService @RequiredArgsConstructor public class ReadNotificationService implements UpdateNotificationUsecase { @@ -26,4 +28,14 @@ public void updateNotification(Long notificationId) { notification.updateNotificationIsRead(); commandNotificationPort.save(notification); } + + @Transactional + @Override + public void updateAllNotification(Long memberId) { + List notificationList = loadNotificationPort.findNotificationsByMemberId(memberId); + for (Notification notification : notificationList) { + notification.updateNotificationIsRead(); + commandNotificationPort.save(notification); + } + } } diff --git a/src/test/java/clap/server/notification/NotificationServiceTest.java b/src/test/java/clap/server/notification/NotificationServiceTest.java deleted file mode 100644 index ae79627b..00000000 --- a/src/test/java/clap/server/notification/NotificationServiceTest.java +++ /dev/null @@ -1,68 +0,0 @@ -package clap.server.notification; - -import clap.server.adapter.inbound.web.dto.notification.FindNotificationListResponse; -import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType; -import clap.server.application.port.outbound.notification.LoadNotificationPort; -import clap.server.application.service.notification.FindNotificationListService; -import org.junit.jupiter.api.Assertions; -import org.junit.jupiter.api.Test; -import org.junit.jupiter.api.extension.ExtendWith; -import org.mockito.InjectMocks; -import org.mockito.Mock; -import org.mockito.junit.jupiter.MockitoExtension; -import org.springframework.data.domain.Page; -import org.springframework.data.domain.PageImpl; -import org.springframework.data.domain.Pageable; -import org.testcontainers.junit.jupiter.Testcontainers; - -import java.time.LocalDateTime; -import java.util.List; - -import static org.mockito.Mockito.when; - -@Testcontainers -@ExtendWith(MockitoExtension.class) -public class NotificationServiceTest { - - @Mock - private LoadNotificationPort loadNotificationPort; - - @InjectMocks - private FindNotificationListService findNotificationListService; - - - @Test - public void testFindNotificationList() { - //Given - // 목록 조회 테스트이므로 여러개의 데이터를 List에 저장 - FindNotificationListResponse findNotificationListResponse = new FindNotificationListResponse( - 1L, 1L, NotificationType.PROCESSOR_ASSIGNED, 1L, "VM 생성해주세요", "이규동", LocalDateTime.now() - ); - FindNotificationListResponse findNotificationListResponse2 = new FindNotificationListResponse( - 1L, 1L, NotificationType.PROCESSOR_CHANGED, 1L, "VM 생성해주세요", "이규동", LocalDateTime.now() - ); - FindNotificationListResponse findNotificationListResponse3 = new FindNotificationListResponse( - 1L, 1L, NotificationType.STATUS_SWITCHED, 1L, "VM 생성해주세요", "진행중", LocalDateTime.now() - ); - - List notificationList = List.of( - findNotificationListResponse, findNotificationListResponse2, findNotificationListResponse3 - ); - - Page page = new PageImpl<>(notificationList); - Pageable pageable = Pageable.ofSize(3); - - //Mock - when(loadNotificationPort.findAllByReceiverId(1L, pageable)).thenReturn(page); - - //When - Page result = findNotificationListService.findNotificationList(1L, pageable); - - //Then - Assertions.assertEquals(3, result.getContent().size()); - - Assertions.assertEquals("VM 생성해주세요", result.getContent().get(0).taskTitle()); - Assertions.assertEquals("VM 생성해주세요", result.getContent().get(1).taskTitle()); - Assertions.assertEquals("VM 생성해주세요", result.getContent().get(2).taskTitle()); - } -}