Skip to content
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: 챌린지 및 목표 응답 DTO 통합 #326

Merged
merged 10 commits into from
Dec 26, 2024
Merged
Show file tree
Hide file tree
Changes from all 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 @@ -3,7 +3,9 @@
import com.dnd.runus.application.running.dto.RunningResultDto;
import com.dnd.runus.application.running.event.RunningRecordAddedEvent;
import com.dnd.runus.domain.challenge.Challenge;
import com.dnd.runus.domain.challenge.ChallengeCondition;
import com.dnd.runus.domain.challenge.ChallengeRepository;
import com.dnd.runus.domain.challenge.ChallengeType;
import com.dnd.runus.domain.challenge.ChallengeWithCondition;
import com.dnd.runus.domain.challenge.GoalMetricType;
import com.dnd.runus.domain.challenge.achievement.ChallengeAchievement;
Expand All @@ -19,13 +21,15 @@
import com.dnd.runus.domain.running.DailyRunningRecordSummary;
import com.dnd.runus.domain.running.RunningRecord;
import com.dnd.runus.domain.running.RunningRecordRepository;
import com.dnd.runus.global.exception.BusinessException;
import com.dnd.runus.global.exception.NotFoundException;
import com.dnd.runus.global.exception.type.ErrorType;
import com.dnd.runus.presentation.v1.running.dto.WeeklyRunningRatingDto;
import com.dnd.runus.presentation.v1.running.dto.request.RunningAchievementMode;
import com.dnd.runus.presentation.v1.running.dto.request.RunningRecordRequestV1;
import com.dnd.runus.presentation.v1.running.dto.request.RunningRecordWeeklySummaryType;
import com.dnd.runus.presentation.v1.running.dto.response.*;
import com.dnd.runus.presentation.v2.running.dto.request.RunningRecordRequestV2;
import com.dnd.runus.presentation.v2.running.dto.request.RunningRecordRequestV2.ChallengeAchievedDto;
import com.dnd.runus.presentation.v2.running.dto.request.RunningRecordRequestV2.GoalAchievedDto;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationEventPublisher;
Expand Down Expand Up @@ -79,7 +83,7 @@ public RunningRecordService(
}

@Transactional(readOnly = true)
public RunningRecordQueryResponse getRunningRecord(long memberId, long runningRecordId) {
public RunningResultDto getRunningRecord(long memberId, long runningRecordId) {
RunningRecord runningRecord = runningRecordRepository
.findById(runningRecordId)
.filter(r -> r.member().memberId() == memberId)
Expand All @@ -97,7 +101,27 @@ public RunningRecordQueryResponse getRunningRecord(long memberId, long runningRe
.orElse(null)
: null;

return RunningRecordQueryResponse.of(runningRecord, challengeAchievement, goalAchievement);
RunningAchievementMode runningAchievementMode = (challengeAchievement != null)
? RunningAchievementMode.CHALLENGE
: (goalAchievement != null) ? RunningAchievementMode.GOAL : RunningAchievementMode.NORMAL;

switch (runningAchievementMode) {
case CHALLENGE -> {
return RunningResultDto.of(
runningRecord,
challengeAchievement,
calChallengeAchievementPercentage(memberId, challengeAchievement));
}
case GOAL -> {
int achievedGoalValue = goalAchievement.goalMetricType().getActualValue(runningRecord);
return RunningResultDto.of(
runningRecord,
goalAchievement,
calPercentage(achievedGoalValue, goalAchievement.achievementValue()));
}
}

return RunningResultDto.from(runningRecord);
}

@Transactional(readOnly = true)
Expand Down Expand Up @@ -223,16 +247,22 @@ public RunningResultDto addRunningRecordV2(long memberId, RunningRecordRequestV2

switch (request.achievementMode()) {
case CHALLENGE -> {
ChallengeAchievedDto challengeAchievedForAdd = request.challengeValues();
Challenge challenge = challengeRepository
.findById(challengeAchievedForAdd.challengeId())
.orElseThrow(
() -> new NotFoundException(Challenge.class, challengeAchievedForAdd.challengeId()));
.findById(request.challengeValues().challengeId())
.orElseThrow(() -> new NotFoundException(
Challenge.class, request.challengeValues().challengeId()));
if (!challenge.isActive()) {
throw new BusinessException(ErrorType.CHALLENGE_NOT_ACTIVE);
}

ChallengeAchievement challengeAchievement = challengeAchievementRepository.save(
new ChallengeAchievement(challenge, record, challengeAchievedForAdd.isSuccess()));
ChallengeAchievement challengeAchievement =
challengeAchievementRepository.save(new ChallengeAchievement(
challenge, record, request.challengeValues().isSuccess()));

return RunningResultDto.of(record, challengeAchievement);
return RunningResultDto.of(
record,
challengeAchievement,
calChallengeAchievementPercentage(memberId, challengeAchievement));
}
case GOAL -> {
GoalAchievedDto goalAchievedForAdd = request.goalValues();
Expand All @@ -244,7 +274,10 @@ public RunningResultDto addRunningRecordV2(long memberId, RunningRecordRequestV2
: goalAchievedForAdd.goalTime(),
goalAchievedForAdd.isSuccess()));

return RunningResultDto.of(record, goalAchievement);
int achievedGoalValue = goalAchievement.goalMetricType().getActualValue(record);

return RunningResultDto.of(
record, goalAchievement, calPercentage(achievedGoalValue, goalAchievement.achievementValue()));
}
}
return RunningResultDto.from(record);
Expand Down Expand Up @@ -337,4 +370,54 @@ private GoalAchievement handleGoalMode(RunningRecord runningRecord, Integer goal
GoalAchievement goalAchievement = new GoalAchievement(runningRecord, goalMetricType, goalValue, isAchieved);
return goalAchievementRepository.save(goalAchievement);
}

/**
* 챌린지 퍼센테이지 계산(V2 이후에서 사용)
* 어제 러닝 기록 이기기 관련 챌린지면 챌린지 목표값 재등록(기본 목표 값 + 어제 러닝 기록)한 후 계산
* @return Double(퍼센테이지 계산하기 힘든, 2 이전에 저장된 챌린지같은 경우 null로 리턴)
*/
private Double calChallengeAchievementPercentage(long memberId, ChallengeAchievement challengeAchievement) {

Challenge challenge = challengeAchievement.challenge();
ChallengeWithCondition challengeWithCondition = challengeRepository
.findChallengeWithConditionsByChallengeId(challenge.challengeId())
.orElseThrow(() -> new NotFoundException(Challenge.class, challenge.challengeId()));

// DISTANCE_IN_TIME, PACE 챌린지인지 확인
// (v2이전에 퍼센테이지를 표현할 수 없는 애들일 경우, 퍼센테이지 null로 리턴함)
if (challenge.challengeType() == ChallengeType.DISTANCE_IN_TIME
|| challengeWithCondition.conditions().stream()
.anyMatch(condition -> !condition.goalMetricType().hasPercentage())) {
return null;
}

ChallengeCondition condition = challengeWithCondition.conditions().getFirst();
RunningRecord runningRecord = challengeAchievement.runningRecord();

if (challenge.isDefeatYesterdayChallenge()) {
// 어제 러닝 기록 이기기 관련 챌린지면, 챌린지 목표값 재등록(어제 기록 + 목표값)
OffsetDateTime runningDate = runningRecord
.startAt()
.toLocalDate()
.atStartOfDay(runningRecord.startAt().getZone())
.toOffsetDateTime();

RunningRecord preRunningRecord =
runningRecordRepository
.findByMemberIdAndStartAtBetween(memberId, runningDate.minusDays(1), runningDate)
.stream()
.findFirst()
.orElseThrow(() -> new NotFoundException("이전 러닝 기록을 가져올 수 없습니다."));
condition.registerComparisonValue(condition.goalMetricType().getActualValue(preRunningRecord));
}

int achievedValue = condition.goalMetricType().getActualValue(runningRecord);
return calPercentage(achievedValue, condition.comparisonValue());
}

private double calPercentage(double part, double total) {
if (total <= 0) return 0;
double percentage = part / total;
return percentage > 1 ? 1 : percentage;
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -9,33 +9,38 @@ public record RunningResultDto(
RunningRecord runningRecord,
com.dnd.runus.presentation.v1.running.dto.request.RunningAchievementMode runningAchievementMode,
ChallengeAchievement challengeAchievement,
GoalAchievement goalAchievement
GoalAchievement goalAchievement,
Double percentage
) {
public static RunningResultDto from(RunningRecord runningRecord) {
return new RunningResultDto(
runningRecord,
com.dnd.runus.presentation.v1.running.dto.request.RunningAchievementMode.NORMAL,
null,
null,
null
);
}

public static RunningResultDto of(RunningRecord runningRecord,
ChallengeAchievement challengeAchievement) {
ChallengeAchievement challengeAchievement, Double percentage) {
return new RunningResultDto(
runningRecord,
com.dnd.runus.presentation.v1.running.dto.request.RunningAchievementMode.CHALLENGE,
challengeAchievement,
null
null,
percentage
);
}

public static RunningResultDto of(RunningRecord runningRecord, GoalAchievement goalAchievement) {
public static RunningResultDto of(RunningRecord runningRecord,
GoalAchievement goalAchievement, Double percentage) {
return new RunningResultDto(
runningRecord,
com.dnd.runus.presentation.v1.running.dto.request.RunningAchievementMode.GOAL,
null,
goalAchievement
goalAchievement,
percentage
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -50,6 +50,9 @@ public enum ErrorType {
GOAL_VALUES_REQUIRED_IN_GOAL_MODE(BAD_REQUEST, DEBUG, "RUNNING_005", "개인 목표 모드에서, 개인 목표 달성값은 필수 잆니다."),
CHALLENGE_VALUES_REQUIRED_IN_CHALLENGE_MODE(BAD_REQUEST, DEBUG, "RUNNING_006", "챌린지 모드에서, 챌린지 달성값은 필수 입니다."),

// ChallengeErrorType
CHALLENGE_NOT_ACTIVE(CONFLICT, DEBUG, "CHALLENGE_001", "해당 챌린지는 현재 활성화된 챌린지가 아닙니다."),

// WeatherErrorType
WEATHER_API_ERROR(SERVICE_UNAVAILABLE, WARN, "WEATHER_001", "날씨 API 호출 중 오류가 발생했습니다"),
;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@ public class RunningRecordController {
@GetMapping("/{runningRecordId}")
@Operation(summary = "러닝 기록 상세 조회", description = "RunngingRecord id로 러닝 상세 기록을 조회합니다.")
public RunningRecordQueryResponse getRunningRecord(@MemberId long memberId, @PathVariable long runningRecordId) {
return runningRecordService.getRunningRecord(memberId, runningRecordId);
return RunningRecordQueryResponse.from(runningRecordService.getRunningRecord(memberId, runningRecordId));
}

@GetMapping("monthly-dates")
Expand Down
Original file line number Diff line number Diff line change
@@ -1,21 +1,31 @@
package com.dnd.runus.presentation.v1.running.dto;

import com.dnd.runus.domain.challenge.achievement.ChallengeAchievement;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

public record ChallengeDto(
@Schema(description = "챌린지 ID")
long challengeId,
@NotBlank
@Schema(description = "챌린지 이름", example = "오늘 30분 동안 뛰기")
String title,
@NotBlank
@Schema(description = "챌린지 결과 문구", example = "성공했어요!")
String subTitle,
@NotBlank
@Schema(description = "챌린지 아이콘 이미지 URL")
String iconUrl,
@Schema(description = "챌린지 성공 여부")
boolean isSuccess
@Schema(description = "챌린지 ID")
long challengeId,
@NotBlank
@Schema(description = "챌린지 이름", example = "오늘 30분 동안 뛰기")
String title,
@NotBlank
@Schema(description = "챌린지 결과 문구", example = "성공했어요!")
String subTitle,
@NotBlank
@Schema(description = "챌린지 아이콘 이미지 URL")
String iconUrl,
@Schema(description = "챌린지 성공 여부")
boolean isSuccess
) {
public static ChallengeDto from(ChallengeAchievement achievement) {
return new ChallengeDto(
achievement.challenge().challengeId(),
achievement.challenge().name(),
achievement.description(),
achievement.challenge().imageUrl(),
achievement.isSuccess()
);
}
}
Original file line number Diff line number Diff line change
@@ -1,17 +1,26 @@
package com.dnd.runus.presentation.v1.running.dto;

import com.dnd.runus.domain.goalAchievement.GoalAchievement;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotBlank;

public record GoalResultDto(
@Schema(description = "설정된 목표 제목", example = "2.5km 달성")
String title,
@Schema(description = "설정된 목표 결과 문구", example = "성공했어요!")
String subTitle,
@NotBlank
@Schema(description = "챌린지 아이콘 이미지 URL")
String iconUrl,
@Schema(description = "설정된 목표 성공 여부")
boolean isSuccess
@Schema(description = "설정된 목표 제목", example = "2.5km 달성")
String title,
@Schema(description = "설정된 목표 결과 문구", example = "성공했어요!")
String subTitle,
@NotBlank
@Schema(description = "챌린지 아이콘 이미지 URL")
String iconUrl,
@Schema(description = "설정된 목표 성공 여부")
boolean isSuccess
) {
public static GoalResultDto from(GoalAchievement achievement) {
return new GoalResultDto(
achievement.getTitle(),
achievement.getDescription(),
achievement.getIconUrl(),
achievement.isAchieved()
);
}
}
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
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.application.running.dto.RunningResultDto;
import com.dnd.runus.domain.running.RunningRecord;
import com.dnd.runus.global.constant.RunningEmoji;
import com.dnd.runus.presentation.v1.running.dto.ChallengeDto;
Expand Down Expand Up @@ -32,45 +31,18 @@ public record RunningRecordQueryResponse(
@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
public static RunningRecordQueryResponse from(RunningResultDto runningResult) {
return buildResponse(
runningResult.runningRecord(),
runningResult.challengeAchievement() == null ? null
: ChallengeDto.from(runningResult.challengeAchievement()),
runningResult.goalAchievement() == null ? null
: GoalResultDto.from(runningResult.goalAchievement()),
runningResult.runningAchievementMode()
);
}

public static RunningRecordQueryResponse of(RunningRecord runningRecord, ChallengeAchievement challengeAchievement, GoalAchievement goalAchievement) {
if (challengeAchievement != null) {
return of(runningRecord, challengeAchievement);
} else if (goalAchievement != null) {
return of(runningRecord, goalAchievement);
}
return from(runningRecord);
}

private static RunningRecordQueryResponse buildResponse(RunningRecord runningRecord, ChallengeDto challenge, GoalResultDto goal, RunningAchievementMode achievementMode) {
return new RunningRecordQueryResponse(
Expand All @@ -89,4 +61,5 @@ private static RunningRecordQueryResponse buildResponse(RunningRecord runningRec
)
);
}

}
Loading