From e54abba393af11a15d3dae536d420e03353744b8 Mon Sep 17 00:00:00 2001 From: gidskql6671 Date: Tue, 28 May 2024 02:22:23 +0900 Subject: [PATCH] =?UTF-8?q?=EA=B7=B8=EB=A3=B9=EC=9B=90=EB=93=A4=EC=9D=98?= =?UTF-8?q?=20=ED=95=B4=EA=B2=B0=ED=95=9C=20=EB=AC=B8=EC=A0=9C=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - 일 단위, 달 단위, 범위 지정 가능 --- .../controller/GroupController.kt | 62 +++++++++++++++++-- .../knu/dong/onedayonebaek/dto/DateUnit.kt | 3 + .../dong/onedayonebaek/dto/ProblemsOfUser.kt | 3 + .../repository/ContainGroupRepository.kt | 4 ++ .../repository/ProblemRepository.kt | 6 ++ .../onedayonebaek/service/GroupService.kt | 54 ++++++++++++++++ 6 files changed, 128 insertions(+), 4 deletions(-) create mode 100644 back/src/main/kotlin/knu/dong/onedayonebaek/dto/DateUnit.kt create mode 100644 back/src/main/kotlin/knu/dong/onedayonebaek/dto/ProblemsOfUser.kt diff --git a/back/src/main/kotlin/knu/dong/onedayonebaek/controller/GroupController.kt b/back/src/main/kotlin/knu/dong/onedayonebaek/controller/GroupController.kt index 58dc704..2dde12d 100644 --- a/back/src/main/kotlin/knu/dong/onedayonebaek/controller/GroupController.kt +++ b/back/src/main/kotlin/knu/dong/onedayonebaek/controller/GroupController.kt @@ -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 @@ -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 {} @@ -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{ + 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 필드가 비어있습니다.")) + } + } + } } \ No newline at end of file diff --git a/back/src/main/kotlin/knu/dong/onedayonebaek/dto/DateUnit.kt b/back/src/main/kotlin/knu/dong/onedayonebaek/dto/DateUnit.kt new file mode 100644 index 0000000..b55e734 --- /dev/null +++ b/back/src/main/kotlin/knu/dong/onedayonebaek/dto/DateUnit.kt @@ -0,0 +1,3 @@ +package knu.dong.onedayonebaek.dto + +enum class DateUnit { DAY, MONTH, RANGE } diff --git a/back/src/main/kotlin/knu/dong/onedayonebaek/dto/ProblemsOfUser.kt b/back/src/main/kotlin/knu/dong/onedayonebaek/dto/ProblemsOfUser.kt new file mode 100644 index 0000000..3f9af61 --- /dev/null +++ b/back/src/main/kotlin/knu/dong/onedayonebaek/dto/ProblemsOfUser.kt @@ -0,0 +1,3 @@ +package knu.dong.onedayonebaek.dto + +data class ProblemsOfUser(val problems: MutableList, val username: String, val userId: Long) \ No newline at end of file diff --git a/back/src/main/kotlin/knu/dong/onedayonebaek/repository/ContainGroupRepository.kt b/back/src/main/kotlin/knu/dong/onedayonebaek/repository/ContainGroupRepository.kt index 72b93cc..076bc46 100644 --- a/back/src/main/kotlin/knu/dong/onedayonebaek/repository/ContainGroupRepository.kt +++ b/back/src/main/kotlin/knu/dong/onedayonebaek/repository/ContainGroupRepository.kt @@ -3,6 +3,7 @@ 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.* @@ -10,4 +11,7 @@ interface ContainGroupRepository: JpaRepository { fun findByGroupAndUser(group: Group, user: User): Optional fun existsByGroupAndUser(group: Group, user: User): Boolean + + @EntityGraph(attributePaths = ["user"]) + fun findAllByGroup(group: Group): List } \ No newline at end of file diff --git a/back/src/main/kotlin/knu/dong/onedayonebaek/repository/ProblemRepository.kt b/back/src/main/kotlin/knu/dong/onedayonebaek/repository/ProblemRepository.kt index a8a5a37..a9af1a5 100644 --- a/back/src/main/kotlin/knu/dong/onedayonebaek/repository/ProblemRepository.kt +++ b/back/src/main/kotlin/knu/dong/onedayonebaek/repository/ProblemRepository.kt @@ -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 { fun findAllBySolvedDateBetweenAndUser(start: LocalDate, end: LocalDate, user: User): List + @EntityGraph(attributePaths = ["user"]) + fun findAllBySolvedDateBetweenAndUserIsIn(start: LocalDate, end: LocalDate, users: List): List fun findAllBySolvedDateAndUser(date: LocalDate, user: User): List + @EntityGraph(attributePaths = ["user"]) + fun findAllBySolvedDateAndUserIsIn(date: LocalDate, users: List): List + } \ No newline at end of file diff --git a/back/src/main/kotlin/knu/dong/onedayonebaek/service/GroupService.kt b/back/src/main/kotlin/knu/dong/onedayonebaek/service/GroupService.kt index 4e5a1a2..0cd69a6 100644 --- a/back/src/main/kotlin/knu/dong/onedayonebaek/service/GroupService.kt +++ b/back/src/main/kotlin/knu/dong/onedayonebaek/service/GroupService.kt @@ -2,6 +2,7 @@ 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 @@ -9,10 +10,13 @@ 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 {} @@ -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 = @@ -124,6 +129,46 @@ class GroupService ( containGroupRepository.delete(containGroup) } + fun getProblems(user: User, groupId: Long, date: LocalDate): List { + 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 { + 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 { + 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') @@ -131,4 +176,13 @@ class GroupService ( .map { charset.random() } .joinToString("") } + + private fun mapUserProblem(users: List, problems: List): List { + val userMap = mutableMapOf() + + 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() + } } \ No newline at end of file