Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -7,7 +7,6 @@
import org.sejongisc.backend.attendance.dto.AttendanceResponse;
import org.sejongisc.backend.attendance.service.AttendanceService;
import org.sejongisc.backend.common.auth.springsecurity.CustomUserDetails;
import org.sejongisc.backend.user.entity.User;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.security.access.prepost.PreAuthorize;
Expand Down Expand Up @@ -40,8 +39,7 @@ public ResponseEntity<AttendanceResponse> checkIn(

log.info("출석 체크인 요청: 사용자={}, 코드={}", userDetails.getName(), request.getCode());

User user = convertToUser(userDetails);
AttendanceResponse response = attendanceService.checkIn(sessionId, request, user);
AttendanceResponse response = attendanceService.checkIn(sessionId, request, userDetails.getUserId());

log.info("출석 체크인 완료: 사용자={}, 상태={}", userDetails.getName(), response.getAttendanceStatus());

Expand All @@ -58,7 +56,7 @@ public ResponseEntity<AttendanceResponse> checkIn(
public ResponseEntity<List<AttendanceResponse>> getAttendancesBySession(@PathVariable UUID sessionId) {
log.info("세션별 출석 목록 조회: 세션ID={}", sessionId);

List<AttendanceResponse> attendances = attendanceService.getAttendanceBySession(sessionId);
List<AttendanceResponse> attendances = attendanceService.getAttendancesBySession(sessionId);

return ResponseEntity.ok(attendances);
}
Expand All @@ -73,8 +71,7 @@ public ResponseEntity<List<AttendanceResponse>> getMyAttendances(
@AuthenticationPrincipal CustomUserDetails userDetails) {
log.info("내 출석 기록 조회: 사용자={}", userDetails.getName());

User user = convertToUser(userDetails);
List<AttendanceResponse> attendances = attendanceService.getAttendancesByUser(user);
List<AttendanceResponse> attendances = attendanceService.getAttendancesByUser(userDetails.getUserId());

return ResponseEntity.ok(attendances);
}
Expand All @@ -95,24 +92,10 @@ public ResponseEntity<AttendanceResponse> updateAttendanceStatus(

log.info("출석 상태 수정: 세션ID={}, 멤버ID={}, 새로운상태={}, 관리자={}", sessionId, memberId, status, userDetails.getName());

User user = convertToUser(userDetails);
AttendanceResponse response = attendanceService.updateAttendanceStatus(sessionId, memberId, status, reason, user);
AttendanceResponse response = attendanceService.updateAttendanceStatus(sessionId, memberId, status, reason, userDetails.getUserId());

log.info("출석 상태 수정 완료: 세션ID={}, 멤버ID={}, 상태={}", sessionId, memberId, response.getAttendanceStatus());

return ResponseEntity.ok(response);
}


private User convertToUser(CustomUserDetails userDetails) {
return User.builder()
.userId(userDetails.getUserId())
.name(userDetails.getName())
.email(userDetails.getEmail())
.passwordHash(userDetails.getPassword())
.phoneNumber(userDetails.getPhoneNumber())
.role(userDetails.getRole())
.point(userDetails.getPoint())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -3,7 +3,6 @@
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.sejongisc.backend.attendance.dto.AttendanceRequest;
import org.sejongisc.backend.attendance.dto.AttendanceSessionRequest;
import org.sejongisc.backend.attendance.dto.AttendanceSessionResponse;
import org.sejongisc.backend.attendance.entity.SessionStatus;
Expand Down Expand Up @@ -50,7 +49,7 @@ public ResponseEntity<AttendanceSessionResponse> createSession(@Valid @RequestBo
*/
@GetMapping("/{sessionId}")
public ResponseEntity<AttendanceSessionResponse> getSession(@PathVariable UUID sessionId) {
log.info("춣석 세션 조회: 세션ID={}", sessionId);
log.info("출석 세션 조회: 세션ID={}", sessionId);

AttendanceSessionResponse response = attendanceSessionService.getSessionById(sessionId);

Expand Down Expand Up @@ -189,7 +188,7 @@ public ResponseEntity<Void> closeSession(@PathVariable UUID sessionId) {

attendanceSessionService.closeSession(sessionId);

log.info("출석 세션 종료 오나료: 세션ID={}", sessionId);
log.info("출석 세션 종료 완료: 세션ID={}", sessionId);

return ResponseEntity.ok().build();
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@
import org.sejongisc.backend.attendance.entity.Location;
import org.sejongisc.backend.attendance.repository.AttendanceRepository;
import org.sejongisc.backend.attendance.repository.AttendanceSessionRepository;
import org.sejongisc.backend.user.dao.UserRepository;
import org.sejongisc.backend.user.entity.User;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
Expand All @@ -27,17 +28,21 @@ public class AttendanceService {

private final AttendanceRepository attendanceRepository;
private final AttendanceSessionRepository attendanceSessionRepository;
private final UserRepository userRepository;


/**
* 출석 체크인 처리
* - 코드 유효성 및 중복 춣석 방지
* - 코드 유효성 및 중복 출석 방지
* - GPS 위치 및 반경 검증
* - 시간 윈도우 검증 및 지각 판별
*/
public AttendanceResponse checkIn(UUID sessionId, AttendanceRequest request, User user) {
public AttendanceResponse checkIn(UUID sessionId, AttendanceRequest request, UUID userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 유저입니다: " + userId));
log.info("출석 체크인 시작: 사용자={}, 세션ID={}, 코드={}", user.getName(), sessionId, request.getCode());


// 세션ID로 세션 조회
AttendanceSession session = attendanceSessionRepository.findById(sessionId)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 세션입니다: " + sessionId));
Expand All @@ -54,9 +59,9 @@ public AttendanceResponse checkIn(UUID sessionId, AttendanceRequest request, Use
// 위치 정보가 있는 세션에 대해서만 사용자 위치 생성 및 검증
Location userLocation = null;
if (session.getLocation() != null) {
if (request.getLatitude() == null || request.getLongitude() == null) {
if (request.getLatitude() == null || request.getLongitude() == null) {
throw new IllegalArgumentException("위치 기반 출석에는 위도와 경도가 필요합니다");
}
}

userLocation = Location.builder()
.lat(request.getLatitude())
Expand All @@ -79,7 +84,9 @@ public AttendanceResponse checkIn(UUID sessionId, AttendanceRequest request, Use
throw new IllegalStateException("출석 시간이 종료되었습니다");
}

AttendanceStatus status = now.isAfter(session.getStartsAt()) ?
// 시작 후 5분 이내는 정상 출석, 이후는 지각
LocalDateTime lateThreshold = session.getStartsAt().plusMinutes(5);
AttendanceStatus status = now.isAfter(lateThreshold) ?
AttendanceStatus.LATE : AttendanceStatus.PRESENT;

Attendance attendance = Attendance.builder()
Expand All @@ -106,7 +113,7 @@ public AttendanceResponse checkIn(UUID sessionId, AttendanceRequest request, Use
* - 출석 시간 순으로 정렬
*/
@Transactional(readOnly = true)
public List<AttendanceResponse> getAttendanceBySession(UUID sessionId) {
public List<AttendanceResponse> getAttendancesBySession(UUID sessionId) {
AttendanceSession session = attendanceSessionRepository.findById(sessionId)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 세션입니다: " + sessionId));

Expand All @@ -123,7 +130,9 @@ public List<AttendanceResponse> getAttendanceBySession(UUID sessionId) {
* - 최신 순으로 정렬
*/
@Transactional(readOnly = true)
public List<AttendanceResponse> getAttendancesByUser(User user) {
public List<AttendanceResponse> getAttendancesByUser(UUID userId) {
User user = userRepository.findById(userId)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 유저입니다: " + userId));
List<Attendance> attendances = attendanceRepository.findByUserOrderByCheckedAtDesc(user);

return attendances.stream()
Expand All @@ -136,7 +145,9 @@ public List<AttendanceResponse> getAttendancesByUser(User user) {
* - PRESENT/LATE/ABSENT 등으로 상태 변경
* - 수정 사유 기록 및 로그 남기기
*/
public AttendanceResponse updateAttendanceStatus(UUID sessionId, UUID memberId, String status, String reason, User adminUser) {
public AttendanceResponse updateAttendanceStatus(UUID sessionId, UUID memberId, String status, String reason, UUID adminId) {
User adminUser = userRepository.findById(adminId)
.orElseThrow(() -> new IllegalArgumentException("존재하지 않는 관리자입니다: " + adminId));
log.info("출석 상태 수정 시작: 세션ID={}, 멤버ID={}, 새로운상태={}, 관리자={}", sessionId, memberId, status, adminUser.getName());

// 세션 존재 확인
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -121,7 +121,7 @@ public List<AttendanceSessionResponse> getSessionsByTag(String tag) {

/**
* 상태별 세션 목록 조회
* - UPCOMING/OPEN/CLOSED 상태펼 필터링
* - UPCOMING/OPEN/CLOSED 상태별 필터링
*/
@Transactional(readOnly = true)
public List<AttendanceSessionResponse> getSessionsByStatus(SessionStatus status) {
Expand Down Expand Up @@ -159,14 +159,14 @@ public List<AttendanceSessionResponse> getActiveSessions() {

return allSessions.stream()
.filter(session -> {
if (session.getStatus() != SessionStatus.OPEN) {
return false;
}
LocalDateTime endTime = session.getStartsAt().plusSeconds(session.getWindowSeconds());
return !now.isBefore(session.getStartsAt()) && now.isBefore(endTime);
})
.map(this::convertToResponse)
.collect(Collectors.toList());
if (session.getStatus() != SessionStatus.OPEN) {
return false;
}
LocalDateTime endTime = session.getStartsAt().plusSeconds(session.getWindowSeconds());
return !now.isBefore(session.getStartsAt()) && now.isBefore(endTime);
})
.map(this::convertToResponse)
.collect(Collectors.toList());
}

/**
Expand Down Expand Up @@ -237,6 +237,7 @@ public void activateSession(UUID sessionId) {

session = session.toBuilder()
.status(SessionStatus.OPEN)
.startsAt(LocalDateTime.now())
.build();

attendanceSessionRepository.save(session);
Expand Down Expand Up @@ -265,7 +266,7 @@ public void closeSession(UUID sessionId) {
}

/**
* 중복되지 않은 6자리 코드 생성
* 중복되지 않는 6자리 코드 생성
* - DB에서 중복 검사 후 유니크 코드 리턴
*/
private String generateUniqueCode() {
Expand Down
Loading