diff --git a/src/main/java/org/runimo/runimo/records/controller/RecordController.java b/src/main/java/org/runimo/runimo/records/controller/RecordController.java index 16f9cfbc..9a6f53e1 100644 --- a/src/main/java/org/runimo/runimo/records/controller/RecordController.java +++ b/src/main/java/org/runimo/runimo/records/controller/RecordController.java @@ -13,6 +13,7 @@ import org.runimo.runimo.records.controller.request.RecordSaveRequest; import org.runimo.runimo.records.controller.request.RecordUpdateRequest; import org.runimo.runimo.records.enums.RecordHttpResponse; +import org.runimo.runimo.records.service.dto.RecordSimpleViewResponse; import org.runimo.runimo.records.service.dto.WeeklyRecordStatResponse; import org.runimo.runimo.records.service.dto.WeeklyStatQuery; import org.runimo.runimo.records.service.usecases.RecordCreateUsecase; @@ -137,4 +138,21 @@ public ResponseEntity> queryMonthlyRe return ResponseEntity.ok( SuccessResponse.of(UserHttpResponseCode.MY_PAGE_DATA_FETCHED, response)); } + + @Operation(summary = "개인 기록 페이지네이션 전체 조회", description = "개인 기록 페이지네이션 조회") + @ApiResponse(responseCode = "200", description = "기록 조회 성공", + content = @Content(schema = @Schema(implementation = RecordSimpleViewResponse.class))) + @GetMapping("/me") + public ResponseEntity> getMyRecordList( + @RequestParam(defaultValue = "0") int page, + @RequestParam(defaultValue = "10") int size, + @UserId Long userId + ) { + return ResponseEntity.ok( + SuccessResponse.of( + RecordHttpResponse.RECORD_FETCHED, + recordQueryUsecase.getUserRecordSimpleView(userId, page, size) + ) + ); + } } diff --git a/src/main/java/org/runimo/runimo/records/repository/RecordRepository.java b/src/main/java/org/runimo/runimo/records/repository/RecordRepository.java index 7e30a621..9e608626 100644 --- a/src/main/java/org/runimo/runimo/records/repository/RecordRepository.java +++ b/src/main/java/org/runimo/runimo/records/repository/RecordRepository.java @@ -43,4 +43,5 @@ Slice findFirstRunOfWeek( List findDailyDistanceByUserIdAndThisWeek(Long userId, LocalDateTime startOfWeek, LocalDateTime now); + List findRecordByUserIdOrderByStartedAtDesc(Long id, Pageable pageable); } diff --git a/src/main/java/org/runimo/runimo/records/service/RecordFinder.java b/src/main/java/org/runimo/runimo/records/service/RecordFinder.java index 01fe0025..f8552ba9 100644 --- a/src/main/java/org/runimo/runimo/records/service/RecordFinder.java +++ b/src/main/java/org/runimo/runimo/records/service/RecordFinder.java @@ -7,7 +7,9 @@ import org.runimo.runimo.records.domain.RunningRecord; import org.runimo.runimo.records.repository.RecordRepository; import org.runimo.runimo.records.service.dto.DailyStat; +import org.runimo.runimo.records.service.dto.RecordSimpleView; import org.springframework.data.domain.PageRequest; +import org.springframework.data.domain.Pageable; import org.springframework.data.domain.Sort; import org.springframework.stereotype.Component; import org.springframework.transaction.annotation.Transactional; @@ -54,4 +56,14 @@ public List findDailyStatByUserIdBetween(Long id, LocalDateTime from, LocalDateTime to) { return recordRepository.findDailyDistanceByUserIdAndThisWeek(id, from, to); } + + @Transactional(readOnly = true) + public List findRecordSimpleViewByUserId(Long id, int page, int size) { + Pageable pageable = PageRequest.of(page, size); + return recordRepository + .findRecordByUserIdOrderByStartedAtDesc(id, pageable) + .stream() + .map(RecordSimpleView::from) + .toList(); + } } diff --git a/src/main/java/org/runimo/runimo/records/service/dto/RecordSimpleView.java b/src/main/java/org/runimo/runimo/records/service/dto/RecordSimpleView.java new file mode 100644 index 00000000..451cba97 --- /dev/null +++ b/src/main/java/org/runimo/runimo/records/service/dto/RecordSimpleView.java @@ -0,0 +1,45 @@ +package org.runimo.runimo.records.service.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.time.LocalDateTime; +import lombok.AccessLevel; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import org.runimo.runimo.records.domain.RunningRecord; + +@Schema(description = "기록 요약 정보") +@Getter +@NoArgsConstructor(access = AccessLevel.PROTECTED) +public class RecordSimpleView { + @Schema(description = "기록 ID", example = "UUID-String") + private String id; + private String title; + private LocalDateTime startDateTime; + private Long distanceInMeters; + private Long durationInSeconds; + private Long averagePaceInMiliseconds; + + @Builder + private RecordSimpleView(String id, String title, LocalDateTime startDateTime, + Long distanceInMeters, + Long durationInSeconds, Long averagePaceInMiliseconds) { + this.id = id; + this.title = title; + this.startDateTime = startDateTime; + this.distanceInMeters = distanceInMeters; + this.durationInSeconds = durationInSeconds; + this.averagePaceInMiliseconds = averagePaceInMiliseconds; + } + + public static RecordSimpleView from(RunningRecord runningRecord) { + return RecordSimpleView.builder() + .id(runningRecord.getRecordPublicId()) + .title(runningRecord.getTitle()) + .startDateTime(runningRecord.getStartedAt()) + .distanceInMeters(runningRecord.getTotalDistance().getAmount()) + .durationInSeconds(runningRecord.getRunningTime().getSeconds()) + .averagePaceInMiliseconds(runningRecord.getAveragePace().getPaceInMilliSeconds()) + .build(); + } +} diff --git a/src/main/java/org/runimo/runimo/records/service/dto/RecordSimpleViewResponse.java b/src/main/java/org/runimo/runimo/records/service/dto/RecordSimpleViewResponse.java new file mode 100644 index 00000000..b7f40c57 --- /dev/null +++ b/src/main/java/org/runimo/runimo/records/service/dto/RecordSimpleViewResponse.java @@ -0,0 +1,9 @@ +package org.runimo.runimo.records.service.dto; + +import io.swagger.v3.oas.annotations.media.Schema; +import java.util.List; + +@Schema(description = "기록 요약 정보 응답 DTO") +public record RecordSimpleViewResponse(List recordList) { + +} diff --git a/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecase.java b/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecase.java index d925db7c..2ffaf301 100644 --- a/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecase.java +++ b/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecase.java @@ -1,5 +1,6 @@ package org.runimo.runimo.records.service.usecases; +import org.runimo.runimo.records.service.dto.RecordSimpleViewResponse; import org.runimo.runimo.records.service.dto.WeeklyRecordStatResponse; import org.runimo.runimo.records.service.dto.WeeklyStatQuery; import org.runimo.runimo.records.service.usecases.dtos.MonthlyRecordStatResponse; @@ -13,4 +14,6 @@ public interface RecordQueryUsecase { WeeklyRecordStatResponse getUserWeeklyRecordStat(WeeklyStatQuery query); MonthlyRecordStatResponse getUserMonthlyRecordStat(MonthlyStatQuery query); + + RecordSimpleViewResponse getUserRecordSimpleView(Long id, int page, int size); } diff --git a/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecaseImpl.java b/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecaseImpl.java index 1e2e3194..660e1460 100644 --- a/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecaseImpl.java +++ b/src/main/java/org/runimo/runimo/records/service/usecases/RecordQueryUsecaseImpl.java @@ -8,6 +8,8 @@ import org.runimo.runimo.records.domain.RunningRecord; import org.runimo.runimo.records.service.RecordFinder; import org.runimo.runimo.records.service.dto.DailyStat; +import org.runimo.runimo.records.service.dto.RecordSimpleView; +import org.runimo.runimo.records.service.dto.RecordSimpleViewResponse; import org.runimo.runimo.records.service.dto.WeeklyRecordStatResponse; import org.runimo.runimo.records.service.dto.WeeklyStatQuery; import org.runimo.runimo.records.service.usecases.dtos.MonthlyRecordStatResponse; @@ -50,4 +52,11 @@ public MonthlyRecordStatResponse getUserMonthlyRecordStat(MonthlyStatQuery query ); return new MonthlyRecordStatResponse(dailyDistances); } + + @Override + public RecordSimpleViewResponse getUserRecordSimpleView(Long id, int page, int size) { + List recordSimpleViews = recordFinder.findRecordSimpleViewByUserId(id, + page, size); + return new RecordSimpleViewResponse(recordSimpleViews); + } } diff --git a/src/test/java/org/runimo/runimo/records/api/RecordAcceptanceTest.java b/src/test/java/org/runimo/runimo/records/api/RecordAcceptanceTest.java index 2b21ffdd..ac9f0fa5 100644 --- a/src/test/java/org/runimo/runimo/records/api/RecordAcceptanceTest.java +++ b/src/test/java/org/runimo/runimo/records/api/RecordAcceptanceTest.java @@ -20,6 +20,7 @@ import org.springframework.beans.factory.annotation.Autowired; import org.springframework.boot.test.context.SpringBootTest; import org.springframework.boot.test.web.server.LocalServerPort; +import org.springframework.http.HttpStatus; import org.springframework.security.test.context.support.WithMockUser; import org.springframework.test.context.ActiveProfiles; import org.springframework.test.context.jdbc.Sql; @@ -261,5 +262,24 @@ void tearDown() { .log().all() .statusCode(401); } + + @Test + @Sql(scripts = "/sql/weekly_record_data.sql", executionPhase = Sql.ExecutionPhase.BEFORE_TEST_METHOD) + void 사용자_기록_페이지네이션_조회() { + + String token = AUTH_HEADER_PREFIX + jwtTokenFactory.generateAccessToken(USER_UUID); + + given() + .contentType(ContentType.JSON) + .header("Authorization", token) + .param("page", 0) + .param("size", 5) + .when() + .get("/api/v1/records/me") + .then() + .log().all() + .statusCode(HttpStatus.OK.value()) + .body("payload.record_list.size()", equalTo(5)); + } }