Skip to content

Commit 0f8565b

Browse files
authored
Merge pull request #89 from TaskFlow-CLAP/clap-64
CLAP-64 알림 목록 조회 API 구현
2 parents b30159b + 83ce274 commit 0f8565b

File tree

10 files changed

+246
-3
lines changed

10 files changed

+246
-3
lines changed
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package clap.server.adapter.inbound.web.dto.notification;
2+
3+
4+
import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType;
5+
import io.swagger.v3.oas.annotations.media.Schema;
6+
7+
import java.time.LocalDateTime;
8+
9+
public record FindNotificationListResponse(
10+
@Schema(description = "알림 고유 ID", example = "1")
11+
Long notificationId,
12+
@Schema(description = "알림에 해당하는 작업 고유 ID", example = "1")
13+
Long taskId,
14+
@Schema(description = "알림 유형", example = "COMMENT or TASK_REQUESTED or STATUS_SWITCHED or " +
15+
"PROCESSOR_ASSIGNED or PROCESSOR_CHANGED")
16+
NotificationType notificationType,
17+
@Schema(description = "알림 받는 회원 고유 ID", example = "1")
18+
Long receiverId,
19+
@Schema(description = "알림 제목", example = "VM 생성해주세요")
20+
String taskTitle,
21+
@Schema(description = "알림 내용", example = "진행 중 or 담당자 이름 등등")
22+
String message,
23+
@Schema(description = "알림 생성 시간", example = "2025-01-24 14:58")
24+
LocalDateTime createdAt
25+
) {
26+
}
Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
package clap.server.adapter.inbound.web.notification;
2+
3+
import clap.server.adapter.inbound.security.SecurityUserDetails;
4+
import clap.server.adapter.inbound.web.dto.notification.FindNotificationListResponse;
5+
import clap.server.application.port.inbound.notification.FindNotificationListUsecase;
6+
import clap.server.common.annotation.architecture.WebAdapter;
7+
import io.swagger.v3.oas.annotations.Operation;
8+
import io.swagger.v3.oas.annotations.Parameter;
9+
import io.swagger.v3.oas.annotations.Parameters;
10+
import io.swagger.v3.oas.annotations.tags.Tag;
11+
import lombok.RequiredArgsConstructor;
12+
import org.springframework.data.domain.Page;
13+
import org.springframework.data.domain.PageRequest;
14+
import org.springframework.data.domain.Pageable;
15+
import org.springframework.http.ResponseEntity;
16+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
17+
import org.springframework.web.bind.annotation.GetMapping;
18+
import org.springframework.web.bind.annotation.RequestMapping;
19+
import org.springframework.web.bind.annotation.RequestParam;
20+
import org.springframework.web.bind.annotation.RestController;
21+
22+
@Tag(name = "알림 관리 - 조회")
23+
@WebAdapter
24+
@RestController
25+
@RequestMapping("/api/notifications")
26+
@RequiredArgsConstructor
27+
public class FindNotificationController {
28+
29+
private final FindNotificationListUsecase findNotificationListUsecase;
30+
31+
@Operation(summary = "알림 목록 조회 API")
32+
@Parameters({
33+
@Parameter(name = "page", description = "조회할 목록 페이지 번호(0부터 시작)", example = "0", required = false),
34+
@Parameter(name = "size", description = "조회할 목록 페이지 당 개수", example = "5", required = false)
35+
})
36+
@GetMapping
37+
public ResponseEntity<Page<FindNotificationListResponse>> findNotificationList(
38+
@AuthenticationPrincipal SecurityUserDetails securityUserDetails,
39+
@RequestParam(defaultValue = "0") int page,
40+
@RequestParam(defaultValue = "5") int size) {
41+
Pageable pageable = PageRequest.of(page, size);
42+
return ResponseEntity.ok(findNotificationListUsecase.findNotificationList(securityUserDetails.getUserId(), pageable));
43+
}
44+
}
Lines changed: 28 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,28 @@
1+
package clap.server.adapter.outbound.persistense;
2+
3+
import clap.server.adapter.inbound.web.dto.notification.FindNotificationListResponse;
4+
import clap.server.adapter.outbound.persistense.mapper.NotificationPersistenceMapper;
5+
import clap.server.adapter.outbound.persistense.repository.notification.NotificationRepository;
6+
import clap.server.application.mapper.NotificationMapper;
7+
import clap.server.application.port.outbound.notification.LoadNotificationPort;
8+
import clap.server.common.annotation.architecture.PersistenceAdapter;
9+
import clap.server.domain.model.notification.Notification;
10+
import lombok.RequiredArgsConstructor;
11+
import org.springframework.data.domain.Page;
12+
import org.springframework.data.domain.Pageable;
13+
14+
@PersistenceAdapter
15+
@RequiredArgsConstructor
16+
public class NotificationPersistenceAdapter implements LoadNotificationPort {
17+
18+
private final NotificationRepository notificationRepository;
19+
private final NotificationPersistenceMapper notificationPersistenceMapper;
20+
21+
22+
@Override
23+
public Page<FindNotificationListResponse> findAllByReceiverId(Long receiverId, Pageable pageable) {
24+
Page<Notification> notificationList = notificationRepository.findAllByReceiver_MemberId(receiverId, pageable)
25+
.map(notificationPersistenceMapper::toDomain);
26+
return notificationList.map(NotificationMapper::toFindNoticeListResponse);
27+
}
28+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package clap.server.adapter.outbound.persistense.mapper;
2+
3+
import clap.server.adapter.outbound.persistense.entity.notification.NotificationEntity;
4+
import clap.server.adapter.outbound.persistense.mapper.common.PersistenceMapper;
5+
import clap.server.domain.model.notification.Notification;
6+
import org.mapstruct.Mapper;
7+
import org.mapstruct.Mapping;
8+
9+
@Mapper(componentModel = "spring", uses = {TaskPersistenceMapper.class, MemberPersistenceMapper.class})
10+
public interface NotificationPersistenceMapper extends PersistenceMapper<NotificationEntity, Notification> {
11+
@Override
12+
@Mapping(source = "read", target = "isRead")
13+
Notification toDomain(final NotificationEntity entity);
14+
15+
@Override
16+
@Mapping(source = "read", target = "isRead")
17+
NotificationEntity toEntity(final Notification notification);
18+
}
Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,9 +1,14 @@
11
package clap.server.adapter.outbound.persistense.repository.notification;
22

33
import clap.server.adapter.outbound.persistense.entity.notification.NotificationEntity;
4+
import org.springframework.data.domain.Page;
5+
import org.springframework.data.domain.Pageable;
46
import org.springframework.data.jpa.repository.JpaRepository;
57
import org.springframework.stereotype.Repository;
68

9+
710
@Repository
811
public interface NotificationRepository extends JpaRepository<NotificationEntity, Long> {
12+
13+
Page<NotificationEntity> findAllByReceiver_MemberId(Long receiverId, Pageable pageable);
914
}
Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,20 @@
1+
package clap.server.application.mapper;
2+
3+
import clap.server.adapter.inbound.web.dto.notification.FindNotificationListResponse;
4+
import clap.server.domain.model.notification.Notification;
5+
6+
public class NotificationMapper {
7+
private NotificationMapper() {throw new IllegalArgumentException();}
8+
9+
public static FindNotificationListResponse toFindNoticeListResponse(Notification notification) {
10+
return new FindNotificationListResponse(
11+
notification.getNotificationId(),
12+
notification.getTask().getTaskId(),
13+
notification.getType(),
14+
notification.getReceiver().getMemberId(),
15+
notification.getTask().getTitle(),
16+
notification.getMessage() != null ? notification.getMessage() : null,
17+
notification.getCreatedAt()
18+
);
19+
}
20+
}
Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,9 @@
1+
package clap.server.application.port.inbound.notification;
2+
3+
import clap.server.adapter.inbound.web.dto.notification.FindNotificationListResponse;
4+
import org.springframework.data.domain.Page;
5+
import org.springframework.data.domain.Pageable;
6+
7+
public interface FindNotificationListUsecase {
8+
Page<FindNotificationListResponse> findNotificationList(Long receiverId, Pageable pageable);
9+
}
Lines changed: 5 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,10 @@
11
package clap.server.application.port.outbound.notification;
22

3-
import clap.server.domain.model.notification.Notification;
4-
import java.util.Optional;
3+
import clap.server.adapter.inbound.web.dto.notification.FindNotificationListResponse;
4+
import org.springframework.data.domain.Page;
5+
import org.springframework.data.domain.Pageable;
6+
57

68
public interface LoadNotificationPort {
7-
Optional<Notification> findById(Long id);
9+
Page<FindNotificationListResponse> findAllByReceiverId(Long receiverId, Pageable pageable);
810
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package clap.server.application.service.notification;
2+
3+
import clap.server.adapter.inbound.web.dto.notification.FindNotificationListResponse;
4+
import clap.server.application.port.inbound.notification.FindNotificationListUsecase;
5+
import clap.server.application.port.outbound.notification.LoadNotificationPort;
6+
import clap.server.common.annotation.architecture.ApplicationService;
7+
import lombok.RequiredArgsConstructor;
8+
import org.springframework.data.domain.Page;
9+
import org.springframework.data.domain.Pageable;
10+
import org.springframework.transaction.annotation.Transactional;
11+
12+
@ApplicationService
13+
@RequiredArgsConstructor
14+
@Transactional(readOnly = true)
15+
public class FindNotificationListService implements FindNotificationListUsecase {
16+
17+
private final LoadNotificationPort loadNotificationPort;
18+
19+
20+
@Override
21+
public Page<FindNotificationListResponse> findNotificationList(Long receiverId, Pageable pageable) {
22+
return loadNotificationPort.findAllByReceiverId(receiverId, pageable);
23+
}
24+
}
Lines changed: 67 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,67 @@
1+
package clap.server.notification;
2+
3+
import clap.server.adapter.inbound.web.dto.notification.FindNotificationListResponse;
4+
import clap.server.adapter.outbound.persistense.entity.notification.constant.NotificationType;
5+
import clap.server.application.port.outbound.notification.LoadNotificationPort;
6+
import clap.server.application.service.notification.FindNotificationListService;
7+
import org.junit.jupiter.api.Assertions;
8+
import org.junit.jupiter.api.Test;
9+
import org.junit.jupiter.api.extension.ExtendWith;
10+
import org.mockito.InjectMocks;
11+
import org.mockito.Mock;
12+
import org.mockito.junit.jupiter.MockitoExtension;
13+
import org.springframework.data.domain.Page;
14+
import org.springframework.data.domain.PageImpl;
15+
import org.springframework.data.domain.Pageable;
16+
17+
import java.time.LocalDateTime;
18+
import java.util.List;
19+
20+
import static org.junit.Assert.assertEquals;
21+
import static org.mockito.Mockito.when;
22+
23+
@ExtendWith(MockitoExtension.class)
24+
public class NotificationServiceTest {
25+
26+
@Mock
27+
private LoadNotificationPort loadNotificationPort;
28+
29+
@InjectMocks
30+
private FindNotificationListService findNotificationListService;
31+
32+
33+
@Test
34+
public void testFindNotificationList() {
35+
//Given
36+
// 목록 조회 테스트이므로 여러개의 데이터를 List에 저장
37+
FindNotificationListResponse findNotificationListResponse = new FindNotificationListResponse(
38+
1L, 1L, NotificationType.PROCESSOR_ASSIGNED, 1L, "VM 생성해주세요", "이규동", LocalDateTime.now()
39+
);
40+
FindNotificationListResponse findNotificationListResponse2 = new FindNotificationListResponse(
41+
1L, 1L, NotificationType.PROCESSOR_CHANGED, 1L, "VM 생성해주세요", "이규동", LocalDateTime.now()
42+
);
43+
FindNotificationListResponse findNotificationListResponse3 = new FindNotificationListResponse(
44+
1L, 1L, NotificationType.STATUS_SWITCHED, 1L, "VM 생성해주세요", "진행중", LocalDateTime.now()
45+
);
46+
47+
List<FindNotificationListResponse> notificationList = List.of(
48+
findNotificationListResponse, findNotificationListResponse2, findNotificationListResponse3
49+
);
50+
51+
Page<FindNotificationListResponse> page = new PageImpl<>(notificationList);
52+
Pageable pageable = Pageable.ofSize(3);
53+
54+
//Mock
55+
when(loadNotificationPort.findAllByReceiverId(1L, pageable)).thenReturn(page);
56+
57+
//When
58+
Page<FindNotificationListResponse> result = findNotificationListService.findNotificationList(1L, pageable);
59+
60+
//Then
61+
Assertions.assertEquals(3, result.getContent().size());
62+
63+
Assertions.assertEquals("VM 생성해주세요", result.getContent().get(0).taskTitle());
64+
Assertions.assertEquals("VM 생성해주세요", result.getContent().get(1).taskTitle());
65+
Assertions.assertEquals("VM 생성해주세요", result.getContent().get(2).taskTitle());
66+
}
67+
}

0 commit comments

Comments
 (0)