Skip to content

Commit 61f5daa

Browse files
committed
feat: group user score 랭킹 조회
1 parent 475d08d commit 61f5daa

File tree

11 files changed

+154
-6
lines changed

11 files changed

+154
-6
lines changed

sql/DDL.sql

Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -74,6 +74,21 @@ CREATE TABLE `group_user`
7474
CREATE UNIQUE INDEX uidx__group_id__uid ON group_user (group_id, uid);
7575
CREATE INDEX idx__uid ON group_user (uid);
7676

77+
-- 그룹 유저 스코어
78+
CREATE TABLE `group_user_score`
79+
(
80+
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'group user score id',
81+
`group_id` bigint NOT NULL COMMENT 'group id',
82+
`group_user_id` bigint NOT NULL COMMENT 'group user id',
83+
`uid` bigint NOT NULL COMMENT 'uid',
84+
`score` int DEFAULT NULL COMMENT '스코어',
85+
`created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
86+
`modified_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
87+
PRIMARY KEY (`id`)
88+
) ENGINE=InnoDB AUTO_INCREMENT=200000 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='그룹 유저 스코어';
89+
CREATE UNIQUE INDEX uidx__group_user_id ON group_user_score (group_user_id);
90+
CREATE INDEX idx__group_id__group_user_id ON group_user_score (group_id, group_user_id);
91+
7792
-- 포즈 스냅샵
7893
CREATE TABLE `pose_snapshot`
7994
(

src/main/kotlin/com/hero/alignlab/domain/group/application/GroupFacade.kt

Lines changed: 30 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -10,22 +10,23 @@ import com.hero.alignlab.domain.group.domain.Group
1010
import com.hero.alignlab.domain.group.model.CreateGroupContext
1111
import com.hero.alignlab.domain.group.model.request.CheckGroupRegisterRequest
1212
import com.hero.alignlab.domain.group.model.request.CreateGroupRequest
13-
import com.hero.alignlab.domain.group.model.response.CreateGroupResponse
14-
import com.hero.alignlab.domain.group.model.response.GetGroupResponse
15-
import com.hero.alignlab.domain.group.model.response.JoinGroupResponse
16-
import com.hero.alignlab.domain.group.model.response.SearchGroupResponse
13+
import com.hero.alignlab.domain.group.model.response.*
14+
import com.hero.alignlab.domain.user.application.UserInfoService
1715
import com.hero.alignlab.event.model.CreateGroupEvent
1816
import com.hero.alignlab.exception.ErrorCode
1917
import com.hero.alignlab.exception.InvalidRequestException
2018
import com.hero.alignlab.exception.NotFoundException
2119
import org.springframework.context.ApplicationEventPublisher
2220
import org.springframework.data.domain.Page
2321
import org.springframework.stereotype.Service
22+
import java.util.concurrent.atomic.AtomicInteger
2423

2524
@Service
2625
class GroupFacade(
2726
private val groupService: GroupService,
2827
private val groupUserService: GroupUserService,
28+
private val groupUserScoreService: GroupUserScoreService,
29+
private val userInfoService: UserInfoService,
2930
private val txTemplates: TransactionTemplates,
3031
private val publisher: ApplicationEventPublisher,
3132
) {
@@ -187,4 +188,29 @@ class GroupFacade(
187188
throw InvalidRequestException(ErrorCode.DUPLICATE_GROUP_NAME_ERROR)
188189
}
189190
}
191+
192+
suspend fun getGroupRank(user: AuthUser, groupId: Long): GetGroupRanksResponse {
193+
val groupUser = groupUserService.findByGroupIdAndUid(groupId, user.uid)
194+
?: throw InvalidRequestException(ErrorCode.NOT_CONTAINS_GROUP_USER_ERROR)
195+
196+
val groupUserScores = groupUserScoreService.findAllByGroupId(groupId)
197+
.filterNot { groupUserScore -> groupUserScore.score == null }
198+
.sortedBy { groupUserScore -> groupUserScore.score }
199+
200+
val userbyId = userInfoService.findAllByIds(groupUserScores.map { it.uid }).associateBy { it.id }
201+
202+
val rank = AtomicInteger(1)
203+
204+
return GetGroupRanksResponse(
205+
groupId = groupUser.groupId,
206+
ranks = groupUserScores.mapNotNull { groupUserScore ->
207+
GetGroupRankResponse(
208+
groupUserId = groupUserScore.groupUserId,
209+
name = userbyId[groupUserScore.uid]?.nickname ?: return@mapNotNull null,
210+
rank = rank.getAndIncrement(),
211+
score = groupUserScore.score ?: return@mapNotNull null,
212+
)
213+
}
214+
)
215+
}
190216
}
Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package com.hero.alignlab.domain.group.application
2+
3+
import com.hero.alignlab.domain.group.domain.GroupUserScore
4+
import com.hero.alignlab.domain.group.infrastructure.GroupUserScoreRepository
5+
import kotlinx.coroutines.Dispatchers
6+
import kotlinx.coroutines.withContext
7+
import org.springframework.stereotype.Service
8+
9+
@Service
10+
class GroupUserScoreService(
11+
private val groupUserScoreRepository: GroupUserScoreRepository,
12+
) {
13+
suspend fun findAllByGroupId(groupId: Long): List<GroupUserScore> {
14+
return withContext(Dispatchers.IO) {
15+
groupUserScoreRepository.findAllByGroupId(groupId)
16+
}
17+
}
18+
}

src/main/kotlin/com/hero/alignlab/domain/group/application/GroupUserService.kt

Lines changed: 7 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -64,6 +64,12 @@ class GroupUserService(
6464
}
6565
}
6666

67+
suspend fun findByGroupIdAndUid(groupId: Long, uid: Long): GroupUser? {
68+
return withContext(Dispatchers.IO) {
69+
groupUserRepository.findByGroupIdAndUid(groupId, uid)
70+
}
71+
}
72+
6773
suspend fun getGroupUsers(user: AuthUser, groupId: Long, pageable: Pageable): Page<GroupUserResponse> {
6874
val exists = existsByGroupIdAndUid(groupId, user.uid)
6975

@@ -75,7 +81,7 @@ class GroupUserService(
7581
.map { groupUser -> GroupUserResponse(groupUser.id, groupUser.uid) }
7682
}
7783

78-
private suspend fun findAllByGroupId(groupId: Long, pageable: Pageable): Page<GroupUser> {
84+
suspend fun findAllByGroupId(groupId: Long, pageable: Pageable): Page<GroupUser> {
7985
return withContext(Dispatchers.IO) {
8086
groupUserRepository.findAllByGroupId(groupId, pageable)
8187
}
Lines changed: 24 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,24 @@
1+
package com.hero.alignlab.domain.group.domain
2+
3+
import com.hero.alignlab.domain.common.domain.BaseEntity
4+
import jakarta.persistence.*
5+
6+
@Entity
7+
@Table(name = "`group_user_score`")
8+
class GroupUserScore(
9+
@Id
10+
@GeneratedValue(strategy = GenerationType.IDENTITY)
11+
val id: Long = -1,
12+
13+
@Column(name = "group_id")
14+
val groupId: Long,
15+
16+
@Column(name = "group_user_id")
17+
val groupUserId: Long,
18+
19+
@Column(name = "uid")
20+
val uid: Long,
21+
22+
@Column(name = "score")
23+
var score: Int?,
24+
) : BaseEntity()

src/main/kotlin/com/hero/alignlab/domain/group/infrastructure/GroupUserRepository.kt

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,6 +21,8 @@ interface GroupUserRepository : JpaRepository<GroupUser, Long> {
2121

2222
fun existsByGroupIdAndUid(groupId: Long, uid: Long): Boolean
2323

24+
fun findByGroupIdAndUid(groupId: Long, uid: Long): GroupUser?
25+
2426
fun findAllByGroupId(groupId: Long, pageable: Pageable): Page<GroupUser>
2527

2628
fun existsByUid(uid: Long): Boolean
Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
package com.hero.alignlab.domain.group.infrastructure
2+
3+
import com.hero.alignlab.domain.group.domain.GroupUserScore
4+
import org.springframework.data.jpa.repository.JpaRepository
5+
import org.springframework.stereotype.Repository
6+
import org.springframework.transaction.annotation.Transactional
7+
8+
@Transactional(readOnly = true)
9+
@Repository
10+
interface GroupUserScoreRepository : JpaRepository<GroupUserScore, Long> {
11+
fun findAllByGroupId(groupId: Long): List<GroupUserScore>
12+
}
Lines changed: 13 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,13 @@
1+
package com.hero.alignlab.domain.group.model.response
2+
3+
data class GetGroupRanksResponse(
4+
val groupId: Long,
5+
val ranks: List<GetGroupRankResponse>
6+
)
7+
8+
data class GetGroupRankResponse(
9+
val groupUserId: Long,
10+
val name: String,
11+
val rank: Int,
12+
val score: Int,
13+
)
Lines changed: 26 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,26 @@
1+
package com.hero.alignlab.domain.group.resource
2+
3+
import com.hero.alignlab.common.extension.wrapOk
4+
import com.hero.alignlab.domain.auth.model.AuthUser
5+
import com.hero.alignlab.domain.group.application.GroupFacade
6+
import io.swagger.v3.oas.annotations.Operation
7+
import io.swagger.v3.oas.annotations.tags.Tag
8+
import org.springframework.http.MediaType
9+
import org.springframework.web.bind.annotation.GetMapping
10+
import org.springframework.web.bind.annotation.RequestMapping
11+
import org.springframework.web.bind.annotation.RequestParam
12+
import org.springframework.web.bind.annotation.RestController
13+
14+
@Tag(name = "Group User Score API")
15+
@RestController
16+
@RequestMapping(produces = [MediaType.APPLICATION_JSON_VALUE])
17+
class GroupUserScoreResource(
18+
private val groupFacade: GroupFacade,
19+
) {
20+
@Operation(summary = "바른 자세 랭킹")
21+
@GetMapping("/api/v1/group-scores")
22+
suspend fun getScores(
23+
user: AuthUser,
24+
@RequestParam groupId: Long,
25+
) = groupFacade.getGroupRank(user, groupId).wrapOk()
26+
}

src/main/kotlin/com/hero/alignlab/exception/ErrorCode.kt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -41,6 +41,7 @@ enum class ErrorCode(val status: HttpStatus, val description: String) {
4141
ALREADY_JOIN_GROUP_ERROR(HttpStatus.BAD_REQUEST, "이미 그룹에 들어가 있습니다."),
4242
NOT_FOUND_JOIN_CODE_ERROR(HttpStatus.BAD_REQUEST, "비밀 그룹의 경우, 비밀번호가 필수 입니다."),
4343
INVALID_JOIN_CODE_ERROR(HttpStatus.BAD_REQUEST, "비밀번호의 조건이 부합하지 않습니다."),
44+
NOT_CONTAINS_GROUP_USER_ERROR(HttpStatus.BAD_REQUEST, "그룹원이 아닙니다."),
4445

4546
/** Group User Error Code */
4647
DUPLICATE_GROUP_JOIN_ERROR(HttpStatus.BAD_REQUEST, "한개의 그룹만 참여 가능합니다."),

0 commit comments

Comments
 (0)