Skip to content

Commit 4833262

Browse files
authored
Merge pull request #20 from enjoy-hack/Daeun
Daeun
2 parents 0ee84f4 + 14db545 commit 4833262

6 files changed

Lines changed: 189 additions & 18 deletions

File tree

Lines changed: 64 additions & 15 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,15 @@
11
package com.example.enjoy.controller;
22

33
import com.example.enjoy.dto.TrackDetailDto;
4+
import com.example.enjoy.dto.TrackProgressDto;
5+
import com.example.enjoy.entity.Track;
46
import com.example.enjoy.service.TrackService;
7+
import io.swagger.v3.oas.annotations.Operation;
58
import lombok.RequiredArgsConstructor;
6-
import org.springframework.web.bind.annotation.GetMapping;
7-
import org.springframework.web.bind.annotation.PathVariable;
8-
import org.springframework.web.bind.annotation.RequestMapping;
9-
import org.springframework.web.bind.annotation.RestController;
9+
import org.springframework.http.ResponseEntity;
10+
import org.springframework.web.bind.annotation.*;
11+
12+
import java.util.List;
1013

1114
@RestController
1215
@RequiredArgsConstructor
@@ -15,18 +18,64 @@ public class TrackController {
1518

1619
private final TrackService trackService;
1720

18-
/**
19-
* 특정 트랙의 상세 정보를 조회하는 API
20-
* @param trackId 조회할 트랙의 ID
21-
* @return TrackDetailDto - 트랙의 상세 정보
22-
*/
21+
// /**
22+
// * 특정 트랙의 상세 정보를 조회하는 API
23+
// * @param trackId 조회할 트랙의 ID
24+
// * @return TrackDetailDto - 트랙의 상세 정보
25+
// */
26+
// @GetMapping("/{trackId}")
27+
// public TrackDetailDto getTrackDetailsById(@PathVariable Long trackId) {
28+
//
29+
// // TODO: 추후 Spring Security 연동 후 실제 로그인한 학생 ID를 가져와야 함
30+
// String currentStudentId = "1";
31+
//
32+
// // 5. 서비스의 메서드를 호출하여 결과를 받아온 후, 그대로 반환
33+
// return trackService.getTrackDetails(currentStudentId, trackId);
34+
// }
35+
36+
@Operation(summary = "트랙 상세 정보 조회", description = "트랙의 이름으로 상세 정보를 조회합니다.")
2337
@GetMapping("/{trackId}")
24-
public TrackDetailDto getTrackDetailsById(@PathVariable Long trackId) {
38+
public ResponseEntity<TrackDetailDto> getTrackDetailsByName(@RequestParam String trackName) {
39+
TrackDetailDto trackDetailDto = trackService.getTrackDetailsByName(trackName);
40+
if (trackDetailDto == null) {
41+
return ResponseEntity.notFound().build();
42+
}
43+
return ResponseEntity.ok(trackDetailDto);
44+
}
2545

26-
// TODO: 추후 Spring Security 연동 후 실제 로그인한 학생 ID를 가져와야 함
27-
String currentStudentId = "1";
46+
@Operation(summary = "트랙 목록 조회", description = "모든 트랙의 목록을 조회합니다.")
47+
@GetMapping("/list")
48+
public ResponseEntity<List<TrackDetailDto>> getAllTracks() {
49+
List<Track> tracks = trackService.getAllTracks();
50+
if (tracks.isEmpty()) {
51+
return ResponseEntity.noContent().build();
52+
}
53+
List<TrackDetailDto> trackDetails = tracks.stream()
54+
.map(track -> new TrackDetailDto().from(track))
55+
.toList();
56+
return ResponseEntity.ok(trackDetails);
57+
}
2858

29-
// 5. 서비스의 메서드를 호출하여 결과를 받아온 후, 그대로 반환
30-
return trackService.getTrackDetails(currentStudentId, trackId);
59+
@Operation(summary = "진척률에 따른 추천 트랙 조회", description = "학생의 진척률에 따라 추천 트랙을 조회합니다.")
60+
@GetMapping("/recommendations/progress")
61+
public ResponseEntity<TrackDetailDto> getRecommendedTrackByProgress(@RequestParam String studentId) {
62+
Track recommendedTrack = trackService.getTopTrackByProgressScore(studentId);
63+
if (recommendedTrack == null) {
64+
return ResponseEntity.noContent().build();
65+
}
66+
TrackDetailDto trackDetailDto = new TrackDetailDto().from(recommendedTrack);
67+
return ResponseEntity.ok(trackDetailDto);
3168
}
32-
}
69+
70+
@Operation(summary = "선호 과목에 따른 추천 트랙 조회", description = "학생의 선호 과목에 따라 추천 트랙을 조회합니다.")
71+
@GetMapping("/recommendations/preferred-courses")
72+
public ResponseEntity<TrackDetailDto> getRecommendedTrackByPreferredCourses(@RequestParam String studentId) {
73+
Track recommendedTrack = trackService.getTopTrackByFavoriteScore(studentId);
74+
if (recommendedTrack == null) {
75+
return ResponseEntity.noContent().build();
76+
}
77+
TrackDetailDto trackDetailDto = new TrackDetailDto().from(recommendedTrack);
78+
return ResponseEntity.ok(trackDetailDto);
79+
}
80+
81+
}

src/main/java/com/example/enjoy/controller/UserController.java

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -124,5 +124,15 @@ public ResponseEntity<MemberDto> saveUserInfo(@Valid @RequestBody MemberDto memb
124124
userService.saveUserInfo(memberDto);
125125
return ResponseEntity.ok().build();
126126
}
127+
128+
@Operation(summary = "학생이 이수 완료한 트랙 조회", description = "학생이 이수 완료한 트랙 목록을 조회합니다.")
129+
@GetMapping("/{studentId}/tracks/completed")
130+
public ResponseEntity<List<Track>> getCompletedTracks(@PathVariable String studentId) {
131+
List<Track> completedTracks = userService.getCompletedTracks(studentId);
132+
if (completedTracks.isEmpty()) {
133+
return ResponseEntity.noContent().build();
134+
}
135+
return ResponseEntity.ok(completedTracks);
136+
}
127137
}
128138

src/main/java/com/example/enjoy/repository/FavoriteCourseRepository.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -13,4 +13,5 @@
1313
public interface FavoriteCourseRepository extends JpaRepository<FavoriteCourse, Long> {
1414
Optional<FavoriteCourse> findByUserAndCourseName(User user, String courseName);
1515
List<FavoriteCourse> findAllByUser(User user);
16+
1617
}

src/main/java/com/example/enjoy/repository/TrackRepository.java

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -22,4 +22,5 @@ public interface TrackRepository extends JpaRepository<Track, Long> {
2222
Optional<Track> findByIdWithCourses(@Param("trackId") Long trackId);
2323

2424
Optional<Track> findByName(String name);
25+
2526
}

src/main/java/com/example/enjoy/service/TrackService.java

Lines changed: 101 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,14 +1,19 @@
11
package com.example.enjoy.service;
22

3+
import ch.qos.logback.core.joran.sanity.Pair;
34
import com.example.enjoy.dto.CourseDto;
45
import com.example.enjoy.dto.CourseStatusDto;
56
import com.example.enjoy.dto.TrackDetailDto;
67
import com.example.enjoy.dto.TrackProgressDto;
8+
import com.example.enjoy.entity.FavoriteCourse;
79
import com.example.enjoy.entity.StudentCourse;
810
import com.example.enjoy.entity.Track;
911
import com.example.enjoy.entity.TrackCourse;
12+
import com.example.enjoy.entity.user.User;
13+
import com.example.enjoy.repository.FavoriteCourseRepository;
1014
import com.example.enjoy.repository.StudentCourseRepository;
1115
import com.example.enjoy.repository.TrackRepository;
16+
import com.example.enjoy.repository.UserRepository;
1217
import org.springframework.stereotype.Service;
1318
import lombok.RequiredArgsConstructor;
1419
import org.springframework.transaction.annotation.Transactional;
@@ -24,7 +29,10 @@ public class TrackService {
2429

2530
private final TrackRepository trackRepository;
2631
private final StudentCourseRepository studentCourseRepository; // 기존 기능
32+
private final FavoriteCourseRepository favoriteCourseRepository;
33+
private final UserRepository userRepository;
2734

35+
//진척률 계산
2836
public List<TrackProgressDto> calculateTrackProgress(String studentId) {
2937
Set<String> completedCourseNames = getCompletedCourseNames(studentId); // 이수 과목명 목록
3038

@@ -60,13 +68,13 @@ public List<TrackProgressDto> calculateTrackProgress(String studentId) {
6068
* 학생이 이수한 과목 이름을 Set으로 반환하는 메서드
6169
*/
6270
@Transactional(readOnly = true)
63-
public TrackDetailDto getTrackDetails(String studentId, Long trackId) {
71+
public TrackDetailDto getTrackDetails(String studentId, String trackName) {
6472

6573
// 1. [리팩토링] 학생 이수 과목 조회 로직을 private 메서드로 호출
6674
Set<String> completedCourseNames = getCompletedCourseNames(studentId);
6775

68-
// 2. ID로 트랙 정보와 소속 과목들을 한번에 조회
69-
Track track = trackRepository.findByIdWithCourses(trackId)
76+
// 2. 트랙 정보와 소속 과목들을 한번에 조회
77+
Track track = trackRepository.findByName(trackName)
7078
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 트랙입니다."));
7179

7280
// 3. 트랙의 과목 목록을 CourseStatusDto 리스트로 변환
@@ -101,6 +109,33 @@ public TrackDetailDto getTrackDetails(String studentId, Long trackId) {
101109
return trackDetailDto;
102110
}
103111

112+
public TrackDetailDto getTrackDetailsByName(String trackName) {
113+
Track track = trackRepository.findByName(trackName)
114+
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 트랙입니다."));
115+
116+
// 트랙의 과목 목록을 CourseStatusDto 리스트로 변환
117+
List<CourseStatusDto> courseStatusList = track.getCourses().stream()
118+
.map(trackCourse -> {
119+
CourseStatusDto dto = new CourseStatusDto();
120+
dto.setTitle(trackCourse.getCourseName());
121+
dto.setCode(trackCourse.getCourseCode());
122+
dto.setYear(trackCourse.getAcademicYear());
123+
dto.setSemester(trackCourse.getAcademicSemester());
124+
dto.setStatus("NONE"); // 기본값 설정, 이수 여부는 별도로 처리
125+
return dto;
126+
})
127+
.collect(Collectors.toList());
128+
129+
// 최종적으로 TrackDetailDto를 조립하여 반환
130+
TrackDetailDto trackDetailDto = new TrackDetailDto();
131+
trackDetailDto.setTrackId(track.getId());
132+
trackDetailDto.setTrackName(track.getName());
133+
trackDetailDto.setDepartment(track.getDepartment());
134+
trackDetailDto.setCourses(courseStatusList);
135+
136+
return trackDetailDto;
137+
}
138+
104139
/**
105140
* 학생 ID로 해당 학생이 이수한 모든 과목명을 조회합니다.
106141
*/
@@ -118,4 +153,67 @@ private boolean isCourseCompleted(TrackCourse course, Set<String> completedCours
118153
return completedCourseNames.contains(course.getCourseName()) ||
119154
(course.getCourseAlias() != null && completedCourseNames.contains(course.getCourseAlias()));
120155
}
156+
157+
public Track getTopTrackByProgressScore(String studentId) {
158+
User user = userRepository.findByStudentId(studentId)
159+
.orElseThrow(() -> new RuntimeException("사용자를 찾을 수 없습니다."));
160+
List<TrackProgressDto> trackProgress = calculateTrackProgress(studentId);
161+
162+
TrackProgressDto topProgress = trackProgress.stream()
163+
.max((a, b) -> Double.compare(
164+
(double) a.getCompletedCount() / a.getRequiredCount(),
165+
(double) b.getCompletedCount() / b.getRequiredCount()))
166+
.orElse(null);
167+
168+
if (topProgress == null) {
169+
return null;
170+
}
171+
172+
return trackRepository.findByName(topProgress.getTrackName())
173+
.orElse(null);
174+
}
175+
176+
// 선호과목 기준 추천 트랙 1개 반환
177+
public Track getTopTrackByFavoriteScore(String studentId) {
178+
User user = userRepository.findByStudentId(studentId)
179+
.orElseThrow(() -> new RuntimeException("사용자를 찾을 수 없습니다."));
180+
Set<String> favoriteCourses = favoriteCourseRepository.findAllByUser(user)
181+
.stream()
182+
.map(FavoriteCourse::getCourseName)
183+
.collect(Collectors.toSet());
184+
185+
List<TrackProgressDto> trackProgress = calculateTrackProgress(studentId);
186+
187+
TrackProgressDto topFavorite = trackProgress.stream()
188+
.max((a, b) -> Double.compare(
189+
calculateFavoriteScore(a, favoriteCourses),
190+
calculateFavoriteScore(b, favoriteCourses)))
191+
.orElse(null);
192+
193+
if (topFavorite == null) {
194+
return null;
195+
}
196+
197+
return trackRepository.findByName(topFavorite.getTrackName())
198+
.orElse(null);
199+
}
200+
201+
private double calculateFavoriteScore(TrackProgressDto track, Set<String> favoriteCourses) {
202+
List<CourseDto> allCourses = new ArrayList<>();
203+
allCourses.addAll(track.getCompletedCourses());
204+
allCourses.addAll(track.getRemainingCourses());
205+
206+
long matchCount = allCourses.stream()
207+
.filter(course ->
208+
(course.getCourseName() != null && favoriteCourses.contains(course.getCourseName())) ||
209+
(course.getCourseAlias() != null && favoriteCourses.contains(course.getCourseAlias()))
210+
)
211+
.count();
212+
213+
return allCourses.isEmpty() ? 0.0 : (double) matchCount / allCourses.size();
214+
}
215+
216+
public List<Track> getAllTracks() {
217+
return trackRepository.findAll();
218+
}
121219
}

src/main/java/com/example/enjoy/service/userService/UserService.java

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -126,4 +126,16 @@ public void updateCourseStatus(String studentId, String courseName, StudentCours
126126

127127
course.updateStatus(newStatus);
128128
}
129+
130+
public List<Track> getCompletedTracks (String studentId) {
131+
List<StudentCourse> completedCourses = getCompletedCourses(studentId);
132+
List<TrackCourse> trackCourses = trackCourseRepository.findAll();
133+
134+
return trackCourses.stream()
135+
.filter(trackCourse -> completedCourses.stream()
136+
.anyMatch(course -> course.getCourseName().equals(trackCourse.getCourseName())))
137+
.map(TrackCourse::getTrack)
138+
.distinct()
139+
.collect(Collectors.toList());
140+
}
129141
}

0 commit comments

Comments
 (0)