-
Notifications
You must be signed in to change notification settings - Fork 0
hotfix: trim & 학기별 과목 정리 #83
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Changes from all commits
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
|
|
@@ -34,30 +34,33 @@ public class CourseRankingService { | |
| private static final List<String> LIBERAL_EXCLUDE_KEYWORDS = | ||
| List.of("공동체리더십훈련", "채플"); | ||
|
|
||
| public CourseRankingService(CourseRankingRepository repository, MajorRoadmapIndex majorRoadmapIndex) { | ||
| public CourseRankingService( | ||
| CourseRankingRepository repository, | ||
| MajorRoadmapIndex majorRoadmapIndex | ||
| ) { | ||
| this.repository = repository; | ||
| this.majorRoadmapIndex = majorRoadmapIndex; | ||
| } | ||
|
|
||
| public RankingResponse getCourseRanking() { | ||
| var major = loadMajorByTerms(); // ✅ 변경 | ||
| var major = loadMajorByTerms(); | ||
|
|
||
| var liberal = new LiberalRanking( | ||
| loadTop10(LIB_FAITH, true), | ||
| loadTop10(LIB_GENERAL_EDU, true), | ||
| loadTop10(LIB_BSM, true), | ||
| loadTop10(LIB_FREE_ELECTIVE, true) | ||
| loadTop10Merged(LIB_FAITH, true), | ||
| loadTop10Merged(LIB_GENERAL_EDU, true), | ||
| loadTop10Merged(LIB_BSM, true), | ||
| loadTop10Merged(LIB_FREE_ELECTIVE, true) | ||
| ); | ||
|
|
||
| return new RankingResponse(major, liberal); | ||
| } | ||
|
|
||
| /** | ||
| * ✅ 전공: MAJOR에서 많이 가져온 뒤 로드맵(term)으로 분류해서 각 term Top10 생성 | ||
| * 1-1은 없음 → y1s2부터 생성 | ||
| * - 1-1은 없음 → y1s2부터 생성 | ||
| * - 같은 과목(공백 차이)은 term 버킷 안에서 합산 | ||
| */ | ||
| private MajorRanking loadMajorByTerms() { | ||
| // term별 버킷 (순서 고정) | ||
| Map<String, List<CourseRankingRepository.CourseCountRow>> bucket = new LinkedHashMap<>(); | ||
| bucket.put("y1s2", new ArrayList<>()); | ||
| bucket.put("y2s1", new ArrayList<>()); | ||
|
|
@@ -79,59 +82,82 @@ private MajorRanking loadMajorByTerms() { | |
|
|
||
| var key = termOpt.get().toBucketKey(); | ||
| var list = bucket.get(key); | ||
| if (list != null) list.add(r); // 혹시 1-1 같은게 들어오면 null이라 무시 | ||
| if (list != null) list.add(r); | ||
| } | ||
|
|
||
| return new MajorRanking( | ||
| toTop10(bucket.get("y1s2")), | ||
| toTop10(bucket.get("y2s1")), | ||
| toTop10(bucket.get("y2s2")), | ||
| toTop10(bucket.get("y3s1")), | ||
| toTop10(bucket.get("y3s2")), | ||
| toTop10(bucket.get("y4s1")), | ||
| toTop10(bucket.get("y4s2")) | ||
| toTop10Merged(bucket.get("y1s2")), | ||
| toTop10Merged(bucket.get("y2s1")), | ||
| toTop10Merged(bucket.get("y2s2")), | ||
| toTop10Merged(bucket.get("y3s1")), | ||
| toTop10Merged(bucket.get("y3s2")), | ||
| toTop10Merged(bucket.get("y4s1")), | ||
| toTop10Merged(bucket.get("y4s2")) | ||
| ); | ||
| } | ||
|
|
||
| private List<RankingItem> toTop10(List<CourseRankingRepository.CourseCountRow> rows) { | ||
| if (rows == null) return List.of(); | ||
| var finalRows = rows.stream().limit(10).toList(); | ||
| /** | ||
| * ✅ 교양/전공 공통: rows를 "공백 제거 키"로 합산 → takenCount desc 정렬 → top10 → rank 부여 | ||
| */ | ||
| private List<RankingItem> toTop10Merged(List<CourseRankingRepository.CourseCountRow> rows) { | ||
| if (rows == null || rows.isEmpty()) return List.of(); | ||
|
|
||
| // normKey -> (sumCount, displayName) | ||
| Map<String, Agg> map = new HashMap<>(); | ||
|
|
||
| for (var r : rows) { | ||
| String raw = r.getName(); | ||
| String key = norm(raw); | ||
| if (key.isBlank()) continue; | ||
|
|
||
| Agg prev = map.get(key); | ||
| long nextCount = (prev == null ? 0L : prev.takenCount) + r.getTakenCount(); | ||
|
|
||
| // ✅ 대표 표기명: 사람이 읽기 편한 쪽(공백 포함, 더 긴 것) 우선 | ||
| String nextDisplay = (prev == null) | ||
| ? safe(raw) | ||
| : chooseDisplay(prev.displayName, safe(raw)); | ||
|
|
||
| map.put(key, new Agg(nextCount, nextDisplay)); | ||
| } | ||
|
|
||
| return IntStream.range(0, finalRows.size()) | ||
| var merged = map.values().stream() | ||
| .sorted((a, b) -> { | ||
| int c = Long.compare(b.takenCount, a.takenCount); | ||
| if (c != 0) return c; | ||
| return a.displayName.compareTo(a.displayName); | ||
| }) | ||
|
Comment on lines
+125
to
+129
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. |
||
| .limit(10) | ||
| .toList(); | ||
|
|
||
| return IntStream.range(0, merged.size()) | ||
| .mapToObj(i -> new RankingItem( | ||
| i + 1, | ||
| finalRows.get(i).getName(), | ||
| finalRows.get(i).getTakenCount(), | ||
| merged.get(i).displayName, | ||
| merged.get(i).takenCount, | ||
| 0 | ||
| )) | ||
| .toList(); | ||
| } | ||
|
|
||
| private List<RankingItem> loadTop10(Set<Category> categories, boolean applyFilter) { | ||
| /** | ||
| * ✅ 교양: 카테고리별로 가져온 뒤(필요시 필터) → 합산 top10 | ||
| */ | ||
| private List<RankingItem> loadTop10Merged(Set<Category> categories, boolean applyFilter) { | ||
| int fetchSize = applyFilter ? 30 : 10; | ||
|
|
||
| var fetchedRows = repository.findTopCoursesByCategories( | ||
| categories, | ||
| PageRequest.of(0, fetchSize) | ||
| ); | ||
|
|
||
| final var finalRows = applyFilter | ||
| var filtered = applyFilter | ||
| ? fetchedRows.stream() | ||
| .filter(r -> !containsAny(r.getName(), LIBERAL_EXCLUDE_KEYWORDS)) | ||
| .limit(10) | ||
| .toList() | ||
| : fetchedRows.stream() | ||
| .limit(10) | ||
| .toList(); | ||
| : fetchedRows; | ||
|
|
||
| return IntStream.range(0, finalRows.size()) | ||
| .mapToObj(i -> new RankingItem( | ||
| i + 1, | ||
| finalRows.get(i).getName(), | ||
| finalRows.get(i).getTakenCount(), | ||
| 0 | ||
| )) | ||
| .toList(); | ||
| return toTop10Merged(filtered); | ||
| } | ||
|
|
||
| private boolean containsAny(String text, List<String> keywords) { | ||
|
|
@@ -141,4 +167,36 @@ private boolean containsAny(String text, List<String> keywords) { | |
| } | ||
| return false; | ||
| } | ||
|
|
||
| // ======================= | ||
| // normalize / display util | ||
| // ======================= | ||
|
|
||
| // ✅ 공백만 제거 + trim (웹서비스개발 / 웹 서비스 개발 동일 키) | ||
| private static String norm(String s) { | ||
| if (s == null) return ""; | ||
| return s.trim().replaceAll("\\s+", ""); | ||
| } | ||
|
Comment on lines
+176
to
+179
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
예시: // in com.gradu.common.util.StringUtil.java
public final class StringUtil {
private StringUtil() {}
public static String normalize(String s) {
if (s == null) return "";
return s.trim().replaceAll("\\s+", "");
}
}References
|
||
|
|
||
| private static String safe(String s) { | ||
| return s == null ? "" : s; | ||
| } | ||
|
|
||
| // 더 읽기 좋은 표기(대개 공백 포함이 더 길다) 우선 | ||
| private static String chooseDisplay(String a, String b) { | ||
| if (a == null || a.isBlank()) return safe(b); | ||
| if (b == null || b.isBlank()) return safe(a); | ||
| if (b.length() > a.length()) return b; | ||
| return a; | ||
| } | ||
|
|
||
| private static final class Agg { | ||
| final long takenCount; | ||
| final String displayName; | ||
|
|
||
| private Agg(long takenCount, String displayName) { | ||
| this.takenCount = takenCount; | ||
| this.displayName = displayName; | ||
| } | ||
| } | ||
|
Comment on lines
+193
to
+201
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
private record Agg(long takenCount, String displayName) {}References
|
||
| } | ||
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
for루프 내의 집계 로직을Map.merge()메서드를 사용하여 더 간결하게 개선할 수 있습니다.merge를 사용하면 키 존재 여부를 확인하고 값을 계산하여 업데이트하는 과정을 하나의 메서드 호출로 처리할 수 있어 코드 가독성이 향상됩니다. (이 제안은Agg가record라고 가정합니다.)