From f1a5ab8bc2a11bc23923fec63bd429b43be1b971 Mon Sep 17 00:00:00 2001 From: Youth <109585620+Youthhing@users.noreply.github.com> Date: Mon, 12 Aug 2024 17:06:43 +0900 Subject: [PATCH] =?UTF-8?q?Feature:=20=EB=A9=A4=EB=B2=84=EC=9D=98=20?= =?UTF-8?q?=EA=B8=B0=EC=88=98=EB=B3=84=20=EC=B6=9C=EA=B2=B0=20=EB=AA=A9?= =?UTF-8?q?=EB=A1=9D=20=EB=B0=98=ED=99=98=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84=20(#89)?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit * refactor: 출결 결과 컬럼명 변경 - 출결 상태 -> 출결 결과로 변경 * refactor: 출결 열림 상태 반환 메서드명 변경 - openStatus로 명시적으로 변경 * feat: 출결 열림 상태에 '시작 전' 상태 추가 * feat: 출결 시작 전 상태 판단 로직 추가 * feat: 출석 목록에서 부원의 출결 기록을 조회하는 JPA 메서드 추가 * feat: 멤버의 기수별 출결 목록 반환 * feat: 출석 기록 반환 값에 출결 형식 추가 * test: 기준 시간이전이면 출석이 '이전'인지에 대한 테스트 * fix: 기존 브랜치 병합에 따른 컨플릭 수정 * refactor: 출결 상태가 고정된 경우 파라미터 대신 생성자에 값을 고정 --- .../controller/AttendanceController.java | 8 +++ .../api/attendance/dto/AttendResponse.java | 6 +-- .../attendance/dto/AttendanceStatistic.java | 10 ++-- .../attendance/dto/MemberAttendResponse.java | 48 +++++++++++++++++ .../dto/MemberAttendanceRecordsResponse.java | 17 +++++++ .../attendance/entity/AttendanceRecord.java | 22 ++++---- .../enums/AttendanceOpenStatus.java | 5 +- ...danceStatus.java => AttendanceResult.java} | 2 +- .../domain/attendance/enums/DeadLine.java | 10 ++++ .../AttendanceRecordRepository.java | 3 ++ .../service/AttendanceRecordService.java | 51 +++++++++++++++++-- .../attendance/service/AttendanceService.java | 2 +- .../service/OfflineAttendClient.java | 8 +-- .../service/OnlineAttendClient.java | 8 +-- .../attendance/util/AttendanceUtil.java | 26 ++++------ .../attendance/util/AttendanceUtilTest.java | 13 +++-- 16 files changed, 183 insertions(+), 56 deletions(-) create mode 100644 src/main/java/org/cotato/csquiz/api/attendance/dto/MemberAttendResponse.java create mode 100644 src/main/java/org/cotato/csquiz/api/attendance/dto/MemberAttendanceRecordsResponse.java rename src/main/java/org/cotato/csquiz/domain/attendance/enums/{AttendanceStatus.java => AttendanceResult.java} (93%) diff --git a/src/main/java/org/cotato/csquiz/api/attendance/controller/AttendanceController.java b/src/main/java/org/cotato/csquiz/api/attendance/controller/AttendanceController.java index 245a4e9f..cb706242 100644 --- a/src/main/java/org/cotato/csquiz/api/attendance/controller/AttendanceController.java +++ b/src/main/java/org/cotato/csquiz/api/attendance/controller/AttendanceController.java @@ -12,6 +12,7 @@ import org.cotato.csquiz.api.attendance.dto.AttendResponse; import org.cotato.csquiz.api.attendance.dto.AttendanceRecordResponse; import org.cotato.csquiz.api.attendance.dto.AttendancesResponse; +import org.cotato.csquiz.api.attendance.dto.MemberAttendanceRecordsResponse; import org.cotato.csquiz.api.attendance.dto.OfflineAttendanceRequest; import org.cotato.csquiz.api.attendance.dto.OnlineAttendanceRequest; import org.cotato.csquiz.api.attendance.dto.UpdateAttendanceRequest; @@ -113,4 +114,11 @@ public ResponseEntity submitOnlineAttendanceRecord(@RequestBody @AuthenticationPrincipal Long memberId) { return ResponseEntity.ok().body(attendanceRecordService.submitRecord(request, memberId)); } + + @Operation(summary = "부원의 기수별 출결 기록 반환 API") + @GetMapping("/records/members") + public ResponseEntity findAllRecordsByGeneration(@RequestParam("generation-id") Long generationId , + @AuthenticationPrincipal Long memberId) { + return ResponseEntity.ok().body(attendanceRecordService.findAllRecordsBy(generationId, memberId)); + } } diff --git a/src/main/java/org/cotato/csquiz/api/attendance/dto/AttendResponse.java b/src/main/java/org/cotato/csquiz/api/attendance/dto/AttendResponse.java index e9095f81..22a62f6b 100644 --- a/src/main/java/org/cotato/csquiz/api/attendance/dto/AttendResponse.java +++ b/src/main/java/org/cotato/csquiz/api/attendance/dto/AttendResponse.java @@ -1,12 +1,12 @@ package org.cotato.csquiz.api.attendance.dto; -import org.cotato.csquiz.domain.attendance.enums.AttendanceStatus; +import org.cotato.csquiz.domain.attendance.enums.AttendanceResult; public record AttendResponse( - AttendanceStatus status, + AttendanceResult status, String message ) { - public static AttendResponse from(AttendanceStatus status) { + public static AttendResponse from(AttendanceResult status) { return new AttendResponse( status, status.getMessage() diff --git a/src/main/java/org/cotato/csquiz/api/attendance/dto/AttendanceStatistic.java b/src/main/java/org/cotato/csquiz/api/attendance/dto/AttendanceStatistic.java index 822b91c5..1c910761 100644 --- a/src/main/java/org/cotato/csquiz/api/attendance/dto/AttendanceStatistic.java +++ b/src/main/java/org/cotato/csquiz/api/attendance/dto/AttendanceStatistic.java @@ -4,7 +4,7 @@ import java.util.Map; import java.util.stream.Collectors; import org.cotato.csquiz.domain.attendance.entity.AttendanceRecord; -import org.cotato.csquiz.domain.attendance.enums.AttendanceStatus; +import org.cotato.csquiz.domain.attendance.enums.AttendanceResult; import org.cotato.csquiz.domain.attendance.enums.AttendanceType; public record AttendanceStatistic( @@ -14,9 +14,9 @@ public record AttendanceStatistic( Integer absent ) { public static AttendanceStatistic of(List attendanceRecords, Integer totalAttendance) { - Map> countByStatus = attendanceRecords.stream() - .collect(Collectors.groupingBy(AttendanceRecord::getAttendanceStatus)); - List presentRecords = countByStatus.getOrDefault(AttendanceStatus.PRESENT, List.of()); + Map> countByStatus = attendanceRecords.stream() + .collect(Collectors.groupingBy(AttendanceRecord::getAttendanceResult)); + List presentRecords = countByStatus.getOrDefault(AttendanceResult.PRESENT, List.of()); int onlineCount = (int) presentRecords.stream() .filter(record -> AttendanceType.ONLINE == record.getAttendanceType()) @@ -28,7 +28,7 @@ public static AttendanceStatistic of(List attendanceRecords, I return new AttendanceStatistic( onlineCount, offLineCount, - countByStatus.getOrDefault(AttendanceStatus.LATE, List.of()).size(), + countByStatus.getOrDefault(AttendanceResult.LATE, List.of()).size(), totalAttendance - attendanceRecords.size() ); } diff --git a/src/main/java/org/cotato/csquiz/api/attendance/dto/MemberAttendResponse.java b/src/main/java/org/cotato/csquiz/api/attendance/dto/MemberAttendResponse.java new file mode 100644 index 00000000..6ab5d34a --- /dev/null +++ b/src/main/java/org/cotato/csquiz/api/attendance/dto/MemberAttendResponse.java @@ -0,0 +1,48 @@ +package org.cotato.csquiz.api.attendance.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDate; +import org.cotato.csquiz.domain.attendance.entity.AttendanceRecord; +import org.cotato.csquiz.domain.attendance.enums.AttendanceOpenStatus; +import org.cotato.csquiz.domain.attendance.enums.AttendanceResult; +import org.cotato.csquiz.domain.attendance.enums.AttendanceType; +import org.cotato.csquiz.domain.generation.entity.Session; + +public record MemberAttendResponse( + @Schema(description = "멤버 PK") + Long memberId, + @Schema(description = "세션 타이틀", example = "3주차 세션") + String sessionTitle, + @Schema(description = "세션 날짜") + LocalDate sessionDate, + @Schema(description = "출결 진행 여부", examples = { + "CLOSED", "OPEN" + }) + AttendanceOpenStatus isOpened, + @Schema(description = "출결 형식") + AttendanceType attendanceType, + @Schema(description = "마감된 출석에 대한 출결 결과", nullable = true) + AttendanceResult attendanceResult +) { + public static MemberAttendResponse closedAttendanceResponse(Session session, AttendanceRecord attendanceRecord) { + return new MemberAttendResponse( + attendanceRecord.getMemberId(), + session.getTitle(), + session.getSessionDate(), + AttendanceOpenStatus.CLOSED, + attendanceRecord.getAttendanceType(), + attendanceRecord.getAttendanceResult() + ); + } + + public static MemberAttendResponse openedAttendanceResponse(Session session, Long memberId) { + return new MemberAttendResponse( + memberId, + session.getTitle(), + session.getSessionDate(), + AttendanceOpenStatus.OPEN, + null, + null + ); + } +} diff --git a/src/main/java/org/cotato/csquiz/api/attendance/dto/MemberAttendanceRecordsResponse.java b/src/main/java/org/cotato/csquiz/api/attendance/dto/MemberAttendanceRecordsResponse.java new file mode 100644 index 00000000..1c0052f5 --- /dev/null +++ b/src/main/java/org/cotato/csquiz/api/attendance/dto/MemberAttendanceRecordsResponse.java @@ -0,0 +1,17 @@ +package org.cotato.csquiz.api.attendance.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; + +public record MemberAttendanceRecordsResponse( + @Schema(description = "요청한 기수 PK") + Long generationId, + List memberAttendResponses +) { + public static MemberAttendanceRecordsResponse of(Long generationId, List memberAttendResponses) { + return new MemberAttendanceRecordsResponse( + generationId, + memberAttendResponses + ); + } +} diff --git a/src/main/java/org/cotato/csquiz/domain/attendance/entity/AttendanceRecord.java b/src/main/java/org/cotato/csquiz/domain/attendance/entity/AttendanceRecord.java index 2739c42f..a6abff08 100644 --- a/src/main/java/org/cotato/csquiz/domain/attendance/entity/AttendanceRecord.java +++ b/src/main/java/org/cotato/csquiz/domain/attendance/entity/AttendanceRecord.java @@ -18,7 +18,7 @@ import lombok.Getter; import lombok.NoArgsConstructor; import org.cotato.csquiz.common.entity.BaseTimeEntity; -import org.cotato.csquiz.domain.attendance.enums.AttendanceStatus; +import org.cotato.csquiz.domain.attendance.enums.AttendanceResult; import org.cotato.csquiz.domain.attendance.enums.AttendanceType; @Table(name = "attendance_record", @@ -39,9 +39,9 @@ public class AttendanceRecord extends BaseTimeEntity { @Enumerated(EnumType.STRING) private AttendanceType attendanceType; - @Column(name = "attendance_status", nullable = false) + @Column(name = "attendance_result", nullable = false) @Enumerated(EnumType.STRING) - private AttendanceStatus attendanceStatus; + private AttendanceResult attendanceResult; @Column(name = "location_accuracy") private Double locationAccuracy; @@ -56,21 +56,21 @@ public class AttendanceRecord extends BaseTimeEntity { @Column(name = "attend_time", nullable = false) private LocalDateTime attendTime; - private AttendanceRecord(AttendanceType attendanceType, AttendanceStatus attendanceStatus, Double locationAccuracy, + private AttendanceRecord(AttendanceType attendanceType, AttendanceResult attendanceResult, Double locationAccuracy, Long memberId, Attendance attendance, LocalDateTime attendTime) { this.attendanceType = attendanceType; - this.attendanceStatus = attendanceStatus; + this.attendanceResult = attendanceResult; this.locationAccuracy = locationAccuracy; this.memberId = memberId; this.attendance = attendance; this.attendTime = attendTime; } - public static AttendanceRecord onLineRecord(Attendance attendance, Long memberId, AttendanceStatus attendanceStatus, + public static AttendanceRecord onLineRecord(Attendance attendance, Long memberId, AttendanceResult attendanceResult, LocalDateTime attendTime) { return new AttendanceRecord( AttendanceType.ONLINE, - attendanceStatus, + attendanceResult, null, memberId, attendance, @@ -79,10 +79,10 @@ public static AttendanceRecord onLineRecord(Attendance attendance, Long memberId } public static AttendanceRecord offlineRecord(Attendance attendance, Long memberId, Double locationAccuracy, - AttendanceStatus attendanceStatus, LocalDateTime attendTime) { + AttendanceResult attendanceResult, LocalDateTime attendTime) { return new AttendanceRecord( AttendanceType.OFFLINE, - attendanceStatus, + attendanceResult, locationAccuracy, memberId, attendance, @@ -98,7 +98,7 @@ public void updateLocationAccuracy(Double accuracy) { this.locationAccuracy = accuracy; } - public void updateAttendanceStatus(AttendanceStatus attendanceStatus) { - this.attendanceStatus = attendanceStatus; + public void updateAttendanceStatus(AttendanceResult attendanceResult) { + this.attendanceResult = attendanceResult; } } diff --git a/src/main/java/org/cotato/csquiz/domain/attendance/enums/AttendanceOpenStatus.java b/src/main/java/org/cotato/csquiz/domain/attendance/enums/AttendanceOpenStatus.java index c48f4fa0..f3d84d03 100644 --- a/src/main/java/org/cotato/csquiz/domain/attendance/enums/AttendanceOpenStatus.java +++ b/src/main/java/org/cotato/csquiz/domain/attendance/enums/AttendanceOpenStatus.java @@ -6,10 +6,11 @@ @Getter @AllArgsConstructor public enum AttendanceOpenStatus { - CLOSED("출결 진행 중이 아닙니다."), + CLOSED("출결 입력 기간이 마감되었습니다."), OPEN("현재 출석 진행 중"), LATE("현재 출결 입력 시 지각"), - ABSENT("현재 출결 입력 시 결석") + ABSENT("현재 출결 입력 시 결석"), + BEFORE("아직 출석 시작 전입니다. 오지 않았습니다.") ; private final String description; diff --git a/src/main/java/org/cotato/csquiz/domain/attendance/enums/AttendanceStatus.java b/src/main/java/org/cotato/csquiz/domain/attendance/enums/AttendanceResult.java similarity index 93% rename from src/main/java/org/cotato/csquiz/domain/attendance/enums/AttendanceStatus.java rename to src/main/java/org/cotato/csquiz/domain/attendance/enums/AttendanceResult.java index a32875e4..3fa7833c 100644 --- a/src/main/java/org/cotato/csquiz/domain/attendance/enums/AttendanceStatus.java +++ b/src/main/java/org/cotato/csquiz/domain/attendance/enums/AttendanceResult.java @@ -5,7 +5,7 @@ @Getter @AllArgsConstructor -public enum AttendanceStatus { +public enum AttendanceResult { PRESENT("출석", "출석에 성공하셨습니다."), LATE("지각", "기준 시간을 지나 지각 처리 되었습니다."), ABSENT("결석", "지각 마감 시간을 지나 결석 처리 되었습니다.") diff --git a/src/main/java/org/cotato/csquiz/domain/attendance/enums/DeadLine.java b/src/main/java/org/cotato/csquiz/domain/attendance/enums/DeadLine.java index 92b58817..63fa5464 100644 --- a/src/main/java/org/cotato/csquiz/domain/attendance/enums/DeadLine.java +++ b/src/main/java/org/cotato/csquiz/domain/attendance/enums/DeadLine.java @@ -1,5 +1,7 @@ package org.cotato.csquiz.domain.attendance.enums; +import java.time.LocalDate; +import java.time.LocalDateTime; import java.time.LocalTime; import lombok.AllArgsConstructor; import lombok.Getter; @@ -16,4 +18,12 @@ public enum DeadLine { private final LocalTime time; private final String description; + + public static LocalDateTime sessionStartTime(LocalDate date) { + return LocalDateTime.of(date, ATTENDANCE_START_TIME.getTime()); + } + + public static LocalDateTime sessionEndTime(LocalDate date) { + return LocalDateTime.of(date, ATTENDANCE_END_TIME.getTime()); + } } diff --git a/src/main/java/org/cotato/csquiz/domain/attendance/repository/AttendanceRecordRepository.java b/src/main/java/org/cotato/csquiz/domain/attendance/repository/AttendanceRecordRepository.java index 51390046..dec5b31f 100644 --- a/src/main/java/org/cotato/csquiz/domain/attendance/repository/AttendanceRecordRepository.java +++ b/src/main/java/org/cotato/csquiz/domain/attendance/repository/AttendanceRecordRepository.java @@ -17,5 +17,8 @@ public interface AttendanceRecordRepository extends JpaRepository findByMemberIdAndAttendanceId(Long memberId, Long attendanceId); + @Query("select a from AttendanceRecord a where a.attendance.id in :attendanceIds and a.memberId = :memberId") + List findAllByAttendanceIdsInQueryAndMemberId(@Param("attendanceIds") List attendanceIds, @Param("memberId") Long memberId); + List findAllByAttendanceId(Long attendanceId); } diff --git a/src/main/java/org/cotato/csquiz/domain/attendance/service/AttendanceRecordService.java b/src/main/java/org/cotato/csquiz/domain/attendance/service/AttendanceRecordService.java index 1c22b578..21de52f3 100644 --- a/src/main/java/org/cotato/csquiz/domain/attendance/service/AttendanceRecordService.java +++ b/src/main/java/org/cotato/csquiz/domain/attendance/service/AttendanceRecordService.java @@ -1,10 +1,12 @@ package org.cotato.csquiz.domain.attendance.service; -import static org.cotato.csquiz.domain.attendance.util.AttendanceUtil.getAttendanceStatus; +import static org.cotato.csquiz.domain.attendance.util.AttendanceUtil.getAttendanceOpenStatus; import jakarta.persistence.EntityNotFoundException; +import java.time.LocalDateTime; import java.util.List; import java.util.Map; +import java.util.function.Function; import java.util.stream.Collectors; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; @@ -12,17 +14,21 @@ import org.cotato.csquiz.api.attendance.dto.AttendanceParams; import org.cotato.csquiz.api.attendance.dto.AttendanceRecordResponse; import org.cotato.csquiz.api.attendance.dto.AttendanceStatistic; +import org.cotato.csquiz.api.attendance.dto.MemberAttendResponse; +import org.cotato.csquiz.api.attendance.dto.MemberAttendanceRecordsResponse; import org.cotato.csquiz.common.error.ErrorCode; import org.cotato.csquiz.common.error.exception.AppException; import org.cotato.csquiz.domain.attendance.entity.Attendance; import org.cotato.csquiz.domain.attendance.entity.AttendanceRecord; import org.cotato.csquiz.domain.attendance.enums.AttendanceOpenStatus; -import org.cotato.csquiz.domain.attendance.enums.AttendanceStatus; +import org.cotato.csquiz.domain.attendance.enums.AttendanceResult; import org.cotato.csquiz.domain.attendance.repository.AttendanceRecordRepository; import org.cotato.csquiz.domain.attendance.repository.AttendanceRepository; import org.cotato.csquiz.domain.attendance.util.AttendanceUtil; import org.cotato.csquiz.domain.auth.entity.Member; import org.cotato.csquiz.domain.auth.service.MemberService; +import org.cotato.csquiz.domain.generation.entity.Session; +import org.cotato.csquiz.domain.generation.repository.SessionRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; @@ -36,6 +42,7 @@ public class AttendanceRecordService { private final AttendanceRepository attendanceRepository; private final MemberService memberService; private final RequestAttendanceService requestAttendanceService; + private final SessionRepository sessionRepository; public List generateAttendanceResponses(List attendances) { List records = attendanceRecordRepository.findAllByAttendanceIdsInQuery( @@ -60,7 +67,7 @@ public AttendResponse submitRecord(AttendanceParams request, final Long memberId .orElseThrow(() -> new EntityNotFoundException("해당 출석이 존재하지 않습니다.")); // 해당 출석이 열려있는지 확인, 닫혀있으면 제외 - if (getAttendanceStatus(attendance, request.requestTime()) == AttendanceOpenStatus.CLOSED) { + if (getAttendanceOpenStatus(attendance, request.requestTime()) == AttendanceOpenStatus.CLOSED) { throw new AppException(ErrorCode.ATTENDANCE_CLOSED); } @@ -73,13 +80,47 @@ public AttendResponse submitRecord(AttendanceParams request, final Long memberId return requestAttendanceService.attend(request, memberId, attendance); } + public MemberAttendanceRecordsResponse findAllRecordsBy(final Long generationId, final Long memberId) { + List sessions = sessionRepository.findAllByGenerationId(generationId); + + Map sessionMap = sessions.stream() + .collect(Collectors.toUnmodifiableMap(Session::getId, Function.identity())); + + List sessionIds = sessions.stream() + .map(Session::getId) + .toList(); + // 세션에 해당하는 모든 출결을 찾아 + LocalDateTime currentTime = LocalDateTime.now(); + + Map> isClosedAttendance = attendanceRepository.findAllBySessionIdsInQuery(sessionIds) + .stream() + .collect(Collectors.partitioningBy(attendance -> + getAttendanceOpenStatus(attendance, currentTime) == AttendanceOpenStatus.CLOSED)); + + List closedAttendanceIds = isClosedAttendance.get(true).stream() + .map(Attendance::getId) + .toList(); + + List responses = attendanceRecordRepository.findAllByAttendanceIdsInQueryAndMemberId(closedAttendanceIds, memberId).stream() + .map(ar -> MemberAttendResponse.closedAttendanceResponse( + sessionMap.get(ar.getAttendance().getSessionId()), ar)) + .collect(Collectors.toList()); + + responses.addAll(isClosedAttendance.get(false).stream() + .map(attendance -> MemberAttendResponse.openedAttendanceResponse( + sessionMap.get(attendance.getSessionId()), memberId)) + .toList()); + + return MemberAttendanceRecordsResponse.of(generationId, responses); + } + @Transactional public void updateAttendanceStatus(Attendance attendance) { List attendanceRecords = attendanceRecordRepository.findAllByAttendanceId(attendance.getId()); for (AttendanceRecord attendanceRecord : attendanceRecords) { - AttendanceStatus attendanceStatus = AttendanceUtil.calculateAttendanceStatus(attendance, attendanceRecord.getAttendTime()); - attendanceRecord.updateAttendanceStatus(attendanceStatus); + AttendanceResult attendanceResult = AttendanceUtil.calculateAttendanceStatus(attendance, attendanceRecord.getAttendTime()); + attendanceRecord.updateAttendanceStatus(attendanceResult); } attendanceRecordRepository.saveAll(attendanceRecords); diff --git a/src/main/java/org/cotato/csquiz/domain/attendance/service/AttendanceService.java b/src/main/java/org/cotato/csquiz/domain/attendance/service/AttendanceService.java index 1c2f6123..10668bf0 100644 --- a/src/main/java/org/cotato/csquiz/domain/attendance/service/AttendanceService.java +++ b/src/main/java/org/cotato/csquiz/domain/attendance/service/AttendanceService.java @@ -47,7 +47,7 @@ public AttendancesResponse findAttendancesByGenerationId(final Long generationId .attendanceId(at.getId()) .sessionTitle(sessionMap.get(at.getSessionId()).getTitle()) .sessionDate(at.getAttendanceDeadLine().toLocalDate()) - .openStatus(AttendanceUtil.getAttendanceStatus(at, currentTime)) + .openStatus(AttendanceUtil.getAttendanceOpenStatus(at, currentTime)) .build()) .toList(); diff --git a/src/main/java/org/cotato/csquiz/domain/attendance/service/OfflineAttendClient.java b/src/main/java/org/cotato/csquiz/domain/attendance/service/OfflineAttendClient.java index b7121e7f..5bd1f66c 100644 --- a/src/main/java/org/cotato/csquiz/domain/attendance/service/OfflineAttendClient.java +++ b/src/main/java/org/cotato/csquiz/domain/attendance/service/OfflineAttendClient.java @@ -8,7 +8,7 @@ import org.cotato.csquiz.common.error.exception.AppException; import org.cotato.csquiz.domain.attendance.entity.Attendance; import org.cotato.csquiz.domain.attendance.entity.AttendanceRecord; -import org.cotato.csquiz.domain.attendance.enums.AttendanceStatus; +import org.cotato.csquiz.domain.attendance.enums.AttendanceResult; import org.cotato.csquiz.domain.attendance.enums.AttendanceType; import org.cotato.csquiz.domain.attendance.repository.AttendanceRecordRepository; import org.cotato.csquiz.domain.attendance.util.AttendanceUtil; @@ -30,21 +30,21 @@ public AttendanceType attendanceType() { public AttendResponse request(AttendanceParams params, Long memberId, Attendance attendance) { OfflineAttendanceRequest request = (OfflineAttendanceRequest) params; - AttendanceStatus attendanceStatus = AttendanceUtil.calculateAttendanceStatus(attendance, params.requestTime()); + AttendanceResult attendanceResult = AttendanceUtil.calculateAttendanceStatus(attendance, params.requestTime()); Double accuracy = attendance.getLocation().calculateAccuracy(request.getLocation()); validateAccuracy(accuracy); AttendanceRecord attendanceRecord = attendanceRecordRepository.findByMemberIdAndAttendanceId(memberId, request.getAttendanceId()) - .orElseGet(() -> AttendanceRecord.offlineRecord(attendance, memberId, accuracy, attendanceStatus, request.getRequestTime())); + .orElseGet(() -> AttendanceRecord.offlineRecord(attendance, memberId, accuracy, attendanceResult, request.getRequestTime())); attendanceRecord.updateAttendanceType(request.attendanceType()); attendanceRecord.updateLocationAccuracy(accuracy); attendanceRecordRepository.save(attendanceRecord); - return AttendResponse.from(attendanceStatus); + return AttendResponse.from(attendanceResult); } private void validateAccuracy(Double accuracy) { diff --git a/src/main/java/org/cotato/csquiz/domain/attendance/service/OnlineAttendClient.java b/src/main/java/org/cotato/csquiz/domain/attendance/service/OnlineAttendClient.java index 50a432ea..28dbd912 100644 --- a/src/main/java/org/cotato/csquiz/domain/attendance/service/OnlineAttendClient.java +++ b/src/main/java/org/cotato/csquiz/domain/attendance/service/OnlineAttendClient.java @@ -5,7 +5,7 @@ import org.cotato.csquiz.api.attendance.dto.AttendanceParams; import org.cotato.csquiz.domain.attendance.entity.Attendance; import org.cotato.csquiz.domain.attendance.entity.AttendanceRecord; -import org.cotato.csquiz.domain.attendance.enums.AttendanceStatus; +import org.cotato.csquiz.domain.attendance.enums.AttendanceResult; import org.cotato.csquiz.domain.attendance.enums.AttendanceType; import org.cotato.csquiz.domain.attendance.repository.AttendanceRecordRepository; import org.cotato.csquiz.domain.attendance.util.AttendanceUtil; @@ -24,10 +24,10 @@ public AttendanceType attendanceType() { @Override public AttendResponse request(AttendanceParams params, Long memberId, Attendance attendance) { - AttendanceStatus attendanceStatus = AttendanceUtil.calculateAttendanceStatus(attendance, params.requestTime()); + AttendanceResult attendanceResult = AttendanceUtil.calculateAttendanceStatus(attendance, params.requestTime()); - attendanceRecordRepository.save(AttendanceRecord.onLineRecord(attendance, memberId, attendanceStatus, params.requestTime())); + attendanceRecordRepository.save(AttendanceRecord.onLineRecord(attendance, memberId, attendanceResult, params.requestTime())); - return AttendResponse.from(attendanceStatus); + return AttendResponse.from(attendanceResult); } } \ No newline at end of file diff --git a/src/main/java/org/cotato/csquiz/domain/attendance/util/AttendanceUtil.java b/src/main/java/org/cotato/csquiz/domain/attendance/util/AttendanceUtil.java index 232341ae..3a61ff08 100644 --- a/src/main/java/org/cotato/csquiz/domain/attendance/util/AttendanceUtil.java +++ b/src/main/java/org/cotato/csquiz/domain/attendance/util/AttendanceUtil.java @@ -6,24 +6,28 @@ import org.cotato.csquiz.common.error.exception.AppException; import org.cotato.csquiz.domain.attendance.entity.Attendance; import org.cotato.csquiz.domain.attendance.enums.AttendanceOpenStatus; -import org.cotato.csquiz.domain.attendance.enums.AttendanceStatus; +import org.cotato.csquiz.domain.attendance.enums.AttendanceResult; import org.cotato.csquiz.domain.attendance.enums.DeadLine; public class AttendanceUtil { // 출석 시간에 따른 지각 여부 구분하기 - public static AttendanceStatus calculateAttendanceStatus(Attendance attendance, LocalDateTime attendTime){ + public static AttendanceResult calculateAttendanceStatus(Attendance attendance, LocalDateTime attendTime){ if (attendTime.isBefore(attendance.getAttendanceDeadLine())) { - return AttendanceStatus.PRESENT; + return AttendanceResult.PRESENT; } if (attendTime.isBefore(attendance.getLateDeadLine())) { - return AttendanceStatus.LATE; + return AttendanceResult.LATE; } - return AttendanceStatus.ABSENT; + return AttendanceResult.ABSENT; } // 현재 시간을 기준으로 출석 open 상태를 반환한다. - public static AttendanceOpenStatus getAttendanceStatus(Attendance attendance, LocalDateTime currentDateTime) { - if (!isToday(attendance, currentDateTime) || !isStarted(currentDateTime.toLocalTime())) { + public static AttendanceOpenStatus getAttendanceOpenStatus(Attendance attendance, LocalDateTime currentDateTime) { + if (currentDateTime.isBefore(DeadLine.sessionStartTime(attendance.getAttendanceDeadLine().toLocalDate()))) { + return AttendanceOpenStatus.BEFORE; + } + + if (currentDateTime.isAfter(DeadLine.sessionEndTime(attendance.getLateDeadLine().toLocalDate()))) { return AttendanceOpenStatus.CLOSED; } @@ -42,14 +46,6 @@ public static AttendanceOpenStatus getAttendanceStatus(Attendance attendance, Lo return AttendanceOpenStatus.ABSENT; } - private static boolean isToday(Attendance attendance, LocalDateTime currentDate) { - return currentDate.toLocalDate().equals(attendance.getAttendanceDeadLine().toLocalDate()); - } - - private static boolean isStarted(LocalTime currentTime) { - return currentTime.isAfter(DeadLine.ATTENDANCE_START_TIME.getTime()); - } - public static void validateAttendanceTime(LocalTime attendDeadLine, LocalTime lateDeadLine) { if (!DeadLine.ATTENDANCE_START_TIME.getTime().isBefore(attendDeadLine)) { throw new AppException(ErrorCode.INVALID_ATTEND_TIME); diff --git a/src/test/java/org/cotato/csquiz/domain/attendance/util/AttendanceUtilTest.java b/src/test/java/org/cotato/csquiz/domain/attendance/util/AttendanceUtilTest.java index e1f7132c..4caf6dcf 100644 --- a/src/test/java/org/cotato/csquiz/domain/attendance/util/AttendanceUtilTest.java +++ b/src/test/java/org/cotato/csquiz/domain/attendance/util/AttendanceUtilTest.java @@ -3,12 +3,15 @@ import static org.assertj.core.api.AssertionsForClassTypes.assertThatThrownBy; import static org.junit.jupiter.api.Assertions.*; +import java.time.LocalDate; import java.time.LocalDateTime; +import java.time.Month; import java.time.LocalTime; import org.cotato.csquiz.common.error.ErrorCode; import org.cotato.csquiz.common.error.exception.AppException; import org.cotato.csquiz.domain.attendance.entity.Attendance; import org.cotato.csquiz.domain.attendance.enums.AttendanceOpenStatus; +import org.cotato.csquiz.domain.attendance.enums.DeadLine; import org.cotato.csquiz.domain.generation.entity.Session; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -26,7 +29,7 @@ class AttendanceUtilTest { .build(); //when - AttendanceOpenStatus attendanceStatus = AttendanceUtil.getAttendanceStatus(attendance, + AttendanceOpenStatus attendanceStatus = AttendanceUtil.getAttendanceOpenStatus(attendance, LocalDateTime.now().plusDays(1)); //then @@ -36,7 +39,7 @@ class AttendanceUtilTest { @Test void 기준시간_전이면_출석이_닫혀있다() { //given - LocalDateTime attendanceDeadLine = LocalDateTime.now(); + LocalDateTime attendanceDeadLine = LocalDateTime.of(2024, Month.AUGUST, 9, 19, 10, 0); Attendance attendance = Attendance.builder() .attendanceDeadLine(attendanceDeadLine) @@ -45,13 +48,13 @@ class AttendanceUtilTest { .build()) .build(); - LocalDateTime beforeTime = attendanceDeadLine.minusMinutes(10); + LocalDateTime beforeTime = LocalDateTime.of(LocalDate.of(2024, Month.AUGUST, 9),DeadLine.ATTENDANCE_START_TIME.getTime().minusMinutes(10)); //when - AttendanceOpenStatus attendanceStatus = AttendanceUtil.getAttendanceStatus(attendance, beforeTime); + AttendanceOpenStatus attendanceStatus = AttendanceUtil.getAttendanceOpenStatus(attendance, beforeTime); //then - assertEquals(attendanceStatus, AttendanceOpenStatus.CLOSED); + assertEquals(attendanceStatus, AttendanceOpenStatus.BEFORE); } @Test