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

Features/cursor pagination #68

Merged
merged 2 commits into from
Jan 16, 2025
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
Original file line number Diff line number Diff line change
Expand Up @@ -19,9 +19,9 @@ class PostCommentController(
@GetMapping
fun getComments(
@PathVariable postId: Long,
@RequestParam(required = false, defaultValue = "0") page: Int,
@RequestParam(required = false) cursorId: Long?,
@RequestParam(required = false, defaultValue = "10") size: Int
) = BaseResponse.of(postCommentService.getComments(postId, PageRequest.of(page, size)))
) = BaseResponse.of(postCommentService.getComments(postId, cursorId, size))

@Operation(summary = "κ²Œμ‹œκΈ€ λŒ“κΈ€ 생성")
@PostMapping
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,6 @@ import com.solve.domain.post.service.PostCommentReplyService
import com.solve.global.common.dto.BaseResponse
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.data.domain.PageRequest
import org.springframework.web.bind.annotation.DeleteMapping
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.PatchMapping
Expand All @@ -28,9 +27,9 @@ class PostCommentReplyController(
fun getReplies(
@PathVariable postId: Long,
@PathVariable commentId: Long,
@RequestParam(required = false, defaultValue = "0") page: Int,
@RequestParam(required = false) cursorId: Long?,
@RequestParam(required = false, defaultValue = "10") size: Int
) = BaseResponse.of(postCommentReplyService.getReplies(postId, commentId, PageRequest.of(page, size)))
) = BaseResponse.of(postCommentReplyService.getReplies(postId, commentId, cursorId, size))

@Operation(summary = "κ²Œμ‹œκΈ€ λŒ“κΈ€ λ‹΅κΈ€ 생성")
@PostMapping
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ import org.springframework.http.HttpStatus

enum class PostCommentError(override val status: HttpStatus, override val message: String) : CustomError {
POST_COMMENT_NOT_FOUND(HttpStatus.NOT_FOUND, "κ²Œμ‹œκΈ€ λŒ“κΈ€μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. (id: %s)"),
POST_COMMENT_NOT_AUTHORIZED(HttpStatus.FORBIDDEN, "κ²Œμ‹œκΈ€ λŒ“κΈ€μ— κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€. (id: %s)")
POST_COMMENT_NOT_AUTHORIZED(HttpStatus.FORBIDDEN, "κ²Œμ‹œκΈ€ λŒ“κΈ€μ— κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€. (id: %s)"),
POST_COMMENT_CURSOR_NOT_FOUND(HttpStatus.NOT_FOUND, "κ²Œμ‹œκΈ€ λŒ“κΈ€μ„ 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. (cursor: %s)"),
}
Original file line number Diff line number Diff line change
Expand Up @@ -5,5 +5,6 @@ import org.springframework.http.HttpStatus

enum class PostCommentReplyError(override val status: HttpStatus, override val message: String): CustomError {
POST_COMMENT_REPLY_NOT_FOUND(HttpStatus.NOT_FOUND, "λŒ“κΈ€ 닡글을 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. (id: %s)"),
POST_COMMENT_REPLY_NOT_AUTHORIZED(HttpStatus.FORBIDDEN, "λŒ“κΈ€ λ‹΅κΈ€ κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€.")
POST_COMMENT_REPLY_NOT_AUTHORIZED(HttpStatus.FORBIDDEN, "λŒ“κΈ€ λ‹΅κΈ€ κΆŒν•œμ΄ μ—†μŠ΅λ‹ˆλ‹€."),
POST_COMMENT_REPLY_CURSOR_NOT_FOUND(HttpStatus.NOT_FOUND, "λŒ“κΈ€ λ‹΅κΈ€ μ»€μ„œλ₯Ό 찾을 수 μ—†μŠ΅λ‹ˆλ‹€. (id: %s)")
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package com.solve.domain.post.repository

import com.solve.domain.post.domain.entity.Post
import com.solve.domain.post.domain.entity.PostComment
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable

interface PostCommentQueryRepository {
fun getComments(post: Post, pageable: Pageable): Page<PostComment>
fun getComments(post: Post, cursor: PostComment?, size: Int): List<PostComment>
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,7 @@ package com.solve.domain.post.repository
import com.solve.domain.post.domain.entity.Post
import com.solve.domain.post.domain.entity.PostComment
import com.solve.domain.post.domain.entity.PostCommentReply
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable

interface PostCommentReplyQueryRepository {
fun getReplies(post: Post, comment: PostComment, pageable: Pageable): Page<PostCommentReply>
fun getReplies(post: Post, comment: PostComment, cursor: PostCommentReply?, size: Int): List<PostCommentReply>
}
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package com.solve.domain.post.repository.impl

import com.querydsl.core.types.dsl.BooleanExpression
import com.querydsl.jpa.impl.JPAQueryFactory
import com.solve.domain.post.domain.entity.Post
import com.solve.domain.post.domain.entity.PostComment
import com.solve.domain.post.domain.entity.QPostComment
import com.solve.domain.post.repository.PostCommentQueryRepository
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Repository
import org.springframework.transaction.annotation.Transactional

Expand All @@ -18,21 +16,27 @@ class PostCommentQueryRepositoryImpl(
private val postComment = QPostComment.postComment

@Transactional(readOnly = true)
override fun getComments(post: Post, pageable: Pageable): Page<PostComment> {
val baseQuery = queryFactory
override fun getComments(
post: Post,
cursor: PostComment?,
size: Int
): List<PostComment> {
return queryFactory
.selectFrom(postComment)
.where(postComment.post.eq(post))
.offset(pageable.offset)
.limit(pageable.pageSize.toLong())
.orderBy(postComment._createdAt.desc())

val comments = baseQuery.fetch()
val totalCount = queryFactory
.select(postComment.count())
.from(postComment)
.where(postComment.post.eq(post))
.fetchOne() ?: 0L
.where(
postComment.post.eq(post),
cursorIdCondition(cursor)
)
.orderBy(postComment._createdAt.desc(), postComment.id.desc())
.limit(size.toLong())
.fetch() ?: emptyList()
}

return PageImpl(comments, pageable, totalCount)
private fun cursorIdCondition(cursor: PostComment?): BooleanExpression? {
return if (cursor != null) {
postComment.id.lt(cursor.id!!)
} else {
null
}
}
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,12 @@
package com.solve.domain.post.repository.impl

import com.querydsl.core.types.dsl.BooleanExpression
import com.querydsl.jpa.impl.JPAQueryFactory
import com.solve.domain.post.domain.entity.Post
import com.solve.domain.post.domain.entity.PostComment
import com.solve.domain.post.domain.entity.PostCommentReply
import com.solve.domain.post.domain.entity.QPostCommentReply
import com.solve.domain.post.repository.PostCommentReplyQueryRepository
import org.springframework.data.domain.Page
import org.springframework.data.domain.PageImpl
import org.springframework.data.domain.Pageable
import org.springframework.stereotype.Repository
import org.springframework.transaction.annotation.Transactional

Expand All @@ -19,27 +17,29 @@ class PostCommentReplyQueryRepositoryImpl(
private val reply = QPostCommentReply.postCommentReply

@Transactional(readOnly = true)
override fun getReplies(post: Post, comment: PostComment, pageable: Pageable): Page<PostCommentReply> {
val baseQuery = queryFactory
override fun getReplies(
post: Post,
comment: PostComment,
cursor: PostCommentReply?,
size: Int
): List<PostCommentReply> {
return queryFactory
.selectFrom(reply)
.where(
reply.post.eq(post),
reply.comment.eq(comment)
reply.comment.eq(comment),
cursorIdCondition(cursor)
)
.offset(pageable.offset)
.limit(pageable.pageSize.toLong())
.orderBy(reply._createdAt.asc())
.limit(size.toLong())
.fetch() ?: emptyList()
}

val replies = baseQuery.fetch()
val totalCount = queryFactory
.select(reply.count())
.from(reply)
.where(
reply.post.eq(post),
reply.comment.eq(comment)
)
.fetchOne() ?: 0L

return PageImpl(replies, pageable, totalCount)
private fun cursorIdCondition(cursor: PostCommentReply?): BooleanExpression? {
return if (cursor != null) {
reply.id.gt(cursor.id)
} else {
null
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,9 @@ package com.solve.domain.post.service
import com.solve.domain.post.dto.request.PostCommentReplyCreateRequest
import com.solve.domain.post.dto.request.PostCommentReplyUpdateRequest
import com.solve.domain.post.dto.response.PostCommentReplyResponse
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable

interface PostCommentReplyService {
fun getReplies(postId: Long, commentId: Long, pageable: Pageable): Page<PostCommentReplyResponse>
fun getReplies(postId: Long, commentId: Long, cursorId: Long?, size: Int): List<PostCommentReplyResponse>
fun createReply(postId: Long, commentId: Long, request: PostCommentReplyCreateRequest)
fun updateReply(postId: Long, commentId: Long, replyId: Long, request: PostCommentReplyUpdateRequest)
fun deleteReply(postId: Long, commentId: Long, replyId: Long)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,7 @@ import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable

interface PostCommentService {
fun getComments(postId: Long, pageable: Pageable): Page<PostCommentResponse>
fun getComments(postId: Long, cursorId: Long?, size: Int): List<PostCommentResponse>
fun createComment(postId: Long, request: PostCommentCreateRequest)
fun updateComment(postId: Long, commentId: Long, request: PostCommentUpdateRequest)
fun deleteComment(postId: Long, commentId: Long)
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -12,8 +12,6 @@ import com.solve.domain.post.repository.*
import com.solve.domain.post.service.PostCommentReplyService
import com.solve.global.error.CustomException
import com.solve.global.security.holder.SecurityHolder
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
Expand All @@ -28,10 +26,11 @@ class PostCommentReplyServiceImpl(
private val postCommentReplyQueryRepository: PostCommentReplyQueryRepository
) : PostCommentReplyService {
@Transactional(readOnly = true)
override fun getReplies(postId: Long, commentId: Long, pageable: Pageable): Page<PostCommentReplyResponse> {
override fun getReplies(postId: Long, commentId: Long, cursorId: Long?, size: Int): List<PostCommentReplyResponse> {
val post = postRepository.findByIdOrNull(postId) ?: throw CustomException(PostError.POST_NOT_FOUND)
val comment = postCommentRepository.findByPostAndId(post, commentId) ?: throw CustomException(PostCommentError.POST_COMMENT_NOT_FOUND)
val replies = postCommentReplyQueryRepository.getReplies(post, comment, pageable)
val cursor = cursorId?.let { postCommentReplyRepository.findByIdOrNull(it) ?: throw CustomException(PostCommentReplyError.POST_COMMENT_REPLY_CURSOR_NOT_FOUND) }
val replies = postCommentReplyQueryRepository.getReplies(post, comment, cursor, size)

return replies.map { it.toResponse() }
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -11,8 +11,6 @@ import com.solve.domain.post.repository.*
import com.solve.domain.post.service.PostCommentService
import com.solve.global.error.CustomException
import com.solve.global.security.holder.SecurityHolder
import org.springframework.data.domain.Page
import org.springframework.data.domain.Pageable
import org.springframework.data.repository.findByIdOrNull
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
Expand All @@ -27,9 +25,10 @@ class PostCommentServiceImpl(
private val postCommentReplyRepository: PostCommentReplyRepository,
) : PostCommentService {
@Transactional(readOnly = true)
override fun getComments(postId: Long, pageable: Pageable): Page<PostCommentResponse> {
override fun getComments(postId: Long, cursorId: Long?, size: Int): List<PostCommentResponse> {
val post = postRepository.findByIdOrNull(postId) ?: throw CustomException(PostError.POST_NOT_FOUND, postId)
val comments = postCommentQueryRepository.getComments(post, pageable)
val cursor = cursorId?.let { postCommentRepository.findByIdOrNull(it) ?: throw CustomException(PostCommentError.POST_COMMENT_CURSOR_NOT_FOUND, it) }
val comments = postCommentQueryRepository.getComments(post, cursor, size)

return comments.map { it.toResponse() }
}
Expand Down