Skip to content

Commit

Permalink
feat : 학기와 이수구분으로 강의 찾기 (#1184)
Browse files Browse the repository at this point in the history
* fix : flyway 116번, 117번 수정

* feat : Repository 정리 및 작성

* feat : Response 추가

* feat : exception 추가

* chore : Repo 정리

* feat : Service 및 Controller 작성

* feat : graduation 도메인 swagger 추가

* feat : 리뷰 반영

* chore : 리뷰 반영
  • Loading branch information
duehee authored Jan 25, 2025
1 parent e5767bd commit 0527431
Show file tree
Hide file tree
Showing 14 changed files with 176 additions and 19 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,10 +3,12 @@
import static in.koreatech.koin.domain.user.model.UserType.STUDENT;

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.multipart.MultipartFile;

import in.koreatech.koin.domain.graduation.dto.CourseTypeLectureResponse;
import in.koreatech.koin.global.auth.Auth;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
Expand Down Expand Up @@ -49,4 +51,22 @@ ResponseEntity<String> uploadStudentGradeExcelFile(
@RequestParam(value = "file") MultipartFile file,
@Auth(permit = {STUDENT}) Integer userId
);

@ApiResponses(
value = {
@ApiResponse(responseCode = "200"),
@ApiResponse(responseCode = "401", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "403", content = @Content(schema = @Schema(hidden = true))),
@ApiResponse(responseCode = "404", content = @Content(schema = @Schema(hidden = true)))
}
)
@Operation(summary = "학기에 따른 이수구분 강의 출력")
@SecurityRequirement(name = "Jwt Authentication")
@GetMapping("/graduation/course-type")
ResponseEntity<CourseTypeLectureResponse> getCourseTypeLecture(
@RequestParam(name = "year") Integer year,
@RequestParam(name = "term") String term,
@RequestParam(name = "name") String courseTypeName,
@Auth(permit = {STUDENT}) Integer userId
);
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package in.koreatech.koin.domain.graduation.controller;

import java.io.IOException;

import static in.koreatech.koin.domain.user.model.UserType.STUDENT;

import org.springframework.http.ResponseEntity;
Expand All @@ -10,7 +11,7 @@
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.multipart.MultipartFile;

import in.koreatech.koin.domain.graduation.dto.GraduationCourseCalculationResponse;
import in.koreatech.koin.domain.graduation.dto.CourseTypeLectureResponse;
import in.koreatech.koin.domain.graduation.service.GraduationService;
import in.koreatech.koin.domain.user.model.UserType;
import in.koreatech.koin.global.auth.Auth;
Expand All @@ -24,8 +25,7 @@ public class GraduationController implements GraduationApi {

@PostMapping("/graduation/agree")
public ResponseEntity<Void> createStudentCourseCalculation(
@Auth(permit = {STUDENT}) Integer userId)
{
@Auth(permit = {STUDENT}) Integer userId) {
graduationService.createStudentCourseCalculation(userId);
return ResponseEntity.ok().build();
}
Expand All @@ -43,10 +43,23 @@ public ResponseEntity<String> uploadStudentGradeExcelFile(
}
}

@GetMapping("/graduation/course-type")
public ResponseEntity<CourseTypeLectureResponse> getCourseTypeLecture(
@RequestParam(name = "year") Integer year,
@RequestParam(name = "term") String term,
@RequestParam(name = "name") String courseTypeName,
@Auth(permit = {STUDENT}) Integer userId
) {
CourseTypeLectureResponse response = graduationService.getLectureByCourseType(year, term, courseTypeName);
return ResponseEntity.ok(response);
}

/*
@GetMapping("/graduation/course/calculation")
public ResponseEntity<GraduationCourseCalculationResponse> getGraduationCourseCalculation(
@Auth(permit = {STUDENT}) Integer userId) {
GraduationCourseCalculationResponse response = graduationService.getGraduationCourseCalculationResponse(userId);
return ResponseEntity.ok(response);
}
*/
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
package in.koreatech.koin.domain.graduation.dto;

import java.util.List;

import in.koreatech.koin.domain.timetable.model.Lecture;
import io.swagger.v3.oas.annotations.media.Schema;

public record CourseTypeLectureResponse(
@Schema(description = "학기", example = "20192")
String semester,

@Schema(description = "이수구분 충족강의")
List<LecturePortionResponse> lectures
) {
public static CourseTypeLectureResponse of(String semester, List<Lecture> lectures) {
List<LecturePortionResponse> lectureList = lectures.stream()
.map(LecturePortionResponse::from)
.toList();
return new CourseTypeLectureResponse(semester, lectureList);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
package in.koreatech.koin.domain.graduation.dto;

import in.koreatech.koin.domain.timetable.model.Lecture;
import io.swagger.v3.oas.annotations.media.Schema;

public record LecturePortionResponse(
@Schema(description = "강의 ID", example = "1")
Integer id,

@Schema(description = "강의 코드", example = "ABC123")
String code,

@Schema(description = "강의 이름", example = "컴퓨터구조")
String name,

@Schema(description = "학점", example = "3")
String grades,

@Schema(description = "학과", example = "컴퓨터공학부")
String department
) {
public static LecturePortionResponse from(Lecture lecture) {
return new LecturePortionResponse(
lecture.getId(),
lecture.getCode(),
lecture.getName(),
lecture.getGrades(),
lecture.getDepartment()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,18 @@ public interface CatalogRepository extends Repository<Catalog, Integer> {

Optional<Catalog> findByDepartmentAndCode(Department department, String code);

// 이거 오류나요..
// List<Catalog> findByLectureNameAndYearAndDepartment(String lectureName, String studentYear, Department department);

Optional<List<Catalog>> findAllByCourseTypeId(Integer courseTypeId);

default Catalog getByDepartmentAndCode(Department department, String code) {
return findByDepartmentAndCode(department, code)
.orElseThrow(() -> CatalogNotFoundException.withDetail("department: " + department + ", code: " + code));
}

List<Catalog> findByLectureNameAndYearAndDepartment(String lectureName, String studentYear, Department department);
default List<Catalog> getAllByCourseTypeId(Integer courseTypeId) {
return findAllByCourseTypeId(courseTypeId)
.orElseThrow(() -> CatalogNotFoundException.withDetail("course_type_id" + courseTypeId));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -19,4 +19,9 @@ default CourseType getCourseTypeById(Integer id) {
return findById(id)
.orElseThrow(() -> CourseTypeNotFoundException.withDetail("course_type_id: " + id));
}

default CourseType getByName(String name) {
return findByName(name)
.orElseThrow(() -> CourseTypeNotFoundException.withDetail("course_type_name: " + name));
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,8 +8,8 @@

public interface DetectGraduationCalculationRepository extends Repository<DetectGraduationCalculation, Integer> {

Optional<DetectGraduationCalculation> findByUserId(Integer userId);

void save(DetectGraduationCalculation detectGraduationCalculation);

Optional<DetectGraduationCalculation> findByUserId(Integer userId);
}

Original file line number Diff line number Diff line change
Expand Up @@ -8,13 +8,13 @@

public interface StudentCourseCalculationRepository extends Repository<StudentCourseCalculation, Integer> {

Optional<StudentCourseCalculation> findByUserId(Integer userId);

void deleteAllByUserId(Integer userId);

void save(StudentCourseCalculation studentCourseCalculation);

Optional<StudentCourseCalculation> findByUserId(Integer userId);

StudentCourseCalculation findByUserIdAndStandardGraduationRequirementsId(Integer userId, Integer id);

void delete(StudentCourseCalculation existingCalculation);

void deleteAllByUserId(Integer userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import java.io.IOException;
import java.io.InputStream;
import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
Expand All @@ -16,6 +15,7 @@
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.multipart.MultipartFile;

import in.koreatech.koin.domain.graduation.dto.CourseTypeLectureResponse;
import in.koreatech.koin.domain.graduation.dto.GraduationCourseCalculationResponse;
import in.koreatech.koin.domain.graduation.model.Catalog;
import in.koreatech.koin.domain.graduation.model.DetectGraduationCalculation;
Expand All @@ -30,7 +30,7 @@
import in.koreatech.koin.domain.student.model.Department;
import in.koreatech.koin.domain.student.model.Student;
import in.koreatech.koin.domain.student.repository.StudentRepository;
import in.koreatech.koin.domain.student.util.StudentUtil;
import in.koreatech.koin.domain.timetableV2.exception.NotFoundSemesterAndCourseTypeException;
import in.koreatech.koin.domain.timetableV2.model.TimetableLecture;
import in.koreatech.koin.domain.timetableV2.repository.TimetableFrameRepositoryV2;

Expand All @@ -45,6 +45,9 @@
import in.koreatech.koin.domain.timetableV2.repository.LectureRepositoryV2;
import in.koreatech.koin.domain.timetableV2.repository.SemesterRepositoryV2;
import in.koreatech.koin.domain.timetableV2.repository.TimetableLectureRepositoryV2;
import in.koreatech.koin.domain.timetableV3.model.Term;
import in.koreatech.koin.domain.timetableV3.repository.SemesterRepositoryV3;
import in.koreatech.koin.domain.timetableV3.service.SemesterServiceV3;
import in.koreatech.koin.domain.user.model.User;
import in.koreatech.koin.domain.user.repository.UserRepository;
import lombok.RequiredArgsConstructor;
Expand All @@ -64,12 +67,14 @@ public class GraduationService {
private final SemesterRepositoryV2 semesterRepositoryV2;
private final LectureRepositoryV2 lectureRepositoryV2;
private final TimetableLectureRepositoryV2 timetableLectureRepositoryV2;
private final SemesterRepositoryV3 semesterRepositoryV3;
private final CatalogRepository catalogRepository;

private static final String MIDDLE_TOTAL = "소 계";
private static final String TOTAL = "합 계";
private static final String RETAKE = "Y";
private static final String UNSATISFACTORY = "U";
private final SemesterServiceV3 semesterServiceV3;

@Transactional
public void createStudentCourseCalculation(Integer userId) {
Expand Down Expand Up @@ -103,6 +108,7 @@ public void resetStudentCourseCalculation(Student student, Department newDepartm
});
}

/*
@Transactional
public GraduationCourseCalculationResponse getGraduationCourseCalculationResponse(Integer userId) {
DetectGraduationCalculation detectGraduationCalculation = detectGraduationCalculationRepository.findByUserId(userId)
Expand Down Expand Up @@ -135,6 +141,7 @@ public GraduationCourseCalculationResponse getGraduationCourseCalculationRespons
return GraduationCourseCalculationResponse.of(courseTypes);
}
*/

@Transactional
public void readStudentGradeExcelFile(MultipartFile file, Integer userId) throws IOException {
Expand All @@ -158,7 +165,7 @@ public void readStudentGradeExcelFile(MultipartFile file, Integer userId) throws
}

String semester = getKoinSemester(data.semester(), data.year());
CourseType courseType = courseTypeRepository.findByName(data.courseType()).orElse(null);
CourseType courseType = mappingCourseType(data.courseType());
Lecture lecture = lectureRepositoryV2.findBySemesterAndCodeAndLectureClass(semester,
data.code(), data.lectureClass()).orElse(null);

Expand Down Expand Up @@ -229,8 +236,8 @@ private void checkFiletype(MultipartFile file) {

private boolean skipRow(GradeExcelData gradeExcelData) {
return gradeExcelData.classTitle().equals(MIDDLE_TOTAL) ||
gradeExcelData.retakeStatus().equals(RETAKE) ||
gradeExcelData.grade().equals(UNSATISFACTORY);
gradeExcelData.retakeStatus().equals(RETAKE) ||
gradeExcelData.grade().equals(UNSATISFACTORY);
}

private String getKoinSemester(String semester, String year) {
Expand All @@ -248,6 +255,16 @@ private void validateStudentField(Object field, String message) {
}
}

public CourseType mappingCourseType(String courseTypeName) {
if ("전필".equals(courseTypeName)) {
return courseTypeRepository.getByName("학과(전공)필수");
} else if ("전선".equals(courseTypeName)) {
return courseTypeRepository.getByName("학과(전공)선택");
} else {
return courseTypeRepository.findByName(courseTypeName).orElse(null);
}
}

private void initializeStudentCourseCalculation(Student student, Department department) {
// 학번에 맞는 이수요건 정보 조회
List<StandardGraduationRequirements> requirementsList =
Expand Down Expand Up @@ -278,6 +295,7 @@ private Student getValidatedStudent(Integer userId) {
return student;
}

/*
private List<Catalog> getCatalogListForStudent(Student student, String studentYear) {
List<TimetableLecture> timetableLectures = timetableFrameRepositoryV2.getAllByUserId(student.getId()).stream()
.flatMap(frame -> frame.getTimetableLectures().stream())
Expand All @@ -297,6 +315,7 @@ private List<Catalog> getCatalogListForStudent(Student student, String studentYe
});
return catalogList;
}
*/

private Map<Integer, Integer> calculateCourseTypeCredits(List<Catalog> catalogList) {
Map<Integer, Integer> courseTypeCreditsMap = new HashMap<>();
Expand Down Expand Up @@ -361,4 +380,19 @@ private int updateStudentCourseCalculation(Integer userId, Student student,

return completedGrades;
}

public CourseTypeLectureResponse getLectureByCourseType(Integer year, String term, String courseTypeName) {
CourseType courseType = courseTypeRepository.getByName(courseTypeName);
List<Catalog> catalogs = catalogRepository.getAllByCourseTypeId(courseType.getId());
List<String> codes = catalogs.stream().map(Catalog::getCode).toList();

Term parsedTerm = Term.fromDescription(term);
Semester foundSemester = semesterRepositoryV3.getByYearAndTerm(year, parsedTerm);
String semester = foundSemester.getSemester();

List<Lecture> lectures = lectureRepositoryV2.findAllByCodesAndSemester(codes, semester)
.orElseThrow(() -> new NotFoundSemesterAndCourseTypeException("학기나 이수구분을 찾을 수 없습니다."));

return CourseTypeLectureResponse.of(semester, lectures);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,19 @@
package in.koreatech.koin.domain.timetableV2.exception;

import in.koreatech.koin.global.exception.DataNotFoundException;

public class NotFoundSemesterAndCourseTypeException extends DataNotFoundException {
private static final String DEFAULT_MESSAGE = "학기나 이수구분을 찾을 수 없습니다.";

public NotFoundSemesterAndCourseTypeException(String message) {
super(message);
}

public NotFoundSemesterAndCourseTypeException(String message, String detail) {
super(message, detail);
}

public static NotFoundSemesterAndCourseTypeException withDetail(String detail) {
return new NotFoundSemesterAndCourseTypeException(DEFAULT_MESSAGE, detail);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,13 @@
import java.util.List;
import java.util.Optional;

import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.Repository;

import in.koreatech.koin.domain.timetable.exception.LectureNotFoundException;
import in.koreatech.koin.domain.timetable.exception.SemesterNotFoundException;
import in.koreatech.koin.domain.timetable.model.Lecture;
import in.koreatech.koin.domain.timetable.model.Semester;
import io.lettuce.core.dynamic.annotation.Param;

public interface LectureRepositoryV2 extends Repository<Lecture, Integer> {

Expand All @@ -20,6 +21,9 @@ public interface LectureRepositoryV2 extends Repository<Lecture, Integer> {

Optional<Lecture> findBySemesterAndCodeAndLectureClass(String semesterDate, String code, String classLecture);

@Query("SELECT l FROM Lecture l WHERE l.code IN :codes AND l.semester = :semesterDate")
Optional<List<Lecture>> findAllByCodesAndSemester(@Param("codes") List<String> codes, @Param("semesterDate") String semesterDate);

default Lecture getBySemesterAndCodeAndLectureClass(String semesterDate, String code, String classLecture) {
return findBySemesterAndCodeAndLectureClass(semesterDate, code, classLecture)
.orElseThrow(() -> SemesterNotFoundException.withDetail(
Expand All @@ -30,4 +34,5 @@ default Lecture getLectureById(Integer id) {
return findById(id)
.orElseThrow(() -> LectureNotFoundException.withDetail("lecture_id: " + id));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -61,7 +61,8 @@ public GroupedOpenApi userApi() {
"in.koreatech.koin.domain.student",
"in.koreatech.koin.domain.timetable",
"in.koreatech.koin.domain.timetableV2",
"in.koreatech.koin.domain.timetableV3"
"in.koreatech.koin.domain.timetableV3",
"in.koreatech.koin.domain.graduation"
};

return createGroupedOpenApi("4. User API", packagesPath);
Expand Down
Loading

0 comments on commit 0527431

Please sign in to comment.