Skip to content

Commit cfa3cf5

Browse files
authored
[EC-62] feat: 유고 결석 신청 처리 API 구현 (#49)
* [EC-62] feat: AbsenceAttendance controller, service, repository 생성 * [EC-62] feat: AbsenceAttendance 클래스 멤버 변수 타입 변경 bool -> * [EC-62] feat: 유고 결석 신청 처리 기능 구현 * [EC-62] feat: 유고 결석 신청 처리 예외 처리 - 권한 체크 - 요청 body 형태가 잘못된 경우 예외 처리 - 패스 파라미터의 코스 id와 유고결석의 코스id가 일치하지 않을 경우 예외 처리
1 parent 0b4e51b commit cfa3cf5

File tree

10 files changed

+128
-4
lines changed

10 files changed

+128
-4
lines changed
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
package org.example.educheck.domain.absenceattendance.controller;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import org.example.educheck.domain.absenceattendance.dto.request.ProcessAbsenceAttendanceRequestDto;
5+
import org.example.educheck.domain.absenceattendance.service.AbsenceAttendanceService;
6+
import org.example.educheck.domain.member.entity.Member;
7+
import org.example.educheck.global.common.dto.ApiResponse;
8+
import org.springframework.http.HttpStatus;
9+
import org.springframework.http.ResponseEntity;
10+
import org.springframework.security.core.annotation.AuthenticationPrincipal;
11+
import org.springframework.web.bind.annotation.*;
12+
13+
@RestController
14+
@RequiredArgsConstructor
15+
@RequestMapping("api/course/{courseId}/absence-attendances")
16+
public class AbsenceAttendanceController {
17+
private final AbsenceAttendanceService absenceAttendanceService;
18+
19+
20+
@PatchMapping("/{absesnceAttendancesId}")
21+
public ResponseEntity<ApiResponse<Void>> processAbsenceAttendanceService(@PathVariable Long courseId, @PathVariable Long absesnceAttendancesId,
22+
@RequestBody ProcessAbsenceAttendanceRequestDto requestDto,
23+
@AuthenticationPrincipal Member member) {
24+
25+
absenceAttendanceService.processAbsenceAttendanceService(courseId, absesnceAttendancesId, requestDto, member);
26+
27+
return ResponseEntity.status(HttpStatus.OK).body(ApiResponse.ok("유고 결석 처리 성공", "OK", null));
28+
}
29+
}
Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,14 @@
1+
package org.example.educheck.domain.absenceattendance.dto.request;
2+
3+
import com.fasterxml.jackson.annotation.JsonProperty;
4+
import lombok.AllArgsConstructor;
5+
import lombok.Getter;
6+
import lombok.NoArgsConstructor;
7+
8+
@Getter
9+
@NoArgsConstructor
10+
@AllArgsConstructor
11+
public class ProcessAbsenceAttendanceRequestDto {
12+
@JsonProperty("isApprove")
13+
private boolean isApprove;
14+
}

api/src/main/java/org/example/educheck/domain/absenceattendance/entity/AbsenceAttendance.java

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,7 @@
44
import lombok.AccessLevel;
55
import lombok.Getter;
66
import lombok.NoArgsConstructor;
7+
import lombok.Setter;
78
import org.example.educheck.domain.course.entity.Course;
89
import org.example.educheck.domain.member.staff.entity.Staff;
910
import org.example.educheck.domain.member.student.entity.Student;
@@ -14,6 +15,7 @@
1415
* isApprove : T 승인 F 반려 null 대기
1516
*/
1617
@Getter
18+
@Setter
1719
@Entity
1820
@NoArgsConstructor(access = AccessLevel.PROTECTED)
1921
public class AbsenceAttendance {
@@ -36,7 +38,7 @@ public class AbsenceAttendance {
3638

3739
private LocalDateTime startTime;
3840
private LocalDateTime endTime;
39-
private char isApprove;
41+
private Character isApprove;
4042
private LocalDateTime approveDate;
4143
private String reason;
4244
}
Lines changed: 7 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,7 @@
1+
package org.example.educheck.domain.absenceattendance.repository;
2+
3+
import org.example.educheck.domain.absenceattendance.entity.AbsenceAttendance;
4+
import org.springframework.data.jpa.repository.JpaRepository;
5+
6+
public interface AbsenceAttendanceRepository extends JpaRepository<AbsenceAttendance, Long> {
7+
}
Lines changed: 53 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,53 @@
1+
package org.example.educheck.domain.absenceattendance.service;
2+
3+
import lombok.RequiredArgsConstructor;
4+
import lombok.extern.slf4j.Slf4j;
5+
import org.example.educheck.domain.absenceattendance.dto.request.ProcessAbsenceAttendanceRequestDto;
6+
import org.example.educheck.domain.absenceattendance.entity.AbsenceAttendance;
7+
import org.example.educheck.domain.absenceattendance.repository.AbsenceAttendanceRepository;
8+
import org.example.educheck.domain.member.entity.Member;
9+
import org.example.educheck.domain.member.repository.StaffRepository;
10+
import org.example.educheck.domain.member.staff.entity.Staff;
11+
import org.example.educheck.global.common.exception.custom.common.ResourceMismatchException;
12+
import org.example.educheck.global.common.exception.custom.common.ResourceNotFoundException;
13+
import org.springframework.security.access.prepost.PreAuthorize;
14+
import org.springframework.stereotype.Service;
15+
import org.springframework.transaction.annotation.Transactional;
16+
17+
import java.time.LocalDateTime;
18+
19+
@Service
20+
@RequiredArgsConstructor
21+
@Transactional(readOnly = true)
22+
@Slf4j
23+
public class AbsenceAttendanceService {
24+
private final AbsenceAttendanceRepository absenceAttendanceRepository;
25+
private final StaffRepository staffRepository;
26+
27+
@Transactional
28+
@PreAuthorize("hasAnyAuthority('MIDDLE_ADMIN')")
29+
public void processAbsenceAttendanceService(Long courseId, Long absesnceAttendancesId, ProcessAbsenceAttendanceRequestDto requestDto, Member member) {
30+
31+
32+
AbsenceAttendance absenceAttendance =
33+
absenceAttendanceRepository.findById(absesnceAttendancesId)
34+
.orElseThrow(() -> new ResourceNotFoundException("유교 결석 조회 불가"));
35+
36+
Staff staff =
37+
staffRepository.findByMember(member)
38+
.orElseThrow(() -> new ResourceNotFoundException("회원 정보를 찾을 수 없습니다."));
39+
40+
if (absenceAttendance.getCourse().getId() != courseId) {
41+
throw new ResourceMismatchException();
42+
}
43+
44+
absenceAttendance.setStaff(staff);
45+
absenceAttendance.setApproveDate(LocalDateTime.now());
46+
absenceAttendance.setIsApprove(
47+
String.valueOf(
48+
requestDto.isApprove()
49+
)
50+
.toUpperCase().charAt(0)
51+
);
52+
}
53+
}

api/src/main/java/org/example/educheck/domain/member/repository/StaffRepository.java

Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,6 @@
11
package org.example.educheck.domain.member.repository;
22

3+
import org.example.educheck.domain.member.entity.Member;
34
import org.example.educheck.domain.member.staff.entity.Staff;
45
import org.springframework.data.jpa.repository.JpaRepository;
56

@@ -8,4 +9,6 @@
89
public interface StaffRepository extends JpaRepository<Staff, Long> {
910

1011
Optional<Staff> findByMemberId(Long memberId);
12+
13+
Optional<Staff> findByMember(Member member);
1114
}

api/src/main/java/org/example/educheck/domain/member/student/entity/Student.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -35,9 +35,10 @@ public class Student {
3535
private List<Registration> registrations = new ArrayList<>();
3636

3737
@Builder
38-
public Student(Member member, Status status, char courseParticipationStatus) {
38+
public Student(Member member, Status status, char courseParticipationStatus, List<Registration> registrations) {
3939
this.member = member;
4040
this.status = status;
4141
this.courseParticipationStatus = courseParticipationStatus;
42+
this.registrations = registrations;
4243
}
4344
}

api/src/main/java/org/example/educheck/global/common/exception/handler/GlobalExceptionHandler.java

Lines changed: 13 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -5,6 +5,7 @@
55
import org.example.educheck.global.common.exception.custom.LoginValidationException;
66
import org.example.educheck.global.common.exception.custom.common.GlobalException;
77
import org.springframework.http.ResponseEntity;
8+
import org.springframework.http.converter.HttpMessageNotReadableException;
89
import org.springframework.validation.FieldError;
910
import org.springframework.web.bind.MethodArgumentNotValidException;
1011
import org.springframework.web.bind.MissingRequestCookieException;
@@ -52,10 +53,21 @@ public ResponseEntity<ApiResponse<Object>> methodArgumentNotValidHandler(
5253
}
5354

5455
@ExceptionHandler(MissingRequestCookieException.class)
55-
public ResponseEntity<ApiResponse<Object>> missingRequestCookieException(MissingRequestCookieException ex) {
56+
public ResponseEntity<ApiResponse<Object>> handleMissingRequestCookieException(MissingRequestCookieException ex) {
57+
5658
return ResponseEntity
5759
.status(ErrorCode.UNAUTHORIZED.getStatus())
5860
.body(ApiResponse.error(ErrorCode.UNAUTHORIZED.getMessage(),
5961
ErrorCode.UNAUTHORIZED.getCode()));
62+
63+
}
64+
65+
@ExceptionHandler(HttpMessageNotReadableException.class)
66+
public ResponseEntity<ApiResponse<Object>> handleHttpMessageNotReadableException(HttpMessageNotReadableException ex) {
67+
68+
return ResponseEntity
69+
.status(ErrorCode.INVALID_INPUT.getStatus())
70+
.body(ApiResponse.error(ErrorCode.INVALID_INPUT.getMessage(),
71+
ErrorCode.INVALID_INPUT.getCode()));
6072
}
6173
}

api/src/main/java/org/example/educheck/global/security/config/SecurityConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -7,6 +7,7 @@
77
import org.springframework.context.annotation.Bean;
88
import org.springframework.context.annotation.Configuration;
99
import org.springframework.security.config.Customizer;
10+
import org.springframework.security.config.annotation.method.configuration.EnableGlobalMethodSecurity;
1011
import org.springframework.security.config.annotation.web.builders.HttpSecurity;
1112
import org.springframework.security.config.annotation.web.configuration.EnableWebSecurity;
1213
import org.springframework.security.config.http.SessionCreationPolicy;
@@ -17,6 +18,7 @@
1718
@EnableWebSecurity
1819
@Configuration
1920
@RequiredArgsConstructor
21+
@EnableGlobalMethodSecurity(prePostEnabled = true)
2022
public class SecurityConfig {
2123
private final JwtAuthenticationFilter jwtAuthenticationFilter;
2224
private final JwtAuthenticationEntryPoint jwtAuthenticationEntryPoint;

api/src/main/java/org/example/educheck/global/security/jwt/JwtTokenUtil.java

Lines changed: 2 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -6,6 +6,7 @@
66
import io.jsonwebtoken.SignatureAlgorithm;
77
import io.jsonwebtoken.security.Keys;
88
import jakarta.annotation.PostConstruct;
9+
import org.example.educheck.global.common.exception.custom.LoginValidationException;
910
import org.springframework.beans.factory.annotation.Value;
1011
import org.springframework.security.core.Authentication;
1112
import org.springframework.security.core.GrantedAuthority;
@@ -45,7 +46,7 @@ private String createToken(Authentication authentication, long validityMilliSeco
4546
field.setAccessible(true);
4647
email = field.get(principal).toString();
4748
} catch (NoSuchFieldException | IllegalAccessException e) {
48-
throw new RuntimeException(e);
49+
throw new LoginValidationException();
4950
}
5051
Claims claims = Jwts.claims().setSubject(email);
5152
claims.put("roles", authentication.getAuthorities()

0 commit comments

Comments
 (0)