Skip to content

Commit 4a018fb

Browse files
authored
내 뱃지 조회에 정렬을 수행한다. (#99)
* feat: implement my badges sorting * refactor: change projection name * feat: modify my badge response
1 parent e68ce62 commit 4a018fb

File tree

16 files changed

+197
-39
lines changed

16 files changed

+197
-39
lines changed

api/src/main/kotlin/com/gotchai/api/presentation/v1/badge/BadgeController.kt

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
package com.gotchai.api.presentation.v1.badge
22

33
import com.gotchai.api.global.annotation.ApiV1Controller
4-
import com.gotchai.api.presentation.v1.badge.response.BadgeListResponse
54
import com.gotchai.api.presentation.v1.badge.response.BadgeResponse
5+
import com.gotchai.api.presentation.v1.badge.response.GetMyBadgesResponse
66
import com.gotchai.domain.badge.port.`in`.BadgeQueryUseCase
77
import org.springframework.security.core.annotation.AuthenticationPrincipal
88
import org.springframework.web.bind.annotation.GetMapping
@@ -25,8 +25,8 @@ class BadgeController(
2525
fun getMyBadges(
2626
@AuthenticationPrincipal
2727
userId: Long
28-
): BadgeListResponse =
28+
): GetMyBadgesResponse =
2929
badgeQueryUseCase
3030
.getMyBadges(userId)
31-
.let { BadgeListResponse.from(it) }
31+
.let { GetMyBadgesResponse.from(it) }
3232
}
Lines changed: 23 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,23 @@
1+
package com.gotchai.api.presentation.v1.badge.response
2+
3+
import com.gotchai.domain.badge.dto.projection.BadgeWithAcquiredAt
4+
import java.time.LocalDateTime
5+
6+
data class GetMyBadgeResponse(
7+
val id: Long,
8+
val name: String,
9+
val image: String,
10+
val acquiredAt: LocalDateTime
11+
) {
12+
companion object {
13+
fun from(badge: BadgeWithAcquiredAt): GetMyBadgeResponse =
14+
with(badge) {
15+
GetMyBadgeResponse(
16+
id = id,
17+
name = name,
18+
image = image,
19+
acquiredAt = acquiredAt
20+
)
21+
}
22+
}
23+
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.gotchai.api.presentation.v1.badge.response
2+
3+
import com.gotchai.domain.badge.dto.result.GetMyBadgesResult
4+
5+
data class GetMyBadgesResponse(
6+
val badges: List<GetMyBadgeResponse>,
7+
val totalBadgeCount: Long
8+
) {
9+
companion object {
10+
fun from(result: GetMyBadgesResult): GetMyBadgesResponse =
11+
with(result) {
12+
GetMyBadgesResponse(
13+
badges = badges.map { GetMyBadgeResponse.from(it) },
14+
totalBadgeCount = totalBadgeCount
15+
)
16+
}
17+
}
18+
}

api/src/test/kotlin/com/gotchai/api/presentation/v1/badge/BadgeControllerTest.kt

Lines changed: 15 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -1,19 +1,20 @@
11
package com.gotchai.api.presentation.v1.badge
22

33
import com.gotchai.api.common.ControllerTest
4-
import com.gotchai.api.docs.badgeListResponseFields
54
import com.gotchai.api.docs.badgeResponseFields
65
import com.gotchai.api.docs.errorResponseFields
6+
import com.gotchai.api.docs.getMyBadgesResponseFields
77
import com.gotchai.api.global.dto.ApiResponse
8-
import com.gotchai.api.presentation.v1.badge.response.BadgeListResponse
98
import com.gotchai.api.presentation.v1.badge.response.BadgeResponse
9+
import com.gotchai.api.presentation.v1.badge.response.GetMyBadgesResponse
1010
import com.gotchai.api.util.desc
1111
import com.gotchai.api.util.document
1212
import com.gotchai.api.util.expectError
1313
import com.gotchai.domain.badge.exception.BadgeNotFoundException
1414
import com.gotchai.domain.badge.port.`in`.BadgeQueryUseCase
1515
import com.gotchai.domain.fixture.ID
1616
import com.gotchai.domain.fixture.createBadge
17+
import com.gotchai.domain.fixture.createGetMyBadgesResult
1718
import com.ninjasquad.springmockk.MockkBean
1819
import io.mockk.every
1920
import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest
@@ -64,21 +65,19 @@ class BadgeControllerTest : ControllerTest() {
6465
}
6566

6667
describe("getMyBadges()는") {
67-
context("뱃지를 취득한 사용자가 존재하는 경우") {
68-
every { badgeQueryUseCase.getMyBadges(ID) } returns listOf(createBadge())
68+
every { badgeQueryUseCase.getMyBadges(ID) } returns createGetMyBadgesResult()
6969

70-
it("상태 코드 200과 GetMyBadgeResponse들을 반환한다.") {
71-
webClient
72-
.get()
73-
.uri("/api/v1/users/me/badges")
74-
.exchange()
75-
.expectStatus()
76-
.isOk
77-
.expectBody<ApiResponse<BadgeListResponse>>()
78-
.document("내가 취득한 뱃지 리스트 조회 성공(200)") {
79-
responseBody(badgeListResponseFields)
80-
}
81-
}
70+
it("상태 코드 200과 GetMyBadgeResponse들을 반환한다.") {
71+
webClient
72+
.get()
73+
.uri("/api/v1/users/me/badges")
74+
.exchange()
75+
.expectStatus()
76+
.isOk
77+
.expectBody<ApiResponse<GetMyBadgesResponse>>()
78+
.document("내가 취득한 뱃지 리스트 조회 성공(200)") {
79+
responseBody(getMyBadgesResponseFields)
80+
}
8281
}
8382
}
8483
}

api/src/testFixtures/kotlin/com/gotchai/api/docs/BadgeDocs.kt

Lines changed: 19 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,8 @@
11
package com.gotchai.api.docs
22

33
import com.gotchai.api.presentation.v1.badge.response.BadgeResponse
4+
import com.gotchai.api.presentation.v1.badge.response.GetMyBadgeResponse
5+
import com.gotchai.api.presentation.v1.badge.response.GetMyBadgesResponse
46
import com.gotchai.api.util.desc
57
import com.gotchai.api.util.fieldsOf
68
import com.gotchai.api.util.listFieldsOf
@@ -21,3 +23,20 @@ val badgeListResponseFields =
2123
"list" desc "뱃지 리스트",
2224
*badgeResponseFields.toTypedArray()
2325
)
26+
27+
val getMyBadgeResponseFields =
28+
fieldsOf(
29+
GetMyBadgeResponse::id desc "식별자",
30+
GetMyBadgeResponse::name desc "이름",
31+
GetMyBadgeResponse::image desc "이미지 URI",
32+
GetMyBadgeResponse::acquiredAt desc "취득 날짜"
33+
)
34+
35+
val getMyBadgesResponseFields =
36+
fieldsOf(
37+
*listFieldsOf(
38+
GetMyBadgesResponse::badges desc "내가 취득한 뱃지 리스트",
39+
*getMyBadgeResponseFields.toTypedArray()
40+
).toTypedArray(),
41+
GetMyBadgesResponse::totalBadgeCount desc "총 뱃지 개수"
42+
)

domain/src/main/kotlin/com/gotchai/domain/badge/adapter/in/BadgeQueryService.kt

Lines changed: 10 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,34 @@
11
package com.gotchai.domain.badge.adapter.`in`
22

3+
import com.gotchai.domain.badge.dto.result.GetMyBadgesResult
34
import com.gotchai.domain.badge.entity.Badge
45
import com.gotchai.domain.badge.entity.Tier
56
import com.gotchai.domain.badge.exception.BadgeNotFoundException
67
import com.gotchai.domain.badge.port.`in`.BadgeQueryUseCase
78
import com.gotchai.domain.badge.port.out.BadgeQueryPort
8-
import com.gotchai.domain.badge.port.out.UserBadgeQueryPort
9+
import com.gotchai.domain.exam.port.out.ExamQueryPort
910
import org.springframework.stereotype.Service
1011
import org.springframework.transaction.annotation.Transactional
1112

1213
@Service
1314
class BadgeQueryService(
1415
private val badgeQueryPort: BadgeQueryPort,
15-
private val userBadgeQueryPort: UserBadgeQueryPort
16+
private val examQueryPort: ExamQueryPort
1617
) : BadgeQueryUseCase {
1718
@Transactional(readOnly = true)
1819
override fun getBadgeById(badgeId: Long): Badge =
1920
badgeQueryPort.getBadgeById(badgeId)
2021
?: throw BadgeNotFoundException()
2122

2223
@Transactional(readOnly = true)
23-
override fun getMyBadges(userId: Long): List<Badge> {
24-
val userBadges = userBadgeQueryPort.getUserBadgesByUserId(userId)
25-
val badges = badgeQueryPort.getBadgesByIdIn(userBadges.map { it.badgeId })
24+
override fun getMyBadges(userId: Long): GetMyBadgesResult {
25+
val badges = badgeQueryPort.getBadgesWithAcquiredAtByUserId(userId)
26+
val examCount = examQueryPort.getExamCount()
2627

27-
return badges
28+
return GetMyBadgesResult(
29+
badges = badges,
30+
totalBadgeCount = examCount
31+
)
2832
}
2933

3034
@Transactional(readOnly = true)
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
package com.gotchai.domain.badge.dto.projection
2+
3+
import com.gotchai.domain.badge.entity.Tier
4+
import java.time.LocalDateTime
5+
6+
data class BadgeWithAcquiredAt(
7+
val id: Long,
8+
val examId: Long,
9+
val name: String,
10+
val description: String,
11+
val image: String,
12+
val tier: Tier,
13+
val createdAt: LocalDateTime,
14+
val acquiredAt: LocalDateTime
15+
)
Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
package com.gotchai.domain.badge.dto.result
2+
3+
import com.gotchai.domain.badge.dto.projection.BadgeWithAcquiredAt
4+
5+
data class GetMyBadgesResult(
6+
val badges: List<BadgeWithAcquiredAt>,
7+
val totalBadgeCount: Long
8+
)

domain/src/main/kotlin/com/gotchai/domain/badge/port/in/BadgeQueryUseCase.kt

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,13 @@
11
package com.gotchai.domain.badge.port.`in`
22

3+
import com.gotchai.domain.badge.dto.result.GetMyBadgesResult
34
import com.gotchai.domain.badge.entity.Badge
45
import com.gotchai.domain.badge.entity.Tier
56

67
interface BadgeQueryUseCase {
78
fun getBadgeById(badgeId: Long): Badge
89

9-
fun getMyBadges(userId: Long): List<Badge>
10+
fun getMyBadges(userId: Long): GetMyBadgesResult
1011

1112
fun getBadgeByExamIdAndTier(
1213
examId: Long,
Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,15 +1,16 @@
11
package com.gotchai.domain.badge.port.out
22

3+
import com.gotchai.domain.badge.dto.projection.BadgeWithAcquiredAt
34
import com.gotchai.domain.badge.entity.Badge
45
import com.gotchai.domain.badge.entity.Tier
56

67
interface BadgeQueryPort {
78
fun getBadgeById(id: Long): Badge?
89

9-
fun getBadgesByIdIn(ids: Collection<Long>): List<Badge>
10-
1110
fun getBadgeByExamIdAndTier(
1211
examId: Long,
1312
badgeTier: Tier
1413
): Badge?
14+
15+
fun getBadgesWithAcquiredAtByUserId(userId: Long): List<BadgeWithAcquiredAt>
1516
}

0 commit comments

Comments
 (0)