Skip to content

Commit

Permalink
Feat: 이번달 러닝 서머리 V2 API 추가 (#300)
Browse files Browse the repository at this point in the history
* Refactor: 이번달 러닝 서머리 V2를 위한 코드 리팩터링(임시)

- V2, V1 공통 리스펀스 DTO RunningRecordMonthlySummaryResponse로 지정(이번달, 이번달 달린 거리)
- V1 리스펀스 추가
- 서비스 로직 변경
- 컨트롤러에서 값 가공후 V1으로 리턴

* Feat: running-records/monthly-summary의 V2 추가

- 현재 달리고 있는 지구 한바퀴 코스에 대한 퍼센테이지 값 조회하는 서비스단구현(임시)
- 컨트롤러 생성
- ResponseDto V2 생성
  • Loading branch information
hee9841 authored Nov 8, 2024
1 parent 9c69238 commit f134e93
Show file tree
Hide file tree
Showing 8 changed files with 230 additions and 55 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -13,10 +13,7 @@
import com.dnd.runus.domain.common.Pace;
import com.dnd.runus.domain.goalAchievement.GoalAchievement;
import com.dnd.runus.domain.goalAchievement.GoalAchievementRepository;
import com.dnd.runus.domain.level.Level;
import com.dnd.runus.domain.member.Member;
import com.dnd.runus.domain.member.MemberLevel;
import com.dnd.runus.domain.member.MemberLevelRepository;
import com.dnd.runus.domain.member.MemberRepository;
import com.dnd.runus.domain.running.DailyRunningRecordSummary;
import com.dnd.runus.domain.running.RunningRecord;
Expand Down Expand Up @@ -47,7 +44,6 @@
public class RunningRecordService {
private final RunningRecordRepository runningRecordRepository;
private final MemberRepository memberRepository;
private final MemberLevelRepository memberLevelRepository;
private final ChallengeRepository challengeRepository;
private final ChallengeAchievementRepository challengeAchievementRepository;
private final ChallengeAchievementPercentageRepository percentageValuesRepository;
Expand All @@ -61,7 +57,6 @@ public class RunningRecordService {
public RunningRecordService(
RunningRecordRepository runningRecordRepository,
MemberRepository memberRepository,
MemberLevelRepository memberLevelRepository,
ChallengeRepository challengeRepository,
ChallengeAchievementRepository challengeAchievementRepository,
ChallengeAchievementPercentageRepository percentageValuesRepository,
Expand All @@ -70,7 +65,6 @@ public RunningRecordService(
@Value("${app.default-zone-offset}") ZoneOffset defaultZoneOffset) {
this.runningRecordRepository = runningRecordRepository;
this.memberRepository = memberRepository;
this.memberLevelRepository = memberLevelRepository;
this.challengeRepository = challengeRepository;
this.challengeAchievementRepository = challengeAchievementRepository;
this.percentageValuesRepository = percentageValuesRepository;
Expand Down Expand Up @@ -223,22 +217,11 @@ public RunningRecordMonthlySummaryResponse getMonthlyRunningSummery(long memberI
OffsetDateTime startDateOfMonth =
OffsetDateTime.now(ZoneId.of(SERVER_TIMEZONE)).withDayOfMonth(1).truncatedTo(ChronoUnit.DAYS);
OffsetDateTime startDateOfNextMonth = startDateOfMonth.plusMonths(1);

int monthValue = startDateOfMonth.getMonthValue();

int monthlyTotalDistance = runningRecordRepository.findTotalDistanceMeterByMemberIdWithRangeDate(
memberId, startDateOfMonth, startDateOfNextMonth);

MemberLevel.Current currentMemberLevel = memberLevelRepository.findByMemberIdWithLevel(memberId);

long nextLevel = currentMemberLevel.level().levelId() + 1;
int remainingKmToNextLevel = currentMemberLevel.level().expRangeEnd() - currentMemberLevel.currentExp();

return new RunningRecordMonthlySummaryResponse(
monthValue,
monthlyTotalDistance,
Level.formatLevelName(nextLevel),
Level.formatExp(remainingKmToNextLevel));
return RunningRecordMonthlySummaryResponse.builder()
.month(startDateOfMonth.getMonthValue())
.monthlyTotalMeter(runningRecordRepository.findTotalDistanceMeterByMemberIdWithRangeDate(
memberId, startDateOfMonth, startDateOfNextMonth))
.build();
}

private ChallengeAchievement handleChallengeMode(Long challengeId, long memberId, RunningRecord runningRecord) {
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,84 @@
package com.dnd.runus.application.running;

import com.dnd.runus.domain.running.RunningRecordRepository;
import com.dnd.runus.domain.scale.ScaleAchievementLog;
import com.dnd.runus.domain.scale.ScaleAchievementRepository;
import com.dnd.runus.presentation.v2.running.dto.response.RunningRecordMonthlySummaryResponseV2;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.text.DecimalFormat;
import java.util.List;

import static com.dnd.runus.global.constant.MetricsConversionFactor.METERS_IN_A_KILOMETER;

@Service
public class RunningRecordServiceV2 {

private final RunningRecordRepository runningRecordRepository;
private final ScaleAchievementRepository scaleAchievementRepository;

private static final DecimalFormat KILO_METER_UNDER_1_POINT_FORMATTER = new DecimalFormat("#,###.#km");

public RunningRecordServiceV2(
RunningRecordRepository runningRecordRepository, ScaleAchievementRepository scaleAchievementRepository) {
this.runningRecordRepository = runningRecordRepository;
this.scaleAchievementRepository = scaleAchievementRepository;
}

// todo 지구 한바퀴 작업 후 리팩터링 예정
@Transactional(readOnly = true)
public RunningRecordMonthlySummaryResponseV2.PercentageBoxWithMessage getPercentageValues(long memberId) {
// 지구한바퀴 코스 조회
List<ScaleAchievementLog> scaleAchievementLogs = scaleAchievementRepository.findScaleAchievementLogs(memberId);
ScaleAchievementLog currentCourseLog = scaleAchievementLogs.stream()
.filter(log -> log.achievedDate() == null)
.findFirst()
.orElse(null);

// todo 정해지면 하드코드 수정
if (currentCourseLog == null) {
return RunningRecordMonthlySummaryResponseV2.PercentageBoxWithMessage.builder()
.message("축하합니다! 지구 한바퀴 완주하셨네요!")
.percentageBox(RunningRecordMonthlySummaryResponseV2.PercentageBox.builder()
.startName("한국")
.endName("지구 한바퀴")
.percentage(100)
.build())
.build();
}

// 사용자가 달린 전체 거리 확인
int totalRunningDistanceMeter = runningRecordRepository.findTotalDistanceMeterByMemberId(memberId);
// 성취한 코스의 전체 합 거리
int achievedCourseMeterSum = scaleAchievementLogs.stream()
.filter(log -> log.achievedDate() != null)
.mapToInt(log -> log.scale().sizeMeter())
.sum();

// 현재 코스에서 사용자가 성취한 거리 합
double currentCourseAchievedKmSum =
(totalRunningDistanceMeter - achievedCourseMeterSum) / METERS_IN_A_KILOMETER;
// 현재 코스 완주를 위해 필요한 키로미터 값
double courseSizeKm = currentCourseLog.scale().sizeMeter() / METERS_IN_A_KILOMETER;

double percentage = calPercentage(courseSizeKm, currentCourseAchievedKmSum);

return RunningRecordMonthlySummaryResponseV2.PercentageBoxWithMessage.builder()
.message(String.format(
"%s까지 %s 남았어요!",
currentCourseLog.scale().endName(),
KILO_METER_UNDER_1_POINT_FORMATTER.format(courseSizeKm - currentCourseAchievedKmSum)))
.percentageBox(RunningRecordMonthlySummaryResponseV2.PercentageBox.builder()
.startName(currentCourseLog.scale().startName())
.endName(currentCourseLog.scale().endName())
.percentage(percentage)
.build())
.build();
}

private double calPercentage(double totalRangeValue, double currentValue) {
if (totalRangeValue <= 0) return 0;
return currentValue / totalRangeValue;
}
}
Original file line number Diff line number Diff line change
@@ -1,9 +1,12 @@
package com.dnd.runus.presentation.v1.running;

import com.dnd.runus.application.member.MemberService;
import com.dnd.runus.application.running.RunningRecordService;
import com.dnd.runus.domain.level.Level;
import com.dnd.runus.global.exception.type.ApiErrorType;
import com.dnd.runus.global.exception.type.ErrorType;
import com.dnd.runus.presentation.annotation.MemberId;
import com.dnd.runus.presentation.v1.member.dto.response.MyProfileResponse;
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.*;
Expand All @@ -23,6 +26,7 @@
@RequestMapping("/api/v1/running-records")
public class RunningRecordController {
private final RunningRecordService runningRecordService;
private final MemberService memberService;

@GetMapping("/{runningRecordId}")
@Operation(summary = "러닝 기록 상세 조회", description = "RunngingRecord id로 러닝 상세 기록을 조회합니다.")
Expand Down Expand Up @@ -76,8 +80,17 @@ public RunningRecordAddResultResponse addRunningRecord(
""")
@ResponseStatus(HttpStatus.OK)
@GetMapping("monthly-summary")
public RunningRecordMonthlySummaryResponse getMonthlyRunningSummary(@MemberId long memberId) {
return runningRecordService.getMonthlyRunningSummery(memberId);
public RunningRecordMonthlySummaryResponseV1 getMonthlyRunningSummary(@MemberId long memberId) {
RunningRecordMonthlySummaryResponse monthlyRunningSummery =
runningRecordService.getMonthlyRunningSummery(memberId);

MyProfileResponse myProfile = memberService.getMyProfile(memberId);

return new RunningRecordMonthlySummaryResponseV1(
monthlyRunningSummery.month(),
monthlyRunningSummery.monthlyTotalMeter(),
Level.formatLevelName(myProfile.nextLevel()),
Level.formatExp(myProfile.nextLevelEndExpMeter() - myProfile.currentExpMeter()));
}

@Operation(
Expand Down
Original file line number Diff line number Diff line change
@@ -1,24 +1,13 @@
package com.dnd.runus.presentation.v1.running.dto.response;

import static com.dnd.runus.global.constant.MetricsConversionFactor.METERS_IN_A_KILOMETER;

import io.swagger.v3.oas.annotations.media.Schema;

import java.text.DecimalFormat;
import lombok.Builder;

//todo API 버저닝 관련 구조 정해지면 패키지 변경 예쩡
@Builder
public record RunningRecordMonthlySummaryResponse(
@Schema(description = "이번 달", example = "8월")
String month,
@Schema(description = "이번 달에 달린 키로 수", example = "2.55km")
String monthlyKm,
@Schema(description = "다음 레벨", example = "Level 2")
String nextLevelName,
@Schema(description = "다음 레벨까지 남은 키로 수", example = "2.55km")
String nextLevelKm
int month,
int monthlyTotalMeter
) {
private static final DecimalFormat KILO_METER_FORMATTER = new DecimalFormat("0.##km");

public RunningRecordMonthlySummaryResponse(int monthValue, int monthlyTotalMeter, String nextLevelName, String nextLevelKm) {
this(monthValue + "월", KILO_METER_FORMATTER.format(monthlyTotalMeter / METERS_IN_A_KILOMETER), nextLevelName, nextLevelKm);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.dnd.runus.presentation.v1.running.dto.response;

import static com.dnd.runus.global.constant.MetricsConversionFactor.METERS_IN_A_KILOMETER;

import io.swagger.v3.oas.annotations.media.Schema;
import java.text.DecimalFormat;

public record RunningRecordMonthlySummaryResponseV1(
@Schema(description = "이번 달", example = "8월")
String month,
@Schema(description = "이번 달에 달린 키로 수", example = "2.55km")
String monthlyKm,
@Schema(description = "다음 레벨", example = "Level 2")
String nextLevelName,
@Schema(description = "다음 레벨까지 남은 키로 수", example = "2.55km")
String nextLevelKm
) {
private static final DecimalFormat KILO_METER_FORMATTER = new DecimalFormat("0.##km");

public RunningRecordMonthlySummaryResponseV1(int monthValue, int monthlyTotalMeter, String nextLevelName, String nextLevelKm) {
this(monthValue + "월", KILO_METER_FORMATTER.format(monthlyTotalMeter / METERS_IN_A_KILOMETER), nextLevelName, nextLevelKm);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
package com.dnd.runus.presentation.v2.running;

import com.dnd.runus.application.running.RunningRecordService;
import com.dnd.runus.application.running.RunningRecordServiceV2;
import com.dnd.runus.presentation.annotation.MemberId;
import com.dnd.runus.presentation.v1.running.dto.response.RunningRecordMonthlySummaryResponse;
import com.dnd.runus.presentation.v2.running.dto.response.RunningRecordMonthlySummaryResponseV2;
import io.swagger.v3.oas.annotations.Operation;
import lombok.RequiredArgsConstructor;
import org.springframework.http.HttpStatus;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.ResponseStatus;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequiredArgsConstructor
@RequestMapping("/api/v2/running-records")
public class RunningRecordControllerV2 {
private final RunningRecordServiceV2 runningRecordService2;
private final RunningRecordService runningRecordService;

@Operation(summary = "이번 달 러닝 기록 조회(홈화면) V2", description = """
홈화면의 이번 달 러닝 기록을 조회 합니다.<br>
""")
@ResponseStatus(HttpStatus.OK)
@GetMapping("monthly-summary")
public RunningRecordMonthlySummaryResponseV2 getMonthlyRunningSummary(@MemberId long memberId) {
RunningRecordMonthlySummaryResponse monthlyRunningSummery =
runningRecordService.getMonthlyRunningSummery(memberId);
return new RunningRecordMonthlySummaryResponseV2(
monthlyRunningSummery.month(),
monthlyRunningSummery.monthlyTotalMeter(),
runningRecordService2.getPercentageValues(memberId));
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,59 @@
package com.dnd.runus.presentation.v2.running.dto.response;


import static com.dnd.runus.global.constant.MetricsConversionFactor.METERS_IN_A_KILOMETER;

import io.swagger.v3.oas.annotations.media.Schema;
import java.text.DecimalFormat;
import lombok.Builder;


public record RunningRecordMonthlySummaryResponseV2(
@Schema(description = "이번 달", example = "8월")
String month,
@Schema(description = "이번 달에 달린 키로 수", example = "2.55km")
String monthlyKm,
@Schema(description = "서브 메세지", example = "대전까지 00km 남았어요!")
String message,
@Schema(description = "퍼센테이지 시작 위치 이름", example = "인천")
String startName,
@Schema(description = "퍼센테이지 종료 위치 이름", example = "대전")
String endName,
@Schema(description = "퍼센테이지값", example = "0.728")
double percentage
) {

private static final DecimalFormat KILO_METER_FORMATTER = new DecimalFormat("#,###.##km");

public RunningRecordMonthlySummaryResponseV2(
int monthValue,
int monthlyTotalMeter,
PercentageBoxWithMessage percentageBoxWithMessage) {
this(
monthValue + "월",
KILO_METER_FORMATTER.format(monthlyTotalMeter / METERS_IN_A_KILOMETER),
percentageBoxWithMessage.message,
percentageBoxWithMessage.percentageBox.startName,
percentageBoxWithMessage.percentageBox.endName,
percentageBoxWithMessage.percentageBox.percentage
);
}

@Schema(name = "RunningRecordMonthlySummaryResponseV2 PercentageBox",
description = "이번달 러닝 서머리 퍼센테이지 관련 값")
@Builder
public record PercentageBox(
String startName,
String endName,
double percentage
) {
}

@Builder
public record PercentageBoxWithMessage(
String message,
RunningRecordMonthlySummaryResponseV2.PercentageBox percentageBox
) {
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -8,10 +8,7 @@
import com.dnd.runus.domain.common.Pace;
import com.dnd.runus.domain.goalAchievement.GoalAchievement;
import com.dnd.runus.domain.goalAchievement.GoalAchievementRepository;
import com.dnd.runus.domain.level.Level;
import com.dnd.runus.domain.member.Member;
import com.dnd.runus.domain.member.MemberLevel;
import com.dnd.runus.domain.member.MemberLevelRepository;
import com.dnd.runus.domain.member.MemberRepository;
import com.dnd.runus.domain.running.DailyRunningRecordSummary;
import com.dnd.runus.domain.running.RunningRecord;
Expand Down Expand Up @@ -60,9 +57,6 @@ class RunningRecordServiceTest {
@Mock
private MemberRepository memberRepository;

@Mock
private MemberLevelRepository memberLevelRepository;

@Mock
private ChallengeRepository challengeRepository;

Expand All @@ -85,7 +79,6 @@ void setUp() {
runningRecordService = new RunningRecordService(
runningRecordRepository,
memberRepository,
memberLevelRepository,
challengeRepository,
challengeAchievementRepository,
percentageValuesRepository,
Expand Down Expand Up @@ -416,7 +409,7 @@ void addRunningRecord_check_cal_pace() {
}

@Test
@DisplayName("이번 달, 달린 키로 수, 러닝 레벨을 조회한다.")
@DisplayName("이번 달, 달린 키로수를 조회한다.")
void getMonthlyRunningSummery() {
// given
long memberId = 1;
Expand All @@ -430,19 +423,14 @@ void getMonthlyRunningSummery() {
given(runningRecordRepository.findTotalDistanceMeterByMemberIdWithRangeDate(eq(memberId), any(), any()))
.willReturn(45_780);

given(memberLevelRepository.findByMemberIdWithLevel(memberId))
.willReturn(new MemberLevel.Current(new Level(1, 0, 50_000, "image"), 45_780));

// when
RunningRecordMonthlySummaryResponse monthlyRunningSummery =
runningRecordService.getMonthlyRunningSummery(memberId);

// then
assertNotNull(monthlyRunningSummery);
assertThat(monthlyRunningSummery.month()).isEqualTo("1월");
assertThat(monthlyRunningSummery.monthlyKm()).isEqualTo("45.78km");
assertThat(monthlyRunningSummery.nextLevelName()).isEqualTo("Level 2");
assertThat(monthlyRunningSummery.nextLevelKm()).isEqualTo("4.22km");
assertThat(monthlyRunningSummery.month()).isEqualTo(1);
assertThat(monthlyRunningSummery.monthlyTotalMeter()).isEqualTo(45_780);
}
}

Expand Down

0 comments on commit f134e93

Please sign in to comment.