From c4e46a6c4483ad2b766a5ffa57ed3ee69e092657 Mon Sep 17 00:00:00 2001 From: BGuga Date: Sun, 8 Jun 2025 16:10:24 +0900 Subject: [PATCH] =?UTF-8?q?feat:=20=EA=B2=BD=ED=97=98=EC=B9=98=20=EB=A1=9C?= =?UTF-8?q?=EC=A7=81=20=EA=B5=AC=ED=98=84=20=EB=B0=8F=20=EC=8B=A4=ED=8C=A8?= =?UTF-8?q?=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../record/controller/RecordController.java | 3 +- .../domain/record/service/RecordService.java | 79 +++++++++++++------ .../cloud/domain/user/entity/User.java | 10 +++ .../domain/user/service/UserService.java | 18 ++--- .../userBodyPart/entity/UserBodyPart.java | 4 + .../domain/user/service/UserServiceTest.java | 39 +-------- 6 files changed, 81 insertions(+), 72 deletions(-) diff --git a/cloud/src/main/java/com/project/cloud/domain/record/controller/RecordController.java b/cloud/src/main/java/com/project/cloud/domain/record/controller/RecordController.java index 8292a15..0a9d99a 100644 --- a/cloud/src/main/java/com/project/cloud/domain/record/controller/RecordController.java +++ b/cloud/src/main/java/com/project/cloud/domain/record/controller/RecordController.java @@ -24,7 +24,6 @@ public SuccessResponse createRecord( @RequestParam int totalTime, @LoginUser String email) { - RecordResponse response = recordService.createRecord(routineId, totalTime, email); return SuccessResponse.ok(response); } @@ -37,4 +36,4 @@ public SuccessResponse> getRecordsByDate( return SuccessResponse.ok(response); } -} \ No newline at end of file +} diff --git a/cloud/src/main/java/com/project/cloud/domain/record/service/RecordService.java b/cloud/src/main/java/com/project/cloud/domain/record/service/RecordService.java index 360314b..c971af8 100644 --- a/cloud/src/main/java/com/project/cloud/domain/record/service/RecordService.java +++ b/cloud/src/main/java/com/project/cloud/domain/record/service/RecordService.java @@ -1,83 +1,116 @@ package com.project.cloud.domain.record.service; +import com.project.cloud.domain.exercise.enumerate.Target; import com.project.cloud.domain.record.dto.RecordResponse; import com.project.cloud.domain.record.entity.Record; import com.project.cloud.domain.record.repository.RecordRepository; import com.project.cloud.domain.routine.entity.Routine; import com.project.cloud.domain.routine.repository.RoutineRepository; +import com.project.cloud.domain.routineItem.entity.RoutineItem; import com.project.cloud.domain.user.entity.User; import com.project.cloud.domain.user.repository.UserRepository; +import com.project.cloud.domain.userBodyPart.entity.UserBodyPart; import com.project.cloud.global.exception.CustomException; import com.project.cloud.global.exception.ErrorCode; -import lombok.RequiredArgsConstructor; -import org.springframework.stereotype.Service; -import org.springframework.transaction.annotation.Transactional; - import java.time.LocalDate; import java.util.List; import java.util.Optional; +import lombok.RequiredArgsConstructor; +import org.springframework.context.ApplicationEventPublisher; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; @Service @RequiredArgsConstructor public class RecordService { + private final ApplicationEventPublisher applicationEventPublisher; private final RecordRepository recordRepository; private final RoutineRepository routineRepository; private final UserRepository userRepository; @Transactional - public RecordResponse createRecord(Long routineId, int totalTime, String email){ + public RecordResponse createRecord(Long routineId, int totalTime, String email) { User user = findUserByEmail(email); Routine routine = routineRepository.findById(routineId) - .orElseThrow(() -> new CustomException(ErrorCode.ROUTINE_NOT_FOUND)); + .orElseThrow(() -> new CustomException(ErrorCode.ROUTINE_NOT_FOUND)); if (!routine.getUser().getId().equals(user.getId())) { throw new CustomException(ErrorCode.ROUTINE_FORBIDDEN); } + saveExperience(user, routine); + LocalDate today = LocalDate.now(); Optional optinalRecord = recordRepository.findByUserAndRoutineAndDate(user, routine, today); Record record; - if (optinalRecord.isPresent()){ + if (optinalRecord.isPresent()) { record = optinalRecord.get(); int updateTime = record.getTotalTime() + totalTime; record.updateTotalTime(updateTime); - } - else { + } else { record = Record.create(user, routine, LocalDate.now(), totalTime); recordRepository.save(record); } - return new RecordResponse( - record.getId(), - routine.getId(), - routine.getName(), - record.getDate(), - record.getTotalTime() + record.getId(), + routine.getId(), + routine.getName(), + record.getDate(), + record.getTotalTime() ); } - @Transactional(readOnly= true) + private void saveExperience(User user, Routine routine) { + for (RoutineItem item : routine.getItems()) { + saveUserBodyExperience(user, item); + } + } + + private void saveUserBodyExperience(User user, RoutineItem item) { + UserBodyPart userBodyPart = findUserBodyPartByTarget(user.getBodyPartStats(), + item.getExercise().getTarget()); + int exp = calculateExp(item); + userBodyPart.saveExperience(exp); + user.saveExperience(exp); + } + + private UserBodyPart findUserBodyPartByTarget(List bodyPartStats, Target target) { + for (UserBodyPart bodyPartStat : bodyPartStats) { + String bodyPartName = bodyPartStat.getBodyPart().getName(); + if (bodyPartName.equals(target.name())) { + return bodyPartStat; + } + } + throw new CustomException(ErrorCode.BODYPART_NOT_FOUND); + } + + private int calculateExp(RoutineItem item) { + return item.getSetCount() * item.getRepeatCount(); + } + + + @Transactional(readOnly = true) public List getRecordsByDate(String email, LocalDate date) { User user = findUserByEmail(email); List records = recordRepository.findAllByUserAndDate(user, date); return records.stream() - .map(this::toRecordResponse) - .toList(); + .map(this::toRecordResponse) + .toList(); } private RecordResponse toRecordResponse(Record record) { return new RecordResponse( - record.getId(), - record.getRoutine().getId(), - record.getRoutine().getName(), - record.getDate(), - record.getTotalTime() + record.getId(), + record.getRoutine().getId(), + record.getRoutine().getName(), + record.getDate(), + record.getTotalTime() ); } diff --git a/cloud/src/main/java/com/project/cloud/domain/user/entity/User.java b/cloud/src/main/java/com/project/cloud/domain/user/entity/User.java index ba12ecf..4f28786 100644 --- a/cloud/src/main/java/com/project/cloud/domain/user/entity/User.java +++ b/cloud/src/main/java/com/project/cloud/domain/user/entity/User.java @@ -122,4 +122,14 @@ public void updateBodyParts(List parts) { this.bodyPartStats.clear(); this.bodyPartStats.addAll(parts); } + + public void saveExperience(int exp) { + while (this.exp >= 300 && this.level < 5) { + this.level++; + this.exp -= 300; + } + if (this.level == 5) { + this.exp = 0; + } + } } diff --git a/cloud/src/main/java/com/project/cloud/domain/user/service/UserService.java b/cloud/src/main/java/com/project/cloud/domain/user/service/UserService.java index 13737f0..0095f44 100644 --- a/cloud/src/main/java/com/project/cloud/domain/user/service/UserService.java +++ b/cloud/src/main/java/com/project/cloud/domain/user/service/UserService.java @@ -2,6 +2,7 @@ import com.project.cloud.domain.bodyPart.entity.BodyPart; import com.project.cloud.domain.bodyPart.repository.BodyPartRepository; +import com.project.cloud.domain.exercise.enumerate.Target; import com.project.cloud.domain.record.entity.Record; import com.project.cloud.domain.user.dto.BodyPartExpResponse; import com.project.cloud.domain.user.dto.DailyDurationResponse; @@ -90,21 +91,20 @@ public UserCharacterResponse getUserCharacterInfo(String email) { makeBodyPartResponse(user.getBodyPartStats())); } - // TODO : bodyPart Enum 작성 필요 private BodyPartExpResponse makeBodyPartResponse(List bodyParts) { return new BodyPartExpResponse( - findBodyPartExpByName(bodyParts, "chest"), - findBodyPartExpByName(bodyParts, "back"), - findBodyPartExpByName(bodyParts, "legs"), - findBodyPartExpByName(bodyParts, "abs"), - findBodyPartExpByName(bodyParts, "shoulders") + findBodyPartExpByName(bodyParts, Target.CHEST), + findBodyPartExpByName(bodyParts, Target.BACK), + findBodyPartExpByName(bodyParts, Target.LEGS), + findBodyPartExpByName(bodyParts, Target.CORE), + findBodyPartExpByName(bodyParts, Target.SHOULDERS) ); } - // TODO : N+1 없애기 - private int findBodyPartExpByName(List bodyParts, String partName) { + private int findBodyPartExpByName(List bodyParts, Target target + ) { Optional part = bodyParts.stream() - .filter(userBodyPart -> userBodyPart.getBodyPart().getName().equals(partName)) + .filter(userBodyPart -> userBodyPart.getBodyPart().getName().equals(target.name())) .findFirst(); if (part.isEmpty()) { throw new CustomException(ErrorCode.BODYPART_NOT_FOUND); diff --git a/cloud/src/main/java/com/project/cloud/domain/userBodyPart/entity/UserBodyPart.java b/cloud/src/main/java/com/project/cloud/domain/userBodyPart/entity/UserBodyPart.java index d2b55c3..05bde51 100644 --- a/cloud/src/main/java/com/project/cloud/domain/userBodyPart/entity/UserBodyPart.java +++ b/cloud/src/main/java/com/project/cloud/domain/userBodyPart/entity/UserBodyPart.java @@ -45,4 +45,8 @@ public UserBodyPart(User user, BodyPart bodyPart, int exp) { public static UserBodyPart create(User user, BodyPart bodyPart, int exp) { return new UserBodyPart(user, bodyPart, exp); } + + public void saveExperience(int exp){ + this.exp += exp; + } } diff --git a/cloud/src/test/java/com/project/cloud/domain/user/service/UserServiceTest.java b/cloud/src/test/java/com/project/cloud/domain/user/service/UserServiceTest.java index 93d4747..ca15f1a 100644 --- a/cloud/src/test/java/com/project/cloud/domain/user/service/UserServiceTest.java +++ b/cloud/src/test/java/com/project/cloud/domain/user/service/UserServiceTest.java @@ -6,6 +6,7 @@ import com.project.cloud.domain.bodyPart.entity.BodyPart; import com.project.cloud.domain.record.entity.Record; import com.project.cloud.domain.bodyPart.repository.BodyPartRepository; +import com.project.cloud.domain.routine.entity.Routine; import com.project.cloud.domain.user.dto.BodyPartExpResponse; import com.project.cloud.domain.user.dto.DailyDurationResponse; import com.project.cloud.domain.user.dto.UserCharacterResponse; @@ -126,42 +127,4 @@ void setUp() { assertThat(exp.abs()).isEqualTo(10); assertThat(exp.shoulders()).isEqualTo(10); } - - @Test - void getUserStatistics_이번달_운동기록_정상조회() { - // given - LocalDate today = LocalDate.now(); - LocalDate earlierThisMonth = today.withDayOfMonth(1).plusDays(1); - LocalDate notThisMonth = today.minusMonths(1).withDayOfMonth(10); - - Record record1 = Record.create(user, earlierThisMonth, 30); - Record record2 = Record.create(user, today, 60); - - Record oldRecord = Record.create(user, notThisMonth, 45); - - user.getRecords().addAll(List.of(record1, record2, oldRecord)); - - // when - UserExerciseStatisticsResponse response = userService.getUserStatistics(email); - - // then - assertThat(response.todayDuration()).isEqualTo(60); - - int expectedAverage = (30 + 60) / today.lengthOfMonth(); - assertThat(response.averageDuration()).isEqualTo(expectedAverage); - - assertThat(response.dailyDurationResponses()).hasSize(today.lengthOfMonth()); - - DailyDurationResponse todayResponse = response.dailyDurationResponses().stream() - .filter(d -> d.date().equals(today)) - .findFirst() - .orElseThrow(); - - assertThat(todayResponse.duration()).isEqualTo(60); - - boolean containsOld = response.dailyDurationResponses().stream() - .anyMatch(d -> d.date().equals(notThisMonth)); - assertThat(containsOld).isFalse(); - } - }