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 d55b9784d..7af5cccfb 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 @@ -93,17 +93,15 @@ public void attend(Long studyDetailId, StudyAttendCreateRequest request) { final Study study = studyDetail.getStudy(); final boolean isAlreadyAttended = attendanceRepository.existsByStudentIdAndStudyDetailId(currentMember.getId(), studyDetailId); - final StudyHistory studyHistory = studyHistoryRepository - .findByStudentAndStudy(currentMember, study) - .orElseThrow(() -> new CustomException(STUDY_HISTORY_NOT_FOUND)); + boolean isAppliedToStudy = studyHistoryRepository.existsByStudentAndStudy(currentMember, study); attendanceValidator.validateAttendance( - studyDetail, request.attendanceNumber(), LocalDate.now(), isAlreadyAttended); + studyDetail, request.attendanceNumber(), LocalDate.now(), isAlreadyAttended, isAppliedToStudy); Attendance attendance = Attendance.create(currentMember, studyDetail); attendanceRepository.save(attendance); - log.info("[StudyService] 스터디 출석: attendanceId={}", attendance.getId()); + log.info("[StudyService] 스터디 출석: attendanceId={}, memberId={}", attendance.getId(), currentMember.getId()); } @Transactional(readOnly = true) diff --git a/src/main/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidator.java b/src/main/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidator.java index 59532818c..aebade893 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidator.java +++ b/src/main/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidator.java @@ -8,8 +8,13 @@ @DomainService public class AttendanceValidator { + public void validateAttendance( - StudyDetail studyDetail, String attendanceNumber, LocalDate date, boolean isAlreadyAttended) { + StudyDetail studyDetail, + String attendanceNumber, + LocalDate date, + boolean isAlreadyAttended, + boolean isAppliedToStudy) { // 출석체크 날짜 검증 LocalDate attendanceDay = studyDetail.getAttendanceDay(); if (!attendanceDay.equals(date)) { @@ -25,5 +30,10 @@ public void validateAttendance( if (isAlreadyAttended) { throw new CustomException(STUDY_DETAIL_ALREADY_ATTENDED); } + + // 스터디 신청 여부 검증 + if (!isAppliedToStudy) { + throw new CustomException(STUDY_HISTORY_NOT_FOUND); + } } } diff --git a/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java b/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java index 069a55f0c..76ac38309 100644 --- a/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java +++ b/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java @@ -116,6 +116,7 @@ public enum ErrorCode { STUDY_DETAIL_ASSIGNMENT_INVALID_UPDATE_DEADLINE(HttpStatus.CONFLICT, "수정하려고 하는 과제의 마감기한은 기존의 마감기한보다 빠르면 안됩니다."), STUDY_DETAIL_ID_INVALID(HttpStatus.CONFLICT, "수정하려는 스터디 상세정보가 서버에 존재하지 않거나 유효하지 않습니다."), STUDY_DETAIL_CURRICULUM_SIZE_MISMATCH(HttpStatus.BAD_REQUEST, "스터디 커리큘럼의 총 개수가 일치하지 않습니다."), + // StudyHistory STUDY_HISTORY_NOT_FOUND(HttpStatus.NOT_FOUND, "존재하지 않는 스터디 수강 기록입니다."), STUDY_HISTORY_DUPLICATE(HttpStatus.CONFLICT, "이미 해당 스터디를 신청했습니다."), diff --git a/src/test/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidatorTest.java b/src/test/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidatorTest.java index a394dff05..f66643223 100644 --- a/src/test/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidatorTest.java +++ b/src/test/java/com/gdschongik/gdsc/domain/study/domain/AttendanceValidatorTest.java @@ -1,10 +1,8 @@ package com.gdschongik.gdsc.domain.study.domain; import static com.gdschongik.gdsc.global.common.constant.StudyConstant.ATTENDANCE_NUMBER; -import static com.gdschongik.gdsc.global.exception.ErrorCode.ATTENDANCE_DATE_INVALID; -import static com.gdschongik.gdsc.global.exception.ErrorCode.ATTENDANCE_NUMBER_MISMATCH; -import static com.gdschongik.gdsc.global.exception.ErrorCode.STUDY_DETAIL_ALREADY_ATTENDED; -import static org.assertj.core.api.Assertions.assertThatThrownBy; +import static com.gdschongik.gdsc.global.exception.ErrorCode.*; +import static org.assertj.core.api.Assertions.*; import com.gdschongik.gdsc.domain.member.domain.Member; import com.gdschongik.gdsc.domain.recruitment.domain.vo.Period; @@ -16,6 +14,7 @@ import org.junit.jupiter.api.Test; public class AttendanceValidatorTest { + FixtureHelper fixtureHelper = new FixtureHelper(); AttendanceValidator attendanceValidator = new AttendanceValidator(); @@ -25,19 +24,19 @@ class 스터디_출석체크시 { @Test void 출석일자가_아니면_실패한다() { // given - Member mentor = fixtureHelper.createAssociateMember(1L); + Member student = fixtureHelper.createRegularMember(1L); LocalDateTime now = LocalDateTime.now(); Period period = Period.createPeriod(now.plusDays(10), now.plusDays(65)); Period applicationPeriod = Period.createPeriod(now.minusDays(10), now.plusDays(5)); - Study study = fixtureHelper.createStudy(mentor, period, applicationPeriod); + Study study = fixtureHelper.createStudy(student, period, applicationPeriod); StudyDetail studyDetail = fixtureHelper.createStudyDetail(study, now, now.plusDays(7)); LocalDate attendanceDay = studyDetail.getAttendanceDay(); // when & then assertThatThrownBy(() -> attendanceValidator.validateAttendance( - studyDetail, ATTENDANCE_NUMBER, attendanceDay.plusDays(1), false)) + studyDetail, ATTENDANCE_NUMBER, attendanceDay.plusDays(1), false, true)) .isInstanceOf(CustomException.class) .hasMessage(ATTENDANCE_DATE_INVALID.getMessage()); } @@ -45,19 +44,19 @@ class 스터디_출석체크시 { @Test void 출석번호가_다르면_실패한다() { // given - Member mentor = fixtureHelper.createAssociateMember(1L); + Member student = fixtureHelper.createRegularMember(1L); LocalDateTime now = LocalDateTime.now(); Period period = Period.createPeriod(now.plusDays(10), now.plusDays(65)); Period applicationPeriod = Period.createPeriod(now.minusDays(10), now.plusDays(5)); - Study study = fixtureHelper.createStudy(mentor, period, applicationPeriod); + Study study = fixtureHelper.createStudy(student, period, applicationPeriod); StudyDetail studyDetail = fixtureHelper.createStudyDetail(study, now, now.plusDays(7)); LocalDate attendanceDay = studyDetail.getAttendanceDay(); // when & then assertThatThrownBy(() -> attendanceValidator.validateAttendance( - studyDetail, ATTENDANCE_NUMBER + 1, attendanceDay, false)) + studyDetail, ATTENDANCE_NUMBER + 1, attendanceDay, false, true)) .isInstanceOf(CustomException.class) .hasMessage(ATTENDANCE_NUMBER_MISMATCH.getMessage()); } @@ -65,21 +64,41 @@ class 스터디_출석체크시 { @Test void 이미_출석했다면_실패한다() { // given - Member mentor = fixtureHelper.createAssociateMember(1L); + Member student = fixtureHelper.createRegularMember(1L); LocalDateTime now = LocalDateTime.now(); Period period = Period.createPeriod(now.plusDays(10), now.plusDays(65)); Period applicationPeriod = Period.createPeriod(now.minusDays(10), now.plusDays(5)); - Study study = fixtureHelper.createStudy(mentor, period, applicationPeriod); + Study study = fixtureHelper.createStudy(student, period, applicationPeriod); StudyDetail studyDetail = fixtureHelper.createStudyDetail(study, now, now.plusDays(7)); LocalDate attendanceDay = studyDetail.getAttendanceDay(); // when & then - assertThatThrownBy(() -> - attendanceValidator.validateAttendance(studyDetail, ATTENDANCE_NUMBER, attendanceDay, true)) + assertThatThrownBy(() -> attendanceValidator.validateAttendance( + studyDetail, ATTENDANCE_NUMBER, attendanceDay, true, true)) .isInstanceOf(CustomException.class) .hasMessage(STUDY_DETAIL_ALREADY_ATTENDED.getMessage()); } + + @Test + void 신청하지_않은_스터디라면_실패한다() { + // given + Member student = fixtureHelper.createRegularMember(1L); + + LocalDateTime now = LocalDateTime.now(); + Period period = Period.createPeriod(now.plusDays(10), now.plusDays(65)); + Period applicationPeriod = Period.createPeriod(now.minusDays(10), now.plusDays(5)); + Study study = fixtureHelper.createStudy(student, period, applicationPeriod); + StudyDetail studyDetail = fixtureHelper.createStudyDetail(study, now, now.plusDays(7)); + + LocalDate attendanceDay = studyDetail.getAttendanceDay(); + + // when & then + assertThatThrownBy(() -> attendanceValidator.validateAttendance( + studyDetail, ATTENDANCE_NUMBER, attendanceDay, false, false)) + .isInstanceOf(CustomException.class) + .hasMessage(STUDY_HISTORY_NOT_FOUND.getMessage()); + } } }