-
Notifications
You must be signed in to change notification settings - Fork 0
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Feat: 알림 조회 no offset 페이징 API 구현 (#91)
- Loading branch information
1 parent
05b50db
commit 2a3d488
Showing
7 changed files
with
195 additions
and
1 deletion.
There are no files selected for viewing
43 changes: 43 additions & 0 deletions
43
...ain/java/com/numberone/backend/domain/notification/controller/NotificationController.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,43 @@ | ||
package com.numberone.backend.domain.notification.controller; | ||
|
||
import com.numberone.backend.domain.notification.dto.request.NotificationSearchParameter; | ||
import com.numberone.backend.domain.notification.dto.response.NotificationTabResponse; | ||
import com.numberone.backend.domain.notification.service.NotificationService; | ||
import io.swagger.v3.oas.annotations.Operation; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.domain.Slice; | ||
import org.springframework.http.ResponseEntity; | ||
import org.springframework.web.bind.annotation.GetMapping; | ||
import org.springframework.web.bind.annotation.ModelAttribute; | ||
import org.springframework.web.bind.annotation.RequestMapping; | ||
import org.springframework.web.bind.annotation.RestController; | ||
|
||
import java.util.List; | ||
|
||
@Slf4j | ||
@RequestMapping("/api/notifications") | ||
@RestController | ||
@RequiredArgsConstructor | ||
public class NotificationController { | ||
private final NotificationService notificationService; | ||
|
||
@Operation(summary = "알림 리스트 조회 no offset Paging API 입니다.", description = """ | ||
요청 예시 url 은 아래와 같습니다. | ||
'http://localhost:8080/api/notifications?page=0&size=10` | ||
size 는 페이지의 사이즈를 의미합니다. | ||
정렬 순서는 생성 시간 순(빠른 순)입니다. | ||
lastNotificationId 은 가장 직전에 조회한 페이지에서, 가장 마지막(작은) NotificationId 를 의미합니다. | ||
- 첫 페이지를 요청하는 경우에는 null 로 보내야 합니다. | ||
- 첫 페이지 이후에 대한 요청은, 직전 페이지에서 얻어온 페이지 리스트 중 가장 작은 NotificationId 를 담아서 보내면 다음 페이지가 요청됩니다. | ||
""") | ||
@GetMapping | ||
public ResponseEntity<Slice<NotificationTabResponse>> getNotificationPage( | ||
Pageable pageable, | ||
@ModelAttribute NotificationSearchParameter param){ | ||
return ResponseEntity.ok(notificationService.getNotificationTabPagesByMember(param, pageable)); | ||
} | ||
|
||
} |
12 changes: 12 additions & 0 deletions
12
...va/com/numberone/backend/domain/notification/dto/request/NotificationSearchParameter.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,12 @@ | ||
package com.numberone.backend.domain.notification.dto.request; | ||
|
||
import lombok.*; | ||
|
||
@ToString | ||
@Builder | ||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@AllArgsConstructor | ||
public class NotificationSearchParameter { | ||
Long lastNotificationId; | ||
} |
38 changes: 38 additions & 0 deletions
38
.../java/com/numberone/backend/domain/notification/dto/response/NotificationTabResponse.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,38 @@ | ||
package com.numberone.backend.domain.notification.dto.response; | ||
|
||
import com.fasterxml.jackson.annotation.JsonFormat; | ||
import com.numberone.backend.domain.notification.entity.NotificationEntity; | ||
import com.numberone.backend.domain.notification.entity.NotificationTag; | ||
import com.querydsl.core.annotations.QueryProjection; | ||
import lombok.*; | ||
|
||
import java.time.LocalDateTime; | ||
|
||
@ToString | ||
@Builder | ||
@Getter | ||
@NoArgsConstructor(access = AccessLevel.PROTECTED) | ||
@AllArgsConstructor | ||
public class NotificationTabResponse { | ||
private Long id; | ||
private String tag; | ||
@JsonFormat(shape = JsonFormat.Shape.STRING, pattern = "yyyy-MM-dd HH:mm", timezone = "Asia/Seoul") | ||
private LocalDateTime createdAt; | ||
private String title; | ||
private String body; | ||
|
||
|
||
@QueryProjection | ||
public NotificationTabResponse(NotificationEntity notification) { | ||
this.id = notification.getId(); | ||
this.tag = switch (notification.getNotificationTag()) { | ||
case FAMILY -> "가족"; | ||
case SUPPORT -> "후원"; | ||
case COMMUNITY -> "커뮤니티"; | ||
}; | ||
this.createdAt = notification.getCreatedAt(); | ||
this.title = notification.getTitle(); | ||
this.body = notification.getBody(); | ||
} | ||
|
||
} |
3 changes: 2 additions & 1 deletion
3
...ain/java/com/numberone/backend/domain/notification/repository/NotificationRepository.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -1,7 +1,8 @@ | ||
package com.numberone.backend.domain.notification.repository; | ||
|
||
import com.numberone.backend.domain.notification.entity.NotificationEntity; | ||
import com.numberone.backend.domain.notification.repository.custom.NotificationRepositoryCustom; | ||
import org.springframework.data.jpa.repository.JpaRepository; | ||
|
||
public interface NotificationRepository extends JpaRepository<NotificationEntity, Long> { | ||
public interface NotificationRepository extends JpaRepository<NotificationEntity, Long>, NotificationRepositoryCustom { | ||
} |
10 changes: 10 additions & 0 deletions
10
...numberone/backend/domain/notification/repository/custom/NotificationRepositoryCustom.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,10 @@ | ||
package com.numberone.backend.domain.notification.repository.custom; | ||
|
||
import com.numberone.backend.domain.notification.dto.request.NotificationSearchParameter; | ||
import com.numberone.backend.domain.notification.dto.response.NotificationTabResponse; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.domain.Slice; | ||
|
||
public interface NotificationRepositoryCustom { | ||
Slice<NotificationTabResponse> getNotificationTabPagesNoOffSetByMember(NotificationSearchParameter param, Long memberId, Pageable pageable); | ||
} |
58 changes: 58 additions & 0 deletions
58
...erone/backend/domain/notification/repository/custom/NotificationRepositoryCustomImpl.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,58 @@ | ||
package com.numberone.backend.domain.notification.repository.custom; | ||
|
||
import com.numberone.backend.domain.notification.dto.request.NotificationSearchParameter; | ||
import com.numberone.backend.domain.notification.dto.response.NotificationTabResponse; | ||
import com.numberone.backend.domain.notification.dto.response.QNotificationTabResponse; | ||
import com.querydsl.core.types.dsl.BooleanExpression; | ||
import com.querydsl.jpa.impl.JPAQueryFactory; | ||
import jakarta.persistence.EntityManager; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.domain.Slice; | ||
import org.springframework.data.domain.SliceImpl; | ||
|
||
import java.util.List; | ||
import java.util.Objects; | ||
|
||
import static com.numberone.backend.domain.member.entity.QMember.member; | ||
import static com.numberone.backend.domain.notification.entity.QNotificationEntity.notificationEntity; | ||
|
||
public class NotificationRepositoryCustomImpl implements NotificationRepositoryCustom { | ||
private final JPAQueryFactory queryFactory; | ||
|
||
public NotificationRepositoryCustomImpl(EntityManager em) { | ||
this.queryFactory = new JPAQueryFactory(em); | ||
} | ||
|
||
@Override | ||
public Slice<NotificationTabResponse> getNotificationTabPagesNoOffSetByMember(NotificationSearchParameter param, Long memberId, Pageable pageable) { | ||
List<NotificationTabResponse> result = queryFactory.select(new QNotificationTabResponse(notificationEntity)) | ||
.from(notificationEntity) | ||
.innerJoin(member) | ||
.on(notificationEntity.receivedMemberId.eq(member.id)) | ||
.where( | ||
ltNotificationEntityId(param.getLastNotificationId()), | ||
member.id.eq(memberId) | ||
) | ||
.orderBy(notificationEntity.id.desc()) | ||
.limit(pageable.getPageSize() + 1) | ||
.fetch(); | ||
return checkLastPage(pageable, result); | ||
} | ||
|
||
private BooleanExpression ltNotificationEntityId(Long notificationId) { | ||
if (Objects.isNull(notificationId)) { | ||
return null; | ||
} | ||
return notificationEntity.id.lt(notificationId); | ||
} | ||
|
||
private Slice<NotificationTabResponse> checkLastPage(Pageable pageable, List<NotificationTabResponse> result) { | ||
boolean hasNext = false; | ||
|
||
if (result.size() > pageable.getPageSize()) { | ||
hasNext = true; | ||
result.remove(pageable.getPageSize()); | ||
} | ||
return new SliceImpl<>(result, pageable, hasNext); | ||
} | ||
} |
32 changes: 32 additions & 0 deletions
32
src/main/java/com/numberone/backend/domain/notification/service/NotificationService.java
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,32 @@ | ||
package com.numberone.backend.domain.notification.service; | ||
|
||
import com.numberone.backend.domain.member.entity.Member; | ||
import com.numberone.backend.domain.member.repository.MemberRepository; | ||
import com.numberone.backend.domain.notification.dto.request.NotificationSearchParameter; | ||
import com.numberone.backend.domain.notification.dto.response.NotificationTabResponse; | ||
import com.numberone.backend.domain.notification.repository.NotificationRepository; | ||
import com.numberone.backend.domain.token.util.SecurityContextProvider; | ||
import com.numberone.backend.exception.notfound.NotFoundMemberException; | ||
import lombok.RequiredArgsConstructor; | ||
import lombok.extern.slf4j.Slf4j; | ||
import org.springframework.data.domain.Pageable; | ||
import org.springframework.data.domain.Slice; | ||
import org.springframework.stereotype.Service; | ||
import org.springframework.transaction.annotation.Transactional; | ||
|
||
@Slf4j | ||
@Service | ||
@RequiredArgsConstructor | ||
@Transactional(readOnly = true) | ||
public class NotificationService { | ||
private final MemberRepository memberRepository; | ||
private final NotificationRepository notificationRepository; | ||
|
||
public Slice<NotificationTabResponse> getNotificationTabPagesByMember(NotificationSearchParameter param, Pageable pageable) { | ||
String principal = SecurityContextProvider.getAuthenticatedUserEmail(); | ||
Member member = memberRepository.findByEmail(principal) | ||
.orElseThrow(NotFoundMemberException::new); | ||
return notificationRepository.getNotificationTabPagesNoOffSetByMember(param, member.getId(), pageable); | ||
} | ||
|
||
} |