Skip to content

Commit

Permalink
Merge pull request #96 from mash-up-kr/junhyoung/feat-picklist-paging…
Browse files Browse the repository at this point in the history
…-logic

feat: picklist paging logic
  • Loading branch information
toychip committed Aug 17, 2024
2 parents f955948 + 9c9bdba commit ad52aba
Show file tree
Hide file tree
Showing 8 changed files with 256 additions and 88 deletions.
3 changes: 1 addition & 2 deletions _endpoint_test/pick.http
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
### 내가 받은 픽 리스트 조회하기

GET {{host}}/pick/picked-list?sort=MOST_PICKED
GET {{host}}/pick/picked-list?pageSize=10&pageNumber=0&sort=MOST_PICKED

### 내가 받은 픽 중 특정 질문의 페이징 조회하기
GET {{host}}/pick/picked-detail?questionId=abc
Expand Down
87 changes: 59 additions & 28 deletions api/src/main/kotlin/com/mashup/dojo/PickController.kt
Original file line number Diff line number Diff line change
Expand Up @@ -8,15 +8,17 @@ import com.mashup.dojo.domain.PickOpenItem
import com.mashup.dojo.domain.PickSort
import com.mashup.dojo.domain.QuestionId
import com.mashup.dojo.dto.CreatePickRequest
import com.mashup.dojo.dto.PickDetailPaging
import com.mashup.dojo.dto.PickOpenItemDto
import com.mashup.dojo.dto.PickOpenRequest
import com.mashup.dojo.dto.PickOpenResponse
import com.mashup.dojo.dto.PickPaging
import com.mashup.dojo.dto.PickResponse
import com.mashup.dojo.dto.ReceivedPickDetail
import com.mashup.dojo.dto.ReceivedPickListGetResponse
import com.mashup.dojo.dto.ReceivedPickPagingGetResponse
import com.mashup.dojo.usecase.PickUseCase
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.Parameter
import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.tags.Tag
import jakarta.validation.Valid
Expand All @@ -37,27 +39,44 @@ class PickController(
) {
@GetMapping("/picked-list")
@Operation(
summary = "내가 받은 픽 List API",
description = "내가 받은 픽들을 정렬하여 보여주는 API. default sort : 최신 순",
summary = "내가 받은 픽 페이징 API",
description = "내가 받은 픽들을 페이징 처리 후 정렬하여 보여주는 API. default sort : 최신 순",
responses = [
ApiResponse(responseCode = "200", description = "내가 받은 픽 리스트")
ApiResponse(responseCode = "200", description = "내가 받은 픽 리스트 페이징")
]
)
fun getReceivedPickList(
// todo : add userinfo
@RequestParam(required = false, defaultValue = "LATEST") sort: PickSort,
): DojoApiResponse<ReceivedPickListGetResponse> {
@Parameter(
description = "정렬 기준. LATEST는 최근에 Pick된 항목을 기준으로 정렬하고, MOST_PICKED는 가장 많이 Pick된 항목을 기준으로 정렬합니다.",
schema = Schema(defaultValue = "LATEST")
)
@RequestParam(required = false, defaultValue = "LATEST") sort: String,
@Parameter(
description = "페이지 번호. 0부터 시작합니다.",
schema = Schema(defaultValue = "0")
)
@RequestParam(required = false, defaultValue = "0") pageNumber: Int,
@Parameter(
description = "페이지 크기. 한 페이지에 포함될 항목의 개수를 설정합니다.",
schema = Schema(defaultValue = "10")
)
@RequestParam(required = false, defaultValue = "10") pageSize: Int,
): DojoApiResponse<ReceivedPickPagingGetResponse> {
val currentMemberId = MemberPrincipalContextHolder.current().id
val receivedPickList: List<PickUseCase.GetReceivedPick> =
val validSort = PickSort.findByValue(sort)

val receivedPickList =
pickUseCase.getReceivedPickList(
PickUseCase.GetReceivedPickListCommand(
PickUseCase.GetReceivedPickPagingCommand(
memberId = currentMemberId,
sort = sort
sort = validSort,
pageNumber = pageNumber,
pageSize = pageSize
)
)

val pickResponseList =
receivedPickList.map {
receivedPickList.picks.map {
PickResponse(
pickId = it.pickId,
questionId = it.questionId,
Expand All @@ -67,7 +86,19 @@ class PickController(
latestPickedAt = it.latestPickedAt
)
}
return DojoApiResponse.success(ReceivedPickListGetResponse(pickResponseList, sort))

return DojoApiResponse.success(
ReceivedPickPagingGetResponse(
pickList = pickResponseList,
totalPage = receivedPickList.totalPage,
totalElements = receivedPickList.totalElements,
isFirst = receivedPickList.isFirst,
isLast = receivedPickList.isLast,
sort = validSort,
pageNumber = pageNumber,
pageSize = pageSize
)
)
}

@GetMapping("/picked-detail")
Expand All @@ -82,13 +113,13 @@ class PickController(
@RequestParam questionId: String,
@RequestParam(required = false, defaultValue = "0") pageNumber: Int,
@RequestParam(required = false, defaultValue = "10") pageSize: Int,
): DojoApiResponse<PickPaging> {
): DojoApiResponse<PickDetailPaging> {
val currentMemberId = MemberPrincipalContextHolder.current().id
val pickPaging: PickUseCase.GetPagingPick =
val pickDetailPaging: PickUseCase.GetPickDetailPaging =
pickUseCase.getReceivedPickDetailPaging(PickUseCase.GetPagingPickCommand(currentMemberId, QuestionId(questionId), pageNumber, pageSize))

val pickDetails =
pickPaging.picks.map {
pickDetailPaging.picks.map {
ReceivedPickDetail(
pickId = it.pickId,
pickerOrdinal = it.pickerOrdinal,
Expand All @@ -105,21 +136,21 @@ class PickController(
latestPickedAt = it.latestPickedAt
)
}
val pickPagingResponse =
PickPaging(
questionId = pickPaging.questionId,
questionContent = pickPaging.questionContent,
questionEmojiImageUrl = pickPaging.questionEmojiImageUrl,
totalReceivedPickCount = pickPaging.totalReceivedPickCount,
anyOpenPickerCount = pickPaging.anyOpenPickerCount,
val pickDetailPagingResponse =
PickDetailPaging(
questionId = pickDetailPaging.questionId,
questionContent = pickDetailPaging.questionContent,
questionEmojiImageUrl = pickDetailPaging.questionEmojiImageUrl,
totalReceivedPickCount = pickDetailPaging.totalReceivedPickCount,
anyOpenPickerCount = pickDetailPaging.anyOpenPickerCount,
picks = pickDetails,
totalPage = pickPaging.totalPage,
totalElements = pickPaging.totalElements,
isFirst = pickPaging.isFirst,
isLast = pickPaging.isLast
totalPage = pickDetailPaging.totalPage,
totalElements = pickDetailPaging.totalElements,
isFirst = pickDetailPaging.isFirst,
isLast = pickDetailPaging.isLast
)

return DojoApiResponse.success(pickPagingResponse)
return DojoApiResponse.success(pickDetailPagingResponse)
}

@PostMapping
Expand Down
10 changes: 8 additions & 2 deletions api/src/main/kotlin/com/mashup/dojo/dto/PickDto.kt
Original file line number Diff line number Diff line change
Expand Up @@ -26,9 +26,15 @@ data class CreatePickRequest(
val pickedId: MemberId,
)

data class ReceivedPickListGetResponse(
data class ReceivedPickPagingGetResponse(
val pickList: List<PickResponse>,
val totalPage: Int,
val totalElements: Long,
val isFirst: Boolean,
val isLast: Boolean,
val sort: PickSort,
val pageNumber: Int,
val pageSize: Int,
)

// todo : 질문의 유형(카테고리)도 전달해줘야 하는가
Expand All @@ -41,7 +47,7 @@ data class PickResponse(
val latestPickedAt: LocalDateTime,
)

data class PickPaging(
data class PickDetailPaging(
val questionId: QuestionId,
val questionContent: String,
val questionEmojiImageUrl: String,
Expand Down
4 changes: 4 additions & 0 deletions common/src/main/kotlin/com/mashup/dojo/DojoExceptionType.kt
Original file line number Diff line number Diff line change
Expand Up @@ -47,4 +47,8 @@ enum class DojoExceptionType(

// MemberRelation
RELATION_NOT_FOUND("Relation not found", "MR001_RELATION_NOT_FOUND", 500),

// Pick Paging Sort
SORT_CLIENT_NOT_FOUND("Bad SortType", "SORT_NOT_FOUND", 400),
SORT_NOT_FOUND("Sort Type not found", "SORT_NOT_FOUND", 500),
}
100 changes: 100 additions & 0 deletions entity/src/main/kotlin/com/mashup/dojo/PickRepository.kt
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import com.mashup.dojo.base.BaseTimeEntity
import com.querydsl.core.annotations.QueryProjection
import com.querydsl.core.types.dsl.BooleanExpression
import com.querydsl.core.types.dsl.Wildcard
import com.querydsl.jpa.impl.JPAQuery
import com.querydsl.jpa.impl.JPAQueryFactory
import jakarta.persistence.Column
import jakarta.persistence.Entity
Expand Down Expand Up @@ -72,6 +73,12 @@ interface PickRepositoryCustom {
memberId: String,
rank: Long,
): List<PickQuestionMapper>

fun findGroupByPickPaging(
pickedId: String,
sort: String,
pageable: Pageable,
): Page<PickQuestionDetailMapper>
}

class PickRepositoryImpl(
Expand Down Expand Up @@ -221,6 +228,75 @@ class PickRepositoryImpl(
.limit(rank)
.fetch()
}

override fun findGroupByPickPaging(
pickedId: String,
sort: String,
pageable: Pageable,
): Page<PickQuestionDetailMapper> {
val content = getGroupByPickContent(pickedId, sort, pageable)
val totalCount = getGroupByPickTotalCount(pickedId)

return PageImpl(content, pageable, totalCount)
}

private fun getGroupByPickContent(
pickedId: String,
sort: String,
pageable: Pageable,
): List<PickQuestionDetailMapper> {
val pickEntity = QPickEntity.pickEntity
val questionEntity = QQuestionEntity.questionEntity
val imageEntity = QImageEntity.imageEntity

val query =
jpaQueryFactory
.select(
QPickQuestionDetailMapper(
pickEntity.id,
questionEntity.id,
questionEntity.content,
imageEntity.url,
// 가장 최근의 Pick 시간 가져오기
pickEntity.createdAt.max().`as`("latestPickedAt"),
pickEntity.id.count().`as`("totalReceivedPickCount")
)
)
.from(pickEntity)
.join(questionEntity).on(pickEntity.questionId.eq(questionEntity.id))
.join(imageEntity).on(questionEntity.emojiImageId.eq(imageEntity.id))
.where(pickEntity.pickedId.eq(pickedId))
// Question, Image URL 기준으로 그룹화
.groupBy(pickEntity.questionId, imageEntity.url)

val sortedQuery = getSorted(sort, query)

return sortedQuery
.limit(pageable.pageSize.toLong())
.offset(pageable.offset)
.fetch()
}

private fun getSorted(
sort: String,
query: JPAQuery<PickQuestionDetailMapper>,
): JPAQuery<PickQuestionDetailMapper> {
val pickEntity = QPickEntity.pickEntity
return when (PickSort.findByValue(sort)) {
PickSort.MOST_PICKED -> query.orderBy(Wildcard.count.desc(), pickEntity.createdAt.desc())
PickSort.LATEST -> query.orderBy(pickEntity.createdAt.desc())
}
}

private fun getGroupByPickTotalCount(pickedId: String): Long {
val pickEntity = QPickEntity.pickEntity

return jpaQueryFactory
.select(pickEntity.questionId.countDistinct())
.from(pickEntity)
.where(pickEntity.pickedId.eq(pickedId))
.fetchOne() ?: 0L
}
}

data class PickEntityMapper
Expand Down Expand Up @@ -253,3 +329,27 @@ data class PickQuestionMapper
val questionContent: String,
val createdAt: LocalDateTime,
)

enum class PickSort {
LATEST,
MOST_PICKED,
;

companion object {
fun findByValue(value: String): PickSort {
return PickSort.entries.find { it.name.equals(value, ignoreCase = true) }
?: throw DojoException.of(DojoExceptionType.SORT_CLIENT_NOT_FOUND)
}
}
}

data class PickQuestionDetailMapper
@QueryProjection
constructor(
val pickId: String,
val questionId: String,
val questionContent: String,
val questionEmojiImageUrl: String,
val latestPickedAt: LocalDateTime,
val totalReceivedPickCount: Long,
)
8 changes: 8 additions & 0 deletions service/src/main/kotlin/com/mashup/dojo/domain/Pick.kt
Original file line number Diff line number Diff line change
Expand Up @@ -100,4 +100,12 @@ enum class PickOpenItem(val value: String, val cost: Int) {
enum class PickSort {
LATEST,
MOST_PICKED,
;

companion object {
fun findByValue(value: String): PickSort {
return PickSort.entries.find { it.name.equals(value, ignoreCase = true) }
?: throw DojoException.of(DojoExceptionType.SORT_CLIENT_NOT_FOUND)
}
}
}
Loading

0 comments on commit ad52aba

Please sign in to comment.