From b4bf42316263aef0c6c07fdaadb719e0ccfdfd6d Mon Sep 17 00:00:00 2001 From: Jiwoo Date: Wed, 25 Jun 2025 13:20:18 +0900 Subject: [PATCH 1/3] =?UTF-8?q?feat:=20=EC=BD=94=EC=8A=A4,=20=ED=8A=B8?= =?UTF-8?q?=EB=9E=99,=20=ED=8A=B8=EB=9E=99=EC=BD=94=EC=8A=A4=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../smartair/entity/StudentCourse.java | 16 ++++++++++ .../com/example/smartair/entity/Track.java | 30 +++++++++++++++++++ .../example/smartair/entity/TrackCourse.java | 18 +++++++++++ 3 files changed, 64 insertions(+) create mode 100644 src/main/java/com/example/smartair/entity/StudentCourse.java create mode 100644 src/main/java/com/example/smartair/entity/Track.java create mode 100644 src/main/java/com/example/smartair/entity/TrackCourse.java diff --git a/src/main/java/com/example/smartair/entity/StudentCourse.java b/src/main/java/com/example/smartair/entity/StudentCourse.java new file mode 100644 index 0000000..6a48db7 --- /dev/null +++ b/src/main/java/com/example/smartair/entity/StudentCourse.java @@ -0,0 +1,16 @@ +package com.example.smartair.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import org.springframework.data.annotation.Id; + +@Entity +public class StudentCourse { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private Long studentId; // 학생 ID (로그인 연동) + private String courseName; + // Getters and Setters +} diff --git a/src/main/java/com/example/smartair/entity/Track.java b/src/main/java/com/example/smartair/entity/Track.java new file mode 100644 index 0000000..a786980 --- /dev/null +++ b/src/main/java/com/example/smartair/entity/Track.java @@ -0,0 +1,30 @@ +package com.example.smartair.entity; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.GenerationType; +import jakarta.persistence.OneToMany; +import lombok.Getter; +import lombok.Setter; +import org.hibernate.annotations.CascadeType; +import org.springframework.data.annotation.Id; + +import java.util.ArrayList; +import java.util.List; + +@Entity +@Getter @Setter +public class Track { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String name; + private String department; + + @OneToMany(mappedBy = "track", cascade = CascadeType.ALL) + private List courses = new ArrayList<>(); +// public void addCourse(TrackCourse course) { +// courses.add(course); +// course.setTrack(this); +// } +} diff --git a/src/main/java/com/example/smartair/entity/TrackCourse.java b/src/main/java/com/example/smartair/entity/TrackCourse.java new file mode 100644 index 0000000..1de5df0 --- /dev/null +++ b/src/main/java/com/example/smartair/entity/TrackCourse.java @@ -0,0 +1,18 @@ +package com.example.smartair.entity; + +import jakarta.persistence.*; +import org.springframework.data.annotation.Id; + +@Entity +public class TrackCourse { + @Id + @GeneratedValue(strategy = GenerationType.IDENTITY) + private Long id; + private String courseName; // 현재 과목명 + private String courseAlias; // 구(과거) 과목명 + + @ManyToOne + @JoinColumn(name = "track_id") + private Track track; + // Getters and Setters +} From cde2b2f6ef5ba9ee13c14f3a8ded556d53c06477 Mon Sep 17 00:00:00 2001 From: Jiwoo Date: Wed, 25 Jun 2025 14:07:57 +0900 Subject: [PATCH 2/3] =?UTF-8?q?fix=20:=20import=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/example/smartair/entity/StudentCourse.java | 3 +-- src/main/java/com/example/smartair/entity/Track.java | 7 +------ src/main/java/com/example/smartair/entity/TrackCourse.java | 1 - 3 files changed, 2 insertions(+), 9 deletions(-) diff --git a/src/main/java/com/example/smartair/entity/StudentCourse.java b/src/main/java/com/example/smartair/entity/StudentCourse.java index 6a48db7..626df67 100644 --- a/src/main/java/com/example/smartair/entity/StudentCourse.java +++ b/src/main/java/com/example/smartair/entity/StudentCourse.java @@ -3,7 +3,7 @@ import jakarta.persistence.Entity; import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; -import org.springframework.data.annotation.Id; +import jakarta.persistence.Id; @Entity public class StudentCourse { @@ -12,5 +12,4 @@ public class StudentCourse { private Long id; private Long studentId; // 학생 ID (로그인 연동) private String courseName; - // Getters and Setters } diff --git a/src/main/java/com/example/smartair/entity/Track.java b/src/main/java/com/example/smartair/entity/Track.java index a786980..124ddbb 100644 --- a/src/main/java/com/example/smartair/entity/Track.java +++ b/src/main/java/com/example/smartair/entity/Track.java @@ -1,13 +1,8 @@ package com.example.smartair.entity; -import jakarta.persistence.Entity; -import jakarta.persistence.GeneratedValue; -import jakarta.persistence.GenerationType; -import jakarta.persistence.OneToMany; +import jakarta.persistence.*; import lombok.Getter; import lombok.Setter; -import org.hibernate.annotations.CascadeType; -import org.springframework.data.annotation.Id; import java.util.ArrayList; import java.util.List; diff --git a/src/main/java/com/example/smartair/entity/TrackCourse.java b/src/main/java/com/example/smartair/entity/TrackCourse.java index 1de5df0..a35887b 100644 --- a/src/main/java/com/example/smartair/entity/TrackCourse.java +++ b/src/main/java/com/example/smartair/entity/TrackCourse.java @@ -1,7 +1,6 @@ package com.example.smartair.entity; import jakarta.persistence.*; -import org.springframework.data.annotation.Id; @Entity public class TrackCourse { From def0c5b79ed99c1ed1d20e9f4761b75023b847cc Mon Sep 17 00:00:00 2001 From: Jiwoo Date: Wed, 25 Jun 2025 14:53:06 +0900 Subject: [PATCH 3/3] =?UTF-8?q?feat:=20home=20=EA=B8=B0=EB=8A=A5=20?= =?UTF-8?q?=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../homecontroller/HomeController.java | 32 +++++++ .../com/example/smartair/dto/CourseDto.java | 17 ++++ .../smartair/dto/TrackProgressDto.java | 22 +++++ .../smartair/entity/StudentCourse.java | 2 + .../example/smartair/entity/TrackCourse.java | 2 + .../repository/StudentCourseRepository.java | 15 +++ .../repository/TrackCourseRepository.java | 7 ++ .../smartair/repository/TrackRepository.java | 12 +++ .../smartair/service/TrackService.java | 92 +++++++++++++++++++ 9 files changed, 201 insertions(+) create mode 100644 src/main/java/com/example/smartair/controller/homecontroller/HomeController.java create mode 100644 src/main/java/com/example/smartair/dto/CourseDto.java create mode 100644 src/main/java/com/example/smartair/dto/TrackProgressDto.java create mode 100644 src/main/java/com/example/smartair/repository/StudentCourseRepository.java create mode 100644 src/main/java/com/example/smartair/repository/TrackCourseRepository.java create mode 100644 src/main/java/com/example/smartair/repository/TrackRepository.java create mode 100644 src/main/java/com/example/smartair/service/TrackService.java diff --git a/src/main/java/com/example/smartair/controller/homecontroller/HomeController.java b/src/main/java/com/example/smartair/controller/homecontroller/HomeController.java new file mode 100644 index 0000000..24c3eb9 --- /dev/null +++ b/src/main/java/com/example/smartair/controller/homecontroller/HomeController.java @@ -0,0 +1,32 @@ +package com.example.smartair.controller.homecontroller; + +import ch.qos.logback.core.model.Model; +import com.example.smartair.dto.TrackProgressDto; +import com.example.smartair.service.TrackService; +import lombok.RequiredArgsConstructor; +import org.springframework.stereotype.Controller; +import org.springframework.web.bind.annotation.GetMapping; + +import java.util.List; + +@Controller +@RequiredArgsConstructor +public class HomeController { + + private final TrackService trackService; + + @GetMapping("/home") + public String showMyProgress(Model model) { // 1. Model 객체를 파라미터로 추가 + // TODO: 추후 Spring Security 등과 연동하여 실제 로그인한 사용자 ID를 가져와야 함 + Long currentStudentId = 1L; // 2. 테스트용 임시 학생 ID 사용 + + // 3. 학생의 이수 현황을 계산하는 새로운 서비스 메서드 호출 + List progressData = trackService.calculateTrackProgress(currentStudentId); + + // 4. 조회된 데이터를 "progressData"라는 이름으로 모델에 추가 + //model.addAttribute("progressData", progressData); + + // 5. 데이터를 표시할 뷰(html)의 이름을 반환 + return "home"; + } +} diff --git a/src/main/java/com/example/smartair/dto/CourseDto.java b/src/main/java/com/example/smartair/dto/CourseDto.java new file mode 100644 index 0000000..133278a --- /dev/null +++ b/src/main/java/com/example/smartair/dto/CourseDto.java @@ -0,0 +1,17 @@ +package com.example.smartair.dto; + +import lombok.AllArgsConstructor; +import lombok.Getter; +import lombok.Setter; + +/** + * 교과목 정보를 담는 DTO + */ +@Getter +@Setter +@AllArgsConstructor // 모든 필드를 인자로 받는 생성자 자동 생성 +public class CourseDto { + + private String courseName; // 현재 과목명 + private String courseAlias; // 과거 과목명 (없으면 null) +} diff --git a/src/main/java/com/example/smartair/dto/TrackProgressDto.java b/src/main/java/com/example/smartair/dto/TrackProgressDto.java new file mode 100644 index 0000000..1528031 --- /dev/null +++ b/src/main/java/com/example/smartair/dto/TrackProgressDto.java @@ -0,0 +1,22 @@ +package com.example.smartair.dto; + +import lombok.Getter; +import lombok.Setter; +import java.util.List; + +/** + * 하나의 트랙에 대한 학생의 이수 진행 현황 정보를 담는 DTO + */ +@Getter +@Setter +public class TrackProgressDto { + + private String trackName; // 트랙 이름 (예: "AI 콘텐츠") + private String department; // 소속 학과 + private int completedCount; // 이수한 과목 수 + private int requiredCount; // 이수 필요 과목 수 (예: 6) + private boolean isCompleted; // 트랙 이수 완료 여부 + + private List completedCourses; // 이수한 과목 목록 + private List remainingCourses; // 이수해야 할 남은 과목 목록 +} diff --git a/src/main/java/com/example/smartair/entity/StudentCourse.java b/src/main/java/com/example/smartair/entity/StudentCourse.java index 626df67..0624742 100644 --- a/src/main/java/com/example/smartair/entity/StudentCourse.java +++ b/src/main/java/com/example/smartair/entity/StudentCourse.java @@ -4,8 +4,10 @@ import jakarta.persistence.GeneratedValue; import jakarta.persistence.GenerationType; import jakarta.persistence.Id; +import lombok.Getter; @Entity +@Getter public class StudentCourse { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/com/example/smartair/entity/TrackCourse.java b/src/main/java/com/example/smartair/entity/TrackCourse.java index a35887b..968b5cd 100644 --- a/src/main/java/com/example/smartair/entity/TrackCourse.java +++ b/src/main/java/com/example/smartair/entity/TrackCourse.java @@ -1,8 +1,10 @@ package com.example.smartair.entity; import jakarta.persistence.*; +import lombok.Getter; @Entity +@Getter public class TrackCourse { @Id @GeneratedValue(strategy = GenerationType.IDENTITY) diff --git a/src/main/java/com/example/smartair/repository/StudentCourseRepository.java b/src/main/java/com/example/smartair/repository/StudentCourseRepository.java new file mode 100644 index 0000000..2f9daac --- /dev/null +++ b/src/main/java/com/example/smartair/repository/StudentCourseRepository.java @@ -0,0 +1,15 @@ +package com.example.smartair.repository; + +import com.example.smartair.entity.StudentCourse; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.transaction.annotation.Transactional; + +import java.util.List; + +public interface StudentCourseRepository extends JpaRepository { + + List findByStudentId(Long studentId); + + @Transactional + void deleteByStudentId(Long studentId); // 학생 ID로 이수과목 한번에 삭제 +} diff --git a/src/main/java/com/example/smartair/repository/TrackCourseRepository.java b/src/main/java/com/example/smartair/repository/TrackCourseRepository.java new file mode 100644 index 0000000..e5e2d1c --- /dev/null +++ b/src/main/java/com/example/smartair/repository/TrackCourseRepository.java @@ -0,0 +1,7 @@ +package com.example.smartair.repository; + +import com.example.smartair.entity.TrackCourse; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface TrackCourseRepository extends JpaRepository { +} diff --git a/src/main/java/com/example/smartair/repository/TrackRepository.java b/src/main/java/com/example/smartair/repository/TrackRepository.java new file mode 100644 index 0000000..3269a91 --- /dev/null +++ b/src/main/java/com/example/smartair/repository/TrackRepository.java @@ -0,0 +1,12 @@ +package com.example.smartair.repository; + +import com.example.smartair.entity.Track; +import org.springframework.data.jpa.repository.JpaRepository; +import org.springframework.data.jpa.repository.Query; +import java.util.List; + +public interface TrackRepository extends JpaRepository { + + @Query("SELECT DISTINCT t FROM Track t LEFT JOIN FETCH t.courses") + List findAllWithCourses(); +} diff --git a/src/main/java/com/example/smartair/service/TrackService.java b/src/main/java/com/example/smartair/service/TrackService.java new file mode 100644 index 0000000..baba871 --- /dev/null +++ b/src/main/java/com/example/smartair/service/TrackService.java @@ -0,0 +1,92 @@ +package com.example.smartair.service; + +import com.example.smartair.dto.CourseDto; +import com.example.smartair.dto.TrackProgressDto; +import com.example.smartair.entity.StudentCourse; +import com.example.smartair.entity.Track; +import com.example.smartair.entity.TrackCourse; +import com.example.smartair.repository.StudentCourseRepository; +import com.example.smartair.repository.TrackRepository; +import org.springframework.stereotype.Service; +import lombok.RequiredArgsConstructor; + +import java.util.ArrayList; +import java.util.List; +import java.util.Map; +import java.util.Set; +import java.util.stream.Collectors; + +@Service +@RequiredArgsConstructor +public class TrackService { + + private final TrackRepository trackRepository; + private final StudentCourseRepository studentCourseRepository; // 기존 기능 + + // ... (기존의 saveStudentCoursesFromExcel, calculateTrackProgress 메서드) ... + + /** + * 모든 트랙 정보를 학과별로 그룹화하여 반환하는 메서드 + */ + public Map> getAllTracksGroupedByDepartment() { + // 1. DB에서 모든 트랙과 관련 과목들을 한번에 조회 + List allTracks = trackRepository.findAllWithCourses(); + + // 2. 조회된 트랙 리스트를 '학과' 이름으로 그룹화하여 Map으로 변환 후 반환 + return allTracks.stream() + .collect(Collectors.groupingBy(Track::getDepartment)); + } + + public List calculateTrackProgress(Long studentId) { + // 1. 학생의 이수 과목 목록 조회 + Set completedCourseNames = studentCourseRepository.findByStudentId(studentId) + .stream() + .map(StudentCourse::getCourseName) + .collect(Collectors.toSet()); + + // 2. 모든 트랙 정보 조회 + List allTracks = trackRepository.findAllWithCourses(); + + List progressList = new ArrayList<>(); + + // 3. 각 트랙별로 진행 현황 계산 + for (Track track : allTracks) { + + // 현재 트랙에서 완료한 과목과 남은 과목을 담을 리스트 초기화 + List completedInThisTrack = new ArrayList<>(); + List remainingInThisTrack = new ArrayList<>(); + + // 현재 트랙에 속한 모든 교과목을 하나씩 확인 + for (TrackCourse trackCourse : track.getCourses()) { + + // 학생이 해당 과목을 이수했는지 확인 (현재 이름 또는 과거 이름으로 체크) + if (completedCourseNames.contains(trackCourse.getCourseName()) || + (trackCourse.getCourseAlias() != null && completedCourseNames.contains(trackCourse.getCourseAlias()))) { + // 이수한 경우: 완료 리스트에 추가 + completedInThisTrack.add(new CourseDto(trackCourse.getCourseName(), trackCourse.getCourseAlias())); + } else { + // 이수하지 않은 경우: 남은 과목 리스트에 추가 + remainingInThisTrack.add(new CourseDto(trackCourse.getCourseName(), trackCourse.getCourseAlias())); + } + } + + // 최종 결과를 담을 DTO 객체 생성 및 데이터 세팅 + TrackProgressDto progressDto = new TrackProgressDto(); + progressDto.setTrackName(track.getName()); + progressDto.setDepartment(track.getDepartment()); + + int completedCount = completedInThisTrack.size(); + progressDto.setCompletedCount(completedCount); + progressDto.setRequiredCount(6); // 트랙 이수 요구 과목 수는 6개 + progressDto.setCompleted(completedCount >= 6); // 6개 이상이면 true + + progressDto.setCompletedCourses(completedInThisTrack); + progressDto.setRemainingCourses(remainingInThisTrack); + + // 완성된 DTO를 최종 결과 리스트에 추가 + progressList.add(progressDto); + } + + return progressList; + } +}