Skip to content

Commit 2565dd9

Browse files
authored
[EC-88] BE/feat: 수강생 유고 결석 취소 api (#77)
* [EC-88] feat: S3 Client delete service 추가 * [EC-88] feat: 리소스 생성자와 수정 및 삭제 요청자 불일치 커스텀 예외 추가 * [EC-88] feat: 삭제 요청 날짜 관리할 필드 추가 * [EC-88] feat: 유고결석 신청 , 유고결석 신청 첨부파일 테이블 삭제 요청 처리 - 소프트 딜리트 적용 * [EC-88] feat: 첨부파일 삭제 로직 적용 - 삭제 요청일로부터 3달 지난 첨부파일은 S3에서 하드 딜리트 - DB 테이블 상에서도 삭제 처리 - 신청 내역은 DB 상에 유지
1 parent 7868775 commit 2565dd9

File tree

13 files changed

+189
-25
lines changed

13 files changed

+189
-25
lines changed

api/src/main/java/org/example/educheck/domain/absenceattendance/controller/AbsenceAttendanceController.java

Lines changed: 11 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -47,4 +47,15 @@ public ResponseEntity<ApiResponse<CreateAbsenceAttendacneReponseDto>> applyAtten
4747
;
4848
}
4949

50+
@DeleteMapping("/my/course/{courseId}/absence-attendances/{absenceAttendancesId}")
51+
public ResponseEntity<ApiResponse<Object>> cancelAttendanceAbsence(@AuthenticationPrincipal Member member,
52+
@PathVariable Long absenceAttendancesId) {
53+
absenceAttendanceService.cancelAttendanceAbsence(member, absenceAttendancesId);
54+
return ResponseEntity.ok(
55+
ApiResponse.ok("유고 결석 신청 취소 성공",
56+
"OK",
57+
null));
58+
59+
}
60+
5061
}

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

Lines changed: 15 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -2,12 +2,16 @@
22

33
import jakarta.persistence.*;
44
import lombok.*;
5+
import org.example.educheck.domain.absenceattendanceattachmentfile.entity.AbsenceAttendanceAttachmentFile;
56
import org.example.educheck.domain.course.entity.Course;
67
import org.example.educheck.domain.member.staff.entity.Staff;
78
import org.example.educheck.domain.member.student.entity.Student;
9+
import org.example.educheck.global.common.entity.BaseTimeEntity;
810

911
import java.time.LocalDate;
1012
import java.time.LocalDateTime;
13+
import java.util.ArrayList;
14+
import java.util.List;
1115

1216
/**
1317
* isApprove : T 승인 F 반려 null 대기
@@ -16,7 +20,7 @@
1620
@Setter
1721
@Entity
1822
@NoArgsConstructor(access = AccessLevel.PROTECTED)
19-
public class AbsenceAttendance {
23+
public class AbsenceAttendance extends BaseTimeEntity {
2024

2125
@Id
2226
@GeneratedValue(strategy = GenerationType.IDENTITY)
@@ -41,6 +45,11 @@ public class AbsenceAttendance {
4145
private String reason;
4246
private String category;
4347

48+
@OneToMany(mappedBy = "absenceAttendance", cascade = CascadeType.ALL, orphanRemoval = true)
49+
private List<AbsenceAttendanceAttachmentFile> absenceAttendanceAttachmentFiles = new ArrayList<>();
50+
51+
private LocalDateTime deletionRequestedAt;
52+
4453
@Builder
4554
public AbsenceAttendance(Staff staff, Course course, Student student, LocalDate startTime, LocalDate endTime, Character isApprove, LocalDateTime approveDate, String reason, String category) {
4655
this.staff = staff;
@@ -53,4 +62,9 @@ public AbsenceAttendance(Staff staff, Course course, Student student, LocalDate
5362
this.reason = reason;
5463
this.category = category;
5564
}
65+
66+
public void markDeletionRequested() {
67+
this.deletionRequestedAt = LocalDateTime.now();
68+
}
69+
5670
}

api/src/main/java/org/example/educheck/domain/absenceattendance/service/AbsenceAttendanceService.java

Lines changed: 28 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -16,6 +16,7 @@
1616
import org.example.educheck.domain.member.student.entity.Student;
1717
import org.example.educheck.domain.registration.entity.Registration;
1818
import org.example.educheck.domain.registration.repository.RegistrationRepository;
19+
import org.example.educheck.global.common.exception.custom.common.NotOwnerException;
1920
import org.example.educheck.global.common.exception.custom.common.ResourceMismatchException;
2021
import org.example.educheck.global.common.exception.custom.common.ResourceNotFoundException;
2122
import org.example.educheck.global.common.s3.S3Service;
@@ -27,6 +28,7 @@
2728
import java.time.LocalDateTime;
2829
import java.util.List;
2930
import java.util.Map;
31+
import java.util.Objects;
3032

3133
@Service
3234
@RequiredArgsConstructor
@@ -40,6 +42,13 @@ public class AbsenceAttendanceService {
4042
private final AbsenceAttendanceAttachmentFileRepository absenceAttendanceAttachmentFileRepository;
4143
private final RegistrationRepository registrationRepository;
4244

45+
private static void validateIsApplicant(Member member, AbsenceAttendance absenceAttendance) {
46+
47+
if (!Objects.equals(member.getStudentId(), absenceAttendance.getStudent().getId())) {
48+
throw new NotOwnerException();
49+
}
50+
}
51+
4352
@Transactional
4453
@PreAuthorize("hasAnyAuthority('MIDDLE_ADMIN')")
4554
public void processAbsenceAttendanceService(Long courseId, Long absesnceAttendancesId, ProcessAbsenceAttendanceRequestDto requestDto, Member member) {
@@ -115,14 +124,31 @@ private void saveAttachementFiles(MultipartFile[] files, AbsenceAttendance saved
115124
}
116125

117126
private void validateRegistrationCourse(Member member, Long courseId) {
118-
log.info("memberId: " + member.getId() + " courseId: " + courseId);
119127
Student student = member.getStudent();
120-
log.info("studentId : {}", student.getId());
128+
121129
Registration registration = registrationRepository.findByStudentIdAndCourseId(member.getStudent().getId(), courseId)
122130
.orElseThrow(ResourceNotFoundException::new);
123131

124132
if (registration == null) {
125133
throw new ResourceMismatchException();
126134
}
127135
}
136+
137+
@Transactional
138+
public void cancelAttendanceAbsence(Member member, Long absenceAttendancesId) {
139+
140+
AbsenceAttendance absenceAttendance = absenceAttendanceRepository.findById(absenceAttendancesId)
141+
.orElseThrow(() -> new ResourceNotFoundException("해당 유고 결석 신청 내역이 존재하지 않습니다."));
142+
143+
validateIsApplicant(member, absenceAttendance);
144+
145+
List<AbsenceAttendanceAttachmentFile> attachmentFiles = absenceAttendance.getAbsenceAttendanceAttachmentFiles();
146+
for (AbsenceAttendanceAttachmentFile attachmentFile : attachmentFiles) {
147+
attachmentFile.markDeletionRequested();
148+
absenceAttendanceAttachmentFileRepository.save(attachmentFile);
149+
}
150+
151+
absenceAttendance.markDeletionRequested();
152+
absenceAttendanceRepository.save(absenceAttendance);
153+
}
128154
}

api/src/main/java/org/example/educheck/domain/absenceattendanceattachmentfile/entity/AbsenceAttendanceAttachmentFile.java

Lines changed: 20 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,8 @@
88
import org.example.educheck.domain.absenceattendance.entity.AbsenceAttendance;
99
import org.example.educheck.global.common.entity.BaseTimeEntity;
1010

11+
import java.time.LocalDateTime;
12+
1113
@Getter
1214
@Entity(name = "absence_attendacne_attachment_file")
1315
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@@ -28,6 +30,8 @@ public class AbsenceAttendanceAttachmentFile extends BaseTimeEntity {
2830
//버킷 내 고유 식별자, 전체 경로 포함
2931
private String s3Key;
3032

33+
private LocalDateTime deletionRequestedAt;
34+
3135
@Builder
3236
public AbsenceAttendanceAttachmentFile(AbsenceAttendance absenceAttendance, String url, String originalName, String s3Key, String mime) {
3337
this.absenceAttendance = absenceAttendance;
@@ -37,4 +41,20 @@ public AbsenceAttendanceAttachmentFile(AbsenceAttendance absenceAttendance, Stri
3741
this.mime = mime;
3842
}
3943

44+
public void markDeletionRequested() {
45+
this.deletionRequestedAt = LocalDateTime.now();
46+
}
47+
48+
@Override
49+
public String toString() {
50+
return "AbsenceAttendanceAttachmentFile{" +
51+
"id=" + id +
52+
", absenceAttendance=" + absenceAttendance +
53+
", url='" + url + '\'' +
54+
", mime='" + mime + '\'' +
55+
", originalName='" + originalName + '\'' +
56+
", s3Key='" + s3Key + '\'' +
57+
", deletionRequestedAt=" + deletionRequestedAt +
58+
'}';
59+
}
4060
}

api/src/main/java/org/example/educheck/domain/absenceattendanceattachmentfile/repository/AbsenceAttendanceAttachmentFileRepository.java

Lines changed: 8 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,6 +2,14 @@
22

33
import org.example.educheck.domain.absenceattendanceattachmentfile.entity.AbsenceAttendanceAttachmentFile;
44
import org.springframework.data.jpa.repository.JpaRepository;
5+
import org.springframework.data.jpa.repository.Query;
6+
7+
import java.time.LocalDateTime;
8+
import java.util.List;
59

610
public interface AbsenceAttendanceAttachmentFileRepository extends JpaRepository<AbsenceAttendanceAttachmentFile, Long> {
11+
12+
@Query("SELECT a FROM absence_attendacne_attachment_file a WHERE a.deletionRequestedAt <= :date")
13+
List<AbsenceAttendanceAttachmentFile> findByDeletionRequestedAtBeforeAndDeleted(LocalDateTime date);
14+
715
}

api/src/main/java/org/example/educheck/domain/member/controller/MyController.java

Lines changed: 0 additions & 21 deletions
This file was deleted.

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

Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -64,4 +64,8 @@ public Collection<? extends GrantedAuthority> getAuthorities() {
6464
public String getUsername() {
6565
return email;
6666
}
67+
68+
public Long getStudentId() {
69+
return student != null ? student.getId() : null;
70+
}
6771
}

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

Lines changed: 14 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -10,6 +10,7 @@
1010

1111
import java.util.ArrayList;
1212
import java.util.List;
13+
import java.util.Objects;
1314

1415

1516
@Entity
@@ -40,4 +41,17 @@ public Student(Member member, Status status, char courseParticipationStatus, Lis
4041
this.courseParticipationStatus = courseParticipationStatus;
4142
this.registrations = registrations;
4243
}
44+
45+
@Override
46+
public boolean equals(Object o) {
47+
if (this == o) return true;
48+
if (o == null || getClass() != o.getClass()) return false;
49+
Student student = (Student) o;
50+
return Objects.equals(id, student.id);
51+
}
52+
53+
@Override
54+
public int hashCode() {
55+
return Objects.hashCode(id);
56+
}
4357
}

api/src/main/java/org/example/educheck/global/common/config/WebConfig.java

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,11 @@
22

33
import org.springframework.beans.factory.annotation.Value;
44
import org.springframework.context.annotation.Configuration;
5+
import org.springframework.scheduling.annotation.EnableScheduling;
56
import org.springframework.web.servlet.config.annotation.CorsRegistry;
67
import org.springframework.web.servlet.config.annotation.WebMvcConfigurer;
78

9+
@EnableScheduling
810
@Configuration
911
public class WebConfig implements WebMvcConfigurer {
1012

Lines changed: 18 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,18 @@
1+
package org.example.educheck.global.common.exception.custom.common;
2+
3+
4+
import org.example.educheck.global.common.exception.ErrorCode;
5+
6+
public class NotOwnerException extends GlobalException {
7+
public NotOwnerException(ErrorCode errorCode) {
8+
super(errorCode);
9+
}
10+
11+
public NotOwnerException() {
12+
super(ErrorCode.NOT_OWNER, ErrorCode.NOT_OWNER.getMessage());
13+
}
14+
15+
public NotOwnerException(String message) {
16+
super(ErrorCode.NOT_OWNER, message);
17+
}
18+
}

0 commit comments

Comments
 (0)