diff --git a/src/main/java/com/gamzabat/algohub/exception/CustomExceptionHandler.java b/src/main/java/com/gamzabat/algohub/exception/CustomExceptionHandler.java index 4dd0729c..5ff9adca 100644 --- a/src/main/java/com/gamzabat/algohub/exception/CustomExceptionHandler.java +++ b/src/main/java/com/gamzabat/algohub/exception/CustomExceptionHandler.java @@ -233,7 +233,7 @@ protected ResponseEntity handleCannotFoundVerificationCodeExcepti .status(HttpStatus.BAD_REQUEST) .body(new ErrorResponse(HttpStatus.BAD_REQUEST.value(), e.getMessage(), null)); } - + @ExceptionHandler(CannotFoundEdgeCaseException.class) protected ResponseEntity handleCannotFoundEdgeCaseException( CannotFoundEdgeCaseException e) { @@ -249,4 +249,5 @@ protected ResponseEntity handleNotAuthorizedUserException( .status(e.getHttpStatus()) .body(new ErrorResponse(e.getHttpStatus().value(), e.getError(), null)); } + } diff --git a/src/main/java/com/gamzabat/algohub/feature/problem/controller/ProblemController.java b/src/main/java/com/gamzabat/algohub/feature/problem/controller/ProblemController.java index 9bd25039..4b3f23ed 100644 --- a/src/main/java/com/gamzabat/algohub/feature/problem/controller/ProblemController.java +++ b/src/main/java/com/gamzabat/algohub/feature/problem/controller/ProblemController.java @@ -6,7 +6,10 @@ import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; +import org.springframework.data.web.PageableDefault; +import org.springframework.http.HttpStatus; import org.springframework.http.ResponseEntity; +import org.springframework.security.core.parameters.P; import org.springframework.validation.Errors; import org.springframework.web.bind.annotation.DeleteMapping; import org.springframework.web.bind.annotation.GetMapping; @@ -23,6 +26,7 @@ import com.gamzabat.algohub.feature.problem.dto.CreateProblemRequest; import com.gamzabat.algohub.feature.problem.dto.EditProblemRequest; import com.gamzabat.algohub.feature.problem.dto.GetProblemResponse; +import com.gamzabat.algohub.feature.problem.enums.ProblemListStatus; import com.gamzabat.algohub.feature.problem.service.ProblemService; import com.gamzabat.algohub.feature.user.domain.User; @@ -59,26 +63,17 @@ public ResponseEntity editProblemDeadline(@AuthedUser User user, return ResponseEntity.ok().build(); } - @GetMapping(value = "/groups/{groupId}/problems/in-progress") - @Operation(summary = "진행 중인 문제 목록 조회 API", description = "특정 그룹에 대한 문제를 모두 조회하는 API") - public ResponseEntity> getInProgressProblemList(@AuthedUser User user, + @GetMapping(value = "/groups/{groupId}/problems") + @Operation(summary = "문제 목록 조회 API", description = "특정 그룹에 대한 문제를 부분 조회하는 API") + public ResponseEntity> getProblems(@AuthedUser User user, @PathVariable Long groupId, - @RequestParam(name = "unsolved-only") Boolean unsolvedOnly, - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "20") int size) { - Pageable pageable = PageRequest.of(page, size, Sort.by(PROBLEM_SORT_BY).descending()); - Page response = problemService.getInProgressProblems(user, groupId, unsolvedOnly, pageable); - return ResponseEntity.ok().body(response); - } + @RequestParam ProblemListStatus status, + @RequestParam(name = "unsolved-only", required = false) Boolean unsolvedOnly, + @PageableDefault(page = 0, size = 20, sort = "endDate", direction = Sort.Direction.DESC) + Pageable pageable) { + + Page response = problemService.getProblems(user, groupId, status, unsolvedOnly, pageable); - @GetMapping(value = "/groups/{groupId}/problems/expired") - @Operation(summary = "마감 된 문제 목록 조회 API", description = "특정 그룹에 대한 문제를 모두 조회하는 API") - public ResponseEntity> getExpiredProblemList(@AuthedUser User user, - @PathVariable Long groupId, - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "20") int size) { - Pageable pageable = PageRequest.of(page, size, Sort.by(PROBLEM_SORT_BY).descending()); - Page response = problemService.getExpiredProblems(user, groupId, pageable); return ResponseEntity.ok().body(response); } @@ -89,23 +84,6 @@ public ResponseEntity getProblem(@AuthedUser User user, @Pat return ResponseEntity.ok().body(response); } - @GetMapping("/groups/{groupId}/problems/deadline-reached") - @Operation(summary = "마감 기한이 내일까지인 문제들 조회 API") - public ResponseEntity> getDeadlineReachedProblemList(@AuthedUser User user, - @PathVariable Long groupId) { - return ResponseEntity.ok().body(problemService.getDeadlineReachedProblemList(user, groupId)); - } - - @GetMapping("/groups/{groupId}/problems/queued") - @Operation(summary = "시작 예정인 문제들 조회 API") - public ResponseEntity> getQueuedProblemList(@AuthedUser User user, - @PathVariable Long groupId, - @RequestParam(defaultValue = "0") int page, - @RequestParam(defaultValue = "20") int size) { - Pageable pageable = PageRequest.of(page, size, Sort.by(PROBLEM_SORT_BY).descending()); - return ResponseEntity.ok().body(problemService.getQueuedProblems(user, groupId, pageable)); - } - @DeleteMapping(value = "/problems/{problemId}") @Operation(summary = "문제 삭제 API") public ResponseEntity deleteProblem(@AuthedUser User user, @PathVariable Long problemId) { diff --git a/src/main/java/com/gamzabat/algohub/feature/problem/enums/ProblemListStatus.java b/src/main/java/com/gamzabat/algohub/feature/problem/enums/ProblemListStatus.java new file mode 100644 index 00000000..da4a1859 --- /dev/null +++ b/src/main/java/com/gamzabat/algohub/feature/problem/enums/ProblemListStatus.java @@ -0,0 +1,8 @@ +package com.gamzabat.algohub.feature.problem.enums; + +public enum ProblemListStatus { + IN_PROGRESS, + EXPIRED, + QUEUED + +} diff --git a/src/main/java/com/gamzabat/algohub/feature/problem/service/ProblemService.java b/src/main/java/com/gamzabat/algohub/feature/problem/service/ProblemService.java index 12355300..62644f77 100644 --- a/src/main/java/com/gamzabat/algohub/feature/problem/service/ProblemService.java +++ b/src/main/java/com/gamzabat/algohub/feature/problem/service/ProblemService.java @@ -3,7 +3,6 @@ import static com.gamzabat.algohub.constants.ApiConstants.*; import java.time.LocalDate; -import java.util.Comparator; import java.util.List; import org.springframework.data.domain.Page; @@ -37,6 +36,7 @@ import com.gamzabat.algohub.feature.problem.dto.CreateProblemRequest; import com.gamzabat.algohub.feature.problem.dto.EditProblemRequest; import com.gamzabat.algohub.feature.problem.dto.GetProblemResponse; +import com.gamzabat.algohub.feature.problem.enums.ProblemListStatus; import com.gamzabat.algohub.feature.problem.exception.NotBojLinkException; import com.gamzabat.algohub.feature.problem.exception.SolvedAcApiErrorException; import com.gamzabat.algohub.feature.problem.repository.ProblemRepository; @@ -151,6 +151,22 @@ private void checkProblemStartDate(EditProblemRequest request, Problem problem) "문제 시작 날짜는 마감 날짜 이후로 수정할 수 없습니다."); } + @Transactional(readOnly = true) + public Page getProblems(User user, Long groupId, ProblemListStatus status, Boolean unsolvedOnly, Pageable pageable) { + Page response; + if (status == ProblemListStatus.IN_PROGRESS) { + if (unsolvedOnly == null) { + unsolvedOnly = false; + } + response = getInProgressProblems(user, groupId, unsolvedOnly, pageable); + } else if (status == ProblemListStatus.EXPIRED) { + response = getExpiredProblems(user, groupId, pageable); + } else { + response = getQueuedProblems(user, groupId, pageable); + } + return response; + } + @Transactional(readOnly = true) public Page getInProgressProblems(User user, Long groupId, Boolean unsolvedOnly, Pageable pageable) { @@ -216,37 +232,6 @@ public void deleteProblem(User user, Long problemId) { log.info("success to delete problem user_id={} , problem_id = {}", user.getId(), problemId); } - @Transactional(readOnly = true) - public List getDeadlineReachedProblemList(User user, Long groupId) { - StudyGroup group = getGroup(groupId); - if (!groupMemberRepository.existsByUserAndStudyGroup(user, group)) - throw new ProblemValidationException(HttpStatus.FORBIDDEN.value(), "문제를 조회할 권한이 없습니다."); - - List problems = problemRepository.findAllByStudyGroupAndEndDateBetween(group, LocalDate.now(), - LocalDate.now().plusDays(1)); - problems.sort(Comparator.comparing(Problem::getEndDate)); - - return problems.stream().map(problem -> { - Integer correctCount = solutionRepository.countDistinctUsersWithCorrectSolutionsByProblemId(problem.getId(), - BOJResultConstants.CORRECT); - Integer submitMemberCount = solutionRepository.countDistinctUsersByProblem(problem); - Integer groupMemberCount = groupMemberRepository.countMembersByStudyGroup(group); - Integer accuracy = calculateAccuracy(submitMemberCount, correctCount); - - return new GetProblemResponse( - problem.getTitle(), - problem.getId(), - problem.getLink(), - problem.getStartDate(), - problem.getEndDate(), - problem.getLevel(), - solutionRepository.existsByUserAndProblemAndResult(user, problem, BOJResultConstants.CORRECT), - submitMemberCount, - groupMemberCount, - accuracy); - }).toList(); - } - @Transactional(readOnly = true) public Page getQueuedProblems(User user, Long groupId, Pageable pageable) { StudyGroup group = getGroup(groupId); diff --git a/src/test/java/com/gamzabat/algohub/feature/problem/controller/ProblemControllerTest.java b/src/test/java/com/gamzabat/algohub/feature/problem/controller/ProblemControllerTest.java index 59d7bb7c..391856d0 100644 --- a/src/test/java/com/gamzabat/algohub/feature/problem/controller/ProblemControllerTest.java +++ b/src/test/java/com/gamzabat/algohub/feature/problem/controller/ProblemControllerTest.java @@ -17,6 +17,7 @@ import org.junit.jupiter.api.Test; import org.junit.jupiter.params.ParameterizedTest; import org.junit.jupiter.params.provider.CsvSource; +import org.mockito.internal.matchers.Null; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.autoconfigure.web.servlet.WebMvcTest; import org.springframework.boot.test.mock.mockito.MockBean; @@ -41,6 +42,7 @@ import com.gamzabat.algohub.feature.problem.dto.CreateProblemRequest; import com.gamzabat.algohub.feature.problem.dto.EditProblemRequest; import com.gamzabat.algohub.feature.problem.dto.GetProblemResponse; +import com.gamzabat.algohub.feature.problem.enums.ProblemListStatus; import com.gamzabat.algohub.feature.problem.exception.SolvedAcApiErrorException; import com.gamzabat.algohub.feature.problem.repository.ProblemRepository; import com.gamzabat.algohub.feature.problem.service.ProblemService; @@ -300,38 +302,24 @@ void editProblemDeadlineFailed_6() throws Exception { } @Test - @DisplayName("진행 중인 문제 목록 조회 성공") + @DisplayName("문제 목록 조회 성공") void getProblemList() throws Exception { // given Pageable pageable = PageRequest.of(0, 20, Sort.by("endDate").descending()); Page response = new PageImpl<>(new ArrayList<>()); - when(problemService.getInProgressProblems(any(User.class), anyLong(), eq(false), + when(problemService.getProblems(any(User.class), anyLong(), eq(ProblemListStatus.IN_PROGRESS) ,eq(false), any(Pageable.class))).thenReturn( response); // when, then - mockMvc.perform(get("/api/groups/{groupId}/problems/in-progress", groupId) + mockMvc.perform(get("/api/groups/{groupId}/problems", groupId) .header("Authorization", token) - .param("unsolved-only", String.valueOf(false))) + .param("page", "0") + .param("size", "20") + .param("unsolved-only", String.valueOf(false)) + .param("status", String.valueOf(ProblemListStatus.IN_PROGRESS))) .andExpect(status().isOk()) .andExpect(content().string(objectMapper.writeValueAsString(response))); - verify(problemService, times(1)).getInProgressProblems(user, groupId, false, pageable); - } - - @Test - @DisplayName("문제 목록 조회 실패 : 권한 없음") - void getProblemListFailed_1() throws Exception { - // given - Pageable pageable = PageRequest.of(0, 20, Sort.by("endDate").descending()); - when( - problemService.getInProgressProblems(any(User.class), anyLong(), eq(false), any(Pageable.class))).thenThrow( - new ProblemValidationException(HttpStatus.FORBIDDEN.value(), "문제를 조회할 권한이 없습니다.")); - // when, then - mockMvc.perform(get("/api/groups/{groupId}/problems/in-progress", groupId) - .header("Authorization", token) - .param("unsolved-only", String.valueOf(false))) - .andExpect(status().isForbidden()) - .andExpect(jsonPath("$.error").value("문제를 조회할 권한이 없습니다.")); - verify(problemService, times(1)).getInProgressProblems(user, groupId, false, pageable); + verify(problemService, times(1)).getProblems(user, groupId,ProblemListStatus.IN_PROGRESS,false, pageable); } @Test @@ -343,6 +331,7 @@ void deleteProblem() throws Exception { mockMvc.perform(delete("/api/problems/{problemId}", problemId) .header("Authorization", token) .param("problemId", String.valueOf(problemId))) + .andExpect(status().isOk()); verify(problemService, times(1)).deleteProblem(user, problemId); }