diff --git a/src/main/java/com/example/enjoy/controller/TrackController.java b/src/main/java/com/example/enjoy/controller/TrackController.java index 425bbd3..4dc48c9 100644 --- a/src/main/java/com/example/enjoy/controller/TrackController.java +++ b/src/main/java/com/example/enjoy/controller/TrackController.java @@ -1,12 +1,15 @@ package com.example.enjoy.controller; import com.example.enjoy.dto.TrackDetailDto; +import com.example.enjoy.dto.TrackProgressDto; +import com.example.enjoy.entity.Track; import com.example.enjoy.service.TrackService; +import io.swagger.v3.oas.annotations.Operation; import lombok.RequiredArgsConstructor; -import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; -import org.springframework.web.bind.annotation.RequestMapping; -import org.springframework.web.bind.annotation.RestController; +import org.springframework.http.ResponseEntity; +import org.springframework.web.bind.annotation.*; + +import java.util.List; @RestController @RequiredArgsConstructor @@ -15,18 +18,64 @@ public class TrackController { private final TrackService trackService; - /** - * 특정 트랙의 상세 정보를 조회하는 API - * @param trackId 조회할 트랙의 ID - * @return TrackDetailDto - 트랙의 상세 정보 - */ +// /** +// * 특정 트랙의 상세 정보를 조회하는 API +// * @param trackId 조회할 트랙의 ID +// * @return TrackDetailDto - 트랙의 상세 정보 +// */ +// @GetMapping("/{trackId}") +// public TrackDetailDto getTrackDetailsById(@PathVariable Long trackId) { +// +// // TODO: 추후 Spring Security 연동 후 실제 로그인한 학생 ID를 가져와야 함 +// String currentStudentId = "1"; +// +// // 5. 서비스의 메서드를 호출하여 결과를 받아온 후, 그대로 반환 +// return trackService.getTrackDetails(currentStudentId, trackId); +// } + + @Operation(summary = "트랙 상세 정보 조회", description = "트랙의 이름으로 상세 정보를 조회합니다.") @GetMapping("/{trackId}") - public TrackDetailDto getTrackDetailsById(@PathVariable Long trackId) { + public ResponseEntity getTrackDetailsByName(@RequestParam String trackName) { + TrackDetailDto trackDetailDto = trackService.getTrackDetailsByName(trackName); + if (trackDetailDto == null) { + return ResponseEntity.notFound().build(); + } + return ResponseEntity.ok(trackDetailDto); + } - // TODO: 추후 Spring Security 연동 후 실제 로그인한 학생 ID를 가져와야 함 - String currentStudentId = "1"; + @Operation(summary = "트랙 목록 조회", description = "모든 트랙의 목록을 조회합니다.") + @GetMapping("/list") + public ResponseEntity> getAllTracks() { + List tracks = trackService.getAllTracks(); + if (tracks.isEmpty()) { + return ResponseEntity.noContent().build(); + } + List trackDetails = tracks.stream() + .map(track -> new TrackDetailDto().from(track)) + .toList(); + return ResponseEntity.ok(trackDetails); + } - // 5. 서비스의 메서드를 호출하여 결과를 받아온 후, 그대로 반환 - return trackService.getTrackDetails(currentStudentId, trackId); + @Operation(summary = "진척률에 따른 추천 트랙 조회", description = "학생의 진척률에 따라 추천 트랙을 조회합니다.") + @GetMapping("/recommendations/progress") + public ResponseEntity getRecommendedTrackByProgress(@RequestParam String studentId) { + Track recommendedTrack = trackService.getTopTrackByProgressScore(studentId); + if (recommendedTrack == null) { + return ResponseEntity.noContent().build(); + } + TrackDetailDto trackDetailDto = new TrackDetailDto().from(recommendedTrack); + return ResponseEntity.ok(trackDetailDto); } -} + + @Operation(summary = "선호 과목에 따른 추천 트랙 조회", description = "학생의 선호 과목에 따라 추천 트랙을 조회합니다.") + @GetMapping("/recommendations/preferred-courses") + public ResponseEntity getRecommendedTrackByPreferredCourses(@RequestParam String studentId) { + Track recommendedTrack = trackService.getTopTrackByFavoriteScore(studentId); + if (recommendedTrack == null) { + return ResponseEntity.noContent().build(); + } + TrackDetailDto trackDetailDto = new TrackDetailDto().from(recommendedTrack); + return ResponseEntity.ok(trackDetailDto); + } + +} \ No newline at end of file diff --git a/src/main/java/com/example/enjoy/controller/UserController.java b/src/main/java/com/example/enjoy/controller/UserController.java index 652ef00..e0ed2d3 100644 --- a/src/main/java/com/example/enjoy/controller/UserController.java +++ b/src/main/java/com/example/enjoy/controller/UserController.java @@ -124,5 +124,15 @@ public ResponseEntity saveUserInfo(@Valid @RequestBody MemberDto memb userService.saveUserInfo(memberDto); return ResponseEntity.ok().build(); } + + @Operation(summary = "학생이 이수 완료한 트랙 조회", description = "학생이 이수 완료한 트랙 목록을 조회합니다.") + @GetMapping("/{studentId}/tracks/completed") + public ResponseEntity> getCompletedTracks(@PathVariable String studentId) { + List completedTracks = userService.getCompletedTracks(studentId); + if (completedTracks.isEmpty()) { + return ResponseEntity.noContent().build(); + } + return ResponseEntity.ok(completedTracks); + } } diff --git a/src/main/java/com/example/enjoy/repository/FavoriteCourseRepository.java b/src/main/java/com/example/enjoy/repository/FavoriteCourseRepository.java index e73dda4..0785bb9 100644 --- a/src/main/java/com/example/enjoy/repository/FavoriteCourseRepository.java +++ b/src/main/java/com/example/enjoy/repository/FavoriteCourseRepository.java @@ -13,4 +13,5 @@ public interface FavoriteCourseRepository extends JpaRepository { Optional findByUserAndCourseName(User user, String courseName); List findAllByUser(User user); + } diff --git a/src/main/java/com/example/enjoy/repository/TrackRepository.java b/src/main/java/com/example/enjoy/repository/TrackRepository.java index 6cfb305..f7ea875 100644 --- a/src/main/java/com/example/enjoy/repository/TrackRepository.java +++ b/src/main/java/com/example/enjoy/repository/TrackRepository.java @@ -22,4 +22,5 @@ public interface TrackRepository extends JpaRepository { Optional findByIdWithCourses(@Param("trackId") Long trackId); Optional findByName(String name); + } \ No newline at end of file diff --git a/src/main/java/com/example/enjoy/service/TrackService.java b/src/main/java/com/example/enjoy/service/TrackService.java index 149e802..cf25282 100644 --- a/src/main/java/com/example/enjoy/service/TrackService.java +++ b/src/main/java/com/example/enjoy/service/TrackService.java @@ -1,14 +1,19 @@ package com.example.enjoy.service; +import ch.qos.logback.core.joran.sanity.Pair; import com.example.enjoy.dto.CourseDto; import com.example.enjoy.dto.CourseStatusDto; import com.example.enjoy.dto.TrackDetailDto; import com.example.enjoy.dto.TrackProgressDto; +import com.example.enjoy.entity.FavoriteCourse; import com.example.enjoy.entity.StudentCourse; import com.example.enjoy.entity.Track; import com.example.enjoy.entity.TrackCourse; +import com.example.enjoy.entity.user.User; +import com.example.enjoy.repository.FavoriteCourseRepository; import com.example.enjoy.repository.StudentCourseRepository; import com.example.enjoy.repository.TrackRepository; +import com.example.enjoy.repository.UserRepository; import org.springframework.stereotype.Service; import lombok.RequiredArgsConstructor; import org.springframework.transaction.annotation.Transactional; @@ -24,7 +29,10 @@ public class TrackService { private final TrackRepository trackRepository; private final StudentCourseRepository studentCourseRepository; // 기존 기능 + private final FavoriteCourseRepository favoriteCourseRepository; + private final UserRepository userRepository; + //진척률 계산 public List calculateTrackProgress(String studentId) { Set completedCourseNames = getCompletedCourseNames(studentId); // 이수 과목명 목록 @@ -60,13 +68,13 @@ public List calculateTrackProgress(String studentId) { * 학생이 이수한 과목 이름을 Set으로 반환하는 메서드 */ @Transactional(readOnly = true) - public TrackDetailDto getTrackDetails(String studentId, Long trackId) { + public TrackDetailDto getTrackDetails(String studentId, String trackName) { // 1. [리팩토링] 학생 이수 과목 조회 로직을 private 메서드로 호출 Set completedCourseNames = getCompletedCourseNames(studentId); - // 2. ID로 트랙 정보와 소속 과목들을 한번에 조회 - Track track = trackRepository.findByIdWithCourses(trackId) + // 2. 트랙 정보와 소속 과목들을 한번에 조회 + Track track = trackRepository.findByName(trackName) .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 트랙입니다.")); // 3. 트랙의 과목 목록을 CourseStatusDto 리스트로 변환 @@ -101,6 +109,33 @@ public TrackDetailDto getTrackDetails(String studentId, Long trackId) { return trackDetailDto; } + public TrackDetailDto getTrackDetailsByName(String trackName) { + Track track = trackRepository.findByName(trackName) + .orElseThrow(() -> new IllegalArgumentException("존재하지 않는 트랙입니다.")); + + // 트랙의 과목 목록을 CourseStatusDto 리스트로 변환 + List courseStatusList = track.getCourses().stream() + .map(trackCourse -> { + CourseStatusDto dto = new CourseStatusDto(); + dto.setTitle(trackCourse.getCourseName()); + dto.setCode(trackCourse.getCourseCode()); + dto.setYear(trackCourse.getAcademicYear()); + dto.setSemester(trackCourse.getAcademicSemester()); + dto.setStatus("NONE"); // 기본값 설정, 이수 여부는 별도로 처리 + return dto; + }) + .collect(Collectors.toList()); + + // 최종적으로 TrackDetailDto를 조립하여 반환 + TrackDetailDto trackDetailDto = new TrackDetailDto(); + trackDetailDto.setTrackId(track.getId()); + trackDetailDto.setTrackName(track.getName()); + trackDetailDto.setDepartment(track.getDepartment()); + trackDetailDto.setCourses(courseStatusList); + + return trackDetailDto; + } + /** * 학생 ID로 해당 학생이 이수한 모든 과목명을 조회합니다. */ @@ -118,4 +153,67 @@ private boolean isCourseCompleted(TrackCourse course, Set completedCours return completedCourseNames.contains(course.getCourseName()) || (course.getCourseAlias() != null && completedCourseNames.contains(course.getCourseAlias())); } + + public Track getTopTrackByProgressScore(String studentId) { + User user = userRepository.findByStudentId(studentId) + .orElseThrow(() -> new RuntimeException("사용자를 찾을 수 없습니다.")); + List trackProgress = calculateTrackProgress(studentId); + + TrackProgressDto topProgress = trackProgress.stream() + .max((a, b) -> Double.compare( + (double) a.getCompletedCount() / a.getRequiredCount(), + (double) b.getCompletedCount() / b.getRequiredCount())) + .orElse(null); + + if (topProgress == null) { + return null; + } + + return trackRepository.findByName(topProgress.getTrackName()) + .orElse(null); + } + + // 선호과목 기준 추천 트랙 1개 반환 + public Track getTopTrackByFavoriteScore(String studentId) { + User user = userRepository.findByStudentId(studentId) + .orElseThrow(() -> new RuntimeException("사용자를 찾을 수 없습니다.")); + Set favoriteCourses = favoriteCourseRepository.findAllByUser(user) + .stream() + .map(FavoriteCourse::getCourseName) + .collect(Collectors.toSet()); + + List trackProgress = calculateTrackProgress(studentId); + + TrackProgressDto topFavorite = trackProgress.stream() + .max((a, b) -> Double.compare( + calculateFavoriteScore(a, favoriteCourses), + calculateFavoriteScore(b, favoriteCourses))) + .orElse(null); + + if (topFavorite == null) { + return null; + } + + return trackRepository.findByName(topFavorite.getTrackName()) + .orElse(null); + } + + private double calculateFavoriteScore(TrackProgressDto track, Set favoriteCourses) { + List allCourses = new ArrayList<>(); + allCourses.addAll(track.getCompletedCourses()); + allCourses.addAll(track.getRemainingCourses()); + + long matchCount = allCourses.stream() + .filter(course -> + (course.getCourseName() != null && favoriteCourses.contains(course.getCourseName())) || + (course.getCourseAlias() != null && favoriteCourses.contains(course.getCourseAlias())) + ) + .count(); + + return allCourses.isEmpty() ? 0.0 : (double) matchCount / allCourses.size(); + } + + public List getAllTracks() { + return trackRepository.findAll(); + } } diff --git a/src/main/java/com/example/enjoy/service/userService/UserService.java b/src/main/java/com/example/enjoy/service/userService/UserService.java index 8677ed1..299ef6a 100644 --- a/src/main/java/com/example/enjoy/service/userService/UserService.java +++ b/src/main/java/com/example/enjoy/service/userService/UserService.java @@ -126,4 +126,16 @@ public void updateCourseStatus(String studentId, String courseName, StudentCours course.updateStatus(newStatus); } + + public List getCompletedTracks (String studentId) { + List completedCourses = getCompletedCourses(studentId); + List trackCourses = trackCourseRepository.findAll(); + + return trackCourses.stream() + .filter(trackCourse -> completedCourses.stream() + .anyMatch(course -> course.getCourseName().equals(trackCourse.getCourseName()))) + .map(TrackCourse::getTrack) + .distinct() + .collect(Collectors.toList()); + } } \ No newline at end of file