-
Notifications
You must be signed in to change notification settings - Fork 0
[Feat]#18 트래킹 세션 #52
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
[Feat]#18 트래킹 세션 #52
Changes from 3 commits
2512a3c
aa62d48
50d6681
fb0eb28
36dd0c4
9d6a245
1e91a35
08ff10d
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,93 @@ | ||
| package com.semosan.api.domain.tracking.controller; | ||
|
|
||
| import com.semosan.api.common.response.ApiResponse; | ||
| import com.semosan.api.common.status.SuccessStatus; | ||
| import com.semosan.api.domain.tracking.controller.docs.TrackingSessionControllerDocs; | ||
| import com.semosan.api.domain.tracking.dto.request.CreateTrackingSessionRequest; | ||
| import com.semosan.api.domain.tracking.dto.response.TrackingSessionResponse; | ||
| import com.semosan.api.domain.tracking.service.TrackingSessionService; | ||
| import jakarta.validation.Valid; | ||
| import lombok.RequiredArgsConstructor; | ||
| import org.springframework.http.ResponseEntity; | ||
| import org.springframework.security.core.annotation.AuthenticationPrincipal; | ||
| import org.springframework.web.bind.annotation.*; | ||
|
|
||
| @RestController | ||
| @RequestMapping("/api/tracking/sessions") | ||
| @RequiredArgsConstructor | ||
| public class TrackingSessionController implements TrackingSessionControllerDocs { | ||
|
|
||
| private final TrackingSessionService trackingSessionService; | ||
|
|
||
| @PostMapping | ||
| @Override | ||
| public ResponseEntity<ApiResponse<TrackingSessionResponse>> createSession( | ||
| @AuthenticationPrincipal Long userId, | ||
| @Valid @RequestBody CreateTrackingSessionRequest request | ||
| ) { | ||
| TrackingSessionResponse response = trackingSessionService.create(userId, request); | ||
| return ApiResponse.success(SuccessStatus.TRACKING_SESSION_CREATE_SUCCESS, response); | ||
| } | ||
|
|
||
| /** | ||
| * 앱 재진입 시 호출. 진행 중 세션이 없으면 data 가 비어 응답된다 (NON_NULL 직렬화). | ||
| */ | ||
| @GetMapping("/me/active") | ||
| @Override | ||
| public ResponseEntity<ApiResponse<TrackingSessionResponse>> getActiveSession( | ||
| @AuthenticationPrincipal Long userId | ||
| ) { | ||
| TrackingSessionResponse response = trackingSessionService.getActive(userId).orElse(null); | ||
| return ApiResponse.success(SuccessStatus.TRACKING_SESSION_GET_ACTIVE_SUCCESS, response); | ||
| } | ||
|
|
||
| @GetMapping("/{sessionId}") | ||
| @Override | ||
| public ResponseEntity<ApiResponse<TrackingSessionResponse>> getSession( | ||
| @AuthenticationPrincipal Long userId, | ||
| @PathVariable Long sessionId | ||
| ) { | ||
| TrackingSessionResponse response = trackingSessionService.get(userId, sessionId); | ||
| return ApiResponse.success(SuccessStatus.TRACKING_SESSION_GET_SUCCESS, response); | ||
| } | ||
|
|
||
| @PostMapping("/{sessionId}/pause") | ||
| @Override | ||
| public ResponseEntity<ApiResponse<TrackingSessionResponse>> pauseSession( | ||
| @AuthenticationPrincipal Long userId, | ||
| @PathVariable Long sessionId | ||
| ) { | ||
| TrackingSessionResponse response = trackingSessionService.pause(userId, sessionId); | ||
| return ApiResponse.success(SuccessStatus.TRACKING_SESSION_PAUSE_SUCCESS, response); | ||
| } | ||
|
|
||
| @PostMapping("/{sessionId}/resume") | ||
| @Override | ||
| public ResponseEntity<ApiResponse<TrackingSessionResponse>> resumeSession( | ||
| @AuthenticationPrincipal Long userId, | ||
| @PathVariable Long sessionId | ||
| ) { | ||
| TrackingSessionResponse response = trackingSessionService.resume(userId, sessionId); | ||
| return ApiResponse.success(SuccessStatus.TRACKING_SESSION_RESUME_SUCCESS, response); | ||
| } | ||
|
|
||
| @PostMapping("/{sessionId}/complete") | ||
| @Override | ||
| public ResponseEntity<ApiResponse<TrackingSessionResponse>> completeSession( | ||
| @AuthenticationPrincipal Long userId, | ||
| @PathVariable Long sessionId | ||
| ) { | ||
| TrackingSessionResponse response = trackingSessionService.complete(userId, sessionId); | ||
| return ApiResponse.success(SuccessStatus.TRACKING_SESSION_COMPLETE_SUCCESS, response); | ||
| } | ||
|
|
||
| @PostMapping("/{sessionId}/abandon") | ||
| @Override | ||
| public ResponseEntity<ApiResponse<TrackingSessionResponse>> abandonSession( | ||
| @AuthenticationPrincipal Long userId, | ||
| @PathVariable Long sessionId | ||
| ) { | ||
| TrackingSessionResponse response = trackingSessionService.abandon(userId, sessionId); | ||
| return ApiResponse.success(SuccessStatus.TRACKING_SESSION_ABANDON_SUCCESS, response); | ||
| } | ||
| } |
| Original file line number | Diff line number | Diff line change | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|---|
| @@ -0,0 +1,87 @@ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| package com.semosan.api.domain.tracking.controller.docs; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.semosan.api.common.response.ApiResponse; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.semosan.api.domain.tracking.dto.request.CreateTrackingSessionRequest; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import com.semosan.api.domain.tracking.dto.response.TrackingSessionResponse; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.swagger.v3.oas.annotations.Operation; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.swagger.v3.oas.annotations.Parameter; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.swagger.v3.oas.annotations.media.Content; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.swagger.v3.oas.annotations.media.Schema; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.swagger.v3.oas.annotations.responses.ApiResponses; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import io.swagger.v3.oas.annotations.tags.Tag; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import jakarta.validation.Valid; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.http.ResponseEntity; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.security.core.annotation.AuthenticationPrincipal; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.web.bind.annotation.PathVariable; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| import org.springframework.web.bind.annotation.RequestBody; | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Tag(name = "Tracking Session", description = "트래킹 세션 관리 API") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| public interface TrackingSessionControllerDocs { | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Operation( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| summary = "트래킹 세션 시작", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description = "산/코스 정보로 새 트래킹 세션을 생성합니다. 자유 기록(isFreeRecording=true)이면 courseId 는 무시됩니다. " | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| + "유저당 진행 중 세션이 이미 있으면 409 응답." | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @ApiResponses({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "201", description = "세션 생성 성공"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "산 또는 코스 없음", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content = @Content(schema = @Schema(implementation = ApiResponse.class))), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "409", description = "이미 진행 중인 세션 존재", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content = @Content(schema = @Schema(implementation = ApiResponse.class))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ResponseEntity<ApiResponse<TrackingSessionResponse>> createSession( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Parameter(hidden = true) @AuthenticationPrincipal Long userId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Valid @RequestBody CreateTrackingSessionRequest request | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Operation(summary = "현재 진행 중 트래킹 세션 조회", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description = "앱 재진입 시 호출. 진행 중(IN_PROGRESS/PAUSED) 세션이 없으면 data 가 비어 응답됩니다.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @ApiResponses({@io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "조회 성공")}) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ResponseEntity<ApiResponse<TrackingSessionResponse>> getActiveSession( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Parameter(hidden = true) @AuthenticationPrincipal Long userId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Operation(summary = "트래킹 세션 상세 조회", description = "본인 소유 세션만 조회 가능합니다.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @ApiResponses({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "조회 성공"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "403", description = "본인 세션 아님", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content = @Content(schema = @Schema(implementation = ApiResponse.class))), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "404", description = "세션 없음", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content = @Content(schema = @Schema(implementation = ApiResponse.class))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ResponseEntity<ApiResponse<TrackingSessionResponse>> getSession( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Parameter(hidden = true) @AuthenticationPrincipal Long userId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Parameter(description = "세션 ID", required = true) @PathVariable Long sessionId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Operation(summary = "트래킹 세션 일시정지", description = "IN_PROGRESS 상태에서만 가능. 점 수집은 멈추고 duration 에서 제외됩니다.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @ApiResponses({ | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "200", description = "일시정지 성공"), | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @io.swagger.v3.oas.annotations.responses.ApiResponse(responseCode = "409", description = "현재 상태에서 일시정지 불가", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| content = @Content(schema = @Schema(implementation = ApiResponse.class))) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| }) | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ResponseEntity<ApiResponse<TrackingSessionResponse>> pauseSession( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Parameter(hidden = true) @AuthenticationPrincipal Long userId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @PathVariable Long sessionId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Operation(summary = "트래킹 세션 재개", description = "PAUSED 상태에서만 가능. 일시정지 누적 시간이 합산됩니다.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ResponseEntity<ApiResponse<TrackingSessionResponse>> resumeSession( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Parameter(hidden = true) @AuthenticationPrincipal Long userId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @PathVariable Long sessionId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Operation(summary = "트래킹 세션 정상 종료", | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| description = "세션 상태/시각만 마감합니다. 실제 HikingRecord 변환은 #20 에서 구현됩니다.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ResponseEntity<ApiResponse<TrackingSessionResponse>> completeSession( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Parameter(hidden = true) @AuthenticationPrincipal Long userId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @PathVariable Long sessionId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
|
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Operation(summary = "트래킹 세션 포기", description = "기록 없이 세션을 ABANDONED 처리합니다.") | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ResponseEntity<ApiResponse<TrackingSessionResponse>> abandonSession( | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @Parameter(hidden = true) @AuthenticationPrincipal Long userId, | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| @PathVariable Long sessionId | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| ); | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
|
Comment on lines
+69
to
+86
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more. 🛠️ Refactor suggestion | 🟠 Major | ⚡ Quick win Complete API documentation with error response codes. The 📝 Add `@ApiResponses` annotations for consistency `@Operation`(summary = "트래킹 세션 재개", description = "PAUSED 상태에서만 가능. 일시정지 누적 시간이 합산됩니다.")
+@ApiResponses({
+ `@io.swagger.v3.oas.annotations.responses.ApiResponse`(responseCode = "200", description = "재개 성공"),
+ `@io.swagger.v3.oas.annotations.responses.ApiResponse`(responseCode = "403", description = "본인 세션 아님",
+ content = `@Content`(schema = `@Schema`(implementation = ApiResponse.class))),
+ `@io.swagger.v3.oas.annotations.responses.ApiResponse`(responseCode = "404", description = "세션 없음",
+ content = `@Content`(schema = `@Schema`(implementation = ApiResponse.class))),
+ `@io.swagger.v3.oas.annotations.responses.ApiResponse`(responseCode = "409", description = "현재 상태에서 재개 불가",
+ content = `@Content`(schema = `@Schema`(implementation = ApiResponse.class)))
+})
ResponseEntity<ApiResponse<TrackingSessionResponse>> resumeSession(
`@Parameter`(hidden = true) `@AuthenticationPrincipal` Long userId,
`@PathVariable` Long sessionId
);
`@Operation`(summary = "트래킹 세션 정상 종료",
description = "세션 상태/시각만 마감합니다. 실제 HikingRecord 변환은 `#20` 에서 구현됩니다.")
+@ApiResponses({
+ `@io.swagger.v3.oas.annotations.responses.ApiResponse`(responseCode = "200", description = "종료 성공"),
+ `@io.swagger.v3.oas.annotations.responses.ApiResponse`(responseCode = "403", description = "본인 세션 아님",
+ content = `@Content`(schema = `@Schema`(implementation = ApiResponse.class))),
+ `@io.swagger.v3.oas.annotations.responses.ApiResponse`(responseCode = "404", description = "세션 없음",
+ content = `@Content`(schema = `@Schema`(implementation = ApiResponse.class))),
+ `@io.swagger.v3.oas.annotations.responses.ApiResponse`(responseCode = "409", description = "현재 상태에서 종료 불가",
+ content = `@Content`(schema = `@Schema`(implementation = ApiResponse.class)))
+})
ResponseEntity<ApiResponse<TrackingSessionResponse>> completeSession(
`@Parameter`(hidden = true) `@AuthenticationPrincipal` Long userId,
`@PathVariable` Long sessionId
);
`@Operation`(summary = "트래킹 세션 포기", description = "기록 없이 세션을 ABANDONED 처리합니다.")
+@ApiResponses({
+ `@io.swagger.v3.oas.annotations.responses.ApiResponse`(responseCode = "200", description = "포기 성공"),
+ `@io.swagger.v3.oas.annotations.responses.ApiResponse`(responseCode = "403", description = "본인 세션 아님",
+ content = `@Content`(schema = `@Schema`(implementation = ApiResponse.class))),
+ `@io.swagger.v3.oas.annotations.responses.ApiResponse`(responseCode = "404", description = "세션 없음",
+ content = `@Content`(schema = `@Schema`(implementation = ApiResponse.class)))
+})
ResponseEntity<ApiResponse<TrackingSessionResponse>> abandonSession(📝 Committable suggestion
Suggested change
🤖 Prompt for AI Agents |
||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| } | ||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||||
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,15 @@ | ||
| package com.semosan.api.domain.tracking.dto.request; | ||
|
|
||
| import jakarta.validation.constraints.NotNull; | ||
|
|
||
| public record CreateTrackingSessionRequest( | ||
| @NotNull(message = "산 ID는 필수입니다.") | ||
| Long mountainId, | ||
|
|
||
| /** 자유 기록(코스 미선택) 인 경우 null */ | ||
| Long courseId, | ||
|
|
||
| @NotNull(message = "자유 기록 여부는 필수입니다.") | ||
| Boolean isFreeRecording | ||
| ) { | ||
| } |
| Original file line number | Diff line number | Diff line change |
|---|---|---|
| @@ -0,0 +1,39 @@ | ||
| package com.semosan.api.domain.tracking.dto.response; | ||
|
|
||
| import com.semosan.api.domain.tracking.entity.TrackingSession; | ||
| import com.semosan.api.domain.tracking.enums.TrackingSessionStatus; | ||
|
|
||
| import java.time.LocalDateTime; | ||
|
|
||
| public record TrackingSessionResponse( | ||
| Long sessionId, | ||
| Long userId, | ||
| Long mountainId, | ||
| String mountainName, | ||
| Long courseId, | ||
| String courseName, | ||
| Boolean isFreeRecording, | ||
| TrackingSessionStatus status, | ||
| LocalDateTime startedAt, | ||
| LocalDateTime endedAt, | ||
| LocalDateTime pausedAt, | ||
| Integer pausedSecondsTotal | ||
| ) { | ||
|
|
||
| public static TrackingSessionResponse from(TrackingSession session) { | ||
| return new TrackingSessionResponse( | ||
| session.getId(), | ||
| session.getUser().getId(), | ||
| session.getMountain().getId(), | ||
| session.getMountain().getName(), | ||
| session.getCourse() != null ? session.getCourse().getId() : null, | ||
| session.getCourse() != null ? session.getCourse().getName() : null, | ||
| session.getIsFreeRecording(), | ||
| session.getStatus(), | ||
| session.getStartedAt(), | ||
| session.getEndedAt(), | ||
| session.getPausedAt(), | ||
| session.getPausedSecondsTotal() | ||
| ); | ||
| } | ||
| } |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
GeneralExceptionAdvice에는MethodArgumentNotValidException처리는 있지만,@RequestParam @Min/@Max에서 발생할 수 있는ConstraintViolationException또는HandlerMethodValidationException처리가 안 되어 있어서 이 경우Exception핸들러로 빠져서 잘못된 좌표 요청이 500으로 응답될 수 있을 듯합니당한 번 체크해봐야할듯!
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
좋습니다 ! 수정할게여