Skip to content

Commit c70c6c1

Browse files
authored
유저가 취득한 뱃지 삭제 API를 구현한다. (#125)
feat: implement user badge delete API
1 parent 1eb6b38 commit c70c6c1

File tree

10 files changed

+109
-2
lines changed

10 files changed

+109
-2
lines changed

api/src/main/kotlin/com/gotchai/api/presentation/v1/admin/AdminController.kt

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -31,4 +31,14 @@ class AdminController(
3131
) {
3232
adminCommandUseCase.deleteExamHistoryByExamIdAndUserId(examId, userId)
3333
}
34+
35+
@DeleteMapping("/admin/users/{userId}/badges/{badgeId}")
36+
fun deleteUserBadge(
37+
@PathVariable
38+
userId: Long,
39+
@PathVariable
40+
badgeId: Long
41+
) {
42+
adminCommandUseCase.deleteUserBadgeByBadgeIdAndUserId(userId, badgeId)
43+
}
3444
}

api/src/test/kotlin/com/gotchai/api/presentation/v1/admin/AdminControllerTest.kt

Lines changed: 46 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,7 @@ package com.gotchai.api.presentation.v1.admin
22

33
import com.gotchai.api.common.ControllerTest
44
import com.gotchai.api.docs.createExamRequestFields
5+
import com.gotchai.api.docs.errorResponseFields
56
import com.gotchai.api.docs.examResponseFields
67
import com.gotchai.api.fixture.createCreateExamRequest
78
import com.gotchai.api.global.dto.ApiResponse
@@ -11,6 +12,7 @@ import com.gotchai.api.util.desc
1112
import com.gotchai.api.util.document
1213
import com.gotchai.api.util.expectError
1314
import com.gotchai.domain.admin.port.`in`.AdminCommandUseCase
15+
import com.gotchai.domain.badge.exception.UserBadgeNotFoundException
1416
import com.gotchai.domain.exam.exception.ExamHistoryNotFoundException
1517
import com.gotchai.domain.fixture.ID
1618
import com.gotchai.domain.fixture.createExam
@@ -87,6 +89,50 @@ class AdminControllerTest : ControllerTest() {
8789
"userId" desc "유저 식별자",
8890
"examId" desc "테스트 식별자"
8991
)
92+
responseBody(errorResponseFields)
93+
}
94+
}
95+
}
96+
}
97+
98+
describe("deleteUserBadge()는") {
99+
context("유저가 취득한 뱃지가 존재하는 경우") {
100+
every { adminCommandUseCase.deleteUserBadgeByBadgeIdAndUserId(ID, ID) } just runs
101+
102+
it("상태 코드 200을 반환한다.") {
103+
webClient
104+
.delete()
105+
.uri("/api/v1/admin/users/{userId}/badges/{badgeId}", ID, ID)
106+
.exchange()
107+
.expectStatus()
108+
.isOk
109+
.expectBody<Void>()
110+
.document("유저가 취득한 뱃지 삭제 성공(200)") {
111+
pathParams(
112+
"userId" desc "유저 식별자",
113+
"badgeId" desc "뱃지 식별자"
114+
)
115+
}
116+
}
117+
}
118+
119+
context("유저가 취득한 뱃지가 존재하지 않는 경우") {
120+
every { adminCommandUseCase.deleteUserBadgeByBadgeIdAndUserId(ID, ID) } throws UserBadgeNotFoundException()
121+
122+
it("상태 코드 404를 반환한다.") {
123+
webClient
124+
.delete()
125+
.uri("/api/v1/admin/users/{userId}/badges/{badgeId}", ID, ID)
126+
.exchange()
127+
.expectStatus()
128+
.isNotFound
129+
.expectError()
130+
.document("유저가 취득한 뱃지 삭제 실패(404)") {
131+
pathParams(
132+
"userId" desc "유저 식별자",
133+
"badgeId" desc "뱃지 식별자"
134+
)
135+
responseBody(errorResponseFields)
90136
}
91137
}
92138
}

domain/src/main/kotlin/com/gotchai/domain/admin/adapter/in/AdminCommandService.kt

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3,6 +3,9 @@ package com.gotchai.domain.admin.adapter.`in`
33
import com.gotchai.domain.admin.dto.command.CreateExamCommand
44
import com.gotchai.domain.admin.exception.InvalidFileException
55
import com.gotchai.domain.admin.port.`in`.AdminCommandUseCase
6+
import com.gotchai.domain.badge.exception.UserBadgeNotFoundException
7+
import com.gotchai.domain.badge.port.out.UserBadgeCommandPort
8+
import com.gotchai.domain.badge.port.out.UserBadgeQueryPort
69
import com.gotchai.domain.exam.entity.Exam
710
import com.gotchai.domain.exam.exception.ExamHistoryNotFoundException
811
import com.gotchai.domain.exam.port.out.ExamCommandPort
@@ -20,6 +23,8 @@ class AdminCommandService(
2023
private val examHistoryQueryPort: ExamHistoryQueryPort,
2124
private val examHistoryCommandPort: ExamHistoryCommandPort,
2225
private val quizHistoryCommandPort: QuizHistoryCommandPort,
26+
private val userBadgeQueryPort: UserBadgeQueryPort,
27+
private val userBadgeCommandPort: UserBadgeCommandPort,
2328
private val objectStorageProvider: ObjectStorageProvider
2429
) : AdminCommandUseCase {
2530
@Transactional
@@ -55,4 +60,14 @@ class AdminCommandService(
5560
quizHistoryCommandPort.deleteQuizHistoriesByExamHistoryId(examHistory.id)
5661
examHistoryCommandPort.deleteExamHistoryByExamIdAndUserId(examId, userId)
5762
}
63+
64+
@Transactional
65+
override fun deleteUserBadgeByBadgeIdAndUserId(
66+
badgeId: Long,
67+
userId: Long
68+
) {
69+
val userBadge = userBadgeQueryPort.getUserBadgeByBadgeIdAndUserId(badgeId, userId) ?: throw UserBadgeNotFoundException()
70+
71+
userBadgeCommandPort.deleteUserBadgeById(userBadge.id)
72+
}
5873
}

domain/src/main/kotlin/com/gotchai/domain/admin/port/in/AdminCommandUseCase.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,4 +10,9 @@ interface AdminCommandUseCase {
1010
examId: Long,
1111
userId: Long
1212
)
13+
14+
fun deleteUserBadgeByBadgeIdAndUserId(
15+
badgeId: Long,
16+
userId: Long
17+
)
1318
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package com.gotchai.domain.badge.exception
2+
3+
import com.gotchai.domain.global.exception.ServerException
4+
5+
class UserBadgeNotFoundException(
6+
override val message: String = "유저가 취득한 뱃지를 찾을 수 없습니다."
7+
) : ServerException(status = 404, message)

domain/src/main/kotlin/com/gotchai/domain/badge/port/out/UserBadgeCommandPort.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,6 @@ import com.gotchai.domain.badge.entity.UserBadge
44

55
interface UserBadgeCommandPort {
66
fun createUserBadge(creation: UserBadge.Creation): UserBadge
7+
8+
fun deleteUserBadgeById(id: Long)
79
}

domain/src/main/kotlin/com/gotchai/domain/badge/port/out/UserBadgeQueryPort.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,4 +4,9 @@ import com.gotchai.domain.badge.entity.UserBadge
44

55
interface UserBadgeQueryPort {
66
fun getUserBadgesByUserId(userId: Long): List<UserBadge>
7+
8+
fun getUserBadgeByBadgeIdAndUserId(
9+
badgeId: Long,
10+
userId: Long
11+
): UserBadge?
712
}

storage/rdb/src/main/kotlin/com/gotchai/storage/rdb/badge/adapter/out/UserBadgeCommandAdapter.kt

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -12,4 +12,8 @@ class UserBadgeCommandAdapter(
1212
) : UserBadgeCommandPort {
1313
override fun createUserBadge(creation: UserBadge.Creation): UserBadge =
1414
userBadgeJpaRepository.save(UserBadgeEntity.from(creation)).toUserBadge()
15+
16+
override fun deleteUserBadgeById(id: Long) {
17+
userBadgeJpaRepository.deleteById(id)
18+
}
1519
}

storage/rdb/src/main/kotlin/com/gotchai/storage/rdb/badge/adapter/out/UserBadgeQueryAdapter.kt

Lines changed: 10 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -2,7 +2,6 @@ package com.gotchai.storage.rdb.badge.adapter.out
22

33
import com.gotchai.domain.badge.entity.UserBadge
44
import com.gotchai.domain.badge.port.out.UserBadgeQueryPort
5-
import com.gotchai.storage.rdb.badge.entity.UserBadgeEntity
65
import com.gotchai.storage.rdb.badge.repository.UserBadgeJpaRepository
76
import com.gotchai.storage.rdb.global.annotation.Adapter
87
import com.gotchai.storage.rdb.global.annotation.ReadOnlyTransactional
@@ -15,5 +14,14 @@ class UserBadgeQueryAdapter(
1514
override fun getUserBadgesByUserId(userId: Long): List<UserBadge> =
1615
userBadgeRepository
1716
.findAllByUserId(userId)
18-
.map(UserBadgeEntity::toUserBadge)
17+
.map { it.toUserBadge() }
18+
19+
@ReadOnlyTransactional
20+
override fun getUserBadgeByBadgeIdAndUserId(
21+
badgeId: Long,
22+
userId: Long
23+
): UserBadge? =
24+
userBadgeRepository
25+
.findByBadgeIdAndUserId(badgeId, userId)
26+
?.toUserBadge()
1927
}

storage/rdb/src/main/kotlin/com/gotchai/storage/rdb/badge/repository/UserBadgeJpaRepository.kt

Lines changed: 5 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -4,5 +4,10 @@ import com.gotchai.storage.rdb.badge.entity.UserBadgeEntity
44
import org.springframework.data.jpa.repository.JpaRepository
55

66
interface UserBadgeJpaRepository : JpaRepository<UserBadgeEntity, Long> {
7+
fun findByBadgeIdAndUserId(
8+
badgeId: Long,
9+
userId: Long
10+
): UserBadgeEntity?
11+
712
fun findAllByUserId(userId: Long): List<UserBadgeEntity>
813
}

0 commit comments

Comments
 (0)