Skip to content

Commit

Permalink
Feat: running record id로 러닝 기록 조회 api 추가 (#226)
Browse files Browse the repository at this point in the history
  • Loading branch information
Jaewon-pro authored Sep 25, 2024
1 parent 19acb06 commit 758f1fd
Show file tree
Hide file tree
Showing 4 changed files with 160 additions and 14 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -26,20 +26,13 @@
import com.dnd.runus.global.exception.NotFoundException;
import com.dnd.runus.presentation.v1.running.dto.request.RunningRecordRequest;
import com.dnd.runus.presentation.v1.running.dto.request.RunningRecordWeeklySummaryType;
import com.dnd.runus.presentation.v1.running.dto.response.RunningRecordAddResultResponse;
import com.dnd.runus.presentation.v1.running.dto.response.RunningRecordMonthlySummaryResponse;
import com.dnd.runus.presentation.v1.running.dto.response.RunningRecordSummaryResponse;
import com.dnd.runus.presentation.v1.running.dto.response.RunningRecordWeeklySummaryResponse;
import com.dnd.runus.presentation.v1.running.dto.response.*;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.DayOfWeek;
import java.time.LocalDate;
import java.time.OffsetDateTime;
import java.time.ZoneId;
import java.time.ZoneOffset;
import java.time.temporal.ChronoUnit;
import java.time.*;
import java.util.List;

import static com.dnd.runus.global.constant.MetricsConversionFactor.METERS_IN_A_KILOMETER;
Expand Down Expand Up @@ -83,6 +76,15 @@ public RunningRecordService(
this.defaultZoneOffset = defaultZoneOffset;
}

@Transactional(readOnly = true)
public RunningRecordQueryResponse getRunningRecord(long memberId, long runningRecordId) {
RunningRecord record = runningRecordRepository
.findById(runningRecordId)
.filter(r -> r.member().memberId() == memberId)
.orElseThrow(() -> new NotFoundException(RunningRecord.class, runningRecordId));
return RunningRecordQueryResponse.from(record);
}

@Transactional(readOnly = true)
public List<LocalDate> getRunningRecordDates(long memberId, int year, int month) {
OffsetDateTime from = LocalDate.of(year, month, 1).atStartOfDay().atOffset(defaultZoneOffset);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,7 @@
import com.dnd.runus.presentation.annotation.MemberId;
import com.dnd.runus.presentation.v1.running.dto.request.RunningRecordRequest;
import com.dnd.runus.presentation.v1.running.dto.request.RunningRecordWeeklySummaryType;
import com.dnd.runus.presentation.v1.running.dto.response.RunningRecordAddResultResponse;
import com.dnd.runus.presentation.v1.running.dto.response.RunningRecordMonthlyDatesResponse;
import com.dnd.runus.presentation.v1.running.dto.response.RunningRecordMonthlySummaryResponse;
import com.dnd.runus.presentation.v1.running.dto.response.RunningRecordSummaryResponse;
import com.dnd.runus.presentation.v1.running.dto.response.RunningRecordWeeklySummaryResponse;
import com.dnd.runus.presentation.v1.running.dto.response.*;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
Expand All @@ -28,6 +24,12 @@
public class RunningRecordController {
private final RunningRecordService runningRecordService;

@GetMapping("{runningRecordId}")
@Operation(summary = "러닝 기록 상세 조회", description = "RunngingRecord id로 러닝 상세 기록을 조회합니다.")
public RunningRecordQueryResponse getRunningRecord(@MemberId long memberId, @PathVariable long runningRecordId) {
return runningRecordService.getRunningRecord(memberId, runningRecordId);
}

@GetMapping("monthly-dates")
@Operation(summary = "해당 월의 러닝 기록 조회", description = "해당 월의 러닝 기록을 조회합니다. 해당 월에 러닝 기록이 있는 날짜를 반환합니다.")
public RunningRecordMonthlyDatesResponse getRunningRecordDates(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
package com.dnd.runus.presentation.v1.running.dto.response;

import com.dnd.runus.domain.challenge.achievement.ChallengeAchievement;
import com.dnd.runus.domain.goalAchievement.GoalAchievement;
import com.dnd.runus.domain.running.RunningRecord;
import com.dnd.runus.global.constant.RunningEmoji;
import com.dnd.runus.presentation.v1.running.dto.ChallengeDto;
import com.dnd.runus.presentation.v1.running.dto.GoalResultDto;
import com.dnd.runus.presentation.v1.running.dto.RunningRecordMetricsDto;
import com.dnd.runus.presentation.v1.running.dto.request.RunningAchievementMode;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;

import java.time.LocalDateTime;

public record RunningRecordQueryResponse(
long runningRecordId,
@Schema(description = "러닝 시작 시간")
LocalDateTime startAt,
@Schema(description = "러닝 종료 시간")
LocalDateTime endAt,
@NotNull
@Schema(description = "감정 표현, very-good: 최고, good: 좋음, soso: 보통, bad: 나쁨, very-bad: 최악")
RunningEmoji emotion,
@NotNull
@Schema(description = "달성 모드, normal: 일반(목표 설정 X), challenge: 챌린지, goal: 목표")
RunningAchievementMode achievementMode,
@Schema(description = "챌린지 정보, achievementMode가 challenge인 경우에만 값이 존재합니다.")
ChallengeDto challenge,
@Schema(description = "목표 결과 정보, achievementMode가 goal인 경우에만 값이 존재합니다.")
GoalResultDto goal,
@NotNull
RunningRecordMetricsDto runningData
) {
public static RunningRecordQueryResponse from(RunningRecord runningRecord) {
return buildResponse(runningRecord, null, null, RunningAchievementMode.NORMAL);
}

public static RunningRecordQueryResponse of(RunningRecord runningRecord, ChallengeAchievement achievement) {
return buildResponse(runningRecord,
new ChallengeDto(
achievement.challenge().challengeId(),
achievement.challenge().name(),
achievement.description(),
achievement.challenge().imageUrl(),
achievement.isSuccess()
),
null,
RunningAchievementMode.CHALLENGE
);
}

public static RunningRecordQueryResponse of(RunningRecord runningRecord, GoalAchievement achievement) {
return buildResponse(runningRecord,
null,
new GoalResultDto(
achievement.getTitle(),
achievement.getDescription(),
achievement.getIconUrl(),
achievement.isAchieved()
),
RunningAchievementMode.GOAL
);
}

private static RunningRecordQueryResponse buildResponse(RunningRecord runningRecord, ChallengeDto challenge, GoalResultDto goal, RunningAchievementMode achievementMode) {
return new RunningRecordQueryResponse(
runningRecord.runningId(),
runningRecord.startAt().toLocalDateTime(),
runningRecord.endAt().toLocalDateTime(),
runningRecord.emoji(),
achievementMode,
challenge,
goal,
new RunningRecordMetricsDto(
runningRecord.averagePace(),
runningRecord.duration(),
runningRecord.distanceMeter(),
runningRecord.calorie()
)
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -20,12 +20,14 @@
import com.dnd.runus.domain.scale.ScaleRepository;
import com.dnd.runus.global.constant.MemberRole;
import com.dnd.runus.global.constant.RunningEmoji;
import com.dnd.runus.global.exception.NotFoundException;
import com.dnd.runus.presentation.v1.running.dto.RunningRecordMetricsDto;
import com.dnd.runus.presentation.v1.running.dto.request.RunningAchievementMode;
import com.dnd.runus.presentation.v1.running.dto.request.RunningRecordRequest;
import com.dnd.runus.presentation.v1.running.dto.request.RunningRecordWeeklySummaryType;
import com.dnd.runus.presentation.v1.running.dto.response.RunningRecordAddResultResponse;
import com.dnd.runus.presentation.v1.running.dto.response.RunningRecordMonthlySummaryResponse;
import com.dnd.runus.presentation.v1.running.dto.response.RunningRecordQueryResponse;
import com.dnd.runus.presentation.v1.running.dto.response.RunningRecordWeeklySummaryResponse;
import org.junit.jupiter.api.BeforeEach;
import org.junit.jupiter.api.DisplayName;
Expand Down Expand Up @@ -96,6 +98,63 @@ void setUp() {
defaultZoneOffset);
}

@Test
@DisplayName("러닝 기록 조회 - 존재하는 러닝 기록 조회")
void getRunningRecord() {
// given
long memberId = 1;
long runningRecordId = 1;
Member member = new Member(memberId, MemberRole.USER, "nickname1", OffsetDateTime.now(), OffsetDateTime.now());
RunningRecord runningRecord = new RunningRecord(
1L,
member,
10_000,
Duration.ofSeconds(10_000),
500,
new Pace(5, 30),
OffsetDateTime.now(),
OffsetDateTime.now(),
List.of(new Coordinate(0, 0, 0), new Coordinate(0, 0, 0)),
"start location",
"end location",
RunningEmoji.VERY_GOOD);

given(runningRecordRepository.findById(runningRecordId)).willReturn(Optional.of(runningRecord));

// when
RunningRecordQueryResponse result = runningRecordService.getRunningRecord(memberId, runningRecordId);

// then
assertEquals(runningRecordId, result.runningRecordId());
assertEquals(runningRecord.emoji(), result.emotion());
}

@Test
@DisplayName("러닝 기록 조회 - 존재하지 않는 러닝 기록 조회한다면 NotFoundException을 던진다.")
void getRunningRecord_not_found() {
// given
long memberId = 1;
long runningRecordId = 1;

given(runningRecordRepository.findById(runningRecordId)).willReturn(Optional.empty());

// when & then
assertThrows(NotFoundException.class, () -> runningRecordService.getRunningRecord(memberId, runningRecordId));
}

@Test
@DisplayName("러닝 기록 조회 - 존재하지 않는 멤버의 러닝 기록 조회한다면 NotFoundException을 던진다.")
void getRunningRecord_member_not_found() {
// given
long memberId = 1;
long runningRecordId = 1;

given(runningRecordRepository.findById(runningRecordId)).willReturn(Optional.empty());

// when & then
assertThrows(NotFoundException.class, () -> runningRecordService.getRunningRecord(memberId, runningRecordId));
}

@Test
@DisplayName("CHALLENGE 모드의 러닝 기록 추가 요청시, challengeId에 해당하는 챌린지가 있을 경우, 정상적으로 러닝 기록이 추가된다.")
void addRunningRecord_challenge() {
Expand Down

0 comments on commit 758f1fd

Please sign in to comment.