Skip to content

Commit

Permalink
그룹원들의 해결한 문제 조회 API 구현
Browse files Browse the repository at this point in the history
- 일 단위, 달 단위, 범위 지정 가능
  • Loading branch information
gidskql6671 committed May 27, 2024
1 parent 1932a13 commit f7996a9
Show file tree
Hide file tree
Showing 6 changed files with 128 additions and 4 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@ import io.swagger.v3.oas.annotations.media.Schema
import io.swagger.v3.oas.annotations.responses.ApiResponse
import io.swagger.v3.oas.annotations.responses.ApiResponses
import knu.dong.onedayonebaek.domain.User
import knu.dong.onedayonebaek.dto.CreateGroupRequest
import knu.dong.onedayonebaek.dto.GroupDetailDto
import knu.dong.onedayonebaek.dto.JoinGroupRequest
import knu.dong.onedayonebaek.dto.JoinGroupWithInviteCodeRequest
import knu.dong.onedayonebaek.dto.*
import knu.dong.onedayonebaek.exception.InvalidReqParamException
import knu.dong.onedayonebaek.exception.response.BadRequestResponse
import knu.dong.onedayonebaek.exception.response.ForbiddenResponse
Expand All @@ -21,6 +18,8 @@ import org.springframework.http.MediaType
import org.springframework.security.core.Authentication
import org.springframework.validation.annotation.Validated
import org.springframework.web.bind.annotation.*
import java.time.LocalDate
import java.time.YearMonth


private val myLogger = KotlinLogging.logger {}
Expand Down Expand Up @@ -177,4 +176,59 @@ class GroupController(
groupService.leaveGroup(user, groupId)
}

@Operation(
summary = "스터디 그룹원들의 해결한 문제 목록 조회",
description = "스터디 그룹원들이 해결한 문제를 특정 일자 단위로 조회한다."
)
@GetMapping("/{groupId}/problems")
@ApiResponses(
ApiResponse(responseCode = "200", description = "스터디 그룹원들이 해결한 문제 목록"),
ApiResponse(
responseCode = "403", description = "속해있지 않은 스터디 그룹",
content = [Content(schema = Schema(implementation = ForbiddenResponse::class))],
),
ApiResponse(
responseCode = "404", description = "존재하지 않는 스터디 그룹",
content = [Content(schema = Schema(implementation = NotFoundResponse::class))],
)
)
fun getProblems(
@PathVariable groupId: Long,

@Schema(description = "조회 단위", required = true, implementation = DateUnit::class)
type: DateUnit,

@Schema(description = "type=day - 특정 일의 해결한 문제 목록을 조회", example = "2024-05-28")
date: LocalDate?,

@Schema(description = "type=month - 특정 달의 해결한 문제 목록을 조회", example = "2024-05",
type = "String",
format = "YYYY-MM")
yearMonth: YearMonth?,

@Schema(description = "type=range - 특정 범위의 날짜 동안 해결한 문제 목록을 조회(시작 날짜)", example = "2024-05-28")
startDate: LocalDate?,

@Schema(description = "type=range - 특정 범위의 날짜 동안 해결한 문제 목록을 조회(종료 날짜)", example = "2024-05-30")
endDate: LocalDate?,
authentication: Authentication
): List<ProblemsOfUser>{
val user = authentication.principal as User

return when (type) {
DateUnit.DAY -> {
groupService.getProblems(user, groupId,
date ?: throw InvalidReqParamException("date 필드가 비어있습니다."))
}
DateUnit.MONTH -> {
groupService.getProblems(user, groupId,
yearMonth ?: throw InvalidReqParamException("yearMonth 필드가 비어있습니다."))
}
else -> {
groupService.getProblems(user, groupId,
startDate ?: throw InvalidReqParamException("startDate 필드가 비어있습니다."),
endDate ?: throw InvalidReqParamException("endDate 필드가 비어있습니다."))
}
}
}
}
3 changes: 3 additions & 0 deletions back/src/main/kotlin/knu/dong/onedayonebaek/dto/DateUnit.kt
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package knu.dong.onedayonebaek.dto

enum class DateUnit { DAY, MONTH, RANGE }
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
package knu.dong.onedayonebaek.dto

data class ProblemsOfUser(val problems: MutableList<ProblemDto>, val username: String, val userId: Long)
Original file line number Diff line number Diff line change
Expand Up @@ -3,11 +3,15 @@ package knu.dong.onedayonebaek.repository
import knu.dong.onedayonebaek.domain.ContainGroup
import knu.dong.onedayonebaek.domain.Group
import knu.dong.onedayonebaek.domain.User
import org.springframework.data.jpa.repository.EntityGraph
import org.springframework.data.jpa.repository.JpaRepository
import java.util.*

interface ContainGroupRepository: JpaRepository<ContainGroup, Long> {

fun findByGroupAndUser(group: Group, user: User): Optional<ContainGroup>
fun existsByGroupAndUser(group: Group, user: User): Boolean

@EntityGraph(attributePaths = ["user"])
fun findAllByGroup(group: Group): List<ContainGroup>
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,11 +2,17 @@ package knu.dong.onedayonebaek.repository

import knu.dong.onedayonebaek.domain.Problem
import knu.dong.onedayonebaek.domain.User
import org.springframework.data.jpa.repository.EntityGraph
import org.springframework.data.jpa.repository.JpaRepository
import java.time.LocalDate


interface ProblemRepository: JpaRepository<Problem, Long> {
fun findAllBySolvedDateBetweenAndUser(start: LocalDate, end: LocalDate, user: User): List<Problem>
@EntityGraph(attributePaths = ["user"])
fun findAllBySolvedDateBetweenAndUserIsIn(start: LocalDate, end: LocalDate, users: List<User>): List<Problem>
fun findAllBySolvedDateAndUser(date: LocalDate, user: User): List<Problem>
@EntityGraph(attributePaths = ["user"])
fun findAllBySolvedDateAndUserIsIn(date: LocalDate, users: List<User>): List<Problem>

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,17 +2,21 @@ package knu.dong.onedayonebaek.service

import io.github.oshai.kotlinlogging.KotlinLogging
import knu.dong.onedayonebaek.domain.ContainGroup
import knu.dong.onedayonebaek.domain.Problem
import knu.dong.onedayonebaek.domain.User
import knu.dong.onedayonebaek.dto.*
import knu.dong.onedayonebaek.exception.ConflictException
import knu.dong.onedayonebaek.exception.ForbiddenException
import knu.dong.onedayonebaek.exception.NotFoundException
import knu.dong.onedayonebaek.repository.ContainGroupRepository
import knu.dong.onedayonebaek.repository.GroupRepository
import knu.dong.onedayonebaek.repository.ProblemRepository
import knu.dong.onedayonebaek.repository.UserRepository
import org.springframework.security.crypto.password.PasswordEncoder
import org.springframework.stereotype.Service
import org.springframework.transaction.annotation.Transactional
import java.time.LocalDate
import java.time.YearMonth
import java.util.stream.Collectors

private val myLogger = KotlinLogging.logger {}
Expand All @@ -23,6 +27,7 @@ class GroupService (
private val userRepository: UserRepository,
private val groupRepository: GroupRepository,
private val containGroupRepository: ContainGroupRepository,
private val problemRepository: ProblemRepository,
private val passwordEncoder: PasswordEncoder
){
fun getGroups(): List<GroupOfListDto> =
Expand Down Expand Up @@ -124,11 +129,60 @@ class GroupService (
containGroupRepository.delete(containGroup)
}

fun getProblems(user: User, groupId: Long, date: LocalDate): List<ProblemsOfUser> {
val group = groupRepository.findById(groupId).orElseThrow{ NotFoundException("해당 그룹이 없습니다.") }
if (group.isPrivate && !containGroupRepository.existsByGroupAndUser(group, user)) {
throw ForbiddenException("해당 비밀 그룹에 속해있지 않습니다.")
}

val users = containGroupRepository.findAllByGroup(group).map { it.user }

val problems = problemRepository.findAllBySolvedDateAndUserIsIn(date, users)

return mapUserProblem(users, problems)
}

fun getProblems(user: User, groupId: Long, yearMonth: YearMonth): List<ProblemsOfUser> {
val group = groupRepository.findById(groupId).orElseThrow{ NotFoundException("해당 그룹이 없습니다.") }
if (group.isPrivate && !containGroupRepository.existsByGroupAndUser(group, user)) {
throw ForbiddenException("해당 비밀 그룹에 속해있지 않습니다.")
}

val users = containGroupRepository.findAllByGroup(group).map { it.user }
val start = yearMonth.atDay(1)
val end = yearMonth.atEndOfMonth()

val problems = problemRepository.findAllBySolvedDateBetweenAndUserIsIn(start, end, users)

return mapUserProblem(users, problems)
}

fun getProblems(user: User, groupId: Long, startDate: LocalDate, endDate: LocalDate): List<ProblemsOfUser> {
val group = groupRepository.findById(groupId).orElseThrow{ NotFoundException("해당 그룹이 없습니다.") }
if (group.isPrivate && !containGroupRepository.existsByGroupAndUser(group, user)) {
throw ForbiddenException("해당 비밀 그룹에 속해있지 않습니다.")
}

val users = containGroupRepository.findAllByGroup(group).map { it.user }
val problems = problemRepository.findAllBySolvedDateBetweenAndUserIsIn(startDate, endDate, users)

return mapUserProblem(users, problems)
}

private fun getRandomInviteCode(length: Int): String {
val charset = ('a'..'z') + ('A'..'Z') + ('0'..'9')

return (1..length)
.map { charset.random() }
.joinToString("")
}

private fun mapUserProblem(users: List<User>, problems: List<Problem>): List<ProblemsOfUser> {
val userMap = mutableMapOf<Long, ProblemsOfUser>()

users.forEach { userMap[it.id!!] = ProblemsOfUser(arrayListOf(), it.name, it.id) }
problems.forEach { userMap[it.user.id]?.problems?.add(it.toProblemDto()) }

return userMap.values.toList()
}
}

0 comments on commit f7996a9

Please sign in to comment.