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 1daeb72b0..0bc4af0c7 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 @@ -90,11 +90,14 @@ public void attend(Long studyDetailId, StudyAttendCreateRequest request) { .orElseThrow(() -> new CustomException(STUDY_DETAIL_NOT_FOUND)); final Member currentMember = memberUtil.getCurrentMember(); 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)); - attendanceValidator.validateAttendance(studyDetail, request.attendanceNumber(), LocalDate.now()); + attendanceValidator.validateAttendance( + studyDetail, request.attendanceNumber(), LocalDate.now(), isAlreadyAttended); Attendance attendance = Attendance.create(currentMember, studyDetail); attendanceRepository.save(attendance); diff --git a/src/main/java/com/gdschongik/gdsc/domain/study/dao/AttendanceRepository.java b/src/main/java/com/gdschongik/gdsc/domain/study/dao/AttendanceRepository.java index 4c8d83d20..d259ab9cd 100644 --- a/src/main/java/com/gdschongik/gdsc/domain/study/dao/AttendanceRepository.java +++ b/src/main/java/com/gdschongik/gdsc/domain/study/dao/AttendanceRepository.java @@ -3,4 +3,6 @@ import com.gdschongik.gdsc.domain.study.domain.Attendance; import org.springframework.data.jpa.repository.JpaRepository; -public interface AttendanceRepository extends JpaRepository, AttendanceCustomRepository {} +public interface AttendanceRepository extends JpaRepository, AttendanceCustomRepository { + boolean existsByStudentIdAndStudyDetailId(Long studentId, Long studyDetailId); +} 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 2637fd41a..59532818c 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,7 +8,8 @@ @DomainService public class AttendanceValidator { - public void validateAttendance(StudyDetail studyDetail, String attendanceNumber, LocalDate date) { + public void validateAttendance( + StudyDetail studyDetail, String attendanceNumber, LocalDate date, boolean isAlreadyAttended) { // 출석체크 날짜 검증 LocalDate attendanceDay = studyDetail.getAttendanceDay(); if (!attendanceDay.equals(date)) { @@ -19,5 +20,10 @@ public void validateAttendance(StudyDetail studyDetail, String attendanceNumber, if (!studyDetail.getAttendanceNumber().equals(attendanceNumber)) { throw new CustomException(ATTENDANCE_NUMBER_MISMATCH); } + + // 출석체크 번호 검증 + if (isAlreadyAttended) { + throw new CustomException(STUDY_DETAIL_ALREADY_ATTENDED); + } } } 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 bec818fe9..069a55f0c 100644 --- a/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java +++ b/src/main/java/com/gdschongik/gdsc/global/exception/ErrorCode.java @@ -130,6 +130,7 @@ public enum ErrorCode { // Attendance ATTENDANCE_DATE_INVALID(HttpStatus.CONFLICT, "강의일이 아니면 출석체크할 수 없습니다."), ATTENDANCE_NUMBER_MISMATCH(HttpStatus.CONFLICT, "출석번호가 일치하지 않습니다."), + STUDY_DETAIL_ALREADY_ATTENDED(HttpStatus.CONFLICT, "이미 출석 처리된 스터디입니다."), // Order ORDER_NOT_FOUND(HttpStatus.NOT_FOUND, "주문이 존재하지 않습니다."), 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 b39091045..a394dff05 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 @@ -3,6 +3,7 @@ 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 com.gdschongik.gdsc.domain.member.domain.Member; @@ -36,7 +37,7 @@ class 스터디_출석체크시 { // when & then assertThatThrownBy(() -> attendanceValidator.validateAttendance( - studyDetail, ATTENDANCE_NUMBER, attendanceDay.plusDays(1))) + studyDetail, ATTENDANCE_NUMBER, attendanceDay.plusDays(1), false)) .isInstanceOf(CustomException.class) .hasMessage(ATTENDANCE_DATE_INVALID.getMessage()); } @@ -55,10 +56,30 @@ class 스터디_출석체크시 { LocalDate attendanceDay = studyDetail.getAttendanceDay(); // when & then - assertThatThrownBy(() -> - attendanceValidator.validateAttendance(studyDetail, ATTENDANCE_NUMBER + 1, attendanceDay)) + assertThatThrownBy(() -> attendanceValidator.validateAttendance( + studyDetail, ATTENDANCE_NUMBER + 1, attendanceDay, false)) .isInstanceOf(CustomException.class) .hasMessage(ATTENDANCE_NUMBER_MISMATCH.getMessage()); } + + @Test + void 이미_출석했다면_실패한다() { + // given + Member mentor = fixtureHelper.createAssociateMember(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); + StudyDetail studyDetail = fixtureHelper.createStudyDetail(study, now, now.plusDays(7)); + + LocalDate attendanceDay = studyDetail.getAttendanceDay(); + + // when & then + assertThatThrownBy(() -> + attendanceValidator.validateAttendance(studyDetail, ATTENDANCE_NUMBER, attendanceDay, true)) + .isInstanceOf(CustomException.class) + .hasMessage(STUDY_DETAIL_ALREADY_ATTENDED.getMessage()); + } } }