From 83cc9a980f01f6e55489a8b13489febdbd1aaa8c Mon Sep 17 00:00:00 2001 From: Jaehyun Ahn <91878695+uwoobeat@users.noreply.github.com> Date: Wed, 4 Sep 2024 22:18:20 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EC=88=98=EA=B0=95=EC=8B=A0=EC=B2=AD=20?= =?UTF-8?q?=EC=B2=A0=ED=9A=8C=20=EC=8B=9C=20=EC=97=B0=EA=B4=80=20=EC=97=94?= =?UTF-8?q?=ED=8B=B0=ED=8B=B0=EB=8F=84=20=EA=B0=99=EC=9D=B4=20=EC=82=AD?= =?UTF-8?q?=EC=A0=9C=ED=95=98=EB=8F=84=EB=A1=9D=20=EB=B3=80=EA=B2=BD=20(#7?= =?UTF-8?q?57)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * feat: 수강철회 시 이벤트 발행 * feat: 이벤트 핸들러 및 공통 스터디 서비스 로직 추가 * feat: 삭제 로직 구현 * docs: todo 추가 --- .../study/application/CommonStudyService.java | 28 ++++++++++++++++++- .../application/StudentStudyService.java | 1 + .../study/application/StudyEventHandler.java | 23 +++++++++++++++ .../AssignmentHistoryCustomRepository.java | 3 ++ ...AssignmentHistoryCustomRepositoryImpl.java | 9 ++++++ .../study/dao/AttendanceCustomRepository.java | 3 ++ .../dao/AttendanceCustomRepositoryImpl.java | 12 ++++++++ .../study/domain/StudyApplyCanceledEvent.java | 3 ++ .../domain/study/domain/StudyHistory.java | 6 ++++ 9 files changed, 87 insertions(+), 1 deletion(-) create mode 100644 src/main/java/com/gdschongik/gdsc/domain/study/application/StudyEventHandler.java create mode 100644 src/main/java/com/gdschongik/gdsc/domain/study/domain/StudyApplyCanceledEvent.java diff --git a/src/main/java/com/gdschongik/gdsc/domain/study/application/CommonStudyService.java b/src/main/java/com/gdschongik/gdsc/domain/study/application/CommonStudyService.java index 6bcf2aae9..ff3456d0d 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/study/application/CommonStudyService.java +++ b/src/main/java/com/gdschongik/gdsc/domain/study/application/CommonStudyService.java @@ -1,8 +1,10 @@ package com.gdschongik.gdsc.domain.study.application; -import static com.gdschongik.gdsc.global.exception.ErrorCode.STUDY_NOT_FOUND; +import static com.gdschongik.gdsc.global.exception.ErrorCode.*; import com.gdschongik.gdsc.domain.member.domain.Member; +import com.gdschongik.gdsc.domain.study.dao.AssignmentHistoryRepository; +import com.gdschongik.gdsc.domain.study.dao.AttendanceRepository; import com.gdschongik.gdsc.domain.study.dao.StudyAnnouncementRepository; import com.gdschongik.gdsc.domain.study.dao.StudyHistoryRepository; import com.gdschongik.gdsc.domain.study.dao.StudyRepository; @@ -29,6 +31,8 @@ public class CommonStudyService { private final StudyRepository studyRepository; private final StudyHistoryRepository studyHistoryRepository; private final StudyAnnouncementRepository studyAnnouncementRepository; + private final AttendanceRepository attendanceRepository; + private final AssignmentHistoryRepository assignmentHistoryRepository; private final MemberUtil memberUtil; private final StudyValidator studyValidator; @@ -51,4 +55,26 @@ public List getStudyAnnouncements(Long studyId) { return studyAnnouncements.stream().map(StudyAnnouncementResponse::from).toList(); } + + /** + * 이벤트 핸들러에서 사용되므로, `@Transactional` 을 사용하지 않습니다. + */ + public void deleteAttendanceByStudyHistory(Long studyHistoryId) { + StudyHistory studyHistory = studyHistoryRepository + .findById(studyHistoryId) + .orElseThrow(() -> new CustomException(STUDY_HISTORY_NOT_FOUND)); + + attendanceRepository.deleteByStudyHistory(studyHistory); + } + + /** + * 이벤트 핸들러에서 사용되므로, `@Transactional` 을 사용하지 않습니다. + */ + public void deleteAssignmentHistoryByStudyHistory(Long studyHistoryId) { + StudyHistory studyHistory = studyHistoryRepository + .findById(studyHistoryId) + .orElseThrow(() -> new CustomException(STUDY_HISTORY_NOT_FOUND)); + + assignmentHistoryRepository.deleteByStudyHistory(studyHistory); + } } diff --git a/src/main/java/com/gdschongik/gdsc/domain/study/application/StudentStudyService.java b/src/main/java/com/gdschongik/gdsc/domain/study/application/StudentStudyService.java index 0bc4af0c7..d55b9784d 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/study/application/StudentStudyService.java +++ b/src/main/java/com/gdschongik/gdsc/domain/study/application/StudentStudyService.java @@ -70,6 +70,7 @@ public void applyStudy(Long studyId) { @Transactional public void cancelStudyApply(Long studyId) { + // TODO: 통합 테스트 통해 수강철회 관련 이벤트 처리 확인 Study study = studyRepository.findById(studyId).orElseThrow(() -> new CustomException(STUDY_NOT_FOUND)); Member currentMember = memberUtil.getCurrentMember(); diff --git a/src/main/java/com/gdschongik/gdsc/domain/study/application/StudyEventHandler.java b/src/main/java/com/gdschongik/gdsc/domain/study/application/StudyEventHandler.java new file mode 100644 index 000000000..521155f1e --- /dev/null +++ b/src/main/java/com/gdschongik/gdsc/domain/study/application/StudyEventHandler.java @@ -0,0 +1,23 @@ +package com.gdschongik.gdsc.domain.study.application; + +import com.gdschongik.gdsc.domain.study.domain.StudyApplyCanceledEvent; +import lombok.RequiredArgsConstructor; +import lombok.extern.slf4j.Slf4j; +import org.springframework.stereotype.Component; +import org.springframework.transaction.event.TransactionPhase; +import org.springframework.transaction.event.TransactionalEventListener; + +@Slf4j +@Component +@RequiredArgsConstructor +public class StudyEventHandler { + + private final CommonStudyService commonStudyService; + + @TransactionalEventListener(phase = TransactionPhase.BEFORE_COMMIT) + public void handleStudyApplyCanceledEvent(StudyApplyCanceledEvent event) { + log.info("[StudyEventHandler] 스터디 수강신청 취소 이벤트 수신: studyHistoryId={}", event.studyHistoryId()); + commonStudyService.deleteAttendanceByStudyHistory(event.studyHistoryId()); + commonStudyService.deleteAssignmentHistoryByStudyHistory(event.studyHistoryId()); + } +} diff --git a/src/main/java/com/gdschongik/gdsc/domain/study/dao/AssignmentHistoryCustomRepository.java b/src/main/java/com/gdschongik/gdsc/domain/study/dao/AssignmentHistoryCustomRepository.java index 108112e6f..c1eb52a81 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/study/dao/AssignmentHistoryCustomRepository.java +++ b/src/main/java/com/gdschongik/gdsc/domain/study/dao/AssignmentHistoryCustomRepository.java @@ -3,6 +3,7 @@ import com.gdschongik.gdsc.domain.member.domain.Member; import com.gdschongik.gdsc.domain.study.domain.AssignmentHistory; import com.gdschongik.gdsc.domain.study.domain.Study; +import com.gdschongik.gdsc.domain.study.domain.StudyHistory; import java.util.List; public interface AssignmentHistoryCustomRepository { @@ -10,4 +11,6 @@ public interface AssignmentHistoryCustomRepository { boolean existsSubmittedAssignmentByMemberAndStudy(Member member, Study study); List findAssignmentHistoriesByStudentAndStudyId(Member member, Long studyId); + + void deleteByStudyHistory(StudyHistory studyHistory); } diff --git a/src/main/java/com/gdschongik/gdsc/domain/study/dao/AssignmentHistoryCustomRepositoryImpl.java b/src/main/java/com/gdschongik/gdsc/domain/study/dao/AssignmentHistoryCustomRepositoryImpl.java index 4c6df554c..598379fe8 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/study/dao/AssignmentHistoryCustomRepositoryImpl.java +++ b/src/main/java/com/gdschongik/gdsc/domain/study/dao/AssignmentHistoryCustomRepositoryImpl.java @@ -7,6 +7,7 @@ import com.gdschongik.gdsc.domain.member.domain.Member; import com.gdschongik.gdsc.domain.study.domain.AssignmentHistory; import com.gdschongik.gdsc.domain.study.domain.Study; +import com.gdschongik.gdsc.domain.study.domain.StudyHistory; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; import java.util.List; @@ -53,4 +54,12 @@ public List findAssignmentHistoriesByStudentAndStudyId(Member private BooleanExpression eqStudyId(Long studyId) { return studyId != null ? studyDetail.study.id.eq(studyId) : null; } + + @Override + public void deleteByStudyHistory(StudyHistory studyHistory) { + queryFactory + .delete(assignmentHistory) + .where(eqMember(studyHistory.getStudent()).and(eqStudy(studyHistory.getStudy()))) + .execute(); + } } diff --git a/src/main/java/com/gdschongik/gdsc/domain/study/dao/AttendanceCustomRepository.java b/src/main/java/com/gdschongik/gdsc/domain/study/dao/AttendanceCustomRepository.java index 175472c02..915756630 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/study/dao/AttendanceCustomRepository.java +++ b/src/main/java/com/gdschongik/gdsc/domain/study/dao/AttendanceCustomRepository.java @@ -2,8 +2,11 @@ import com.gdschongik.gdsc.domain.member.domain.Member; import com.gdschongik.gdsc.domain.study.domain.Attendance; +import com.gdschongik.gdsc.domain.study.domain.StudyHistory; import java.util.List; public interface AttendanceCustomRepository { List findByMemberAndStudyId(Member member, Long studyId); + + void deleteByStudyHistory(StudyHistory studyHistory); } diff --git a/src/main/java/com/gdschongik/gdsc/domain/study/dao/AttendanceCustomRepositoryImpl.java b/src/main/java/com/gdschongik/gdsc/domain/study/dao/AttendanceCustomRepositoryImpl.java index 16ed1b15c..f4bd3fb0f 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/study/dao/AttendanceCustomRepositoryImpl.java +++ b/src/main/java/com/gdschongik/gdsc/domain/study/dao/AttendanceCustomRepositoryImpl.java @@ -6,6 +6,7 @@ import com.gdschongik.gdsc.domain.member.domain.Member; import com.gdschongik.gdsc.domain.study.domain.Attendance; +import com.gdschongik.gdsc.domain.study.domain.StudyHistory; import com.querydsl.core.types.dsl.BooleanExpression; import com.querydsl.jpa.impl.JPAQueryFactory; import java.util.List; @@ -33,4 +34,15 @@ private BooleanExpression eqMemberId(Long memberId) { private BooleanExpression eqStudyId(Long studyId) { return studyId != null ? attendance.studyDetail.study.id.eq(studyId) : null; } + + @Override + public void deleteByStudyHistory(StudyHistory studyHistory) { + queryFactory + .delete(attendance) + .where(attendance + .student + .eq(studyHistory.getStudent()) + .and(attendance.studyDetail.study.eq(studyHistory.getStudy()))) + .execute(); + } } diff --git a/src/main/java/com/gdschongik/gdsc/domain/study/domain/StudyApplyCanceledEvent.java b/src/main/java/com/gdschongik/gdsc/domain/study/domain/StudyApplyCanceledEvent.java new file mode 100644 index 000000000..94894812b --- /dev/null +++ b/src/main/java/com/gdschongik/gdsc/domain/study/domain/StudyApplyCanceledEvent.java @@ -0,0 +1,3 @@ +package com.gdschongik.gdsc.domain.study.domain; + +public record StudyApplyCanceledEvent(Long studyHistoryId) {} diff --git a/src/main/java/com/gdschongik/gdsc/domain/study/domain/StudyHistory.java b/src/main/java/com/gdschongik/gdsc/domain/study/domain/StudyHistory.java index e8cd313d8..3129beb4f 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/study/domain/StudyHistory.java +++ b/src/main/java/com/gdschongik/gdsc/domain/study/domain/StudyHistory.java @@ -10,6 +10,7 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import jakarta.persistence.PreRemove; import jakarta.persistence.Table; import jakarta.persistence.UniqueConstraint; import lombok.AccessLevel; @@ -48,6 +49,11 @@ public static StudyHistory create(Member student, Study study) { return StudyHistory.builder().student(student).study(study).build(); } + @PreRemove + private void preRemove() { + registerEvent(new StudyApplyCanceledEvent(this.id)); + } + /** * 레포지토리 링크를 업데이트합니다. */