Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
12 changes: 12 additions & 0 deletions src/main/java/com/example/enjoy/controller/HomeController.java
Original file line number Diff line number Diff line change
Expand Up @@ -15,6 +15,18 @@ public class HomeController {
private final TrackService trackService;

@GetMapping("/home")
public String showMyProgress(Model model) { // 1. Model 객체를 파라미터로 추가
// TODO: 추후 Spring Security 등과 연동하여 실제 로그인한 사용자 ID를 가져와야 함
String currentStudentId = "1"; // 2. 테스트용 임시 학생 ID 사용

// 3. 학생의 이수 현황을 계산하는 새로운 서비스 메서드 호출
List<TrackProgressDto> progressData = trackService.calculateTrackProgress(currentStudentId);

// 4. 조회된 데이터를 "progressData"라는 이름으로 모델에 추가
//model.addAttribute("progressData", progressData);

// 5. 데이터를 표시할 뷰(html)의 이름을 반환
return "home";
public List<TrackProgressDto> getProgress() {
// 1. 반환 타입을 List<TrackProgressDto>로 변경
String currentStudentId = "1";
Expand Down
Original file line number Diff line number Diff line change
@@ -1,4 +1,7 @@
package com.example.enjoy.controller;

import org.springframework.web.bind.annotation.RestController;

@RestController
public class StudentDataController {
}
75 changes: 74 additions & 1 deletion src/main/java/com/example/enjoy/controller/UserController.java
Original file line number Diff line number Diff line change
@@ -1,4 +1,77 @@
package com.example.enjoy.controller;

import com.example.enjoy.dto.AddManualCourseRequest;
import com.example.enjoy.dto.StudentCourseStatus;
import com.example.enjoy.dto.loginDto.MemberCommand;
import com.example.enjoy.dto.loginDto.MemberDto;
import com.example.enjoy.entity.StudentCourse;
import com.example.enjoy.entity.Track;
import com.example.enjoy.service.loginService.SejongLoginService;
import com.example.enjoy.service.userService.UserService;
import io.swagger.v3.oas.annotations.Operation;
import jakarta.validation.Valid;
import okhttp3.OkHttpClient;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.io.IOException;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/api/student")
public class UserController {
}

private final SejongLoginService sejongLoginService;
private final UserService userService;

public UserController(SejongLoginService sejongLoginService, UserService userService) {
this.sejongLoginService = sejongLoginService;
this.userService = userService;
}

@Operation(summary = "학생 정보 조회", description = "세종대학교 포털 인증을 통해 학생 정보를 조회합니다.")
@GetMapping("/detail")
public ResponseEntity<MemberDto> getStudentDetail(@RequestBody MemberCommand command) throws IOException {
MemberDto memberInfo = sejongLoginService.getMemberAuthInfos(command);
return ResponseEntity.ok(memberInfo);
}

@Operation(summary = "수동 과목 등록", description = "학생이 직접 수강한 과목을 등록합니다.")
@PostMapping("/courses")
public ResponseEntity<Void> addManualCourse(@Valid @RequestBody AddManualCourseRequest request) {
userService.addManualCourse(request);
return ResponseEntity.ok().build();
}

@Operation(summary = "수동 과목 삭제", description = "수동으로 등록한 과목을 삭제합니다.")
@DeleteMapping("/courses")
public ResponseEntity<Void> removeManualCourse(
@RequestParam String studentId,
@RequestParam String courseName) {
userService.removeManualCourse(studentId, courseName);
return ResponseEntity.ok().build();
}

@Operation(summary = "완료 과목 조회", description = "학생이 수강 완료한 과목 목록을 조회합니다.")
@GetMapping("/{studentId}/courses/completed")
public ResponseEntity<List<StudentCourse>> getCompletedCourses(@PathVariable String studentId) {
return ResponseEntity.ok(userService.getCompletedCourses(studentId));
}

@Operation(summary = "트랙 진행률 조회", description = "학생의 각 트랙별 진행률을 조회합니다.")
@GetMapping("/{studentId}/tracks/progress")
public ResponseEntity<Map<Track, Double>> getTrackProgress(@PathVariable String studentId) {
return ResponseEntity.ok(userService.getTrackProgress(studentId));
}

@Operation(summary = "과목 상태 변경", description = "등록된 과목의 수강 상태를 변경합니다.")
@PatchMapping("/courses/status")
public ResponseEntity<Void> updateCourseStatus(
@RequestParam String studentId,
@RequestParam String courseName,
@RequestParam StudentCourseStatus newStatus) {
userService.updateCourseStatus(studentId, courseName, newStatus);
return ResponseEntity.ok().build();
}

25 changes: 25 additions & 0 deletions src/main/java/com/example/enjoy/dto/AddManualCourseRequest.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package com.example.enjoy.dto;

import jakarta.validation.constraints.NotBlank;
import jakarta.validation.constraints.NotNull;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;


@Getter
@Builder
@NoArgsConstructor
@AllArgsConstructor
public class AddManualCourseRequest {

@NotBlank(message = "학번은 필수입니다.")
private String studentId;

@NotBlank(message = "과목명은 필수입니다.")
private String courseName;

@NotNull(message = "수강 상태는 필수입니다.")
private StudentCourseStatus status;
}
13 changes: 13 additions & 0 deletions src/main/java/com/example/enjoy/entity/TrackCertification.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package com.example.enjoy.entity;

import jakarta.persistence.Entity;
import jakarta.persistence.GeneratedValue;
import jakarta.persistence.GenerationType;
import jakarta.persistence.Id;

@Entity
public class TrackCertification {
@Id
@GeneratedValue(strategy = GenerationType.IDENTITY)
private Long id;
}
Original file line number Diff line number Diff line change
@@ -1,14 +1,25 @@
package com.example.enjoy.repository;

import com.example.enjoy.dto.StudentCourseStatus;
import com.example.enjoy.entity.StudentCourse;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Optional;

@Repository
public interface StudentCourseRepository extends JpaRepository<StudentCourse, Long> {

List<StudentCourse> findByStudentId(String studentId);

boolean existsByStudentIdAndCourseName(String studentId, String courseName);

Optional<StudentCourse> findByStudentIdAndCourseNameAndManualIsTrue(String studentId, String courseName);

List<StudentCourse> findAllByStudentIdAndStatus(String studentId, StudentCourseStatus status);

Optional<StudentCourse> findByStudentIdAndCourseName(String studentId, String courseName);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,88 @@
package com.example.enjoy.service.userService;

import com.amazonaws.services.cloudformation.model.AlreadyExistsException;
import com.example.enjoy.dto.AddManualCourseRequest;
import com.example.enjoy.dto.StudentCourseStatus;
import com.example.enjoy.entity.StudentCourse;
import com.example.enjoy.entity.Track;
import com.example.enjoy.entity.TrackCourse;
import com.example.enjoy.repository.StudentCourseRepository;
import com.example.enjoy.repository.TrackCourseRepository;
import com.example.enjoy.repository.TrackRepository;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.util.List;
import java.util.Map;
import java.util.stream.Collectors;

@Service
@RequiredArgsConstructor
@Transactional(readOnly = true)
public class UserService {

private final StudentCourseRepository studentCourseRepository;
private final TrackRepository trackRepository;
private final TrackCourseRepository trackCourseRepository;

@Transactional
public void addManualCourse(AddManualCourseRequest request) { //수동으로 과목 등록
if (studentCourseRepository.existsByStudentIdAndCourseName(
request.getStudentId(),
request.getCourseName())) {
throw new AlreadyExistsException("이미 등록된 과목입니다.");
}

StudentCourse sc = StudentCourse.builder() //수강 여부 설정 가능
.studentId(request.getStudentId())
.courseName(request.getCourseName())
.status(request.getStatus())
.manual(true)
.build();
studentCourseRepository.save(sc);
}


@Transactional
public void removeManualCourse(String studentId, String courseName) { //수동 등록 과목 삭제
StudentCourse course = studentCourseRepository.findByStudentIdAndCourseNameAndManualIsTrue(studentId, courseName)
.orElseThrow(() -> new IllegalArgumentException("수동 등록된 과목을 찾을 수 없습니다."));
studentCourseRepository.delete(course);
}

public List<StudentCourse> getCompletedCourses(String studentId) { //수강 완료 과목 조회
return studentCourseRepository.findAllByStudentIdAndStatus(studentId, StudentCourseStatus.COMPLETED);
}

public Map<Track, Double> getTrackProgress(String studentId) { //트랙별 진행률 조회
List<Track> allTracks = trackRepository.findAll();
List<StudentCourse> completedCourses = getCompletedCourses(studentId);

return allTracks.stream().collect(Collectors.toMap(
track -> track,
track -> calculateTrackProgress(track, completedCourses)
));
}

private double calculateTrackProgress(Track track, List<StudentCourse> completedCourses) {
List<TrackCourse> trackCourses = trackCourseRepository.findAllByTrack(track);
if (trackCourses.isEmpty()) return 0.0;

long completedCount = trackCourses.stream()
.filter(trackCourse -> completedCourses.stream()
.anyMatch(completed -> completed.getCourseName().equals(trackCourse.getCourseName())))
.count();

return (double) completedCount / trackCourses.size() * 100;
}

@Transactional
public void updateCourseStatus(String studentId, String courseName, StudentCourseStatus newStatus) {
StudentCourse course = studentCourseRepository
.findByStudentIdAndCourseName(studentId, courseName)
.orElseThrow(() -> new IllegalArgumentException("등록된 과목을 찾을 수 없습니다."));

course.updateStatus(newStatus);
}
}
Loading