Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

feat: 졸업학점 계산 동의 API 및 학과, 시간표 수정 대응 #1154

Merged
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
package in.koreatech.koin.domain.graduation.aop;

import org.aspectj.lang.JoinPoint;
import org.aspectj.lang.annotation.AfterReturning;
import org.aspectj.lang.annotation.Aspect;
import org.springframework.stereotype.Component;

import in.koreatech.koin.domain.graduation.repository.DetectGraduationCalculationRepository;
import lombok.RequiredArgsConstructor;

@Aspect
@Component
@RequiredArgsConstructor
public class GraduationAspect {

private final DetectGraduationCalculationRepository detectGraduationCalculationRepository;

/**
* 졸업 요건 계산 변경 여부를 업데이트할 controller 메서드.
*/
@AfterReturning(pointcut = "execution(* in.koreatech.koin.domain.timetable.controller.TimetableController.createTimetables(..)) || " +
Soundbar91 marked this conversation as resolved.
Show resolved Hide resolved
"execution(* in.koreatech.koin.domain.timetable.controller.TimetableController.updateTimetable(..)) || " +
"execution(* in.koreatech.koin.domain.timetable.controller.TimetableController.deleteTimetableById(..)) || " +
"execution(* in.koreatech.koin.domain.timetableV2.controller.TimetableControllerV2.createTimetableLecture(..)) || " +
"execution(* in.koreatech.koin.domain.timetableV2.controller.TimetableControllerV2.updateTimetableLecture(..)) || " +
"execution(* in.koreatech.koin.domain.timetableV2.controller.TimetableControllerV2.deleteTimetableLecture(..)) || " +
"execution(* in.koreatech.koin.domain.timetableV2.controller.TimetableControllerV2.deleteTimetablesFrame(..))",
returning = "result")
public void afterTimetableLecture(JoinPoint joinPoint, Object result) {
Object[] args = joinPoint.getArgs();
Integer userId = (Integer) args[1];
Soundbar91 marked this conversation as resolved.
Show resolved Hide resolved

detectGraduationCalculationRepository.findByUserId(userId).ifPresent(detectGraduationCalculation -> {
detectGraduationCalculation.updatedIsChanged(true);
});
}
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C

👀

Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package in.koreatech.koin.domain.graduation.controller;

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

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;

import in.koreatech.koin.global.auth.Auth;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.security.SecurityRequirement;
import io.swagger.v3.oas.annotations.tags.Tag;

@Tag(name = "(Normal) Graduation: 졸업학점 계산기", description = "졸업학점 관련 API")
public interface GraduationApi {

@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")
@PostMapping("/graduation/agree")
ResponseEntity<Void> createStudentCourseCalculation(
@Auth(permit = {STUDENT}) Integer userId
);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package in.koreatech.koin.domain.graduation.controller;

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

import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RestController;

import in.koreatech.koin.domain.graduation.service.GraduationService;
import in.koreatech.koin.global.auth.Auth;
import lombok.RequiredArgsConstructor;

@RestController
@RequiredArgsConstructor
public class GraduationController implements GraduationApi{

private final GraduationService graduationService;

@PostMapping("/graduation/agree")
public ResponseEntity<Void> createStudentCourseCalculation(
@Auth(permit = {STUDENT}) Integer userId)
{
graduationService.createStudentCourseCalculation(userId);
return ResponseEntity.ok().build();
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package in.koreatech.koin.domain.graduation.exception;

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

public class CatalogNotFoundException extends DataNotFoundException {

private static final String DEFAULT_MESSAGE = "존재하지 않는 대학 요람입니다.";

protected CatalogNotFoundException(String message) {
super(message);
}

protected CatalogNotFoundException(String message, String detail) {
super(message, detail);
}

public static CatalogNotFoundException withDetail(String detail) {
return new CatalogNotFoundException(DEFAULT_MESSAGE, detail);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import static jakarta.persistence.GenerationType.IDENTITY;
import static lombok.AccessLevel.PROTECTED;

import in.koreatech.koin.domain.student.model.Department;
import in.koreatech.koin.global.domain.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@
import jakarta.persistence.JoinColumn;
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import lombok.Builder;
import lombok.Getter;
import lombok.NoArgsConstructor;

Expand All @@ -36,6 +37,15 @@ public class DetectGraduationCalculation {
@Column(name = "is_changed", columnDefinition = "TINYINT")
private boolean isChanged = false;

@Builder
private DetectGraduationCalculation(
User user,
boolean isChanged
) {
this.user = user;
this.isChanged = isChanged;
}

public void updatedIsChanged(boolean isChanged) {
this.isChanged = isChanged;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@

import org.hibernate.annotations.Where;

import in.koreatech.koin.domain.student.model.Department;
import in.koreatech.koin.global.domain.BaseEntity;
import jakarta.persistence.Column;
import jakarta.persistence.Entity;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
package in.koreatech.koin.domain.graduation.repository;

import java.util.Optional;

import org.springframework.data.repository.Repository;

import in.koreatech.koin.domain.graduation.exception.CatalogNotFoundException;
import in.koreatech.koin.domain.graduation.model.Catalog;
import in.koreatech.koin.domain.student.model.Department;

public interface CatalogRepository extends Repository<Catalog, Integer> {

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

default Catalog getByYearAndDepartmentAndCode(String year, Department department, String code) {
return findByYearAndDepartmentAndCode(year, department, code)
.orElseThrow(() -> CatalogNotFoundException.withDetail(
"year: " + year + ", department: " + department + ", code: " + code));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
package in.koreatech.koin.domain.graduation.repository;

import java.util.Optional;

import org.springframework.data.repository.Repository;

import in.koreatech.koin.domain.graduation.model.DetectGraduationCalculation;

public interface DetectGraduationCalculationRepository extends Repository<DetectGraduationCalculation, Integer> {

Optional<DetectGraduationCalculation> findByUserId(Integer userId);

void save(DetectGraduationCalculation detectGraduationCalculation);
}

Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
package in.koreatech.koin.domain.graduation.repository;

import java.util.List;

import org.springframework.data.repository.Repository;

import in.koreatech.koin.domain.graduation.model.StandardGraduationRequirements;
import in.koreatech.koin.domain.student.model.Department;

public interface StandardGraduationRequirementsRepository extends Repository<StandardGraduationRequirements, Integer> {

List<StandardGraduationRequirements> findAllByDepartmentAndYear(Department department, String year);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package in.koreatech.koin.domain.graduation.repository;

import java.util.Optional;

import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.stereotype.Repository;

import in.koreatech.koin.domain.graduation.model.StudentCourseCalculation;

@Repository
public interface StudentCourseCalculationRepository extends JpaRepository<StudentCourseCalculation, Integer> {
Soundbar91 marked this conversation as resolved.
Show resolved Hide resolved

Optional<StudentCourseCalculation> findByUserId(Integer userId);

void deleteAllByUserId(Integer userId);
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,86 @@
package in.koreatech.koin.domain.graduation.service;

import java.util.List;

import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import in.koreatech.koin.domain.graduation.model.DetectGraduationCalculation;
import in.koreatech.koin.domain.graduation.model.StandardGraduationRequirements;
import in.koreatech.koin.domain.graduation.model.StudentCourseCalculation;
import in.koreatech.koin.domain.graduation.repository.DetectGraduationCalculationRepository;
import in.koreatech.koin.domain.graduation.repository.StandardGraduationRequirementsRepository;
import in.koreatech.koin.domain.graduation.repository.StudentCourseCalculationRepository;
import in.koreatech.koin.domain.student.exception.DepartmentNotFoundException;
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 lombok.RequiredArgsConstructor;

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

private final StudentRepository studentRepository;
private final StudentCourseCalculationRepository studentCourseCalculationRepository;
private final StandardGraduationRequirementsRepository standardGraduationRequirementsRepository;
private final DetectGraduationCalculationRepository detectGraduationCalculationRepository;

@Transactional
public void createStudentCourseCalculation(Integer userId) {
Student student = studentRepository.getById(userId);

if (student.getDepartment() == null) {
DepartmentNotFoundException.withDetail("학과를 추가하세요.");
}
if (student.getStudentNumber() == null) {
DepartmentNotFoundException.withDetail("학번을 추가하세요.");
}
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

C

예외처리 묶어서 처리하는 메소드로 관리하면 좋을 거 같아요!

Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

!넵!


List<StandardGraduationRequirements> requirementsList =
standardGraduationRequirementsRepository.findAllByDepartmentAndYear(
student.getDepartment(), student.getStudentNumber().substring(0, 4));
Soundbar91 marked this conversation as resolved.
Show resolved Hide resolved

requirementsList.forEach(requirement -> studentCourseCalculationRepository.save(
StudentCourseCalculation.builder()
.completedGrades(0)
.user(student.getUser())
.standardGraduationRequirements(requirement)
.build()));

DetectGraduationCalculation detectGraduationCalculation = DetectGraduationCalculation.builder()
.user(student.getUser())
.isChanged(false)
.build();
detectGraduationCalculationRepository.save(detectGraduationCalculation);
}

@Transactional
public void resetStudentCourseCalculation(Student student, Department newDepartment) {
// 기존 학생 졸업요건 계산 정보 삭제
studentCourseCalculationRepository.findByUserId(student.getUser().getId())
.ifPresent(studentCourseCalculation -> {
studentCourseCalculationRepository.deleteAllByUserId(student.getUser().getId());
});
// 학번에 맞는 이수요건 정보 조회
List<StandardGraduationRequirements> requirementsList =
standardGraduationRequirementsRepository.findAllByDepartmentAndYear(
newDepartment, student.getStudentNumber().substring(0, 4));
// 학생 졸업요건 계산 정보 초기화
requirementsList.forEach(requirement ->
studentCourseCalculationRepository.save(
StudentCourseCalculation.builder()
.completedGrades(0)
.user(student.getUser())
.standardGraduationRequirements(requirement)
.build()
)
);
Soundbar91 marked this conversation as resolved.
Show resolved Hide resolved
// DetectGraduationCalculation 정보 업데이트
detectGraduationCalculationRepository.findByUserId(student.getUser().getId())
.ifPresent(detectGraduationCalculation -> {
detectGraduationCalculation.updatedIsChanged(true);
});
}
}
Original file line number Diff line number Diff line change
@@ -1,8 +1,16 @@
package in.koreatech.koin.domain.student.service;

import java.time.Clock;
import java.util.List;
import java.util.Optional;
import java.util.UUID;

import in.koreatech.koin.domain.graduation.model.StandardGraduationRequirements;
import in.koreatech.koin.domain.graduation.model.StudentCourseCalculation;
import in.koreatech.koin.domain.graduation.repository.DetectGraduationCalculationRepository;
import in.koreatech.koin.domain.graduation.repository.StandardGraduationRequirementsRepository;
import in.koreatech.koin.domain.graduation.repository.StudentCourseCalculationRepository;
import in.koreatech.koin.domain.graduation.service.GraduationService;
import in.koreatech.koin.domain.student.model.Department;
import in.koreatech.koin.domain.student.model.Student;
import in.koreatech.koin.domain.student.model.StudentEmailRequestEvent;
Expand All @@ -18,6 +26,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.servlet.ModelAndView;

import in.koreatech.koin.domain.user.dto.AuthTokenRequest;
import in.koreatech.koin.domain.user.dto.FindPasswordRequest;
import in.koreatech.koin.domain.student.dto.StudentLoginRequest;
Expand Down Expand Up @@ -54,6 +63,10 @@ public class StudentService {
private final StudentRepository studentRepository;
private final StudentRedisRepository studentRedisRepository;
private final DepartmentRepository departmentRepository;
private final StudentCourseCalculationRepository studentCourseCalculationRepository;
private final StandardGraduationRequirementsRepository standardGraduationRequirementsRepository;
private final DetectGraduationCalculationRepository detectGraduationCalculationRepository;
private final GraduationService graduationService;
private final PasswordEncoder passwordEncoder;
private final ApplicationEventPublisher eventPublisher;
private final Clock clock;
Expand Down Expand Up @@ -91,7 +104,12 @@ public StudentUpdateResponse updateStudent(Integer userId, StudentUpdateRequest
Student student = studentRepository.getById(userId);
User user = student.getUser();

Department oldDepartment = student.getDepartment();
Department newDepartment = departmentRepository.getByName(request.major());
// 학부(학과) 변경 시 학생의 졸업 요건 계산 정보 초기화
if (isChangedDepartment(oldDepartment, newDepartment) && student.getStudentNumber() != null) {
graduationService.resetStudentCourseCalculation(student, newDepartment);
}
user.update(request.nickname(), request.name(), request.phoneNumber(), request.gender());
user.updateStudentPassword(passwordEncoder, request.password());
student.updateInfo(request.studentNumber(), newDepartment);
Expand Down Expand Up @@ -150,4 +168,8 @@ public ModelAndView checkResetToken(String resetToken, String serverUrl) {
modelAndView.addObject("resetToken", resetToken);
return modelAndView;
}

private boolean isChangedDepartment(Department oldDepartment, Department newDepartment) {
return !oldDepartment.equals(newDepartment);
}
}
Loading
Loading