Skip to content

Commit

Permalink
feat: group user score 랭킹 조회
Browse files Browse the repository at this point in the history
  • Loading branch information
DongGeon0908 committed Aug 21, 2024
1 parent 475d08d commit 61f5daa
Show file tree
Hide file tree
Showing 11 changed files with 154 additions and 6 deletions.
15 changes: 15 additions & 0 deletions sql/DDL.sql
Original file line number Diff line number Diff line change
Expand Up @@ -74,6 +74,21 @@ CREATE TABLE `group_user`
CREATE UNIQUE INDEX uidx__group_id__uid ON group_user (group_id, uid);
CREATE INDEX idx__uid ON group_user (uid);

-- 그룹 유저 스코어
CREATE TABLE `group_user_score`
(
`id` bigint NOT NULL AUTO_INCREMENT COMMENT 'group user score id',
`group_id` bigint NOT NULL COMMENT 'group id',
`group_user_id` bigint NOT NULL COMMENT 'group user id',
`uid` bigint NOT NULL COMMENT 'uid',
`score` int DEFAULT NULL COMMENT '스코어',
`created_at` datetime DEFAULT CURRENT_TIMESTAMP COMMENT '생성일',
`modified_at` datetime DEFAULT CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP COMMENT '수정일',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=200000 DEFAULT CHARSET=utf8mb4 COLLATE=utf8mb4_general_ci COMMENT='그룹 유저 스코어';
CREATE UNIQUE INDEX uidx__group_user_id ON group_user_score (group_user_id);
CREATE INDEX idx__group_id__group_user_id ON group_user_score (group_id, group_user_id);

-- 포즈 스냅샵
CREATE TABLE `pose_snapshot`
(
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,22 +10,23 @@ import com.hero.alignlab.domain.group.domain.Group
import com.hero.alignlab.domain.group.model.CreateGroupContext
import com.hero.alignlab.domain.group.model.request.CheckGroupRegisterRequest
import com.hero.alignlab.domain.group.model.request.CreateGroupRequest
import com.hero.alignlab.domain.group.model.response.CreateGroupResponse
import com.hero.alignlab.domain.group.model.response.GetGroupResponse
import com.hero.alignlab.domain.group.model.response.JoinGroupResponse
import com.hero.alignlab.domain.group.model.response.SearchGroupResponse
import com.hero.alignlab.domain.group.model.response.*
import com.hero.alignlab.domain.user.application.UserInfoService
import com.hero.alignlab.event.model.CreateGroupEvent
import com.hero.alignlab.exception.ErrorCode
import com.hero.alignlab.exception.InvalidRequestException
import com.hero.alignlab.exception.NotFoundException
import org.springframework.context.ApplicationEventPublisher
import org.springframework.data.domain.Page
import org.springframework.stereotype.Service
import java.util.concurrent.atomic.AtomicInteger

@Service
class GroupFacade(
private val groupService: GroupService,
private val groupUserService: GroupUserService,
private val groupUserScoreService: GroupUserScoreService,
private val userInfoService: UserInfoService,
private val txTemplates: TransactionTemplates,
private val publisher: ApplicationEventPublisher,
) {
Expand Down Expand Up @@ -187,4 +188,29 @@ class GroupFacade(
throw InvalidRequestException(ErrorCode.DUPLICATE_GROUP_NAME_ERROR)
}
}

suspend fun getGroupRank(user: AuthUser, groupId: Long): GetGroupRanksResponse {
val groupUser = groupUserService.findByGroupIdAndUid(groupId, user.uid)
?: throw InvalidRequestException(ErrorCode.NOT_CONTAINS_GROUP_USER_ERROR)

val groupUserScores = groupUserScoreService.findAllByGroupId(groupId)
.filterNot { groupUserScore -> groupUserScore.score == null }
.sortedBy { groupUserScore -> groupUserScore.score }

val userbyId = userInfoService.findAllByIds(groupUserScores.map { it.uid }).associateBy { it.id }

val rank = AtomicInteger(1)

return GetGroupRanksResponse(
groupId = groupUser.groupId,
ranks = groupUserScores.mapNotNull { groupUserScore ->
GetGroupRankResponse(
groupUserId = groupUserScore.groupUserId,
name = userbyId[groupUserScore.uid]?.nickname ?: return@mapNotNull null,
rank = rank.getAndIncrement(),
score = groupUserScore.score ?: return@mapNotNull null,
)
}
)
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,18 @@
package com.hero.alignlab.domain.group.application

import com.hero.alignlab.domain.group.domain.GroupUserScore
import com.hero.alignlab.domain.group.infrastructure.GroupUserScoreRepository
import kotlinx.coroutines.Dispatchers
import kotlinx.coroutines.withContext
import org.springframework.stereotype.Service

@Service
class GroupUserScoreService(
private val groupUserScoreRepository: GroupUserScoreRepository,
) {
suspend fun findAllByGroupId(groupId: Long): List<GroupUserScore> {
return withContext(Dispatchers.IO) {
groupUserScoreRepository.findAllByGroupId(groupId)
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -64,6 +64,12 @@ class GroupUserService(
}
}

suspend fun findByGroupIdAndUid(groupId: Long, uid: Long): GroupUser? {
return withContext(Dispatchers.IO) {
groupUserRepository.findByGroupIdAndUid(groupId, uid)
}
}

suspend fun getGroupUsers(user: AuthUser, groupId: Long, pageable: Pageable): Page<GroupUserResponse> {
val exists = existsByGroupIdAndUid(groupId, user.uid)

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

private suspend fun findAllByGroupId(groupId: Long, pageable: Pageable): Page<GroupUser> {
suspend fun findAllByGroupId(groupId: Long, pageable: Pageable): Page<GroupUser> {
return withContext(Dispatchers.IO) {
groupUserRepository.findAllByGroupId(groupId, pageable)
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,24 @@
package com.hero.alignlab.domain.group.domain

import com.hero.alignlab.domain.common.domain.BaseEntity
import jakarta.persistence.*

@Entity
@Table(name = "`group_user_score`")
class GroupUserScore(
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
val id: Long = -1,

@Column(name = "group_id")
val groupId: Long,

@Column(name = "group_user_id")
val groupUserId: Long,

@Column(name = "uid")
val uid: Long,

@Column(name = "score")
var score: Int?,
) : BaseEntity()
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,8 @@ interface GroupUserRepository : JpaRepository<GroupUser, Long> {

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

fun findByGroupIdAndUid(groupId: Long, uid: Long): GroupUser?

fun findAllByGroupId(groupId: Long, pageable: Pageable): Page<GroupUser>

fun existsByUid(uid: Long): Boolean
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package com.hero.alignlab.domain.group.infrastructure

import com.hero.alignlab.domain.group.domain.GroupUserScore
import org.springframework.data.jpa.repository.JpaRepository
import org.springframework.stereotype.Repository
import org.springframework.transaction.annotation.Transactional

@Transactional(readOnly = true)
@Repository
interface GroupUserScoreRepository : JpaRepository<GroupUserScore, Long> {
fun findAllByGroupId(groupId: Long): List<GroupUserScore>
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.hero.alignlab.domain.group.model.response

data class GetGroupRanksResponse(
val groupId: Long,
val ranks: List<GetGroupRankResponse>
)

data class GetGroupRankResponse(
val groupUserId: Long,
val name: String,
val rank: Int,
val score: Int,
)
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package com.hero.alignlab.domain.group.resource

import com.hero.alignlab.common.extension.wrapOk
import com.hero.alignlab.domain.auth.model.AuthUser
import com.hero.alignlab.domain.group.application.GroupFacade
import io.swagger.v3.oas.annotations.Operation
import io.swagger.v3.oas.annotations.tags.Tag
import org.springframework.http.MediaType
import org.springframework.web.bind.annotation.GetMapping
import org.springframework.web.bind.annotation.RequestMapping
import org.springframework.web.bind.annotation.RequestParam
import org.springframework.web.bind.annotation.RestController

@Tag(name = "Group User Score API")
@RestController
@RequestMapping(produces = [MediaType.APPLICATION_JSON_VALUE])
class GroupUserScoreResource(
private val groupFacade: GroupFacade,
) {
@Operation(summary = "바른 자세 랭킹")
@GetMapping("/api/v1/group-scores")
suspend fun getScores(
user: AuthUser,
@RequestParam groupId: Long,
) = groupFacade.getGroupRank(user, groupId).wrapOk()
}
1 change: 1 addition & 0 deletions src/main/kotlin/com/hero/alignlab/exception/ErrorCode.kt
Original file line number Diff line number Diff line change
Expand Up @@ -41,6 +41,7 @@ enum class ErrorCode(val status: HttpStatus, val description: String) {
ALREADY_JOIN_GROUP_ERROR(HttpStatus.BAD_REQUEST, "이미 그룹에 들어가 있습니다."),
NOT_FOUND_JOIN_CODE_ERROR(HttpStatus.BAD_REQUEST, "비밀 그룹의 경우, 비밀번호가 필수 입니다."),
INVALID_JOIN_CODE_ERROR(HttpStatus.BAD_REQUEST, "비밀번호의 조건이 부합하지 않습니다."),
NOT_CONTAINS_GROUP_USER_ERROR(HttpStatus.BAD_REQUEST, "그룹원이 아닙니다."),

/** Group User Error Code */
DUPLICATE_GROUP_JOIN_ERROR(HttpStatus.BAD_REQUEST, "한개의 그룹만 참여 가능합니다."),
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,8 @@ data class ConcurrentMessage(
val groupUserId: Long,
val uid: Long,
val nickname: String,
val rank: Int,
val score: Int,
)

companion object {
Expand All @@ -29,7 +31,10 @@ data class ConcurrentMessage(
ConcurrentUser(
groupUserId = groupUSer.id,
uid = uid,
nickname = info.nickname
nickname = info.nickname,
// 더미 데이터
rank = 1,
score = 1
)
}
)
Expand Down

0 comments on commit 61f5daa

Please sign in to comment.