Skip to content
Merged
Show file tree
Hide file tree
Changes from 2 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 @@ -9,6 +9,7 @@
import com.gpt.geumpumtabackend.study.dto.request.HeartBeatRequest;
import com.gpt.geumpumtabackend.study.dto.request.StudyEndRequest;
import com.gpt.geumpumtabackend.study.dto.request.StudyStartRequest;
import com.gpt.geumpumtabackend.study.dto.response.HeartBeatResponse;
import com.gpt.geumpumtabackend.study.dto.response.StudySessionResponse;
import com.gpt.geumpumtabackend.study.dto.response.StudyStartResponse;
import io.swagger.v3.oas.annotations.Operation;
Expand Down Expand Up @@ -147,8 +148,7 @@ ResponseEntity<ResponseBody<Void>> endStudySession(
🔄 **동작 원리:**
1. Wi-Fi 연결 상태 재검증 (Gateway IP + IP 대역 확인)
2. 클라이언트 실제 IP 주소 재확인 (서버에서 추출)
3. 세션의 lastHeartBeatAt 시간 업데이트
4. 90초 이상 하트비트 없으면 좀비 세션으로 분류
3.

🚨 **실패 시 대응:**
- Wi-Fi 연결 끊김: 재연결 후 다시 `/start` 호출
Expand All @@ -170,7 +170,7 @@ ResponseEntity<ResponseBody<Void>> endStudySession(
@PostMapping("/heart-beat")
@AssignUserId
@PreAuthorize("isAuthenticated() and hasRole('USER')")
ResponseEntity<ResponseBody<Void>> processHeartBeat(
ResponseEntity<ResponseBody<HeartBeatResponse>> processHeartBeat(
@Valid @RequestBody HeartBeatRequest heartBeatRequest,
@Parameter(hidden = true) Long userId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.gpt.geumpumtabackend.study.dto.request.HeartBeatRequest;
import com.gpt.geumpumtabackend.study.dto.request.StudyEndRequest;
import com.gpt.geumpumtabackend.study.dto.request.StudyStartRequest;
import com.gpt.geumpumtabackend.study.dto.response.HeartBeatResponse;
import com.gpt.geumpumtabackend.study.dto.response.StudySessionResponse;
import com.gpt.geumpumtabackend.study.dto.response.StudyStartResponse;
import com.gpt.geumpumtabackend.study.service.StudySessionService;
Expand Down Expand Up @@ -64,8 +65,7 @@ public ResponseEntity<ResponseBody<Void>> endStudySession(@Valid @RequestBody St
@PostMapping("/heart-beat")
@PreAuthorize("isAuthenticated() and hasRole('USER')")
@AssignUserId
public ResponseEntity<ResponseBody<Void>> processHeartBeat(@Valid @RequestBody HeartBeatRequest heartBeatRequest, Long userId){
studySessionService.updateHeartBeat(heartBeatRequest, userId);
return ResponseEntity.ok(ResponseUtil.createSuccessResponse());
public ResponseEntity<ResponseBody<HeartBeatResponse>> processHeartBeat(@Valid @RequestBody HeartBeatRequest heartBeatRequest, Long userId){
return ResponseEntity.ok(ResponseUtil.createSuccessResponse(studySessionService.updateHeartBeat(heartBeatRequest, userId)));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
package com.gpt.geumpumtabackend.study.dto.response;

public record HeartBeatResponse(boolean sessionActive, String message) {
}

This file was deleted.

Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.gpt.geumpumtabackend.study.dto.request.HeartBeatRequest;
import com.gpt.geumpumtabackend.study.dto.request.StudyEndRequest;
import com.gpt.geumpumtabackend.study.dto.request.StudyStartRequest;
import com.gpt.geumpumtabackend.study.dto.response.HeartBeatResponse;
import com.gpt.geumpumtabackend.study.dto.response.StudySessionResponse;
import com.gpt.geumpumtabackend.study.dto.response.StudyStartResponse;
import com.gpt.geumpumtabackend.study.repository.StudySessionRepository;
Expand All @@ -19,6 +20,7 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.Duration;
import java.time.LocalDate;
import java.time.LocalDateTime;

Expand All @@ -30,7 +32,7 @@ public class StudySessionService {
private final StudySessionRepository studySessionRepository;
private final UserRepository userRepository;
private final CampusWiFiValidationService wifiValidationService;

private static final Integer MAX_FOCUS_TIME = 3;
/*
메인 홈
*/
Expand Down Expand Up @@ -81,7 +83,7 @@ public void endStudySession(StudyEndRequest request, Long userId) {
하트비트 처리
*/
@Transactional
public void updateHeartBeat(HeartBeatRequest heartBeatRequest, Long userId) {
public HeartBeatResponse updateHeartBeat(HeartBeatRequest heartBeatRequest, Long userId) {
Long sessionId = heartBeatRequest.sessionId();

// Wi-Fi 검증 (캐시 우선 사용)
Expand All @@ -98,7 +100,14 @@ public void updateHeartBeat(HeartBeatRequest heartBeatRequest, Long userId) {
// 유효하면 해당 세션의 lastHeartBeatAt 시간을 now()로 갱신한다.
StudySession studySession = studySessionRepository.findByIdAndUser_Id(sessionId, userId)
.orElseThrow(()->new BusinessException(ExceptionType.STUDY_SESSION_NOT_FOUND));

Duration elapsed = Duration.between(studySession.getStartTime(), LocalDateTime.now());
if(elapsed.compareTo(Duration.ofHours(MAX_FOCUS_TIME)) >= 0) {
studySession.endStudySession(studySession.getStartTime().plusHours(MAX_FOCUS_TIME));
return new HeartBeatResponse(false, "최대 집중시간은 3시간입니다.");
}
studySession.updateHeartBeatAt(LocalDateTime.now());
return new HeartBeatResponse(true,"정상 세션");
}

private BusinessException mapWiFiValidationException(WiFiValidationResult result) {
Expand Down