From 6f54616d4b5b4fcb8bbd4f25fccb09d082541436 Mon Sep 17 00:00:00 2001 From: ryuwldnjs Date: Mon, 16 Feb 2026 18:05:22 +0900 Subject: [PATCH] =?UTF-8?q?problem:=20Middleman=20=EC=A0=9C=EA=B1=B0=20?= =?UTF-8?q?=EB=B0=8F=20=EB=8D=B0=EB=93=9C=20=EC=BD=94=EB=93=9C=20=EC=A0=95?= =?UTF-8?q?=EB=A6=AC?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit - ProblemService 삭제 (SolvedAcClient 단순 위임만 수행) - RecommendationCreator가 SolvedAcClient 직접 호출 - ProblemController, ProblemRecommendRequest 삭제 (사용처 없는 프록시 엔드포인트) - TeamProblem, TeamProblemRecommendResponse 삭제 (데드 코드) --- .../problem/ProblemController.java | 73 ------------------- .../problem/domain/TeamProblem.java | 32 -------- .../problem/dto/ProblemRecommendRequest.java | 20 ----- .../dto/TeamProblemRecommendResponse.java | 14 ---- .../problem/service/ProblemService.java | 38 ---------- .../service/RecommendationCreator.java | 6 +- .../service/RecommendationCreatorTest.java | 10 +-- 7 files changed, 8 insertions(+), 185 deletions(-) delete mode 100644 src/main/java/com/ryu/studyhelper/problem/ProblemController.java delete mode 100644 src/main/java/com/ryu/studyhelper/problem/domain/TeamProblem.java delete mode 100644 src/main/java/com/ryu/studyhelper/problem/dto/ProblemRecommendRequest.java delete mode 100644 src/main/java/com/ryu/studyhelper/problem/dto/TeamProblemRecommendResponse.java delete mode 100644 src/main/java/com/ryu/studyhelper/problem/service/ProblemService.java diff --git a/src/main/java/com/ryu/studyhelper/problem/ProblemController.java b/src/main/java/com/ryu/studyhelper/problem/ProblemController.java deleted file mode 100644 index 0b541c0..0000000 --- a/src/main/java/com/ryu/studyhelper/problem/ProblemController.java +++ /dev/null @@ -1,73 +0,0 @@ -package com.ryu.studyhelper.problem; - -import com.ryu.studyhelper.config.security.PrincipalDetails; -import com.ryu.studyhelper.member.domain.Member; -import com.ryu.studyhelper.problem.dto.ProblemRecommendRequest; -import com.ryu.studyhelper.problem.service.ProblemService; -import com.ryu.studyhelper.infrastructure.solvedac.dto.ProblemInfo; -import io.swagger.v3.oas.annotations.Operation; -import io.swagger.v3.oas.annotations.Parameter; -import jakarta.validation.Valid; -import lombok.RequiredArgsConstructor; -import lombok.extern.slf4j.Slf4j; -import org.springframework.http.ResponseEntity; -import org.springframework.security.core.annotation.AuthenticationPrincipal; -import org.springframework.web.bind.annotation.*; - -import java.util.List; - -@RestController -@RequiredArgsConstructor -@RequestMapping("/api/problem") -@Slf4j -public class ProblemController { - - private final ProblemService problemService; - - @Operation( - summary = "문제 추천 API", - description = """ - 주어진 Solved.ac 핸들에 기반해 추천 문제 목록을 반환합니다. - 추천 개수(`count`)를 지정하지 않으면 기본값은 3개입니다. - """ - ) - @GetMapping("/recommend") - public ResponseEntity> recommend( - @Parameter(description = "Solved.ac 핸들", example = "ryu_handle") - @RequestParam String handle, - - @Parameter(description = "추천 문제 개수 (기본값 3)", example = "5") - @RequestParam(defaultValue = "3") int count, - - @AuthenticationPrincipal PrincipalDetails principalDetails) { - - Member currentUser = principalDetails.getMember(); - log.info("User {} requesting problem recommendations for handle: {}", currentUser.getId(), handle); - - List problems = problemService.recommend(handle, count); - return ResponseEntity.ok(problems); - } - - - - @Operation( - summary = "문제 추천 API (여러 유저)", - description = """ - 여러 Solved.ac 핸들을 기반으로 추천 문제 목록을 반환합니다. - count가 null이면 기본값은 1개입니다. - """ - ) - @PostMapping("/recommend") - public ResponseEntity> recommendMulti( - @Valid @RequestBody ProblemRecommendRequest req, - @AuthenticationPrincipal PrincipalDetails principalDetails) { - - Member currentUser = principalDetails.getMember(); - log.info("User {} requesting multi-user problem recommendations for handles: {}", - currentUser.getId(), req.handles()); - - int count = (req.count() != null) ? req.count() : 1; // 기본값 1 - List problems = problemService.recommend(req, count); - return ResponseEntity.ok(problems); - } -} \ No newline at end of file diff --git a/src/main/java/com/ryu/studyhelper/problem/domain/TeamProblem.java b/src/main/java/com/ryu/studyhelper/problem/domain/TeamProblem.java deleted file mode 100644 index acb9ac9..0000000 --- a/src/main/java/com/ryu/studyhelper/problem/domain/TeamProblem.java +++ /dev/null @@ -1,32 +0,0 @@ -package com.ryu.studyhelper.problem.domain; - -import com.ryu.studyhelper.common.entity.BaseEntity; -import com.ryu.studyhelper.team.domain.Team; -import jakarta.persistence.*; -import lombok.*; - -import java.time.LocalDateTime; - -@Entity -@Getter -@NoArgsConstructor(access = AccessLevel.PROTECTED) -@AllArgsConstructor -@Builder -public class TeamProblem extends BaseEntity { - - @Id - @GeneratedValue(strategy = GenerationType.IDENTITY) - private Long id; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "team_id", nullable = false) - private Team team; - - @ManyToOne(fetch = FetchType.LAZY) - @JoinColumn(name = "problem_id", nullable = false) - private Problem problem; - - private LocalDateTime deadline; - - private boolean isAuto; // 자동 추천 여부 -} \ No newline at end of file diff --git a/src/main/java/com/ryu/studyhelper/problem/dto/ProblemRecommendRequest.java b/src/main/java/com/ryu/studyhelper/problem/dto/ProblemRecommendRequest.java deleted file mode 100644 index 1e415aa..0000000 --- a/src/main/java/com/ryu/studyhelper/problem/dto/ProblemRecommendRequest.java +++ /dev/null @@ -1,20 +0,0 @@ -package com.ryu.studyhelper.problem.dto; - -import io.swagger.v3.oas.annotations.media.Schema; -import jakarta.validation.constraints.NotEmpty; -import jakarta.validation.constraints.NotBlank; -import jakarta.validation.constraints.Positive; -import java.util.List; - -@Schema(description = "문제 추천 요청 DTO (record)") -public record ProblemRecommendRequest( - - @NotEmpty(message = "handles는 최소 1개 이상이어야 합니다.") - @Schema(description = "추천을 받을 Solved.ac 핸들 목록", example = "[\"alice\", \"bob\"]") - List<@NotBlank(message = "handle 값은 비어 있을 수 없습니다.") String> handles, - - // null이면 컨트롤러에서 기본값 1로 처리, 값이 오면 1 이상이어야 함 - @Positive(message = "count가 null이 아니면 1 이상이어야 합니다.") - @Schema(description = "추천 문제 개수 (null이면 기본값 1)", example = "5", defaultValue = "1") - Integer count -) {} \ No newline at end of file diff --git a/src/main/java/com/ryu/studyhelper/problem/dto/TeamProblemRecommendResponse.java b/src/main/java/com/ryu/studyhelper/problem/dto/TeamProblemRecommendResponse.java deleted file mode 100644 index 94bc359..0000000 --- a/src/main/java/com/ryu/studyhelper/problem/dto/TeamProblemRecommendResponse.java +++ /dev/null @@ -1,14 +0,0 @@ -package com.ryu.studyhelper.problem.dto; - -import com.ryu.studyhelper.infrastructure.solvedac.dto.ProblemInfo; - -import java.util.List; - -public record TeamProblemRecommendResponse( - List problems, - List handles -) { - public static TeamProblemRecommendResponse from(List problems, List handles) { - return new TeamProblemRecommendResponse(problems, handles); - } -} diff --git a/src/main/java/com/ryu/studyhelper/problem/service/ProblemService.java b/src/main/java/com/ryu/studyhelper/problem/service/ProblemService.java deleted file mode 100644 index ae41d7d..0000000 --- a/src/main/java/com/ryu/studyhelper/problem/service/ProblemService.java +++ /dev/null @@ -1,38 +0,0 @@ -package com.ryu.studyhelper.problem.service; - - -import com.ryu.studyhelper.infrastructure.solvedac.SolvedAcClient; -import com.ryu.studyhelper.infrastructure.solvedac.dto.ProblemInfo; -import com.ryu.studyhelper.problem.dto.ProblemRecommendRequest; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - -import java.util.List; - -@RequiredArgsConstructor -@Service -@Transactional -public class ProblemService { - - private final SolvedAcClient solvedAcClient; - - /** - * 문제 추천 - */ - public List recommend(List handles, int count, Integer minLevel, Integer maxLevel, List tagKeys) { - return solvedAcClient.recommendUnsolvedProblems(handles, count, minLevel, maxLevel, tagKeys); - } - - public List recommend(ProblemRecommendRequest request, int count) { - return recommend(request.handles(), count, null, null, null); - } - - public List recommend(String handle, int count) { - return recommend(List.of(handle), count, null, null, null); - } - - public List recommend(List handles, int count) { - return recommend(handles, count, null, null, null); - } -} \ No newline at end of file diff --git a/src/main/java/com/ryu/studyhelper/recommendation/service/RecommendationCreator.java b/src/main/java/com/ryu/studyhelper/recommendation/service/RecommendationCreator.java index 5d78e95..8ecfb56 100644 --- a/src/main/java/com/ryu/studyhelper/recommendation/service/RecommendationCreator.java +++ b/src/main/java/com/ryu/studyhelper/recommendation/service/RecommendationCreator.java @@ -2,10 +2,10 @@ import com.ryu.studyhelper.common.enums.CustomResponseStatus; import com.ryu.studyhelper.common.exception.CustomException; +import com.ryu.studyhelper.infrastructure.solvedac.SolvedAcClient; import com.ryu.studyhelper.infrastructure.solvedac.dto.ProblemInfo; import com.ryu.studyhelper.member.domain.Member; import com.ryu.studyhelper.problem.domain.Problem; -import com.ryu.studyhelper.problem.service.ProblemService; import com.ryu.studyhelper.problem.service.ProblemSyncService; import com.ryu.studyhelper.recommendation.domain.Recommendation; import com.ryu.studyhelper.recommendation.domain.RecommendationProblem; @@ -34,7 +34,7 @@ class RecommendationCreator { private final TeamMemberRepository teamMemberRepository; private final TeamIncludeTagRepository teamIncludeTagRepository; - private final ProblemService problemService; + private final SolvedAcClient solvedAcClient; private final ProblemSyncService problemSyncService; private final RecommendationRepository recommendationRepository; private final RecommendationProblemRepository recommendationProblemRepository; @@ -86,7 +86,7 @@ private List recommendProblemsForTeam(Team team) { List tagKeys = teamIncludeTagRepository.findTagKeysByTeamId(team.getId()); - List problemInfos = problemService.recommend( + List problemInfos = solvedAcClient.recommendUnsolvedProblems( handles, team.getProblemCount(), team.getEffectiveMinProblemLevel(), diff --git a/src/test/java/com/ryu/studyhelper/recommendation/service/RecommendationCreatorTest.java b/src/test/java/com/ryu/studyhelper/recommendation/service/RecommendationCreatorTest.java index e819f03..29c57e2 100644 --- a/src/test/java/com/ryu/studyhelper/recommendation/service/RecommendationCreatorTest.java +++ b/src/test/java/com/ryu/studyhelper/recommendation/service/RecommendationCreatorTest.java @@ -2,10 +2,10 @@ import com.ryu.studyhelper.common.enums.CustomResponseStatus; import com.ryu.studyhelper.common.exception.CustomException; +import com.ryu.studyhelper.infrastructure.solvedac.SolvedAcClient; import com.ryu.studyhelper.infrastructure.solvedac.dto.ProblemInfo; import com.ryu.studyhelper.member.domain.Member; import com.ryu.studyhelper.problem.domain.Problem; -import com.ryu.studyhelper.problem.service.ProblemService; import com.ryu.studyhelper.problem.service.ProblemSyncService; import com.ryu.studyhelper.recommendation.domain.Recommendation; import com.ryu.studyhelper.recommendation.domain.RecommendationType; @@ -43,7 +43,7 @@ class RecommendationCreatorTest { private TeamIncludeTagRepository teamIncludeTagRepository; @Mock - private ProblemService problemService; + private SolvedAcClient solvedAcClient; @Mock private ProblemSyncService problemSyncService; @@ -76,7 +76,7 @@ void createManualRecommendation() { when(teamMemberRepository.findHandlesByTeamId(TEAM_ID)).thenReturn(List.of("handle1")); when(teamIncludeTagRepository.findTagKeysByTeamId(TEAM_ID)).thenReturn(List.of()); - when(problemService.recommend(anyList(), anyInt(), anyInt(), anyInt(), anyList())) + when(solvedAcClient.recommendUnsolvedProblems(anyList(), anyInt(), anyInt(), anyInt(), anyList())) .thenReturn(List.of(mock(ProblemInfo.class))); when(problemSyncService.syncProblems(anyList())).thenReturn(problems); when(recommendationRepository.save(any(Recommendation.class))) @@ -104,7 +104,7 @@ void createScheduledRecommendation() { when(teamMemberRepository.findHandlesByTeamId(TEAM_ID)).thenReturn(List.of("handle1")); when(teamIncludeTagRepository.findTagKeysByTeamId(TEAM_ID)).thenReturn(List.of()); - when(problemService.recommend(anyList(), anyInt(), anyInt(), anyInt(), anyList())) + when(solvedAcClient.recommendUnsolvedProblems(anyList(), anyInt(), anyInt(), anyInt(), anyList())) .thenReturn(List.of(mock(ProblemInfo.class))); when(problemSyncService.syncProblems(anyList())).thenReturn(problems); when(recommendationRepository.save(any(Recommendation.class))) @@ -128,7 +128,7 @@ void createsMemberRecommendationsForAllMembers() { when(teamMemberRepository.findHandlesByTeamId(TEAM_ID)).thenReturn(List.of("handle1")); when(teamIncludeTagRepository.findTagKeysByTeamId(TEAM_ID)).thenReturn(List.of()); - when(problemService.recommend(anyList(), anyInt(), anyInt(), anyInt(), anyList())) + when(solvedAcClient.recommendUnsolvedProblems(anyList(), anyInt(), anyInt(), anyInt(), anyList())) .thenReturn(List.of(mock(ProblemInfo.class))); when(problemSyncService.syncProblems(anyList())).thenReturn(problems); when(recommendationRepository.save(any(Recommendation.class)))