From 46d845b3bb4c259da205805be1c0ce9e1035390f Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:00:03 +0800 Subject: [PATCH 01/37] =?UTF-8?q?test:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20API=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20=EC=BC=80?= =?UTF-8?q?=EC=9D=B4=EC=8A=A4,=2012=EA=B0=80=EC=A7=80=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=A7=84=ED=96=89=20=EC=84=B1=EA=B3=B5?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/TrainerServiceTest.java | 375 ++++++++++++++++++ 1 file changed, 375 insertions(+) create mode 100644 src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java diff --git a/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java b/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java new file mode 100644 index 00000000..5f0f3c52 --- /dev/null +++ b/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java @@ -0,0 +1,375 @@ +package com.project.trainingdiary.service; + +import com.project.trainingdiary.dto.request.AddInBodyInfoRequestDto; +import com.project.trainingdiary.dto.request.EditTraineeInfoRequestDto; +import com.project.trainingdiary.dto.response.AddInBodyInfoResponseDto; +import com.project.trainingdiary.dto.response.EditTraineeInfoResponseDto; +import com.project.trainingdiary.dto.response.TraineeInfoResponseDto; +import com.project.trainingdiary.entity.InBodyRecordHistoryEntity; +import com.project.trainingdiary.entity.PtContractEntity; +import com.project.trainingdiary.entity.TraineeEntity; +import com.project.trainingdiary.entity.TrainerEntity; +import com.project.trainingdiary.exception.impl.PtContractNotExistException; +import com.project.trainingdiary.exception.impl.TraineeNotExistException; +import com.project.trainingdiary.exception.impl.TrainerNotFoundException; +import com.project.trainingdiary.model.TargetType; +import com.project.trainingdiary.repository.InBodyRecordHistoryRepository; +import com.project.trainingdiary.repository.PtContractRepository; +import com.project.trainingdiary.repository.TraineeRepository; +import com.project.trainingdiary.repository.TrainerRepository; +import java.time.LocalDate; +import org.junit.jupiter.api.BeforeEach; +import org.junit.jupiter.api.DisplayName; +import org.junit.jupiter.api.Test; +import org.junit.jupiter.api.extension.ExtendWith; +import org.mockito.InjectMocks; +import org.mockito.Mock; +import org.mockito.junit.jupiter.MockitoExtension; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContext; +import org.springframework.security.core.context.SecurityContextHolder; + +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; + +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.*; + +@ExtendWith(MockitoExtension.class) +public class TrainerServiceTest { + + @Mock + private TraineeRepository traineeRepository; + + @Mock + private TrainerRepository trainerRepository; + + @Mock + private InBodyRecordHistoryRepository inBodyRecordHistoryRepository; + + @Mock + private PtContractRepository ptContractRepository; + + @Mock + private Authentication authentication; + + @Mock + private SecurityContext securityContext; + + @InjectMocks + private TrainerService trainerService; + + @BeforeEach + public void setUp() { + SecurityContextHolder.setContext(securityContext); + when(securityContext.getAuthentication()).thenReturn(authentication); + } + + @Test + @DisplayName("인증되지 않은 트레이너가 트레이니 정보를 요청할 때 예외 발생") + void testGetTraineeInfo_AuthenticationFailure() { + // given + when(securityContext.getAuthentication()).thenReturn(null); + // when / then + assertThrows(TrainerNotFoundException.class, () -> trainerService.getTraineeInfo(1L)); + } + + @Test + @DisplayName("인증되지 않은 트레이너가 인바디 기록을 추가할 때 예외 발생") + void testAddInBodyRecord_AuthenticationFailure() { + // given + AddInBodyInfoRequestDto dto = new AddInBodyInfoRequestDto(); + dto.setTraineeId(1L); + + when(securityContext.getAuthentication()).thenReturn(null); + + // when / then + assertThrows(TrainerNotFoundException.class, () -> trainerService.addInBodyRecord(dto)); + } + + @Test + @DisplayName("인증되지 않은 트레이너가 트레이니 정보를 수정할 때 예외 발생") + void testEditTraineeInfo_AuthenticationFailure() { + // given + EditTraineeInfoRequestDto dto = new EditTraineeInfoRequestDto(); + dto.setTraineeId(1L); + + when(securityContext.getAuthentication()).thenReturn(null); + + // when / then + assertThrows(TrainerNotFoundException.class, () -> trainerService.editTraineeInfo(dto)); + } + + @Test + @DisplayName("트레이니 정보 조회 성공") + void testGetTraineeInfo_Success() { + // given + Long traineeId = 1L; + Long trainerId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(trainerId); + + TraineeEntity trainee = new TraineeEntity(); + trainee.setId(traineeId); + trainee.setInBodyRecords(new ArrayList<>()); + + PtContractEntity contract = new PtContractEntity(); + contract.setTrainer(trainer); + contract.setTrainee(trainee); + contract.setTotalSession(10); + contract.setUsedSession(5); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); + when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(true); + when(ptContractRepository.findByTrainee(trainee)).thenReturn(List.of(contract)); + + // when + TraineeInfoResponseDto responseDto = trainerService.getTraineeInfo(traineeId); + + // then + assertEquals(5, responseDto.getRemainingSessions()); + assertEquals(traineeId, responseDto.getTraineeId()); + verify(trainerRepository, times(1)).findByEmail(trainerEmail); + verify(traineeRepository, times(1)).findById(traineeId); + verify(ptContractRepository, times(1)).existsByTrainerIdAndTraineeId(trainerId, traineeId); + } + + @Test + @DisplayName("트레이니가 존재하지 않을 때 예외 발생") + void testGetTraineeInfo_TraineeNotExistException() { + // given + Long traineeId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(1L); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.empty()); + + // when / then + assertThrows(TraineeNotExistException.class, () -> trainerService.getTraineeInfo(traineeId)); + verify(trainerRepository, times(1)).findByEmail(trainerEmail); + verify(traineeRepository, times(1)).findById(traineeId); + } + + @Test + @DisplayName("트레이니 정보 수정 성공") + void testEditTraineeInfo_Success() { + // given + Long traineeId = 1L; + Long trainerId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(trainerId); + + TraineeEntity trainee = new TraineeEntity(); + trainee.setId(traineeId); + + EditTraineeInfoRequestDto dto = new EditTraineeInfoRequestDto(); + dto.setTraineeId(traineeId); + dto.setBirthDate(LocalDate.parse("2000-01-01")); + dto.setGender("Male"); + dto.setHeight(180); + dto.setTargetType(TargetType.TARGET_BODY_FAT_PERCENTAGE); + dto.setTargetValue(70); + dto.setTargetReward("Reward"); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); + when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(true); + + // when + EditTraineeInfoResponseDto responseDto = trainerService.editTraineeInfo(dto); + + // then + assertEquals(dto.getBirthDate(), responseDto.getBirthDate()); + assertEquals(dto.getGender(), responseDto.getGender()); + assertEquals(dto.getHeight(), responseDto.getHeight()); + assertEquals(dto.getTargetType(), responseDto.getTargetType()); + assertEquals(dto.getTargetValue(), responseDto.getTargetValue()); + assertEquals(dto.getTargetReward(), responseDto.getTargetReward()); + + verify(trainerRepository, times(1)).findByEmail(trainerEmail); + verify(traineeRepository, times(1)).findById(traineeId); + verify(ptContractRepository, times(1)).existsByTrainerIdAndTraineeId(trainerId, traineeId); + } + + @Test + @DisplayName("트레이니 정보 수정 시 트레이니가 존재하지 않을 때 예외 발생") + void testEditTraineeInfo_TraineeNotExistException() { + // given + Long traineeId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(1L); + + EditTraineeInfoRequestDto dto = new EditTraineeInfoRequestDto(); + dto.setTraineeId(traineeId); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.empty()); + + // when / then + assertThrows(TraineeNotExistException.class, () -> trainerService.editTraineeInfo(dto)); + verify(trainerRepository, times(1)).findByEmail(trainerEmail); + verify(traineeRepository, times(1)).findById(traineeId); + } + + @Test + @DisplayName("인바디 기록 추가 성공") + void testAddInBodyRecord_Success() { + // given + Long traineeId = 1L; + Long trainerId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(trainerId); + + TraineeEntity trainee = new TraineeEntity(); + trainee.setId(traineeId); + + AddInBodyInfoRequestDto dto = new AddInBodyInfoRequestDto(); + dto.setTraineeId(traineeId); + dto.setWeight(70.0); + dto.setSkeletalMuscleMass(30.0); + dto.setBodyFatPercentage(20.0); + + InBodyRecordHistoryEntity inBodyRecord = new InBodyRecordHistoryEntity(); + inBodyRecord.setTrainee(trainee); + inBodyRecord.setWeight(dto.getWeight()); + inBodyRecord.setSkeletalMuscleMass(dto.getSkeletalMuscleMass()); + inBodyRecord.setBodyFatPercentage(dto.getBodyFatPercentage()); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); + when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(true); + when(inBodyRecordHistoryRepository.save(any(InBodyRecordHistoryEntity.class))).thenReturn(inBodyRecord); + + // when + AddInBodyInfoResponseDto responseDto = trainerService.addInBodyRecord(dto); + + // then + assertEquals(dto.getWeight(), responseDto.getWeight()); + assertEquals(dto.getSkeletalMuscleMass(), responseDto.getSkeletalMuscleMass()); + assertEquals(dto.getBodyFatPercentage(), responseDto.getBodyFatPercentage()); + + verify(trainerRepository, times(1)).findByEmail(trainerEmail); + verify(traineeRepository, times(1)).findById(traineeId); + verify(ptContractRepository, times(1)).existsByTrainerIdAndTraineeId(trainerId, traineeId); + verify(inBodyRecordHistoryRepository, times(1)).save(any(InBodyRecordHistoryEntity.class)); + } + + @Test + @DisplayName("인바디 기록 추가 시 트레이니가 존재하지 않을 때 예외 발생") + void testAddInBodyRecord_TraineeNotExistException() { + // given + Long traineeId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(1L); + + AddInBodyInfoRequestDto dto = new AddInBodyInfoRequestDto(); + dto.setTraineeId(traineeId); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.empty()); + + // when / then + assertThrows(TraineeNotExistException.class, () -> trainerService.addInBodyRecord(dto)); + verify(trainerRepository, times(1)).findByEmail(trainerEmail); + verify(traineeRepository, times(1)).findById(traineeId); + + } + + @Test + @DisplayName("트레이너와 트레이니 사이에 계약이 존재하지 않을 때 예외 발생") + void testGetTraineeInfo_ContractNotExist() { + // given + Long traineeId = 1L; + Long trainerId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(trainerId); + + TraineeEntity trainee = new TraineeEntity(); + trainee.setId(traineeId); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); + when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(false); + + // when / then + assertThrows(PtContractNotExistException.class, () -> trainerService.getTraineeInfo(traineeId)); + } + + @Test + @DisplayName("트레이너와 트레이니 사이에 계약이 존재하지 않을 때 인바디 기록 추가 시 예외 발생") + void testAddInBodyRecord_ContractNotExist() { + // given + Long traineeId = 1L; + Long trainerId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(trainerId); + + TraineeEntity trainee = new TraineeEntity(); + trainee.setId(traineeId); + + AddInBodyInfoRequestDto dto = new AddInBodyInfoRequestDto(); + dto.setTraineeId(traineeId); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); + when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(false); + + // when / then + assertThrows(PtContractNotExistException.class, () -> trainerService.addInBodyRecord(dto)); + } + + @Test + @DisplayName("트레이너와 트레이니 사이에 계약이 존재하지 않을 때 트레이니 정보 수정 시 예외 발생") + void testEditTraineeInfo_ContractNotExist() { + // given + Long traineeId = 1L; + Long trainerId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(trainerId); + + TraineeEntity trainee = new TraineeEntity(); + trainee.setId(traineeId); + + EditTraineeInfoRequestDto dto = new EditTraineeInfoRequestDto(); + dto.setTraineeId(traineeId); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); + when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(false); + + // when / then + assertThrows(PtContractNotExistException.class, () -> trainerService.editTraineeInfo(dto)); + } + +} \ No newline at end of file From ed4d8da284b5214100c19b0ddc1c2165ba7b64c3 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:00:30 +0800 Subject: [PATCH 02/37] =?UTF-8?q?fix:=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=20=EC=8B=9C=20=EC=9D=B8=EC=A6=9D=EB=B2=88=ED=98=B8=20?= =?UTF-8?q?=EC=97=AC=EB=B6=80=20=ED=99=95=EC=9D=B8=20=EC=95=88=ED=95=98?= =?UTF-8?q?=EB=8A=94=20=EB=B2=84=EA=B7=B8=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trainingdiary/service/UserService.java | 161 +++++------------- 1 file changed, 46 insertions(+), 115 deletions(-) diff --git a/src/main/java/com/project/trainingdiary/service/UserService.java b/src/main/java/com/project/trainingdiary/service/UserService.java index 9dc21cde..1ccbc09b 100644 --- a/src/main/java/com/project/trainingdiary/service/UserService.java +++ b/src/main/java/com/project/trainingdiary/service/UserService.java @@ -10,8 +10,6 @@ import com.project.trainingdiary.entity.TrainerEntity; import com.project.trainingdiary.entity.VerificationEntity; import com.project.trainingdiary.exception.impl.PasswordMismatchedException; -import com.project.trainingdiary.exception.impl.TraineeEmailDuplicateException; -import com.project.trainingdiary.exception.impl.TrainerEmailDuplicateException; import com.project.trainingdiary.exception.impl.UserNotFoundException; import com.project.trainingdiary.exception.impl.VerificationCodeExpiredException; import com.project.trainingdiary.exception.impl.VerificationCodeNotMatchedException; @@ -30,7 +28,6 @@ import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; import java.time.LocalDateTime; -import java.util.Optional; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; import org.springframework.security.core.userdetails.UserDetails; @@ -59,85 +56,58 @@ public class UserService implements UserDetailsService { private static final String REFRESH_TOKEN_COOKIE_NAME = "Refresh-Token"; private static final String ACCESS_TOKEN_COOKIE_NAME = "Access-Token"; - /** - * 이메일 중복을 확인하고 인증 코드를 발송합니다. - */ public void checkDuplicateEmailAndSendVerification( SendVerificationAndCheckDuplicateRequestDto dto) { validateEmailNotExists(dto.getEmail()); sendVerificationCode(dto.getEmail()); } - /** - * 인증 코드를 확인합니다. - */ public void checkVerificationCode(VerifyCodeRequestDto dto) { VerificationEntity verificationEntity = getVerificationEntity(dto.getEmail()); validateVerificationCode(verificationEntity, dto.getVerificationCode()); } - /** - * 회원가입을 처리합니다. - */ @Transactional public void signUp(SignUpRequestDto dto, HttpServletResponse response) { + VerificationEntity verificationEntity = getVerificationEntity(dto.getEmail()); validateEmailNotExists(dto.getEmail()); validatePasswordsMatch(dto.getPassword(), dto.getConfirmPassword()); + validateVerificationCode(verificationEntity, dto.getVerificationCode()); String encodedPassword = passwordEncoder.encode(dto.getPassword()); saveUser(dto, encodedPassword); generateTokensAndSetCookies(dto.getEmail(), response); verificationRepository.deleteByEmail(dto.getEmail()); } - /** - * 로그인 처리를 합니다. - */ public SignInResponseDto signIn(SignInRequestDto dto, HttpServletResponse response) { UserDetails userDetails = loadUserByUsername(dto.getEmail()); validatePassword(dto.getPassword(), userDetails.getPassword()); return generateTokensAndSetCookies(userDetails.getUsername(), response); } - /** - * 로그아웃 처리를 합니다. - */ public void signOut(HttpServletRequest request, HttpServletResponse response) { blacklistAndClearCookies(request, response); } - /** - * 주어진 이메일이 이미 존재하는지 확인합니다. - */ private void validateEmailNotExists(String email) { - if (traineeRepository.findByEmail(email).isPresent()) { - throw new TraineeEmailDuplicateException(); - } - if (trainerRepository.findByEmail(email).isPresent()) { - throw new TrainerEmailDuplicateException(); + if (traineeRepository.findByEmail(email).isPresent() || trainerRepository.findByEmail(email) + .isPresent()) { + throw new UserNotFoundException(); } } - /** - * 인증 코드를 발송합니다. - */ private void sendVerificationCode(String email) { String verificationCode = VerificationCodeGeneratorUtil.generateVerificationCode(); emailProvider.sendVerificationEmail(email, verificationCode); verificationRepository.save(VerificationEntity.of(email, verificationCode)); } - /** - * 주어진 비밀번호와 확인 비밀번호가 일치하는지 확인합니다. - */ private void validatePasswordsMatch(String password, String confirmPassword) { if (!password.equals(confirmPassword)) { throw new PasswordMismatchedException(); } } - /** - * 인증 코드를 검증합니다. - */ private void validateVerificationCode(VerificationEntity verificationEntity, String verificationCode) { if (!verificationEntity.getVerificationCode().equals(verificationCode)) { @@ -148,16 +118,10 @@ private void validateVerificationCode(VerificationEntity verificationEntity, } } - /** - * 이메일에 대한 인증 엔티티를 가져옵니다. - */ private VerificationEntity getVerificationEntity(String email) { return verificationRepository.findByEmail(email).orElseThrow(UserNotFoundException::new); } - /** - * 사용자를 저장합니다. - */ private void saveUser(SignUpRequestDto dto, String encodedPassword) { if (dto.getRole() == UserRoleType.TRAINEE) { saveTrainee(dto, encodedPassword); @@ -168,42 +132,32 @@ private void saveUser(SignUpRequestDto dto, String encodedPassword) { } } - /** - * 새로운 트레이니를 저장합니다. - */ private void saveTrainee(SignUpRequestDto dto, String encodedPassword) { - TraineeEntity trainee = new TraineeEntity(); - trainee.setName(dto.getName()); - trainee.setEmail(dto.getEmail()); - trainee.setPassword(encodedPassword); - trainee.setRole(UserRoleType.TRAINEE); + TraineeEntity trainee = TraineeEntity.builder() + .name(dto.getName()) + .email(dto.getEmail()) + .password(encodedPassword) + .role(UserRoleType.TRAINEE) + .build(); traineeRepository.save(trainee); } - /** - * 새로운 트레이너를 저장합니다. - */ private void saveTrainer(SignUpRequestDto dto, String encodedPassword) { - TrainerEntity trainer = new TrainerEntity(); - trainer.setName(dto.getName()); - trainer.setEmail(dto.getEmail()); - trainer.setPassword(encodedPassword); - trainer.setRole(UserRoleType.TRAINER); + TrainerEntity trainer = TrainerEntity.builder() + .name(dto.getName()) + .email(dto.getEmail()) + .password(encodedPassword) + .role(UserRoleType.TRAINER) + .build(); trainerRepository.save(trainer); } - /** - * 비밀번호를 검증합니다. - */ private void validatePassword(String rawPassword, String encodedPassword) { if (!passwordEncoder.matches(rawPassword, encodedPassword)) { throw new WrongPasswordException(); } } - /** - * 토큰을 생성하고 쿠키를 설정합니다. - */ private SignInResponseDto generateTokensAndSetCookies(String username, HttpServletResponse response) { String accessToken = tokenProvider.createAccessToken(username); @@ -223,71 +177,48 @@ private SignInResponseDto generateTokensAndSetCookies(String username, return new SignInResponseDto(accessToken, refreshToken); } - /** - * 토큰을 블랙리스트에 추가하고 쿠키를 제거합니다. - */ private void blacklistAndClearCookies(HttpServletRequest request, HttpServletResponse response) { Cookie accessTokenCookie = cookieProvider.getCookie(request, ACCESS_TOKEN_COOKIE_NAME); Cookie refreshTokenCookie = cookieProvider.getCookie(request, REFRESH_TOKEN_COOKIE_NAME); - if (accessTokenCookie != null && tokenProvider.validateToken(accessTokenCookie.getValue())) { - log.info("블랙리스트에 추가된 access 토큰: {}", accessTokenCookie.getValue()); - tokenProvider.blacklistToken(accessTokenCookie.getValue()); - redisTokenRepository.deleteToken( - "accessToken:" + tokenProvider.getUsernameFromToken(accessTokenCookie.getValue())); - } - - if (refreshTokenCookie != null && tokenProvider.validateToken(refreshTokenCookie.getValue())) { - log.info("블랙리스트에 추가된 refresh 토큰: {}", refreshTokenCookie.getValue()); - tokenProvider.blacklistToken(refreshTokenCookie.getValue()); - redisTokenRepository.deleteToken( - "refreshToken:" + tokenProvider.getUsernameFromToken(refreshTokenCookie.getValue())); - } + blacklistToken(accessTokenCookie); + blacklistToken(refreshTokenCookie); cookieProvider.clearCookie(response, ACCESS_TOKEN_COOKIE_NAME); cookieProvider.clearCookie(response, REFRESH_TOKEN_COOKIE_NAME); } - /** - * 주어진 사용자 이름으로 사용자를 로드합니다. - */ + private void blacklistToken(Cookie tokenCookie) { + if (tokenCookie != null && tokenProvider.validateToken(tokenCookie.getValue())) { + log.info("블랙리스트에 추가된 토큰: {}", tokenCookie.getValue()); + tokenProvider.blacklistToken(tokenCookie.getValue()); + redisTokenRepository.deleteToken(tokenProvider.getUsernameFromToken(tokenCookie.getValue())); + } + } + @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { - TraineeEntity trainee = traineeRepository.findByEmail(username).orElse(null); - - if (trainee != null) { - return UserPrincipal.create(trainee); - } - TrainerEntity trainer = trainerRepository.findByEmail(username) - .orElseThrow(UserNotFoundException::new); - return UserPrincipal.create(trainer); + return traineeRepository.findByEmail(username) + .map(UserPrincipal::create) + .orElseGet(() -> UserPrincipal.create(trainerRepository.findByEmail(username) + .orElseThrow(UserNotFoundException::new))); } - /** - * 주어진 사용자 이름으로 사용자를 로드합니다. - */ public MemberInfoResponseDto memberInfo(Long id) { - - Optional traineeOpt = traineeRepository.findById(id); - - if (traineeOpt.isPresent()) { - TraineeEntity trainee = traineeOpt.get(); - return MemberInfoResponseDto.builder() - .id(trainee.getId()) - .email(trainee.getEmail()) - .name(trainee.getName()) - .role(trainee.getRole()) - .build(); - } - - TrainerEntity trainer = trainerRepository.findById(id) - .orElseThrow(UserNotFoundException::new); - - return MemberInfoResponseDto.builder() - .id(trainer.getId()) - .name(trainer.getName()) - .role(trainer.getRole()) - .email(trainer.getEmail()) - .build(); + return traineeRepository.findById(id) + .map(trainee -> MemberInfoResponseDto.builder() + .id(trainee.getId()) + .email(trainee.getEmail()) + .name(trainee.getName()) + .role(trainee.getRole()) + .build()) + .orElseGet(() -> trainerRepository.findById(id) + .map(trainer -> MemberInfoResponseDto.builder() + .id(trainer.getId()) + .email(trainer.getEmail()) + .name(trainer.getName()) + .role(trainer.getRole()) + .build()) + .orElseThrow(UserNotFoundException::new)); } } \ No newline at end of file From c3e8af71ac4ef2b7a3597840b4cb01588752835d Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:00:44 +0800 Subject: [PATCH 03/37] =?UTF-8?q?feat:=20=EC=9D=B8=EB=B0=94=EB=94=94=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=20=EA=B4=80=EB=A0=A8=20DTO=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/request/AddInBodyInfoRequestDto.java | 44 +++++++++++++++++++ .../response/AddInBodyInfoResponseDto.java | 24 ++++++++++ 2 files changed, 68 insertions(+) create mode 100644 src/main/java/com/project/trainingdiary/dto/request/AddInBodyInfoRequestDto.java create mode 100644 src/main/java/com/project/trainingdiary/dto/response/AddInBodyInfoResponseDto.java diff --git a/src/main/java/com/project/trainingdiary/dto/request/AddInBodyInfoRequestDto.java b/src/main/java/com/project/trainingdiary/dto/request/AddInBodyInfoRequestDto.java new file mode 100644 index 00000000..8bd9ce2b --- /dev/null +++ b/src/main/java/com/project/trainingdiary/dto/request/AddInBodyInfoRequestDto.java @@ -0,0 +1,44 @@ +package com.project.trainingdiary.dto.request; + +import com.project.trainingdiary.entity.InBodyRecordHistoryEntity; +import com.project.trainingdiary.entity.TraineeEntity; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.PositiveOrZero; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Getter +@Setter +@Builder +@AllArgsConstructor +@NoArgsConstructor +public class AddInBodyInfoRequestDto { + + @NotNull(message = "traineeId 값은 null이 안됩니다.") + @Positive(message = "traineeId는 양수여야 합니다.") + private Long traineeId; + + @PositiveOrZero(message = "weight는 0 이상이어야 합니다.") + private double weight; + + @PositiveOrZero(message = "bodyFatPercentage는 0 이상이어야 합니다.") + private double bodyFatPercentage; + + @PositiveOrZero(message = "skeletalMuscleMass는 0 이상이어야 합니다.") + private double skeletalMuscleMass; + + public static InBodyRecordHistoryEntity toEntity(AddInBodyInfoRequestDto dto, + TraineeEntity trainee) { + return InBodyRecordHistoryEntity + .builder() + .trainee(trainee) + .weight(dto.getWeight()) + .bodyFatPercentage(dto.getBodyFatPercentage()) + .skeletalMuscleMass(dto.getSkeletalMuscleMass()) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/project/trainingdiary/dto/response/AddInBodyInfoResponseDto.java b/src/main/java/com/project/trainingdiary/dto/response/AddInBodyInfoResponseDto.java new file mode 100644 index 00000000..78a0f8de --- /dev/null +++ b/src/main/java/com/project/trainingdiary/dto/response/AddInBodyInfoResponseDto.java @@ -0,0 +1,24 @@ +package com.project.trainingdiary.dto.response; + +import com.project.trainingdiary.entity.InBodyRecordHistoryEntity; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class AddInBodyInfoResponseDto { + + private long id; + private double weight; + private double bodyFatPercentage; + private double skeletalMuscleMass; + + public static AddInBodyInfoResponseDto fromEntity(InBodyRecordHistoryEntity entity) { + return AddInBodyInfoResponseDto.builder() + .id(entity.getId()) + .weight(entity.getWeight()) + .bodyFatPercentage(entity.getBodyFatPercentage()) + .skeletalMuscleMass(entity.getSkeletalMuscleMass()) + .build(); + } +} \ No newline at end of file From 3795348812d3fac3e764e17913def486c889adc4 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:00:50 +0800 Subject: [PATCH 04/37] =?UTF-8?q?feat:=20=EC=9D=B8=EB=B0=94=EB=94=94=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=20=EA=B4=80=EB=A0=A8=20DTO=20=EC=83=9D?= =?UTF-8?q?=EC=84=B1?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InBodyRecordHistoryDto.java | 24 +++++++++++++++++++ 1 file changed, 24 insertions(+) create mode 100644 src/main/java/com/project/trainingdiary/dto/traineeRecordDto/InBodyRecordHistoryDto.java diff --git a/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/InBodyRecordHistoryDto.java b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/InBodyRecordHistoryDto.java new file mode 100644 index 00000000..955583d9 --- /dev/null +++ b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/InBodyRecordHistoryDto.java @@ -0,0 +1,24 @@ +package com.project.trainingdiary.dto.traineeRecordDto; + +import com.project.trainingdiary.entity.InBodyRecordHistoryEntity; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class InBodyRecordHistoryDto { + + private long id; + private double weight; + private double bodyFatPercentage; + private double skeletalMuscleMass; + + public static InBodyRecordHistoryDto fromEntity(InBodyRecordHistoryEntity entity) { + return InBodyRecordHistoryDto.builder() + .id(entity.getId()) + .weight(entity.getWeight()) + .bodyFatPercentage(entity.getBodyFatPercentage()) + .skeletalMuscleMass(entity.getSkeletalMuscleMass()) + .build(); + } +} \ No newline at end of file From 94f1469507c0a382854fc88e1cbd69c1f50df8ef Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:01:04 +0800 Subject: [PATCH 05/37] =?UTF-8?q?feat:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20=EC=A1=B4=EC=9E=AC=ED=95=98=EC=A7=80=20=EC=95=8A?= =?UTF-8?q?=EC=9D=84=EB=96=84=20exception?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/impl/TrainerNotFoundException.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/com/project/trainingdiary/exception/impl/TrainerNotFoundException.java diff --git a/src/main/java/com/project/trainingdiary/exception/impl/TrainerNotFoundException.java b/src/main/java/com/project/trainingdiary/exception/impl/TrainerNotFoundException.java new file mode 100644 index 00000000..be177b30 --- /dev/null +++ b/src/main/java/com/project/trainingdiary/exception/impl/TrainerNotFoundException.java @@ -0,0 +1,11 @@ +package com.project.trainingdiary.exception.impl; + +import com.project.trainingdiary.exception.GlobalException; +import org.springframework.http.HttpStatus; + +public class TrainerNotFoundException extends GlobalException { + + public TrainerNotFoundException() { + super(HttpStatus.NOT_FOUND, "트레이너가 존재하지 않습니다."); + } +} From 16bd654d9439b20bab57613d0a370d1e1f6e508a Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:01:22 +0800 Subject: [PATCH 06/37] =?UTF-8?q?feat:=20=EC=9D=B8=EB=B0=94=EB=94=94=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=20=EA=B4=80=EB=A0=A8=20dto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../traineeRecordDto/BodyFatHistoryDto.java | 21 +++++++++++++++++++ .../MuscleMassHistoryDto.java | 21 +++++++++++++++++++ .../traineeRecordDto/WeightHistoryDto.java | 21 +++++++++++++++++++ 3 files changed, 63 insertions(+) create mode 100644 src/main/java/com/project/trainingdiary/dto/traineeRecordDto/BodyFatHistoryDto.java create mode 100644 src/main/java/com/project/trainingdiary/dto/traineeRecordDto/MuscleMassHistoryDto.java create mode 100644 src/main/java/com/project/trainingdiary/dto/traineeRecordDto/WeightHistoryDto.java diff --git a/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/BodyFatHistoryDto.java b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/BodyFatHistoryDto.java new file mode 100644 index 00000000..b2c59d37 --- /dev/null +++ b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/BodyFatHistoryDto.java @@ -0,0 +1,21 @@ +package com.project.trainingdiary.dto.traineeRecordDto; + +import com.project.trainingdiary.entity.InBodyRecordHistoryEntity; +import java.time.LocalDateTime; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class BodyFatHistoryDto { + + private LocalDateTime date; + private double bodyFatPercentage; + + public static BodyFatHistoryDto fromEntity(InBodyRecordHistoryEntity entity) { + return BodyFatHistoryDto.builder() + .date(entity.getCreatedAt()) + .bodyFatPercentage(entity.getBodyFatPercentage()) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/MuscleMassHistoryDto.java b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/MuscleMassHistoryDto.java new file mode 100644 index 00000000..b96bb3df --- /dev/null +++ b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/MuscleMassHistoryDto.java @@ -0,0 +1,21 @@ +package com.project.trainingdiary.dto.traineeRecordDto; + +import com.project.trainingdiary.entity.InBodyRecordHistoryEntity; +import java.time.LocalDateTime; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class MuscleMassHistoryDto { + + private LocalDateTime date; + private double muscleMass; + + public static MuscleMassHistoryDto fromEntity(InBodyRecordHistoryEntity entity) { + return MuscleMassHistoryDto.builder() + .date(entity.getCreatedAt()) + .muscleMass(entity.getSkeletalMuscleMass()) + .build(); + } +} \ No newline at end of file diff --git a/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/WeightHistoryDto.java b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/WeightHistoryDto.java new file mode 100644 index 00000000..588eb380 --- /dev/null +++ b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/WeightHistoryDto.java @@ -0,0 +1,21 @@ +package com.project.trainingdiary.dto.traineeRecordDto; + +import com.project.trainingdiary.entity.InBodyRecordHistoryEntity; +import java.time.LocalDateTime; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class WeightHistoryDto { + + private LocalDateTime date; + private double weight; + + public static WeightHistoryDto fromEntity(InBodyRecordHistoryEntity entity) { + return WeightHistoryDto.builder() + .date(entity.getCreatedAt()) + .weight(entity.getWeight()) + .build(); + } +} \ No newline at end of file From 86aca2180c484667b5eb0a8bdb6a9b2c32748484 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:01:47 +0800 Subject: [PATCH 07/37] =?UTF-8?q?feat:=20=EB=AA=A9=ED=91=9C=20=EC=88=98?= =?UTF-8?q?=EC=B9=98=20enum=20type=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/project/trainingdiary/model/TargetType.java | 8 ++++++++ 1 file changed, 8 insertions(+) create mode 100644 src/main/java/com/project/trainingdiary/model/TargetType.java diff --git a/src/main/java/com/project/trainingdiary/model/TargetType.java b/src/main/java/com/project/trainingdiary/model/TargetType.java new file mode 100644 index 00000000..744925a7 --- /dev/null +++ b/src/main/java/com/project/trainingdiary/model/TargetType.java @@ -0,0 +1,8 @@ +package com.project.trainingdiary.model; + +public enum TargetType { + + TARGET_WEIGHT, + TARGET_BODY_FAT_PERCENTAGE, + TARGET_SKELETAL_MUSCLE_MASS +} From bf758723963d602e4808e3614a08cbc6a81aae94 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:01:59 +0800 Subject: [PATCH 08/37] =?UTF-8?q?feat:=20=EC=9D=B8=EB=B0=94=EB=94=94=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=20=EC=BB=AC=EB=9F=BC=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trainingdiary/entity/TraineeEntity.java | 22 ++++++++++--------- 1 file changed, 12 insertions(+), 10 deletions(-) diff --git a/src/main/java/com/project/trainingdiary/entity/TraineeEntity.java b/src/main/java/com/project/trainingdiary/entity/TraineeEntity.java index 4faaaf50..b6738883 100644 --- a/src/main/java/com/project/trainingdiary/entity/TraineeEntity.java +++ b/src/main/java/com/project/trainingdiary/entity/TraineeEntity.java @@ -1,18 +1,22 @@ package com.project.trainingdiary.entity; +import static jakarta.persistence.CascadeType.ALL; import static jakarta.persistence.EnumType.STRING; import static jakarta.persistence.GenerationType.IDENTITY; +import com.project.trainingdiary.model.TargetType; import com.project.trainingdiary.model.UserRoleType; import jakarta.persistence.Column; import jakarta.persistence.Entity; import jakarta.persistence.Enumerated; import jakarta.persistence.GeneratedValue; import jakarta.persistence.Id; +import jakarta.persistence.OneToMany; import jakarta.persistence.Table; import jakarta.persistence.Temporal; import jakarta.persistence.TemporalType; -import java.util.Date; +import java.time.LocalDate; +import java.util.List; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -47,23 +51,21 @@ public class TraineeEntity extends BaseEntity { private UserRoleType role; @Temporal(TemporalType.DATE) - private Date birthDate; + private LocalDate birthDate; private String gender; private int totalSessions; - private double currentWeight; + private double height; - private double currentSkeletalMuscleMass; + @OneToMany(mappedBy = "trainee", cascade = ALL, orphanRemoval = true) + private List inBodyRecords; - private double currentBodyFatPercentage; - - private double targetWeight; - - private double targetSkeletalMuscleMass; + @Enumerated(STRING) + private TargetType targetType; - private double targetBodyFatPercentage; + private double targetValue; private String targetReward; } From cd1a8c10869dbd509a6b7979be5b1d607d173cc1 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:02:16 +0800 Subject: [PATCH 09/37] =?UTF-8?q?feat:=20test=EC=9A=A9=20@Setter=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/project/trainingdiary/entity/PtContractEntity.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/project/trainingdiary/entity/PtContractEntity.java b/src/main/java/com/project/trainingdiary/entity/PtContractEntity.java index cbbd5b70..bbccc014 100644 --- a/src/main/java/com/project/trainingdiary/entity/PtContractEntity.java +++ b/src/main/java/com/project/trainingdiary/entity/PtContractEntity.java @@ -14,10 +14,12 @@ import lombok.Builder; import lombok.Getter; import lombok.NoArgsConstructor; +import lombok.Setter; import lombok.ToString; @Builder @Getter +@Setter @ToString(exclude = {"trainer", "trainee"}) @NoArgsConstructor @AllArgsConstructor From 026b4ca38d60cb670eec8478d441d7d6bfc6fae3 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:02:30 +0800 Subject: [PATCH 10/37] =?UTF-8?q?feat:=20=EA=B3=84=EC=95=BD=20=EC=A1=B0?= =?UTF-8?q?=ED=9A=8C=EC=9A=A9=20=ED=95=A8=EC=88=98=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trainingdiary/repository/PtContractRepository.java | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/src/main/java/com/project/trainingdiary/repository/PtContractRepository.java b/src/main/java/com/project/trainingdiary/repository/PtContractRepository.java index 3a2a651f..2766427d 100644 --- a/src/main/java/com/project/trainingdiary/repository/PtContractRepository.java +++ b/src/main/java/com/project/trainingdiary/repository/PtContractRepository.java @@ -1,6 +1,8 @@ package com.project.trainingdiary.repository; import com.project.trainingdiary.entity.PtContractEntity; +import com.project.trainingdiary.entity.TraineeEntity; +import java.util.Collection; import java.util.Optional; import org.springframework.data.domain.Page; import org.springframework.data.domain.Pageable; @@ -41,4 +43,6 @@ public interface PtContractRepository extends JpaRepository findByTrainerEmail(String email, Pageable pageable); + + Collection findByTrainee(TraineeEntity trainee); } From 5b07bc6b181cbe98942fec03c8fe7a7974680d0c Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:02:41 +0800 Subject: [PATCH 11/37] =?UTF-8?q?feat:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8B=88=EA=B0=80=20=EC=A1=B4=EC=9E=AC=ED=95=98=EC=A7=80=20?= =?UTF-8?q?=EC=95=8A=EC=9D=84=EB=96=84=20exception?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../exception/impl/TraineeNotExistException.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/com/project/trainingdiary/exception/impl/TraineeNotExistException.java diff --git a/src/main/java/com/project/trainingdiary/exception/impl/TraineeNotExistException.java b/src/main/java/com/project/trainingdiary/exception/impl/TraineeNotExistException.java new file mode 100644 index 00000000..bac63ba7 --- /dev/null +++ b/src/main/java/com/project/trainingdiary/exception/impl/TraineeNotExistException.java @@ -0,0 +1,11 @@ +package com.project.trainingdiary.exception.impl; + +import com.project.trainingdiary.exception.GlobalException; +import org.springframework.http.HttpStatus; + +public class TraineeNotExistException extends GlobalException { + + public TraineeNotExistException() { + super(HttpStatus.NOT_FOUND, "트레이니가 존재하지 않습니다."); + } +} From 347396c7e84be60bb3317d0b4ca194635b8c484d Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:02:55 +0800 Subject: [PATCH 12/37] =?UTF-8?q?feat:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8B=88=20=EC=A0=95=EB=B3=B4=20=EC=88=98=EC=A0=95=20dto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../request/EditTraineeInfoRequestDto.java | 40 +++++++++++++++++++ .../response/EditTraineeInfoResponseDto.java | 33 +++++++++++++++ 2 files changed, 73 insertions(+) create mode 100644 src/main/java/com/project/trainingdiary/dto/request/EditTraineeInfoRequestDto.java create mode 100644 src/main/java/com/project/trainingdiary/dto/response/EditTraineeInfoResponseDto.java diff --git a/src/main/java/com/project/trainingdiary/dto/request/EditTraineeInfoRequestDto.java b/src/main/java/com/project/trainingdiary/dto/request/EditTraineeInfoRequestDto.java new file mode 100644 index 00000000..f176a7f2 --- /dev/null +++ b/src/main/java/com/project/trainingdiary/dto/request/EditTraineeInfoRequestDto.java @@ -0,0 +1,40 @@ +package com.project.trainingdiary.dto.request; + +import com.project.trainingdiary.model.TargetType; +import jakarta.validation.constraints.NotNull; +import jakarta.validation.constraints.Positive; +import jakarta.validation.constraints.Size; +import java.time.LocalDate; +import lombok.Getter; +import lombok.Setter; + +@Getter +@Setter +public class EditTraineeInfoRequestDto { + + @NotNull(message = "traineeId 값은 null이 될 수 없습니다.") + private Long traineeId; + + @NotNull(message = "birthDate 값은 null이 될 수 없습니다.") + private LocalDate birthDate; + + @NotNull(message = "gender 값은 null이 될 수 없습니다.") + @Size(min = 1, max = 1, message = "gender 값은 M 또는 F이어야 합니다.") + private String gender; + + @Positive(message = "height 값은 양수이어야 합니다.") + private double height; + + @Positive(message = "remainingSessions 값은 양수이어야 합니다.") + private int remainingSessions; + + @NotNull(message = "targetType 값은 null이 될 수 없습니다.") + private TargetType targetType; + + @Positive(message = "targetValue 값은 양수이어야 합니다.") + private double targetValue; + + @NotNull(message = "targetReward 값은 null이 될 수 없습니다.") + @Size(min = 1, max = 255, message = "targetReward 값은 비어있을 수 없으며 255자를 초과할 수 없습니다.") + private String targetReward; +} \ No newline at end of file diff --git a/src/main/java/com/project/trainingdiary/dto/response/EditTraineeInfoResponseDto.java b/src/main/java/com/project/trainingdiary/dto/response/EditTraineeInfoResponseDto.java new file mode 100644 index 00000000..117a2037 --- /dev/null +++ b/src/main/java/com/project/trainingdiary/dto/response/EditTraineeInfoResponseDto.java @@ -0,0 +1,33 @@ +package com.project.trainingdiary.dto.response; + +import com.project.trainingdiary.entity.TraineeEntity; +import com.project.trainingdiary.model.TargetType; +import java.time.LocalDate; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +public class EditTraineeInfoResponseDto { + + private Long traineeId; + private LocalDate birthDate; + private String gender; + private double height; + private int remainingSessions; + private TargetType targetType; + private double targetValue; + private String targetReward; + + public static EditTraineeInfoResponseDto fromEntity(TraineeEntity trainee) { + return EditTraineeInfoResponseDto.builder() + .traineeId(trainee.getId()) + .birthDate(trainee.getBirthDate()) + .gender(trainee.getGender()) + .height(trainee.getHeight()) + .targetType(trainee.getTargetType()) + .targetValue(trainee.getTargetValue()) + .targetReward(trainee.getTargetReward()) + .build(); + } +} \ No newline at end of file From 9bb03d2d52350473cd2c32b13587fc8bd9d3a341 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:03:26 +0800 Subject: [PATCH 13/37] =?UTF-8?q?feat:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8B=88=20=EC=A1=B0=ED=9A=8C,=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8B=88=20=EC=A0=95=EB=B3=B4=20=EC=88=98=EC=A0=95,=20?= =?UTF-8?q?=ED=8A=B8=EB=A0=88=EC=9D=B4=EB=8B=88=20=EC=9D=B8=EB=B0=94?= =?UTF-8?q?=EB=94=94=20=EA=B8=B0=EB=A1=9D=20API=20=EA=B5=AC=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/TrainerController.java | 51 +++++++++++++++++++ 1 file changed, 51 insertions(+) diff --git a/src/main/java/com/project/trainingdiary/controller/TrainerController.java b/src/main/java/com/project/trainingdiary/controller/TrainerController.java index 20d62388..dba33e98 100644 --- a/src/main/java/com/project/trainingdiary/controller/TrainerController.java +++ b/src/main/java/com/project/trainingdiary/controller/TrainerController.java @@ -1,5 +1,56 @@ package com.project.trainingdiary.controller; +import com.project.trainingdiary.dto.request.AddInBodyInfoRequestDto; +import com.project.trainingdiary.dto.request.EditTraineeInfoRequestDto; +import com.project.trainingdiary.dto.response.AddInBodyInfoResponseDto; +import com.project.trainingdiary.dto.response.EditTraineeInfoResponseDto; +import com.project.trainingdiary.dto.response.TraineeInfoResponseDto; +import com.project.trainingdiary.service.TrainerService; +import jakarta.validation.Valid; +import lombok.RequiredArgsConstructor; +import org.springframework.http.ResponseEntity; +import org.springframework.security.access.prepost.PreAuthorize; +import org.springframework.web.bind.annotation.GetMapping; +import org.springframework.web.bind.annotation.PathVariable; +import org.springframework.web.bind.annotation.PostMapping; +import org.springframework.web.bind.annotation.PutMapping; +import org.springframework.web.bind.annotation.RequestBody; +import org.springframework.web.bind.annotation.RequestMapping; +import org.springframework.web.bind.annotation.RestController; + +@RestController +@RequiredArgsConstructor +@RequestMapping("api/trainers") public class TrainerController { + private final TrainerService trainerService; + + @PreAuthorize("hasRole('TRAINER')") + @GetMapping("/trainees/{id}") + public ResponseEntity getTraineeInfo( + @PathVariable Long id + ) { + + TraineeInfoResponseDto traineeInfo = trainerService.getTraineeInfo(id); + return ResponseEntity.ok(traineeInfo); + } + + @PreAuthorize("hasRole('TRAINER')") + @PutMapping("/trainees/") + public ResponseEntity editTraineeInfo( + @RequestBody @Valid EditTraineeInfoRequestDto dto + ) { + EditTraineeInfoResponseDto editTraineeInfo = trainerService.editTraineeInfo(dto); + return ResponseEntity.ok(editTraineeInfo); + } + + @PreAuthorize("hasRole('TRAINER')") + @PostMapping("/trainees/") + public ResponseEntity addInBodyRecord( + @RequestBody @Valid AddInBodyInfoRequestDto dto + ) { + + AddInBodyInfoResponseDto addInBodyInfo = trainerService.addInBodyRecord(dto); + return ResponseEntity.ok(addInBodyInfo); + } } From 73c02fbf14ea043e8d12290472de9fa97bd0277f Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:03:38 +0800 Subject: [PATCH 14/37] =?UTF-8?q?feat:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8B=88=20=EC=A1=B0=ED=9A=8C,=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8B=88=20=EC=A0=95=EB=B3=B4=20=EC=88=98=EC=A0=95,=20?= =?UTF-8?q?=ED=8A=B8=EB=A0=88=EC=9D=B4=EB=8B=88=20=EC=9D=B8=EB=B0=94?= =?UTF-8?q?=EB=94=94=20=EA=B8=B0=EB=A1=9D=20=EA=B8=B0=EB=8A=A5=20=EA=B5=AC?= =?UTF-8?q?=ED=98=84?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trainingdiary/service/TrainerService.java | 100 +++++++++++++++++- 1 file changed, 99 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/project/trainingdiary/service/TrainerService.java b/src/main/java/com/project/trainingdiary/service/TrainerService.java index 7af279d6..3f95feb1 100644 --- a/src/main/java/com/project/trainingdiary/service/TrainerService.java +++ b/src/main/java/com/project/trainingdiary/service/TrainerService.java @@ -1,6 +1,104 @@ package com.project.trainingdiary.service; +import com.project.trainingdiary.dto.request.AddInBodyInfoRequestDto; +import com.project.trainingdiary.dto.request.EditTraineeInfoRequestDto; +import com.project.trainingdiary.dto.response.AddInBodyInfoResponseDto; +import com.project.trainingdiary.dto.response.EditTraineeInfoResponseDto; +import com.project.trainingdiary.dto.response.TraineeInfoResponseDto; +import com.project.trainingdiary.entity.InBodyRecordHistoryEntity; +import com.project.trainingdiary.entity.PtContractEntity; +import com.project.trainingdiary.entity.TraineeEntity; +import com.project.trainingdiary.entity.TrainerEntity; +import com.project.trainingdiary.exception.impl.PtContractNotExistException; +import com.project.trainingdiary.exception.impl.TraineeNotExistException; +import com.project.trainingdiary.exception.impl.TrainerNotFoundException; +import com.project.trainingdiary.repository.InBodyRecordHistoryRepository; +import com.project.trainingdiary.repository.PtContractRepository; +import com.project.trainingdiary.repository.TraineeRepository; +import com.project.trainingdiary.repository.TrainerRepository; +import lombok.RequiredArgsConstructor; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; +import org.springframework.stereotype.Service; +import org.springframework.transaction.annotation.Transactional; + +@Service +@RequiredArgsConstructor public class TrainerService { -} + private final TraineeRepository traineeRepository; + private final TrainerRepository trainerRepository; + private final InBodyRecordHistoryRepository inBodyRecordHistoryRepository; + private final PtContractRepository ptContractRepository; + + public TraineeInfoResponseDto getTraineeInfo(Long id) { + TrainerEntity trainer = getAuthenticatedTrainer(); + TraineeEntity trainee = getTraineeById(id); + + checkContract(trainer, trainee); + + int totalSessions = ptContractRepository.findByTrainee(trainee).stream() + .mapToInt(PtContractEntity::getTotalSession) + .sum(); + int usedSessions = ptContractRepository.findByTrainee(trainee).stream() + .mapToInt(PtContractEntity::getUsedSession) + .sum(); + + int remainingSessions = totalSessions - usedSessions; + + return TraineeInfoResponseDto.fromEntity(trainee, remainingSessions); + } + + public AddInBodyInfoResponseDto addInBodyRecord(AddInBodyInfoRequestDto dto) { + TrainerEntity trainer = getAuthenticatedTrainer(); + TraineeEntity trainee = getTraineeById(dto.getTraineeId()); + + checkContract(trainer, trainee); + + InBodyRecordHistoryEntity inBodyRecord = AddInBodyInfoRequestDto.toEntity(dto, trainee); + inBodyRecordHistoryRepository.save(inBodyRecord); + return AddInBodyInfoResponseDto.fromEntity(inBodyRecord); + } + + @Transactional + public EditTraineeInfoResponseDto editTraineeInfo(EditTraineeInfoRequestDto dto) { + TrainerEntity trainer = getAuthenticatedTrainer(); + TraineeEntity trainee = getTraineeById(dto.getTraineeId()); + + checkContract(trainer, trainee); + + updateTraineeInfo(trainee, dto); + + return EditTraineeInfoResponseDto.fromEntity(trainee); + } + + private TraineeEntity getTraineeById(Long id) { + return traineeRepository.findById(id) + .orElseThrow(TraineeNotExistException::new); + } + + private TrainerEntity getAuthenticatedTrainer() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null || authentication.getName() == null) { + throw new TrainerNotFoundException(); + } + String email = authentication.getName(); + return trainerRepository.findByEmail(email) + .orElseThrow(TrainerNotFoundException::new); + } + + private void checkContract(TrainerEntity trainer, TraineeEntity trainee) { + if (!ptContractRepository.existsByTrainerIdAndTraineeId(trainer.getId(), trainee.getId())) { + throw new PtContractNotExistException(); + } + } + private void updateTraineeInfo(TraineeEntity trainee, EditTraineeInfoRequestDto dto) { + trainee.setBirthDate(dto.getBirthDate()); + trainee.setGender(dto.getGender()); + trainee.setHeight(dto.getHeight()); + trainee.setTargetType(dto.getTargetType()); + trainee.setTargetValue(dto.getTargetValue()); + trainee.setTargetReward(dto.getTargetReward()); + } +} \ No newline at end of file From d6bf21b5d0e5c13c8129b330899a6bf6203d0f3a Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:03:50 +0800 Subject: [PATCH 15/37] =?UTF-8?q?feat:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=8B=88=20=EC=A0=95=EB=B3=B4=20=EC=A1=B0=ED=9A=8C=20dto?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/response/TraineeInfoResponseDto.java | 65 +++++++++++++++++++ 1 file changed, 65 insertions(+) create mode 100644 src/main/java/com/project/trainingdiary/dto/response/TraineeInfoResponseDto.java diff --git a/src/main/java/com/project/trainingdiary/dto/response/TraineeInfoResponseDto.java b/src/main/java/com/project/trainingdiary/dto/response/TraineeInfoResponseDto.java new file mode 100644 index 00000000..4c643815 --- /dev/null +++ b/src/main/java/com/project/trainingdiary/dto/response/TraineeInfoResponseDto.java @@ -0,0 +1,65 @@ +package com.project.trainingdiary.dto.response; + +import com.project.trainingdiary.dto.traineeRecordDto.BodyFatHistoryDto; +import com.project.trainingdiary.dto.traineeRecordDto.MuscleMassHistoryDto; +import com.project.trainingdiary.dto.traineeRecordDto.WeightHistoryDto; +import com.project.trainingdiary.entity.TraineeEntity; +import com.project.trainingdiary.model.TargetType; +import java.time.LocalDate; +import java.time.Period; +import java.util.List; +import java.util.stream.Collectors; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; + +@Getter +@Builder +@AllArgsConstructor +public class TraineeInfoResponseDto { + + private long traineeId; + private String name; + private int age; + private String gender; + private double height; + private int remainingSessions; + + private List weightHistory; + private List bodyFatHistory; + private List muscleMassHistory; + private TargetType targetType; + private double targetValue; + private String targetReward; + + public static TraineeInfoResponseDto fromEntity(TraineeEntity trainee, int remainingSessions) { + return TraineeInfoResponseDto.builder() + .traineeId(trainee.getId()) + .name(trainee.getName()) + .age(calculateAge(trainee.getBirthDate())) + .gender(trainee.getGender()) + .height(trainee.getHeight()) + .remainingSessions(remainingSessions) + .weightHistory(trainee.getInBodyRecords().stream() + .map(WeightHistoryDto::fromEntity) + .collect(Collectors.toList())) + .bodyFatHistory(trainee.getInBodyRecords().stream() + .map(BodyFatHistoryDto::fromEntity) + .collect(Collectors.toList())) + .muscleMassHistory(trainee.getInBodyRecords().stream() + .map(MuscleMassHistoryDto::fromEntity) + .collect(Collectors.toList())) + .targetType(trainee.getTargetType()) + .targetValue(trainee.getTargetValue()) + .targetReward(trainee.getTargetReward()) + .build(); + } + + private static int calculateAge(LocalDate birthDate) { + if (birthDate != null) { + return Period.between(birthDate, LocalDate.now()).getYears(); + } else { + return 0; + } + } +} \ No newline at end of file From b3f42cdc5349e01f4dea6314614d5178b5432a3c Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:04:03 +0800 Subject: [PATCH 16/37] =?UTF-8?q?feat:=20=EC=9D=B8=EB=B0=94=EB=94=94=20?= =?UTF-8?q?=EA=B8=B0=EB=A1=9D=EC=9A=A9=20entity=20=EB=B0=8F=20repository?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../entity/InBodyRecordHistoryEntity.java | 35 +++++++++++++++++++ .../InBodyRecordHistoryRepository.java | 9 +++++ 2 files changed, 44 insertions(+) create mode 100644 src/main/java/com/project/trainingdiary/entity/InBodyRecordHistoryEntity.java create mode 100644 src/main/java/com/project/trainingdiary/repository/InBodyRecordHistoryRepository.java diff --git a/src/main/java/com/project/trainingdiary/entity/InBodyRecordHistoryEntity.java b/src/main/java/com/project/trainingdiary/entity/InBodyRecordHistoryEntity.java new file mode 100644 index 00000000..a4b13da7 --- /dev/null +++ b/src/main/java/com/project/trainingdiary/entity/InBodyRecordHistoryEntity.java @@ -0,0 +1,35 @@ +package com.project.trainingdiary.entity; + +import static jakarta.persistence.GenerationType.IDENTITY; + +import jakarta.persistence.Entity; +import jakarta.persistence.GeneratedValue; +import jakarta.persistence.Id; +import jakarta.persistence.JoinColumn; +import jakarta.persistence.ManyToOne; +import lombok.AllArgsConstructor; +import lombok.Builder; +import lombok.Getter; +import lombok.NoArgsConstructor; +import lombok.Setter; + +@Entity(name = "in_body_record_history") +@Builder +@Getter +@Setter +@AllArgsConstructor +@NoArgsConstructor +public class InBodyRecordHistoryEntity extends BaseEntity { + + @Id + @GeneratedValue(strategy = IDENTITY) + private long id; + + private double weight; + private double bodyFatPercentage; + private double skeletalMuscleMass; + + @ManyToOne + @JoinColumn(name = "trainee_id", nullable = false) + private TraineeEntity trainee; +} \ No newline at end of file diff --git a/src/main/java/com/project/trainingdiary/repository/InBodyRecordHistoryRepository.java b/src/main/java/com/project/trainingdiary/repository/InBodyRecordHistoryRepository.java new file mode 100644 index 00000000..39c37e78 --- /dev/null +++ b/src/main/java/com/project/trainingdiary/repository/InBodyRecordHistoryRepository.java @@ -0,0 +1,9 @@ +package com.project.trainingdiary.repository; + +import com.project.trainingdiary.entity.InBodyRecordHistoryEntity; +import org.springframework.data.jpa.repository.JpaRepository; + +public interface InBodyRecordHistoryRepository extends + JpaRepository { + +} From 48eef3051b9ad66deebe6e3f44512c9a36e1e201 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:04:35 +0800 Subject: [PATCH 17/37] =?UTF-8?q?test:=20=ED=8A=B8=EB=A0=88=EC=9D=B4?= =?UTF-8?q?=EB=84=88=20API=20test=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/TrainerServiceTest.java | 684 +++++++++--------- 1 file changed, 345 insertions(+), 339 deletions(-) diff --git a/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java b/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java index 5f0f3c52..bae491b3 100644 --- a/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java +++ b/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java @@ -1,5 +1,12 @@ package com.project.trainingdiary.service; +import static org.junit.jupiter.api.Assertions.assertEquals; +import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.mockito.Mockito.any; +import static org.mockito.Mockito.times; +import static org.mockito.Mockito.verify; +import static org.mockito.Mockito.when; + import com.project.trainingdiary.dto.request.AddInBodyInfoRequestDto; import com.project.trainingdiary.dto.request.EditTraineeInfoRequestDto; import com.project.trainingdiary.dto.response.AddInBodyInfoResponseDto; @@ -18,6 +25,9 @@ import com.project.trainingdiary.repository.TraineeRepository; import com.project.trainingdiary.repository.TrainerRepository; import java.time.LocalDate; +import java.util.ArrayList; +import java.util.List; +import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; import org.junit.jupiter.api.Test; @@ -29,347 +39,343 @@ import org.springframework.security.core.context.SecurityContext; import org.springframework.security.core.context.SecurityContextHolder; -import java.util.ArrayList; -import java.util.List; -import java.util.Optional; - -import static org.junit.jupiter.api.Assertions.assertEquals; -import static org.junit.jupiter.api.Assertions.assertThrows; -import static org.mockito.Mockito.*; - @ExtendWith(MockitoExtension.class) public class TrainerServiceTest { - @Mock - private TraineeRepository traineeRepository; - - @Mock - private TrainerRepository trainerRepository; - - @Mock - private InBodyRecordHistoryRepository inBodyRecordHistoryRepository; - - @Mock - private PtContractRepository ptContractRepository; - - @Mock - private Authentication authentication; - - @Mock - private SecurityContext securityContext; - - @InjectMocks - private TrainerService trainerService; - - @BeforeEach - public void setUp() { - SecurityContextHolder.setContext(securityContext); - when(securityContext.getAuthentication()).thenReturn(authentication); - } - - @Test - @DisplayName("인증되지 않은 트레이너가 트레이니 정보를 요청할 때 예외 발생") - void testGetTraineeInfo_AuthenticationFailure() { - // given - when(securityContext.getAuthentication()).thenReturn(null); - // when / then - assertThrows(TrainerNotFoundException.class, () -> trainerService.getTraineeInfo(1L)); - } - - @Test - @DisplayName("인증되지 않은 트레이너가 인바디 기록을 추가할 때 예외 발생") - void testAddInBodyRecord_AuthenticationFailure() { - // given - AddInBodyInfoRequestDto dto = new AddInBodyInfoRequestDto(); - dto.setTraineeId(1L); - - when(securityContext.getAuthentication()).thenReturn(null); - - // when / then - assertThrows(TrainerNotFoundException.class, () -> trainerService.addInBodyRecord(dto)); - } - - @Test - @DisplayName("인증되지 않은 트레이너가 트레이니 정보를 수정할 때 예외 발생") - void testEditTraineeInfo_AuthenticationFailure() { - // given - EditTraineeInfoRequestDto dto = new EditTraineeInfoRequestDto(); - dto.setTraineeId(1L); - - when(securityContext.getAuthentication()).thenReturn(null); - - // when / then - assertThrows(TrainerNotFoundException.class, () -> trainerService.editTraineeInfo(dto)); - } - - @Test - @DisplayName("트레이니 정보 조회 성공") - void testGetTraineeInfo_Success() { - // given - Long traineeId = 1L; - Long trainerId = 1L; - String trainerEmail = "trainer@example.com"; - - TrainerEntity trainer = new TrainerEntity(); - trainer.setId(trainerId); - - TraineeEntity trainee = new TraineeEntity(); - trainee.setId(traineeId); - trainee.setInBodyRecords(new ArrayList<>()); - - PtContractEntity contract = new PtContractEntity(); - contract.setTrainer(trainer); - contract.setTrainee(trainee); - contract.setTotalSession(10); - contract.setUsedSession(5); - - when(authentication.getName()).thenReturn(trainerEmail); - when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); - when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); - when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(true); - when(ptContractRepository.findByTrainee(trainee)).thenReturn(List.of(contract)); - - // when - TraineeInfoResponseDto responseDto = trainerService.getTraineeInfo(traineeId); - - // then - assertEquals(5, responseDto.getRemainingSessions()); - assertEquals(traineeId, responseDto.getTraineeId()); - verify(trainerRepository, times(1)).findByEmail(trainerEmail); - verify(traineeRepository, times(1)).findById(traineeId); - verify(ptContractRepository, times(1)).existsByTrainerIdAndTraineeId(trainerId, traineeId); - } - - @Test - @DisplayName("트레이니가 존재하지 않을 때 예외 발생") - void testGetTraineeInfo_TraineeNotExistException() { - // given - Long traineeId = 1L; - String trainerEmail = "trainer@example.com"; - - TrainerEntity trainer = new TrainerEntity(); - trainer.setId(1L); - - when(authentication.getName()).thenReturn(trainerEmail); - when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); - when(traineeRepository.findById(traineeId)).thenReturn(Optional.empty()); - - // when / then - assertThrows(TraineeNotExistException.class, () -> trainerService.getTraineeInfo(traineeId)); - verify(trainerRepository, times(1)).findByEmail(trainerEmail); - verify(traineeRepository, times(1)).findById(traineeId); - } - - @Test - @DisplayName("트레이니 정보 수정 성공") - void testEditTraineeInfo_Success() { - // given - Long traineeId = 1L; - Long trainerId = 1L; - String trainerEmail = "trainer@example.com"; - - TrainerEntity trainer = new TrainerEntity(); - trainer.setId(trainerId); - - TraineeEntity trainee = new TraineeEntity(); - trainee.setId(traineeId); - - EditTraineeInfoRequestDto dto = new EditTraineeInfoRequestDto(); - dto.setTraineeId(traineeId); - dto.setBirthDate(LocalDate.parse("2000-01-01")); - dto.setGender("Male"); - dto.setHeight(180); - dto.setTargetType(TargetType.TARGET_BODY_FAT_PERCENTAGE); - dto.setTargetValue(70); - dto.setTargetReward("Reward"); - - when(authentication.getName()).thenReturn(trainerEmail); - when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); - when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); - when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(true); - - // when - EditTraineeInfoResponseDto responseDto = trainerService.editTraineeInfo(dto); - - // then - assertEquals(dto.getBirthDate(), responseDto.getBirthDate()); - assertEquals(dto.getGender(), responseDto.getGender()); - assertEquals(dto.getHeight(), responseDto.getHeight()); - assertEquals(dto.getTargetType(), responseDto.getTargetType()); - assertEquals(dto.getTargetValue(), responseDto.getTargetValue()); - assertEquals(dto.getTargetReward(), responseDto.getTargetReward()); - - verify(trainerRepository, times(1)).findByEmail(trainerEmail); - verify(traineeRepository, times(1)).findById(traineeId); - verify(ptContractRepository, times(1)).existsByTrainerIdAndTraineeId(trainerId, traineeId); - } - - @Test - @DisplayName("트레이니 정보 수정 시 트레이니가 존재하지 않을 때 예외 발생") - void testEditTraineeInfo_TraineeNotExistException() { - // given - Long traineeId = 1L; - String trainerEmail = "trainer@example.com"; - - TrainerEntity trainer = new TrainerEntity(); - trainer.setId(1L); - - EditTraineeInfoRequestDto dto = new EditTraineeInfoRequestDto(); - dto.setTraineeId(traineeId); - - when(authentication.getName()).thenReturn(trainerEmail); - when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); - when(traineeRepository.findById(traineeId)).thenReturn(Optional.empty()); - - // when / then - assertThrows(TraineeNotExistException.class, () -> trainerService.editTraineeInfo(dto)); - verify(trainerRepository, times(1)).findByEmail(trainerEmail); - verify(traineeRepository, times(1)).findById(traineeId); - } - - @Test - @DisplayName("인바디 기록 추가 성공") - void testAddInBodyRecord_Success() { - // given - Long traineeId = 1L; - Long trainerId = 1L; - String trainerEmail = "trainer@example.com"; - - TrainerEntity trainer = new TrainerEntity(); - trainer.setId(trainerId); - - TraineeEntity trainee = new TraineeEntity(); - trainee.setId(traineeId); - - AddInBodyInfoRequestDto dto = new AddInBodyInfoRequestDto(); - dto.setTraineeId(traineeId); - dto.setWeight(70.0); - dto.setSkeletalMuscleMass(30.0); - dto.setBodyFatPercentage(20.0); - - InBodyRecordHistoryEntity inBodyRecord = new InBodyRecordHistoryEntity(); - inBodyRecord.setTrainee(trainee); - inBodyRecord.setWeight(dto.getWeight()); - inBodyRecord.setSkeletalMuscleMass(dto.getSkeletalMuscleMass()); - inBodyRecord.setBodyFatPercentage(dto.getBodyFatPercentage()); - - when(authentication.getName()).thenReturn(trainerEmail); - when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); - when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); - when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(true); - when(inBodyRecordHistoryRepository.save(any(InBodyRecordHistoryEntity.class))).thenReturn(inBodyRecord); - - // when - AddInBodyInfoResponseDto responseDto = trainerService.addInBodyRecord(dto); - - // then - assertEquals(dto.getWeight(), responseDto.getWeight()); - assertEquals(dto.getSkeletalMuscleMass(), responseDto.getSkeletalMuscleMass()); - assertEquals(dto.getBodyFatPercentage(), responseDto.getBodyFatPercentage()); - - verify(trainerRepository, times(1)).findByEmail(trainerEmail); - verify(traineeRepository, times(1)).findById(traineeId); - verify(ptContractRepository, times(1)).existsByTrainerIdAndTraineeId(trainerId, traineeId); - verify(inBodyRecordHistoryRepository, times(1)).save(any(InBodyRecordHistoryEntity.class)); - } - - @Test - @DisplayName("인바디 기록 추가 시 트레이니가 존재하지 않을 때 예외 발생") - void testAddInBodyRecord_TraineeNotExistException() { - // given - Long traineeId = 1L; - String trainerEmail = "trainer@example.com"; - - TrainerEntity trainer = new TrainerEntity(); - trainer.setId(1L); - - AddInBodyInfoRequestDto dto = new AddInBodyInfoRequestDto(); - dto.setTraineeId(traineeId); - - when(authentication.getName()).thenReturn(trainerEmail); - when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); - when(traineeRepository.findById(traineeId)).thenReturn(Optional.empty()); - - // when / then - assertThrows(TraineeNotExistException.class, () -> trainerService.addInBodyRecord(dto)); - verify(trainerRepository, times(1)).findByEmail(trainerEmail); - verify(traineeRepository, times(1)).findById(traineeId); - - } - - @Test - @DisplayName("트레이너와 트레이니 사이에 계약이 존재하지 않을 때 예외 발생") - void testGetTraineeInfo_ContractNotExist() { - // given - Long traineeId = 1L; - Long trainerId = 1L; - String trainerEmail = "trainer@example.com"; - - TrainerEntity trainer = new TrainerEntity(); - trainer.setId(trainerId); - - TraineeEntity trainee = new TraineeEntity(); - trainee.setId(traineeId); - - when(authentication.getName()).thenReturn(trainerEmail); - when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); - when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); - when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(false); - - // when / then - assertThrows(PtContractNotExistException.class, () -> trainerService.getTraineeInfo(traineeId)); - } - - @Test - @DisplayName("트레이너와 트레이니 사이에 계약이 존재하지 않을 때 인바디 기록 추가 시 예외 발생") - void testAddInBodyRecord_ContractNotExist() { - // given - Long traineeId = 1L; - Long trainerId = 1L; - String trainerEmail = "trainer@example.com"; - - TrainerEntity trainer = new TrainerEntity(); - trainer.setId(trainerId); - - TraineeEntity trainee = new TraineeEntity(); - trainee.setId(traineeId); - - AddInBodyInfoRequestDto dto = new AddInBodyInfoRequestDto(); - dto.setTraineeId(traineeId); - - when(authentication.getName()).thenReturn(trainerEmail); - when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); - when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); - when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(false); - - // when / then - assertThrows(PtContractNotExistException.class, () -> trainerService.addInBodyRecord(dto)); - } - - @Test - @DisplayName("트레이너와 트레이니 사이에 계약이 존재하지 않을 때 트레이니 정보 수정 시 예외 발생") - void testEditTraineeInfo_ContractNotExist() { - // given - Long traineeId = 1L; - Long trainerId = 1L; - String trainerEmail = "trainer@example.com"; - - TrainerEntity trainer = new TrainerEntity(); - trainer.setId(trainerId); - - TraineeEntity trainee = new TraineeEntity(); - trainee.setId(traineeId); - - EditTraineeInfoRequestDto dto = new EditTraineeInfoRequestDto(); - dto.setTraineeId(traineeId); - - when(authentication.getName()).thenReturn(trainerEmail); - when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); - when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); - when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(false); - - // when / then - assertThrows(PtContractNotExistException.class, () -> trainerService.editTraineeInfo(dto)); - } + @Mock + private TraineeRepository traineeRepository; + + @Mock + private TrainerRepository trainerRepository; + + @Mock + private InBodyRecordHistoryRepository inBodyRecordHistoryRepository; + + @Mock + private PtContractRepository ptContractRepository; + + @Mock + private Authentication authentication; + + @Mock + private SecurityContext securityContext; + + @InjectMocks + private TrainerService trainerService; + + @BeforeEach + public void setUp() { + SecurityContextHolder.setContext(securityContext); + when(securityContext.getAuthentication()).thenReturn(authentication); + } + + @Test + @DisplayName("인증되지 않은 트레이너가 트레이니 정보를 요청할 때 예외 발생") + void testGetTraineeInfo_AuthenticationFailure() { + // given + when(securityContext.getAuthentication()).thenReturn(null); + // when / then + assertThrows(TrainerNotFoundException.class, () -> trainerService.getTraineeInfo(1L)); + } + + @Test + @DisplayName("인증되지 않은 트레이너가 인바디 기록을 추가할 때 예외 발생") + void testAddInBodyRecord_AuthenticationFailure() { + // given + AddInBodyInfoRequestDto dto = new AddInBodyInfoRequestDto(); + dto.setTraineeId(1L); + + when(securityContext.getAuthentication()).thenReturn(null); + + // when / then + assertThrows(TrainerNotFoundException.class, () -> trainerService.addInBodyRecord(dto)); + } + + @Test + @DisplayName("인증되지 않은 트레이너가 트레이니 정보를 수정할 때 예외 발생") + void testEditTraineeInfo_AuthenticationFailure() { + // given + EditTraineeInfoRequestDto dto = new EditTraineeInfoRequestDto(); + dto.setTraineeId(1L); + + when(securityContext.getAuthentication()).thenReturn(null); + + // when / then + assertThrows(TrainerNotFoundException.class, () -> trainerService.editTraineeInfo(dto)); + } + + @Test + @DisplayName("트레이니 정보 조회 성공") + void testGetTraineeInfo_Success() { + // given + Long traineeId = 1L; + Long trainerId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(trainerId); + + TraineeEntity trainee = new TraineeEntity(); + trainee.setId(traineeId); + trainee.setInBodyRecords(new ArrayList<>()); + + PtContractEntity contract = new PtContractEntity(); + contract.setTrainer(trainer); + contract.setTrainee(trainee); + contract.setTotalSession(10); + contract.setUsedSession(5); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); + when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(true); + when(ptContractRepository.findByTrainee(trainee)).thenReturn(List.of(contract)); + + // when + TraineeInfoResponseDto responseDto = trainerService.getTraineeInfo(traineeId); + + // then + assertEquals(5, responseDto.getRemainingSessions()); + assertEquals(traineeId, responseDto.getTraineeId()); + verify(trainerRepository, times(1)).findByEmail(trainerEmail); + verify(traineeRepository, times(1)).findById(traineeId); + verify(ptContractRepository, times(1)).existsByTrainerIdAndTraineeId(trainerId, traineeId); + } + + @Test + @DisplayName("트레이니가 존재하지 않을 때 예외 발생") + void testGetTraineeInfo_TraineeNotExistException() { + // given + Long traineeId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(1L); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.empty()); + + // when / then + assertThrows(TraineeNotExistException.class, () -> trainerService.getTraineeInfo(traineeId)); + verify(trainerRepository, times(1)).findByEmail(trainerEmail); + verify(traineeRepository, times(1)).findById(traineeId); + } + + @Test + @DisplayName("트레이니 정보 수정 성공") + void testEditTraineeInfo_Success() { + // given + Long traineeId = 1L; + Long trainerId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(trainerId); + + TraineeEntity trainee = new TraineeEntity(); + trainee.setId(traineeId); + + EditTraineeInfoRequestDto dto = new EditTraineeInfoRequestDto(); + dto.setTraineeId(traineeId); + dto.setBirthDate(LocalDate.parse("2000-01-01")); + dto.setGender("Male"); + dto.setHeight(180); + dto.setTargetType(TargetType.TARGET_BODY_FAT_PERCENTAGE); + dto.setTargetValue(70); + dto.setTargetReward("Reward"); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); + when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(true); + + // when + EditTraineeInfoResponseDto responseDto = trainerService.editTraineeInfo(dto); + + // then + assertEquals(dto.getBirthDate(), responseDto.getBirthDate()); + assertEquals(dto.getGender(), responseDto.getGender()); + assertEquals(dto.getHeight(), responseDto.getHeight()); + assertEquals(dto.getTargetType(), responseDto.getTargetType()); + assertEquals(dto.getTargetValue(), responseDto.getTargetValue()); + assertEquals(dto.getTargetReward(), responseDto.getTargetReward()); + + verify(trainerRepository, times(1)).findByEmail(trainerEmail); + verify(traineeRepository, times(1)).findById(traineeId); + verify(ptContractRepository, times(1)).existsByTrainerIdAndTraineeId(trainerId, traineeId); + } + + @Test + @DisplayName("트레이니 정보 수정 시 트레이니가 존재하지 않을 때 예외 발생") + void testEditTraineeInfo_TraineeNotExistException() { + // given + Long traineeId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(1L); + + EditTraineeInfoRequestDto dto = new EditTraineeInfoRequestDto(); + dto.setTraineeId(traineeId); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.empty()); + + // when / then + assertThrows(TraineeNotExistException.class, () -> trainerService.editTraineeInfo(dto)); + verify(trainerRepository, times(1)).findByEmail(trainerEmail); + verify(traineeRepository, times(1)).findById(traineeId); + } + + @Test + @DisplayName("인바디 기록 추가 성공") + void testAddInBodyRecord_Success() { + // given + Long traineeId = 1L; + Long trainerId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(trainerId); + + TraineeEntity trainee = new TraineeEntity(); + trainee.setId(traineeId); + + AddInBodyInfoRequestDto dto = new AddInBodyInfoRequestDto(); + dto.setTraineeId(traineeId); + dto.setWeight(70.0); + dto.setSkeletalMuscleMass(30.0); + dto.setBodyFatPercentage(20.0); + + InBodyRecordHistoryEntity inBodyRecord = new InBodyRecordHistoryEntity(); + inBodyRecord.setTrainee(trainee); + inBodyRecord.setWeight(dto.getWeight()); + inBodyRecord.setSkeletalMuscleMass(dto.getSkeletalMuscleMass()); + inBodyRecord.setBodyFatPercentage(dto.getBodyFatPercentage()); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); + when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(true); + when(inBodyRecordHistoryRepository.save(any(InBodyRecordHistoryEntity.class))).thenReturn( + inBodyRecord); + + // when + AddInBodyInfoResponseDto responseDto = trainerService.addInBodyRecord(dto); + + // then + assertEquals(dto.getWeight(), responseDto.getWeight()); + assertEquals(dto.getSkeletalMuscleMass(), responseDto.getSkeletalMuscleMass()); + assertEquals(dto.getBodyFatPercentage(), responseDto.getBodyFatPercentage()); + + verify(trainerRepository, times(1)).findByEmail(trainerEmail); + verify(traineeRepository, times(1)).findById(traineeId); + verify(ptContractRepository, times(1)).existsByTrainerIdAndTraineeId(trainerId, traineeId); + verify(inBodyRecordHistoryRepository, times(1)).save(any(InBodyRecordHistoryEntity.class)); + } + + @Test + @DisplayName("인바디 기록 추가 시 트레이니가 존재하지 않을 때 예외 발생") + void testAddInBodyRecord_TraineeNotExistException() { + // given + Long traineeId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(1L); + + AddInBodyInfoRequestDto dto = new AddInBodyInfoRequestDto(); + dto.setTraineeId(traineeId); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.empty()); + + // when / then + assertThrows(TraineeNotExistException.class, () -> trainerService.addInBodyRecord(dto)); + verify(trainerRepository, times(1)).findByEmail(trainerEmail); + verify(traineeRepository, times(1)).findById(traineeId); + + } + + @Test + @DisplayName("트레이너와 트레이니 사이에 계약이 존재하지 않을 때 예외 발생") + void testGetTraineeInfo_ContractNotExist() { + // given + Long traineeId = 1L; + Long trainerId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(trainerId); + + TraineeEntity trainee = new TraineeEntity(); + trainee.setId(traineeId); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); + when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn( + false); + + // when / then + assertThrows(PtContractNotExistException.class, () -> trainerService.getTraineeInfo(traineeId)); + } + + @Test + @DisplayName("트레이너와 트레이니 사이에 계약이 존재하지 않을 때 인바디 기록 추가 시 예외 발생") + void testAddInBodyRecord_ContractNotExist() { + // given + Long traineeId = 1L; + Long trainerId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(trainerId); + + TraineeEntity trainee = new TraineeEntity(); + trainee.setId(traineeId); + + AddInBodyInfoRequestDto dto = new AddInBodyInfoRequestDto(); + dto.setTraineeId(traineeId); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); + when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn( + false); + + // when / then + assertThrows(PtContractNotExistException.class, () -> trainerService.addInBodyRecord(dto)); + } + + @Test + @DisplayName("트레이너와 트레이니 사이에 계약이 존재하지 않을 때 트레이니 정보 수정 시 예외 발생") + void testEditTraineeInfo_ContractNotExist() { + // given + Long traineeId = 1L; + Long trainerId = 1L; + String trainerEmail = "trainer@example.com"; + + TrainerEntity trainer = new TrainerEntity(); + trainer.setId(trainerId); + + TraineeEntity trainee = new TraineeEntity(); + trainee.setId(traineeId); + + EditTraineeInfoRequestDto dto = new EditTraineeInfoRequestDto(); + dto.setTraineeId(traineeId); + + when(authentication.getName()).thenReturn(trainerEmail); + when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); + when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); + when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn( + false); + + // when / then + assertThrows(PtContractNotExistException.class, () -> trainerService.editTraineeInfo(dto)); + } } \ No newline at end of file From 78e5ffa240b1402b1b89562d3cf4eca6af25774a Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:09:01 +0800 Subject: [PATCH 18/37] =?UTF-8?q?refactor:=20conflict=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../repository/ptContract/PtContractRepository.java | 4 ++++ .../com/project/trainingdiary/service/TrainerService.java | 2 +- 2 files changed, 5 insertions(+), 1 deletion(-) diff --git a/src/main/java/com/project/trainingdiary/repository/ptContract/PtContractRepository.java b/src/main/java/com/project/trainingdiary/repository/ptContract/PtContractRepository.java index a6a2c53b..6592b0ea 100644 --- a/src/main/java/com/project/trainingdiary/repository/ptContract/PtContractRepository.java +++ b/src/main/java/com/project/trainingdiary/repository/ptContract/PtContractRepository.java @@ -1,6 +1,8 @@ package com.project.trainingdiary.repository.ptContract; import com.project.trainingdiary.entity.PtContractEntity; +import com.project.trainingdiary.entity.TraineeEntity; +import java.util.Collection; import java.util.Optional; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.data.jpa.repository.Query; @@ -26,4 +28,6 @@ public interface PtContractRepository extends JpaRepository findByTrainerIdAndTraineeId(long trainerId, long traineeId); + + Collection findByTrainee(TraineeEntity trainee); } diff --git a/src/main/java/com/project/trainingdiary/service/TrainerService.java b/src/main/java/com/project/trainingdiary/service/TrainerService.java index 3f95feb1..2a77bdca 100644 --- a/src/main/java/com/project/trainingdiary/service/TrainerService.java +++ b/src/main/java/com/project/trainingdiary/service/TrainerService.java @@ -13,9 +13,9 @@ import com.project.trainingdiary.exception.impl.TraineeNotExistException; import com.project.trainingdiary.exception.impl.TrainerNotFoundException; import com.project.trainingdiary.repository.InBodyRecordHistoryRepository; -import com.project.trainingdiary.repository.PtContractRepository; import com.project.trainingdiary.repository.TraineeRepository; import com.project.trainingdiary.repository.TrainerRepository; +import com.project.trainingdiary.repository.ptContract.PtContractRepository; import lombok.RequiredArgsConstructor; import org.springframework.security.core.Authentication; import org.springframework.security.core.context.SecurityContextHolder; From 9b1a93f8402b8fd1d58126e7d0220ca30939ccec Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:29:01 +0800 Subject: [PATCH 19/37] =?UTF-8?q?refactor:=20=ED=85=8C=EC=8A=A4=ED=8A=B8?= =?UTF-8?q?=20=EC=BC=80=EC=9D=B4=EC=8A=A4=20=EC=84=9C=EB=B9=84=EC=8A=A4?= =?UTF-8?q?=EC=97=90=20=EB=A7=9E=EC=B6=B0=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trainingdiary/service/TrainerServiceTest.java | 2 +- .../project/trainingdiary/service/UserServiceTest.java | 10 ++++++++++ 2 files changed, 11 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java b/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java index bae491b3..0f7c9b83 100644 --- a/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java +++ b/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java @@ -21,9 +21,9 @@ import com.project.trainingdiary.exception.impl.TrainerNotFoundException; import com.project.trainingdiary.model.TargetType; import com.project.trainingdiary.repository.InBodyRecordHistoryRepository; -import com.project.trainingdiary.repository.PtContractRepository; import com.project.trainingdiary.repository.TraineeRepository; import com.project.trainingdiary.repository.TrainerRepository; +import com.project.trainingdiary.repository.ptContract.PtContractRepository; import java.time.LocalDate; import java.util.ArrayList; import java.util.List; diff --git a/src/test/java/com/project/trainingdiary/service/UserServiceTest.java b/src/test/java/com/project/trainingdiary/service/UserServiceTest.java index e615b3ef..b014777e 100644 --- a/src/test/java/com/project/trainingdiary/service/UserServiceTest.java +++ b/src/test/java/com/project/trainingdiary/service/UserServiceTest.java @@ -195,12 +195,22 @@ void checkVerificationCodeSuccess() { @Test @DisplayName("회원가입 실패 - 비밀번호 불일치") void signUpFailPasswordMismatch() { + // given SignUpRequestDto signUpDto = new SignUpRequestDto(); signUpDto.setEmail("new@example.com"); signUpDto.setPassword("password"); signUpDto.setConfirmPassword("differentPassword"); signUpDto.setRole(UserRoleType.TRAINEE); + VerificationEntity verificationEntity = new VerificationEntity(); + verificationEntity.setEmail("new@example.com"); + verificationEntity.setVerificationCode("123456"); + verificationEntity.setExpiredAt(LocalDateTime.now().plusMinutes(5)); + + when(verificationRepository.findByEmail(signUpDto.getEmail())) + .thenReturn(java.util.Optional.of(verificationEntity)); + + // when / then assertThrows(PasswordMismatchedException.class, () -> userService.signUp(signUpDto, null)); } From 3b9667e1d563792b8552c4117c94f13152939182 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 21:29:24 +0800 Subject: [PATCH 20/37] =?UTF-8?q?refactor:=20=EB=A9=94=EC=84=9C=EB=93=9C?= =?UTF-8?q?=EB=A7=88=EB=8B=A4=20=EC=A3=BC=EC=84=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trainingdiary/service/TrainerService.java | 44 ++++++ .../trainingdiary/service/UserService.java | 130 +++++++++++++++++- 2 files changed, 171 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/project/trainingdiary/service/TrainerService.java b/src/main/java/com/project/trainingdiary/service/TrainerService.java index 2a77bdca..64aedf2c 100644 --- a/src/main/java/com/project/trainingdiary/service/TrainerService.java +++ b/src/main/java/com/project/trainingdiary/service/TrainerService.java @@ -31,6 +31,12 @@ public class TrainerService { private final InBodyRecordHistoryRepository inBodyRecordHistoryRepository; private final PtContractRepository ptContractRepository; + /** + * 트레이니 정보를 조회합니다. + * + * @param id 트레이니의 ID + * @return TraineeInfoResponseDto 트레이니의 정보와 남은 세션 수를 포함한 응답 DTO + */ public TraineeInfoResponseDto getTraineeInfo(Long id) { TrainerEntity trainer = getAuthenticatedTrainer(); TraineeEntity trainee = getTraineeById(id); @@ -49,6 +55,12 @@ public TraineeInfoResponseDto getTraineeInfo(Long id) { return TraineeInfoResponseDto.fromEntity(trainee, remainingSessions); } + /** + * 새로운 인바디 기록을 추가합니다. + * + * @param dto 인바디 기록 추가 요청 DTO + * @return AddInBodyInfoResponseDto 추가된 인바디 기록을 포함한 응답 DTO + */ public AddInBodyInfoResponseDto addInBodyRecord(AddInBodyInfoRequestDto dto) { TrainerEntity trainer = getAuthenticatedTrainer(); TraineeEntity trainee = getTraineeById(dto.getTraineeId()); @@ -60,6 +72,12 @@ public AddInBodyInfoResponseDto addInBodyRecord(AddInBodyInfoRequestDto dto) { return AddInBodyInfoResponseDto.fromEntity(inBodyRecord); } + /** + * 트레이니 정보를 수정합니다. + * + * @param dto 트레이니 정보 수정 요청 DTO + * @return EditTraineeInfoResponseDto 수정된 트레이니 정보를 포함한 응답 DTO + */ @Transactional public EditTraineeInfoResponseDto editTraineeInfo(EditTraineeInfoRequestDto dto) { TrainerEntity trainer = getAuthenticatedTrainer(); @@ -72,11 +90,24 @@ public EditTraineeInfoResponseDto editTraineeInfo(EditTraineeInfoRequestDto dto) return EditTraineeInfoResponseDto.fromEntity(trainee); } + /** + * 트레이니 ID로 트레이니를 조회합니다. + * + * @param id 트레이니의 ID + * @return TraineeEntity 트레이니 엔티티 + * @throws TraineeNotExistException 트레이니가 존재하지 않을 경우 예외 발생 + */ private TraineeEntity getTraineeById(Long id) { return traineeRepository.findById(id) .orElseThrow(TraineeNotExistException::new); } + /** + * 인증된 트레이너를 조회합니다. + * + * @return TrainerEntity 트레이너 엔티티 + * @throws TrainerNotFoundException 트레이너가 존재하지 않을 경우 예외 발생 + */ private TrainerEntity getAuthenticatedTrainer() { Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); if (authentication == null || authentication.getName() == null) { @@ -87,12 +118,25 @@ private TrainerEntity getAuthenticatedTrainer() { .orElseThrow(TrainerNotFoundException::new); } + /** + * 트레이너와 트레이니 간의 계약을 확인합니다. + * + * @param trainer 트레이너 엔티티 + * @param trainee 트레이니 엔티티 + * @throws PtContractNotExistException 계약이 존재하지 않을 경우 예외 발생 + */ private void checkContract(TrainerEntity trainer, TraineeEntity trainee) { if (!ptContractRepository.existsByTrainerIdAndTraineeId(trainer.getId(), trainee.getId())) { throw new PtContractNotExistException(); } } + /** + * 트레이니 정보를 업데이트합니다. + * + * @param trainee 트레이니 엔티티 + * @param dto 트레이니 정보 수정 요청 DTO + */ private void updateTraineeInfo(TraineeEntity trainee, EditTraineeInfoRequestDto dto) { trainee.setBirthDate(dto.getBirthDate()); trainee.setGender(dto.getGender()); diff --git a/src/main/java/com/project/trainingdiary/service/UserService.java b/src/main/java/com/project/trainingdiary/service/UserService.java index 1ccbc09b..771e4e69 100644 --- a/src/main/java/com/project/trainingdiary/service/UserService.java +++ b/src/main/java/com/project/trainingdiary/service/UserService.java @@ -10,6 +10,8 @@ import com.project.trainingdiary.entity.TrainerEntity; import com.project.trainingdiary.entity.VerificationEntity; import com.project.trainingdiary.exception.impl.PasswordMismatchedException; +import com.project.trainingdiary.exception.impl.TraineeEmailDuplicateException; +import com.project.trainingdiary.exception.impl.TrainerEmailDuplicateException; import com.project.trainingdiary.exception.impl.UserNotFoundException; import com.project.trainingdiary.exception.impl.VerificationCodeExpiredException; import com.project.trainingdiary.exception.impl.VerificationCodeNotMatchedException; @@ -56,17 +58,33 @@ public class UserService implements UserDetailsService { private static final String REFRESH_TOKEN_COOKIE_NAME = "Refresh-Token"; private static final String ACCESS_TOKEN_COOKIE_NAME = "Access-Token"; + /** + * 이메일 중복을 확인하고 인증 코드를 전송합니다. + * + * @param dto 이메일 중복 확인 및 인증 코드 전송 요청 DTO + */ public void checkDuplicateEmailAndSendVerification( SendVerificationAndCheckDuplicateRequestDto dto) { validateEmailNotExists(dto.getEmail()); sendVerificationCode(dto.getEmail()); } + /** + * 인증 코드를 확인합니다. + * + * @param dto 인증 코드 확인 요청 DTO + */ public void checkVerificationCode(VerifyCodeRequestDto dto) { VerificationEntity verificationEntity = getVerificationEntity(dto.getEmail()); validateVerificationCode(verificationEntity, dto.getVerificationCode()); } + /** + * 회원가입을 처리합니다. + * + * @param dto 회원가입 요청 DTO + * @param response HTTP 응답 객체 + */ @Transactional public void signUp(SignUpRequestDto dto, HttpServletResponse response) { VerificationEntity verificationEntity = getVerificationEntity(dto.getEmail()); @@ -79,35 +97,77 @@ public void signUp(SignUpRequestDto dto, HttpServletResponse response) { verificationRepository.deleteByEmail(dto.getEmail()); } + /** + * 로그인 요청을 처리합니다. + * + * @param dto 로그인 요청 DTO + * @param response HTTP 응답 객체 + * @return SignInResponseDto 로그인 응답 DTO + */ public SignInResponseDto signIn(SignInRequestDto dto, HttpServletResponse response) { UserDetails userDetails = loadUserByUsername(dto.getEmail()); validatePassword(dto.getPassword(), userDetails.getPassword()); return generateTokensAndSetCookies(userDetails.getUsername(), response); } + /** + * 로그아웃 요청을 처리합니다. + * + * @param request HTTP 요청 객체 + * @param response HTTP 응답 객체 + */ public void signOut(HttpServletRequest request, HttpServletResponse response) { blacklistAndClearCookies(request, response); } + /** + * 이메일 중복을 확인합니다. + * + * @param email 확인할 이메일 + * @throws TraineeEmailDuplicateException 트레이니 이메일이 중복되면 예외 발생 + * @throws TrainerEmailDuplicateException 트레이너 이메일이 중복되면 예외 발생 + */ private void validateEmailNotExists(String email) { - if (traineeRepository.findByEmail(email).isPresent() || trainerRepository.findByEmail(email) - .isPresent()) { - throw new UserNotFoundException(); + if (traineeRepository.findByEmail(email).isPresent()) { + throw new TraineeEmailDuplicateException(); + } + if (trainerRepository.findByEmail(email).isPresent()) { + throw new TrainerEmailDuplicateException(); } } + /** + * 인증 코드를 전송합니다. + * + * @param email 인증 코드를 전송할 이메일 + */ private void sendVerificationCode(String email) { String verificationCode = VerificationCodeGeneratorUtil.generateVerificationCode(); emailProvider.sendVerificationEmail(email, verificationCode); verificationRepository.save(VerificationEntity.of(email, verificationCode)); } + /** + * 비밀번호와 비밀번호 확인이 일치하는지 확인합니다. + * + * @param password 비밀번호 + * @param confirmPassword 비밀번호 확인 + * @throws PasswordMismatchedException 비밀번호와 비밀번호 확인이 일치하지 않으면 예외 발생 + */ private void validatePasswordsMatch(String password, String confirmPassword) { if (!password.equals(confirmPassword)) { throw new PasswordMismatchedException(); } } + /** + * 인증 코드를 확인합니다. + * + * @param verificationEntity 인증 엔티티 + * @param verificationCode 인증 코드 + * @throws VerificationCodeNotMatchedException 인증 코드가 일치하지 않으면 예외 발생 + * @throws VerificationCodeExpiredException 인증 코드가 만료되면 예외 발생 + */ private void validateVerificationCode(VerificationEntity verificationEntity, String verificationCode) { if (!verificationEntity.getVerificationCode().equals(verificationCode)) { @@ -118,10 +178,23 @@ private void validateVerificationCode(VerificationEntity verificationEntity, } } + /** + * 이메일로 인증 엔티티를 조회합니다. + * + * @param email 이메일 + * @return VerificationEntity 인증 엔티티 + * @throws UserNotFoundException 인증 엔티티가 존재하지 않으면 예외 발생 + */ private VerificationEntity getVerificationEntity(String email) { return verificationRepository.findByEmail(email).orElseThrow(UserNotFoundException::new); } + /** + * 회원가입 정보를 저장합니다. + * + * @param dto 회원가입 요청 DTO + * @param encodedPassword 인코딩된 비밀번호 + */ private void saveUser(SignUpRequestDto dto, String encodedPassword) { if (dto.getRole() == UserRoleType.TRAINEE) { saveTrainee(dto, encodedPassword); @@ -132,6 +205,12 @@ private void saveUser(SignUpRequestDto dto, String encodedPassword) { } } + /** + * 트레이니 정보를 저장합니다. + * + * @param dto 회원가입 요청 DTO + * @param encodedPassword 인코딩된 비밀번호 + */ private void saveTrainee(SignUpRequestDto dto, String encodedPassword) { TraineeEntity trainee = TraineeEntity.builder() .name(dto.getName()) @@ -142,6 +221,12 @@ private void saveTrainee(SignUpRequestDto dto, String encodedPassword) { traineeRepository.save(trainee); } + /** + * 트레이너 정보를 저장합니다. + * + * @param dto 회원가입 요청 DTO + * @param encodedPassword 인코딩된 비밀번호 + */ private void saveTrainer(SignUpRequestDto dto, String encodedPassword) { TrainerEntity trainer = TrainerEntity.builder() .name(dto.getName()) @@ -152,12 +237,26 @@ private void saveTrainer(SignUpRequestDto dto, String encodedPassword) { trainerRepository.save(trainer); } + /** + * 비밀번호를 확인합니다. + * + * @param rawPassword 입력한 비밀번호 + * @param encodedPassword 저장된 인코딩된 비밀번호 + * @throws WrongPasswordException 비밀번호가 일치하지 않으면 예외 발생 + */ private void validatePassword(String rawPassword, String encodedPassword) { if (!passwordEncoder.matches(rawPassword, encodedPassword)) { throw new WrongPasswordException(); } } + /** + * 토큰을 생성하고 쿠키에 설정합니다. + * + * @param username 사용자 이름 + * @param response HTTP 응답 객체 + * @return SignInResponseDto 로그인 응답 DTO + */ private SignInResponseDto generateTokensAndSetCookies(String username, HttpServletResponse response) { String accessToken = tokenProvider.createAccessToken(username); @@ -177,6 +276,12 @@ private SignInResponseDto generateTokensAndSetCookies(String username, return new SignInResponseDto(accessToken, refreshToken); } + /** + * 블랙리스트에 토큰을 추가하고 쿠키를 삭제합니다. + * + * @param request HTTP 요청 객체 + * @param response HTTP 응답 객체 + */ private void blacklistAndClearCookies(HttpServletRequest request, HttpServletResponse response) { Cookie accessTokenCookie = cookieProvider.getCookie(request, ACCESS_TOKEN_COOKIE_NAME); Cookie refreshTokenCookie = cookieProvider.getCookie(request, REFRESH_TOKEN_COOKIE_NAME); @@ -188,6 +293,11 @@ private void blacklistAndClearCookies(HttpServletRequest request, HttpServletRes cookieProvider.clearCookie(response, REFRESH_TOKEN_COOKIE_NAME); } + /** + * 토큰을 블랙리스트에 추가합니다. + * + * @param tokenCookie 토큰 쿠키 + */ private void blacklistToken(Cookie tokenCookie) { if (tokenCookie != null && tokenProvider.validateToken(tokenCookie.getValue())) { log.info("블랙리스트에 추가된 토큰: {}", tokenCookie.getValue()); @@ -196,6 +306,13 @@ private void blacklistToken(Cookie tokenCookie) { } } + /** + * 사용자 이름으로 사용자 정보를 로드합니다. + * + * @param username 사용자 이름 + * @return UserDetails 사용자 정보 + * @throws UsernameNotFoundException 사용자 이름이 존재하지 않으면 예외 발생 + */ @Override public UserDetails loadUserByUsername(String username) throws UsernameNotFoundException { return traineeRepository.findByEmail(username) @@ -204,6 +321,13 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx .orElseThrow(UserNotFoundException::new))); } + /** + * 회원 정보를 조회합니다. + * + * @param id 회원 ID + * @return MemberInfoResponseDto 회원 정보 응답 DTO + * @throws UserNotFoundException 회원이 존재하지 않으면 예외 발생 + */ public MemberInfoResponseDto memberInfo(Long id) { return traineeRepository.findById(id) .map(trainee -> MemberInfoResponseDto.builder() From 536fed9c8d180bd8238cf5d0cc5562c3ff18ca17 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:53:19 +0800 Subject: [PATCH 21/37] =?UTF-8?q?refactor:=20gender=20Enum=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=9C=BC=EB=A1=9C=20=EB=B3=80=EA=B2=BD?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trainingdiary/dto/request/EditTraineeInfoRequestDto.java | 4 ++-- .../dto/response/EditTraineeInfoResponseDto.java | 3 ++- .../trainingdiary/dto/response/TraineeInfoResponseDto.java | 3 ++- .../java/com/project/trainingdiary/entity/TraineeEntity.java | 4 +++- 4 files changed, 9 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/project/trainingdiary/dto/request/EditTraineeInfoRequestDto.java b/src/main/java/com/project/trainingdiary/dto/request/EditTraineeInfoRequestDto.java index f176a7f2..f7681bcd 100644 --- a/src/main/java/com/project/trainingdiary/dto/request/EditTraineeInfoRequestDto.java +++ b/src/main/java/com/project/trainingdiary/dto/request/EditTraineeInfoRequestDto.java @@ -1,5 +1,6 @@ package com.project.trainingdiary.dto.request; +import com.project.trainingdiary.model.GenderType; import com.project.trainingdiary.model.TargetType; import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; @@ -19,8 +20,7 @@ public class EditTraineeInfoRequestDto { private LocalDate birthDate; @NotNull(message = "gender 값은 null이 될 수 없습니다.") - @Size(min = 1, max = 1, message = "gender 값은 M 또는 F이어야 합니다.") - private String gender; + private GenderType gender; @Positive(message = "height 값은 양수이어야 합니다.") private double height; diff --git a/src/main/java/com/project/trainingdiary/dto/response/EditTraineeInfoResponseDto.java b/src/main/java/com/project/trainingdiary/dto/response/EditTraineeInfoResponseDto.java index 117a2037..11ee2d6d 100644 --- a/src/main/java/com/project/trainingdiary/dto/response/EditTraineeInfoResponseDto.java +++ b/src/main/java/com/project/trainingdiary/dto/response/EditTraineeInfoResponseDto.java @@ -1,6 +1,7 @@ package com.project.trainingdiary.dto.response; import com.project.trainingdiary.entity.TraineeEntity; +import com.project.trainingdiary.model.GenderType; import com.project.trainingdiary.model.TargetType; import java.time.LocalDate; import lombok.Builder; @@ -12,7 +13,7 @@ public class EditTraineeInfoResponseDto { private Long traineeId; private LocalDate birthDate; - private String gender; + private GenderType gender; private double height; private int remainingSessions; private TargetType targetType; diff --git a/src/main/java/com/project/trainingdiary/dto/response/TraineeInfoResponseDto.java b/src/main/java/com/project/trainingdiary/dto/response/TraineeInfoResponseDto.java index 4c643815..02ef393a 100644 --- a/src/main/java/com/project/trainingdiary/dto/response/TraineeInfoResponseDto.java +++ b/src/main/java/com/project/trainingdiary/dto/response/TraineeInfoResponseDto.java @@ -4,6 +4,7 @@ import com.project.trainingdiary.dto.traineeRecordDto.MuscleMassHistoryDto; import com.project.trainingdiary.dto.traineeRecordDto.WeightHistoryDto; import com.project.trainingdiary.entity.TraineeEntity; +import com.project.trainingdiary.model.GenderType; import com.project.trainingdiary.model.TargetType; import java.time.LocalDate; import java.time.Period; @@ -21,7 +22,7 @@ public class TraineeInfoResponseDto { private long traineeId; private String name; private int age; - private String gender; + private GenderType gender; private double height; private int remainingSessions; diff --git a/src/main/java/com/project/trainingdiary/entity/TraineeEntity.java b/src/main/java/com/project/trainingdiary/entity/TraineeEntity.java index b6738883..d793534d 100644 --- a/src/main/java/com/project/trainingdiary/entity/TraineeEntity.java +++ b/src/main/java/com/project/trainingdiary/entity/TraineeEntity.java @@ -4,6 +4,7 @@ import static jakarta.persistence.EnumType.STRING; import static jakarta.persistence.GenerationType.IDENTITY; +import com.project.trainingdiary.model.GenderType; import com.project.trainingdiary.model.TargetType; import com.project.trainingdiary.model.UserRoleType; import jakarta.persistence.Column; @@ -53,7 +54,8 @@ public class TraineeEntity extends BaseEntity { @Temporal(TemporalType.DATE) private LocalDate birthDate; - private String gender; + @Enumerated(STRING) + private GenderType gender; private int totalSessions; From f12f46c445f82be1ce66ac9226b016ed39b7f03a Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Tue, 16 Jul 2024 22:53:36 +0800 Subject: [PATCH 22/37] =?UTF-8?q?feat:=20Gender=20enum=20=ED=83=80?= =?UTF-8?q?=EC=9E=85=EC=9C=BC=EB=A1=9C=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/project/trainingdiary/model/GenderType.java | 7 +++++++ 1 file changed, 7 insertions(+) create mode 100644 src/main/java/com/project/trainingdiary/model/GenderType.java diff --git a/src/main/java/com/project/trainingdiary/model/GenderType.java b/src/main/java/com/project/trainingdiary/model/GenderType.java new file mode 100644 index 00000000..cc01e45e --- /dev/null +++ b/src/main/java/com/project/trainingdiary/model/GenderType.java @@ -0,0 +1,7 @@ +package com.project.trainingdiary.model; + +public enum GenderType { + + MALE, + FEMALE +} From 88335d6bb1ea01c0602ebd472a47334fc2f970d7 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:43:16 +0800 Subject: [PATCH 23/37] =?UTF-8?q?test:=20=EC=83=88=EB=A1=9C=20=EB=B0=94?= =?UTF-8?q?=EB=80=90=20=EA=B2=80=EC=A6=9D=20=ED=85=8C=EC=8A=A4=ED=8A=B8=20?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../service/UserServiceTest.java | 53 ++++++++++++++++++- 1 file changed, 51 insertions(+), 2 deletions(-) diff --git a/src/test/java/com/project/trainingdiary/service/UserServiceTest.java b/src/test/java/com/project/trainingdiary/service/UserServiceTest.java index b014777e..8de3aca9 100644 --- a/src/test/java/com/project/trainingdiary/service/UserServiceTest.java +++ b/src/test/java/com/project/trainingdiary/service/UserServiceTest.java @@ -2,7 +2,9 @@ import static org.junit.jupiter.api.Assertions.assertEquals; import static org.junit.jupiter.api.Assertions.assertNotNull; +import static org.junit.jupiter.api.Assertions.assertNull; import static org.junit.jupiter.api.Assertions.assertThrows; +import static org.junit.jupiter.api.Assertions.assertTrue; import static org.mockito.ArgumentMatchers.anyString; import static org.mockito.ArgumentMatchers.eq; import static org.mockito.Mockito.doNothing; @@ -25,6 +27,7 @@ import com.project.trainingdiary.exception.impl.UserNotFoundException; import com.project.trainingdiary.exception.impl.VerificationCodeExpiredException; import com.project.trainingdiary.exception.impl.VerificationCodeNotMatchedException; +import com.project.trainingdiary.exception.impl.VerificationCodeNotYetVerifiedException; import com.project.trainingdiary.exception.impl.WrongPasswordException; import com.project.trainingdiary.model.UserRoleType; import com.project.trainingdiary.provider.CookieProvider; @@ -190,6 +193,52 @@ void checkVerificationCodeSuccess() { Optional.of(verificationEntity)); userService.checkVerificationCode(verifyDto); + + verify(verificationRepository, times(1)).save(verificationEntityCaptor.capture()); + VerificationEntity capturedVerificationEntity = verificationEntityCaptor.getValue(); + assertNotNull(capturedVerificationEntity); + assertTrue(capturedVerificationEntity.isVerified()); + assertNull(capturedVerificationEntity.getExpiredAt()); + } + + @Test + @DisplayName("인증 코드 만료 후에도 검증됨 - 예외 발생하지 않음") + void checkVerificationCodeSuccessEvenIfExpiredAfterVerification() { + verificationEntity.setVerified(true); // Already verified + verificationEntity.setExpiredAt(LocalDateTime.now().minusMinutes(1)); // Expired + + when(verificationRepository.findByEmail(verifyDto.getEmail())).thenReturn( + Optional.of(verificationEntity)); + + userService.checkVerificationCode(verifyDto); + + verify(verificationRepository, times(1)).save(verificationEntityCaptor.capture()); + VerificationEntity capturedVerificationEntity = verificationEntityCaptor.getValue(); + assertNotNull(capturedVerificationEntity); + assertTrue(capturedVerificationEntity.isVerified()); + assertNull(capturedVerificationEntity.getExpiredAt()); + } + + @Test + @DisplayName("회원가입 실패 - 인증 코드 검증되지 않음") + void signUpFailWithoutVerification() { + SignUpRequestDto signUpDto = new SignUpRequestDto(); + signUpDto.setEmail("new@example.com"); + signUpDto.setPassword("password"); + signUpDto.setConfirmPassword("password"); + signUpDto.setRole(UserRoleType.TRAINEE); + + VerificationEntity verificationEntity = new VerificationEntity(); + verificationEntity.setEmail("new@example.com"); + verificationEntity.setVerificationCode("123456"); + verificationEntity.setExpiredAt(LocalDateTime.now().plusMinutes(5)); + verificationEntity.setVerified(false); // Not verified + + when(verificationRepository.findByEmail(signUpDto.getEmail())) + .thenReturn(Optional.of(verificationEntity)); + + assertThrows( + VerificationCodeNotYetVerifiedException.class, () -> userService.signUp(signUpDto, null)); } @Test @@ -243,8 +292,8 @@ void signInSuccess() { SignInResponseDto responseDto = userService.signIn(signInDto, response); - assertEquals("accessToken", responseDto.getAccessToken()); - assertEquals("refreshToken", responseDto.getRefreshToken()); + assertEquals("trainee@example.com", responseDto.getEmail()); + assertEquals("[ROLE_TRAINEE]", responseDto.getRole()); assertEquals(accessTokenExpiryDate, tokenProvider.getExpiryDateFromToken("accessToken")); assertEquals(refreshTokenExpiryDate, tokenProvider.getExpiryDateFromToken("refreshToken")); From 7fffc1231e16e31c9f70a9bc41ea3eaf7d4cc097 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:43:39 +0800 Subject: [PATCH 24/37] =?UTF-8?q?feat:=20=EC=9D=B8=EC=A6=9D=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EA=B2=80=EC=A6=9D=20=ED=99=95=EC=9D=B8=20=EC=A7=84?= =?UTF-8?q?=ED=96=89=EC=95=88=ED=95=9C=20exception?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../impl/VerificationCodeNotYetVerifiedException.java | 11 +++++++++++ 1 file changed, 11 insertions(+) create mode 100644 src/main/java/com/project/trainingdiary/exception/impl/VerificationCodeNotYetVerifiedException.java diff --git a/src/main/java/com/project/trainingdiary/exception/impl/VerificationCodeNotYetVerifiedException.java b/src/main/java/com/project/trainingdiary/exception/impl/VerificationCodeNotYetVerifiedException.java new file mode 100644 index 00000000..28442094 --- /dev/null +++ b/src/main/java/com/project/trainingdiary/exception/impl/VerificationCodeNotYetVerifiedException.java @@ -0,0 +1,11 @@ +package com.project.trainingdiary.exception.impl; + +import com.project.trainingdiary.exception.GlobalException; +import org.springframework.http.HttpStatus; + +public class VerificationCodeNotYetVerifiedException extends GlobalException { + + public VerificationCodeNotYetVerifiedException() { + super(HttpStatus.BAD_REQUEST, "아직 인증 처리를 안했습니다."); + } +} From 47949c42d161e4c28a48ff6f7a5365d5691f3d7b Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:44:13 +0800 Subject: [PATCH 25/37] =?UTF-8?q?test:=20trainer=20=ED=85=8C=EC=8A=A4?= =?UTF-8?q?=ED=8A=B8=20=EC=BD=94=EB=93=9C=20=EB=A6=AC=ED=8C=A9=ED=86=A0?= =?UTF-8?q?=EB=A7=81?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/project/trainingdiary/service/TrainerServiceTest.java | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java b/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java index 0f7c9b83..2f924351 100644 --- a/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java +++ b/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java @@ -19,6 +19,7 @@ import com.project.trainingdiary.exception.impl.PtContractNotExistException; import com.project.trainingdiary.exception.impl.TraineeNotExistException; import com.project.trainingdiary.exception.impl.TrainerNotFoundException; +import com.project.trainingdiary.model.GenderType; import com.project.trainingdiary.model.TargetType; import com.project.trainingdiary.repository.InBodyRecordHistoryRepository; import com.project.trainingdiary.repository.TraineeRepository; @@ -179,7 +180,7 @@ void testEditTraineeInfo_Success() { EditTraineeInfoRequestDto dto = new EditTraineeInfoRequestDto(); dto.setTraineeId(traineeId); dto.setBirthDate(LocalDate.parse("2000-01-01")); - dto.setGender("Male"); + dto.setGender(GenderType.MALE); dto.setHeight(180); dto.setTargetType(TargetType.TARGET_BODY_FAT_PERCENTAGE); dto.setTargetValue(70); From 985d23ac5d1a2efac9a7f1ac7e549342065efbd5 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:46:18 +0800 Subject: [PATCH 26/37] =?UTF-8?q?feat:=20addedDate=EA=B0=92=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trainingdiary/dto/request/AddInBodyInfoRequestDto.java | 5 +++++ .../trainingdiary/dto/response/AddInBodyInfoResponseDto.java | 3 +++ 2 files changed, 8 insertions(+) diff --git a/src/main/java/com/project/trainingdiary/dto/request/AddInBodyInfoRequestDto.java b/src/main/java/com/project/trainingdiary/dto/request/AddInBodyInfoRequestDto.java index 8bd9ce2b..689c330b 100644 --- a/src/main/java/com/project/trainingdiary/dto/request/AddInBodyInfoRequestDto.java +++ b/src/main/java/com/project/trainingdiary/dto/request/AddInBodyInfoRequestDto.java @@ -5,6 +5,7 @@ import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Positive; import jakarta.validation.constraints.PositiveOrZero; +import java.time.LocalDate; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -31,12 +32,16 @@ public class AddInBodyInfoRequestDto { @PositiveOrZero(message = "skeletalMuscleMass는 0 이상이어야 합니다.") private double skeletalMuscleMass; + @NotNull(message = "addedDate 값은 null이 안됩니다.") + private LocalDate addedDate; + public static InBodyRecordHistoryEntity toEntity(AddInBodyInfoRequestDto dto, TraineeEntity trainee) { return InBodyRecordHistoryEntity .builder() .trainee(trainee) .weight(dto.getWeight()) + .addedDate(dto.getAddedDate()) .bodyFatPercentage(dto.getBodyFatPercentage()) .skeletalMuscleMass(dto.getSkeletalMuscleMass()) .build(); diff --git a/src/main/java/com/project/trainingdiary/dto/response/AddInBodyInfoResponseDto.java b/src/main/java/com/project/trainingdiary/dto/response/AddInBodyInfoResponseDto.java index 78a0f8de..3f209afe 100644 --- a/src/main/java/com/project/trainingdiary/dto/response/AddInBodyInfoResponseDto.java +++ b/src/main/java/com/project/trainingdiary/dto/response/AddInBodyInfoResponseDto.java @@ -1,6 +1,7 @@ package com.project.trainingdiary.dto.response; import com.project.trainingdiary.entity.InBodyRecordHistoryEntity; +import java.time.LocalDate; import lombok.Builder; import lombok.Getter; @@ -12,6 +13,7 @@ public class AddInBodyInfoResponseDto { private double weight; private double bodyFatPercentage; private double skeletalMuscleMass; + private LocalDate addedDate; public static AddInBodyInfoResponseDto fromEntity(InBodyRecordHistoryEntity entity) { return AddInBodyInfoResponseDto.builder() @@ -19,6 +21,7 @@ public static AddInBodyInfoResponseDto fromEntity(InBodyRecordHistoryEntity enti .weight(entity.getWeight()) .bodyFatPercentage(entity.getBodyFatPercentage()) .skeletalMuscleMass(entity.getSkeletalMuscleMass()) + .addedDate(entity.getAddedDate()) .build(); } } \ No newline at end of file From 2816dd40baca26217c6813757b014a7d16f362d9 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:46:24 +0800 Subject: [PATCH 27/37] =?UTF-8?q?feat:=20addedDate=EA=B0=92=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/traineeRecordDto/BodyFatHistoryDto.java | 6 +++--- .../dto/traineeRecordDto/MuscleMassHistoryDto.java | 6 +++--- .../trainingdiary/entity/InBodyRecordHistoryEntity.java | 2 ++ 3 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/BodyFatHistoryDto.java b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/BodyFatHistoryDto.java index b2c59d37..22af9084 100644 --- a/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/BodyFatHistoryDto.java +++ b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/BodyFatHistoryDto.java @@ -1,7 +1,7 @@ package com.project.trainingdiary.dto.traineeRecordDto; import com.project.trainingdiary.entity.InBodyRecordHistoryEntity; -import java.time.LocalDateTime; +import java.time.LocalDate; import lombok.Builder; import lombok.Getter; @@ -9,12 +9,12 @@ @Builder public class BodyFatHistoryDto { - private LocalDateTime date; + private LocalDate addedDate; private double bodyFatPercentage; public static BodyFatHistoryDto fromEntity(InBodyRecordHistoryEntity entity) { return BodyFatHistoryDto.builder() - .date(entity.getCreatedAt()) + .addedDate(entity.getAddedDate()) .bodyFatPercentage(entity.getBodyFatPercentage()) .build(); } diff --git a/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/MuscleMassHistoryDto.java b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/MuscleMassHistoryDto.java index b96bb3df..3f8277c0 100644 --- a/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/MuscleMassHistoryDto.java +++ b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/MuscleMassHistoryDto.java @@ -1,7 +1,7 @@ package com.project.trainingdiary.dto.traineeRecordDto; import com.project.trainingdiary.entity.InBodyRecordHistoryEntity; -import java.time.LocalDateTime; +import java.time.LocalDate; import lombok.Builder; import lombok.Getter; @@ -9,12 +9,12 @@ @Builder public class MuscleMassHistoryDto { - private LocalDateTime date; + private LocalDate addedDate; private double muscleMass; public static MuscleMassHistoryDto fromEntity(InBodyRecordHistoryEntity entity) { return MuscleMassHistoryDto.builder() - .date(entity.getCreatedAt()) + .addedDate(entity.getAddedDate()) .muscleMass(entity.getSkeletalMuscleMass()) .build(); } diff --git a/src/main/java/com/project/trainingdiary/entity/InBodyRecordHistoryEntity.java b/src/main/java/com/project/trainingdiary/entity/InBodyRecordHistoryEntity.java index a4b13da7..c6beeb59 100644 --- a/src/main/java/com/project/trainingdiary/entity/InBodyRecordHistoryEntity.java +++ b/src/main/java/com/project/trainingdiary/entity/InBodyRecordHistoryEntity.java @@ -7,6 +7,7 @@ import jakarta.persistence.Id; import jakarta.persistence.JoinColumn; import jakarta.persistence.ManyToOne; +import java.time.LocalDate; import lombok.AllArgsConstructor; import lombok.Builder; import lombok.Getter; @@ -28,6 +29,7 @@ public class InBodyRecordHistoryEntity extends BaseEntity { private double weight; private double bodyFatPercentage; private double skeletalMuscleMass; + private LocalDate addedDate; @ManyToOne @JoinColumn(name = "trainee_id", nullable = false) From 92caca8e65c88a557d34958bb132d27d8ce55107 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:47:36 +0800 Subject: [PATCH 28/37] =?UTF-8?q?refactor:=20CustomeResponse=20=EC=97=90?= =?UTF-8?q?=EC=84=9C=20ResponseEntity=20=ED=83=80=EC=9E=85=EC=9C=BC?= =?UTF-8?q?=EB=A1=9C=20=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../controller/UserController.java | 29 +++++++++---------- 1 file changed, 13 insertions(+), 16 deletions(-) diff --git a/src/main/java/com/project/trainingdiary/controller/UserController.java b/src/main/java/com/project/trainingdiary/controller/UserController.java index 5ee0fb2d..d2b74401 100644 --- a/src/main/java/com/project/trainingdiary/controller/UserController.java +++ b/src/main/java/com/project/trainingdiary/controller/UserController.java @@ -15,7 +15,6 @@ import lombok.RequiredArgsConstructor; import org.springframework.http.ResponseEntity; import org.springframework.web.bind.annotation.GetMapping; -import org.springframework.web.bind.annotation.PathVariable; import org.springframework.web.bind.annotation.PostMapping; import org.springframework.web.bind.annotation.RequestBody; import org.springframework.web.bind.annotation.RequestMapping; @@ -29,50 +28,48 @@ public class UserController { private final UserService userService; @PostMapping("/check-duplicate-and-send-verification") - public CustomResponse checkDuplicateAndSendVerification( + public ResponseEntity checkDuplicateAndSendVerification( @RequestBody @Valid SendVerificationAndCheckDuplicateRequestDto dto ) { userService.checkDuplicateEmailAndSendVerification(dto); - return CustomResponse.success(SuccessMessage.SENT_VERIFICATION_SUCCESS); + return ResponseEntity.ok().build(); } @PostMapping("/check-verification-code") - public CustomResponse verifyCode( + public ResponseEntity verifyCode( @RequestBody @Valid VerifyCodeRequestDto dto ) { userService.checkVerificationCode(dto); - return CustomResponse.success(SuccessMessage.VERIFICATION_SUCCESS); + return ResponseEntity.ok().build(); } @PostMapping("/sign-up") - public CustomResponse signUp( + public ResponseEntity signUp( @RequestBody @Valid SignUpRequestDto dto, HttpServletResponse response ) { userService.signUp(dto, response); - return CustomResponse.success(SuccessMessage.SIGN_UP_SUCCESS); + return ResponseEntity.ok().build(); } @PostMapping("/sign-in") - public CustomResponse signIn( + public ResponseEntity signIn( @RequestBody @Valid SignInRequestDto dto, HttpServletResponse response ) { SignInResponseDto signInResponse = userService.signIn(dto, response); - return CustomResponse.success(signInResponse, SuccessMessage.SIGN_IN_SUCCESS); + return ResponseEntity.ok(signInResponse); } @PostMapping("/sign-out") - public CustomResponse signOut( + public ResponseEntity signOut( HttpServletRequest request, HttpServletResponse response ) { userService.signOut(request, response); - return CustomResponse.success(SuccessMessage.SIGN_OUT_SUCCESS); + return ResponseEntity.ok().build(); } - @GetMapping("/{id}") - public ResponseEntity viewUserInfo( - @PathVariable Long id - ) { - MemberInfoResponseDto user = userService.memberInfo(id); + @GetMapping("/info") + public ResponseEntity userInfo() { + MemberInfoResponseDto user = userService.memberInfo(); return ResponseEntity.ok(user); } } From 0d8e24c96f7b8176cfb5b568aba05c5b91742228 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:47:56 +0800 Subject: [PATCH 29/37] =?UTF-8?q?feat:=20=EC=9D=B8=EC=A6=9D=EB=B2=88?= =?UTF-8?q?=ED=98=B8=20=EA=B2=80=EC=A6=9D=ED=95=98=EB=8A=94=20=EB=B6=80?= =?UTF-8?q?=EB=B6=84=EC=97=90=EC=84=9C=20=ED=9A=8C=EC=9B=90=EA=B0=80?= =?UTF-8?q?=EC=9E=85=EC=8B=9C=20=EB=A7=8C=EB=A3=8C=EC=8B=9C=EA=B0=84=20?= =?UTF-8?q?=EB=AC=B4=EC=8B=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trainingdiary/service/UserService.java | 83 +++++++++++++++---- 1 file changed, 66 insertions(+), 17 deletions(-) diff --git a/src/main/java/com/project/trainingdiary/service/UserService.java b/src/main/java/com/project/trainingdiary/service/UserService.java index 771e4e69..bb39c458 100644 --- a/src/main/java/com/project/trainingdiary/service/UserService.java +++ b/src/main/java/com/project/trainingdiary/service/UserService.java @@ -12,9 +12,11 @@ import com.project.trainingdiary.exception.impl.PasswordMismatchedException; import com.project.trainingdiary.exception.impl.TraineeEmailDuplicateException; import com.project.trainingdiary.exception.impl.TrainerEmailDuplicateException; +import com.project.trainingdiary.exception.impl.TrainerNotFoundException; import com.project.trainingdiary.exception.impl.UserNotFoundException; import com.project.trainingdiary.exception.impl.VerificationCodeExpiredException; import com.project.trainingdiary.exception.impl.VerificationCodeNotMatchedException; +import com.project.trainingdiary.exception.impl.VerificationCodeNotYetVerifiedException; import com.project.trainingdiary.exception.impl.WrongPasswordException; import com.project.trainingdiary.model.UserPrincipal; import com.project.trainingdiary.model.UserRoleType; @@ -32,6 +34,8 @@ import java.time.LocalDateTime; import lombok.RequiredArgsConstructor; import lombok.extern.slf4j.Slf4j; +import org.springframework.security.core.Authentication; +import org.springframework.security.core.context.SecurityContextHolder; import org.springframework.security.core.userdetails.UserDetails; import org.springframework.security.core.userdetails.UserDetailsService; import org.springframework.security.core.userdetails.UsernameNotFoundException; @@ -81,6 +85,9 @@ public void checkVerificationCode(VerifyCodeRequestDto dto) { /** * 회원가입을 처리합니다. + *

+ * 이 메서드는 제공된 이메일로 인증 엔티티를 조회하고, 비밀번호 일치 여부를 확인하며, 인증이 완료되었는지를 검증합니다. 검증에 성공하면 사용자를 저장하고, 토큰을 생성하여 + * 쿠키에 설정한 후, 인증 엔티티를 삭제합니다. * * @param dto 회원가입 요청 DTO * @param response HTTP 응답 객체 @@ -90,7 +97,8 @@ public void signUp(SignUpRequestDto dto, HttpServletResponse response) { VerificationEntity verificationEntity = getVerificationEntity(dto.getEmail()); validateEmailNotExists(dto.getEmail()); validatePasswordsMatch(dto.getPassword(), dto.getConfirmPassword()); - validateVerificationCode(verificationEntity, dto.getVerificationCode()); + validateIfVerified(verificationEntity); + String encodedPassword = passwordEncoder.encode(dto.getPassword()); saveUser(dto, encodedPassword); generateTokensAndSetCookies(dto.getEmail(), response); @@ -99,6 +107,8 @@ public void signUp(SignUpRequestDto dto, HttpServletResponse response) { /** * 로그인 요청을 처리합니다. + *

+ * 이 메서드는 제공된 이메일로 사용자 정보를 로드하고, 비밀번호를 검증합니다. 검증에 성공하면 토큰을 생성하여 쿠키에 설정하고, 로그인 응답 DTO를 반환합니다. * * @param dto 로그인 요청 DTO * @param response HTTP 응답 객체 @@ -112,6 +122,8 @@ public SignInResponseDto signIn(SignInRequestDto dto, HttpServletResponse respon /** * 로그아웃 요청을 처리합니다. + *

+ * 이 메서드는 요청에 포함된 쿠키에서 토큰을 추출하여 블랙리스트에 추가하고, 응답에서 쿠키를 삭제합니다. * * @param request HTTP 요청 객체 * @param response HTTP 응답 객체 @@ -122,6 +134,8 @@ public void signOut(HttpServletRequest request, HttpServletResponse response) { /** * 이메일 중복을 확인합니다. + *

+ * 이 메서드는 제공된 이메일이 트레이니 또는 트레이너로 존재하는지를 확인하고, 중복된 경우 적절한 예외를 발생시킵니다. * * @param email 확인할 이메일 * @throws TraineeEmailDuplicateException 트레이니 이메일이 중복되면 예외 발생 @@ -138,6 +152,8 @@ private void validateEmailNotExists(String email) { /** * 인증 코드를 전송합니다. + *

+ * 이 메서드는 인증 코드를 생성하여 제공된 이메일로 전송하고, 인증 엔티티를 저장합니다. * * @param email 인증 코드를 전송할 이메일 */ @@ -161,10 +177,13 @@ private void validatePasswordsMatch(String password, String confirmPassword) { } /** - * 인증 코드를 확인합니다. + * 제공된 인증 코드를 검증합니다. + *

+ * 이 메서드는 제공된 인증 코드가 저장된 인증 엔티티의 코드와 일치하는지, 그리고 코드가 만료되지 않았는지를 확인합니다. 두 조건 중 하나라도 실패하면 적절한 예외가 + * 발생합니다. 검증에 성공하면 엔티티는 검증 상태를 반영하도록 업데이트되며, 만료 시간은 무효화됩니다. * - * @param verificationEntity 인증 엔티티 - * @param verificationCode 인증 코드 + * @param verificationEntity 저장된 코드와 만료 시간이 포함된 인증 엔티티 + * @param verificationCode 제공된 인증 코드 * @throws VerificationCodeNotMatchedException 인증 코드가 일치하지 않으면 예외 발생 * @throws VerificationCodeExpiredException 인증 코드가 만료되면 예외 발생 */ @@ -173,9 +192,28 @@ private void validateVerificationCode(VerificationEntity verificationEntity, if (!verificationEntity.getVerificationCode().equals(verificationCode)) { throw new VerificationCodeNotMatchedException(); } - if (verificationEntity.getExpiredAt().isBefore(LocalDateTime.now())) { + if (!verificationEntity.isVerified() && + verificationEntity.getExpiredAt() != null && + verificationEntity.getExpiredAt().isBefore(LocalDateTime.now())) { throw new VerificationCodeExpiredException(); } + verificationEntity.setExpiredAt(null); // 무효화 + verificationEntity.setVerified(true); + verificationRepository.save(verificationEntity); + } + + /** + * 인증이 완료되었는지 확인합니다. + *

+ * 이 메서드는 인증 엔티티가 검증되었는지를 확인하고, 검증되지 않았으면 예외를 발생시킵니다. + * + * @param verificationEntity 인증 엔티티 + * @throws VerificationCodeNotYetVerifiedException 인증이 완료되지 않았으면 예외 발생 + */ + private void validateIfVerified(VerificationEntity verificationEntity) { + if (!verificationEntity.isVerified()) { + throw new VerificationCodeNotYetVerifiedException(); + } } /** @@ -273,7 +311,8 @@ private SignInResponseDto generateTokensAndSetCookies(String username, redisTokenRepository.saveAccessToken(username, accessToken, accessTokenExpiryDate); redisTokenRepository.saveRefreshToken(username, refreshToken, refreshTokenExpiryDate); - return new SignInResponseDto(accessToken, refreshToken); + return new SignInResponseDto(username, + loadUserByUsername(username).getAuthorities().toString()); } /** @@ -323,26 +362,36 @@ public UserDetails loadUserByUsername(String username) throws UsernameNotFoundEx /** * 회원 정보를 조회합니다. + *

+ * // * @param id 회원 ID * - * @param id 회원 ID * @return MemberInfoResponseDto 회원 정보 응답 DTO * @throws UserNotFoundException 회원이 존재하지 않으면 예외 발생 */ - public MemberInfoResponseDto memberInfo(Long id) { - return traineeRepository.findById(id) + public MemberInfoResponseDto memberInfo() { + Authentication authentication = SecurityContextHolder.getContext().getAuthentication(); + if (authentication == null || authentication.getName() == null) { + throw new UserNotFoundException(); + } + String role = authentication.getAuthorities().toString(); + + if (role.contains("ROLE_TRAINER")) { + return trainerRepository.findByEmail(authentication.getName()) + .map(trainer -> MemberInfoResponseDto.builder() + .id(trainer.getId()) + .email(trainer.getEmail()) + .name(trainer.getName()) + .role(trainer.getRole()) + .build()) + .orElseThrow(TrainerNotFoundException::new); + } + return traineeRepository.findByEmail(authentication.getName()) .map(trainee -> MemberInfoResponseDto.builder() .id(trainee.getId()) .email(trainee.getEmail()) .name(trainee.getName()) .role(trainee.getRole()) .build()) - .orElseGet(() -> trainerRepository.findById(id) - .map(trainer -> MemberInfoResponseDto.builder() - .id(trainer.getId()) - .email(trainer.getEmail()) - .name(trainer.getName()) - .role(trainer.getRole()) - .build()) - .orElseThrow(UserNotFoundException::new)); + .orElseThrow(TrainerNotFoundException::new); } } \ No newline at end of file From 59e7ff5edb5e8bffca94002d2c9abf459cd929b4 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:48:43 +0800 Subject: [PATCH 30/37] =?UTF-8?q?feat:=20addedDate=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../dto/traineeRecordDto/WeightHistoryDto.java | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/WeightHistoryDto.java b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/WeightHistoryDto.java index 588eb380..98528d75 100644 --- a/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/WeightHistoryDto.java +++ b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/WeightHistoryDto.java @@ -1,7 +1,7 @@ package com.project.trainingdiary.dto.traineeRecordDto; import com.project.trainingdiary.entity.InBodyRecordHistoryEntity; -import java.time.LocalDateTime; +import java.time.LocalDate; import lombok.Builder; import lombok.Getter; @@ -9,12 +9,12 @@ @Builder public class WeightHistoryDto { - private LocalDateTime date; + private LocalDate addedDate; private double weight; public static WeightHistoryDto fromEntity(InBodyRecordHistoryEntity entity) { return WeightHistoryDto.builder() - .date(entity.getCreatedAt()) + .addedDate(entity.getAddedDate()) .weight(entity.getWeight()) .build(); } From 99a5eb782ef1260e9273a1bc50e5c5eef8da7bf5 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:48:58 +0800 Subject: [PATCH 31/37] =?UTF-8?q?feat:=20isVerified=EB=A1=9C=20=EC=B6=94?= =?UTF-8?q?=EA=B0=80=20=EA=B2=80=EC=A6=9D=20=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/project/trainingdiary/entity/VerificationEntity.java | 3 +++ 1 file changed, 3 insertions(+) diff --git a/src/main/java/com/project/trainingdiary/entity/VerificationEntity.java b/src/main/java/com/project/trainingdiary/entity/VerificationEntity.java index 365c7c5d..57e1ccd6 100644 --- a/src/main/java/com/project/trainingdiary/entity/VerificationEntity.java +++ b/src/main/java/com/project/trainingdiary/entity/VerificationEntity.java @@ -26,11 +26,14 @@ public class VerificationEntity extends BaseEntity { private LocalDateTime expiredAt; + private boolean isVerified; + public static VerificationEntity of(String email, String verificationCode) { return VerificationEntity.builder() .email(email) .verificationCode(verificationCode) .expiredAt(LocalDateTime.now().plusMinutes(10)) + .isVerified(false) .build(); } } From e3de95fe770eae8e265a02509e785d699bc77e63 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:49:18 +0800 Subject: [PATCH 32/37] =?UTF-8?q?feat:=20Validation=20=EB=B6=80=EB=B6=84?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20response=EA=B0=92=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trainingdiary/dto/request/SignInRequestDto.java | 8 +++++--- .../trainingdiary/dto/response/SignInResponseDto.java | 4 ++-- 2 files changed, 7 insertions(+), 5 deletions(-) diff --git a/src/main/java/com/project/trainingdiary/dto/request/SignInRequestDto.java b/src/main/java/com/project/trainingdiary/dto/request/SignInRequestDto.java index a8286416..a02dee29 100644 --- a/src/main/java/com/project/trainingdiary/dto/request/SignInRequestDto.java +++ b/src/main/java/com/project/trainingdiary/dto/request/SignInRequestDto.java @@ -1,7 +1,8 @@ package com.project.trainingdiary.dto.request; +import io.swagger.v3.oas.annotations.media.Schema; import jakarta.validation.constraints.Email; -import jakarta.validation.constraints.NotBlank; +import jakarta.validation.constraints.NotNull; import jakarta.validation.constraints.Pattern; import lombok.Getter; import lombok.Setter; @@ -10,11 +11,12 @@ @Setter public class SignInRequestDto { - @NotBlank(message = "email은 필수 입력 값입니다.") + @Schema(example = "newUser@gmail.com") + @NotNull(message = "email은 필수 입력 값입니다.") @Email(message = "email 형식에 맞지 않습니다.") private String email; - @NotBlank(message = "password은 필수 입력 값입니다.") + @NotNull(message = "password은 필수 입력 값입니다.") @Pattern(regexp = "^(?=.*[a-zA-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,15}$", message = "비밀번호 형식이 잘못 되었습니다.") private String password; } diff --git a/src/main/java/com/project/trainingdiary/dto/response/SignInResponseDto.java b/src/main/java/com/project/trainingdiary/dto/response/SignInResponseDto.java index 3fd74b09..0ec00430 100644 --- a/src/main/java/com/project/trainingdiary/dto/response/SignInResponseDto.java +++ b/src/main/java/com/project/trainingdiary/dto/response/SignInResponseDto.java @@ -11,6 +11,6 @@ @NoArgsConstructor public class SignInResponseDto { - private String accessToken; - private String refreshToken; + private String email; + private String role; } From 4c9833c8bdd48329b2ee239aeb65197ea8d8bb38 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:49:24 +0800 Subject: [PATCH 33/37] =?UTF-8?q?feat:=20Validation=20=EB=B6=80=EB=B6=84?= =?UTF-8?q?=20=EC=B6=94=EA=B0=80=20=EB=B0=8F=20response=EA=B0=92=20?= =?UTF-8?q?=EC=88=98=EC=A0=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../project/trainingdiary/dto/request/SignUpRequestDto.java | 5 ----- 1 file changed, 5 deletions(-) diff --git a/src/main/java/com/project/trainingdiary/dto/request/SignUpRequestDto.java b/src/main/java/com/project/trainingdiary/dto/request/SignUpRequestDto.java index c0d2dde7..98771adb 100644 --- a/src/main/java/com/project/trainingdiary/dto/request/SignUpRequestDto.java +++ b/src/main/java/com/project/trainingdiary/dto/request/SignUpRequestDto.java @@ -7,7 +7,6 @@ import jakarta.validation.constraints.Pattern; import lombok.Getter; import lombok.Setter; -import org.hibernate.validator.constraints.Length; @Getter @Setter @@ -17,10 +16,6 @@ public class SignUpRequestDto { @Email(message = "email 형식에 맞지 않습니다.") private String email; - @NotBlank(message = "인증 번호는 필수 입력 값입니다.") - @Length(min = 6, max = 6, message = "인증 번호는 6개의 숫자로 만듭니다.") - private String verificationCode; - @NotBlank(message = "password은 필수 입력 값입니다.") @Pattern(regexp = "^(?=.*[a-zA-Z])(?=.*\\d)(?=.*[@$!%*?&])[A-Za-z\\d@$!%*?&]{8,15}$", message = "비밀번호 형식이 잘못 되었습니다.") private String password; From e1518b83554f718fd5120db68f7767a5dc2e1f7f Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:50:10 +0800 Subject: [PATCH 34/37] =?UTF-8?q?feat:=20totalSessions=20redundant=20?= =?UTF-8?q?=ED=95=98=EB=AF=80=EB=A1=9C=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../java/com/project/trainingdiary/entity/TraineeEntity.java | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/main/java/com/project/trainingdiary/entity/TraineeEntity.java b/src/main/java/com/project/trainingdiary/entity/TraineeEntity.java index d793534d..c5e5d5cc 100644 --- a/src/main/java/com/project/trainingdiary/entity/TraineeEntity.java +++ b/src/main/java/com/project/trainingdiary/entity/TraineeEntity.java @@ -57,8 +57,6 @@ public class TraineeEntity extends BaseEntity { @Enumerated(STRING) private GenderType gender; - private int totalSessions; - private double height; @OneToMany(mappedBy = "trainee", cascade = ALL, orphanRemoval = true) From caa516683f119b11c98a808f2a6eabd08959c442 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:50:22 +0800 Subject: [PATCH 35/37] =?UTF-8?q?feat:=20response=EC=97=90=20birthdate?= =?UTF-8?q?=EC=B6=94=EA=B0=80?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../trainingdiary/dto/response/TraineeInfoResponseDto.java | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/main/java/com/project/trainingdiary/dto/response/TraineeInfoResponseDto.java b/src/main/java/com/project/trainingdiary/dto/response/TraineeInfoResponseDto.java index 02ef393a..9521c92a 100644 --- a/src/main/java/com/project/trainingdiary/dto/response/TraineeInfoResponseDto.java +++ b/src/main/java/com/project/trainingdiary/dto/response/TraineeInfoResponseDto.java @@ -24,6 +24,7 @@ public class TraineeInfoResponseDto { private int age; private GenderType gender; private double height; + private LocalDate birthDate; private int remainingSessions; private List weightHistory; @@ -38,6 +39,7 @@ public static TraineeInfoResponseDto fromEntity(TraineeEntity trainee, int remai .traineeId(trainee.getId()) .name(trainee.getName()) .age(calculateAge(trainee.getBirthDate())) + .birthDate(trainee.getBirthDate()) .gender(trainee.getGender()) .height(trainee.getHeight()) .remainingSessions(remainingSessions) From c426ddf40ab3eeac231eb18666c2b3f80bbe3f91 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:50:40 +0800 Subject: [PATCH 36/37] =?UTF-8?q?refactor:=20=EB=B6=88=ED=95=84=EC=9A=94?= =?UTF-8?q?=ED=95=9C=20response=20=EC=82=AD=EC=A0=9C?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../InBodyRecordHistoryDto.java | 24 ------------------- 1 file changed, 24 deletions(-) delete mode 100644 src/main/java/com/project/trainingdiary/dto/traineeRecordDto/InBodyRecordHistoryDto.java diff --git a/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/InBodyRecordHistoryDto.java b/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/InBodyRecordHistoryDto.java deleted file mode 100644 index 955583d9..00000000 --- a/src/main/java/com/project/trainingdiary/dto/traineeRecordDto/InBodyRecordHistoryDto.java +++ /dev/null @@ -1,24 +0,0 @@ -package com.project.trainingdiary.dto.traineeRecordDto; - -import com.project.trainingdiary.entity.InBodyRecordHistoryEntity; -import lombok.Builder; -import lombok.Getter; - -@Getter -@Builder -public class InBodyRecordHistoryDto { - - private long id; - private double weight; - private double bodyFatPercentage; - private double skeletalMuscleMass; - - public static InBodyRecordHistoryDto fromEntity(InBodyRecordHistoryEntity entity) { - return InBodyRecordHistoryDto.builder() - .id(entity.getId()) - .weight(entity.getWeight()) - .bodyFatPercentage(entity.getBodyFatPercentage()) - .skeletalMuscleMass(entity.getSkeletalMuscleMass()) - .build(); - } -} \ No newline at end of file From eeb229ab10e1d0ff3cae15b0e6deb4e6c880d9e4 Mon Sep 17 00:00:00 2001 From: Lee Kyoo Min <58134412+kyoo0115@users.noreply.github.com> Date: Wed, 17 Jul 2024 21:55:27 +0800 Subject: [PATCH 37/37] =?UTF-8?q?refactor:=20reformat=20code=20=EC=A7=84?= =?UTF-8?q?=ED=96=89?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- .../com/project/trainingdiary/controller/UserController.java | 2 -- .../com/project/trainingdiary/service/TrainerService.java | 4 ++-- .../com/project/trainingdiary/service/TrainerServiceTest.java | 3 +-- 3 files changed, 3 insertions(+), 6 deletions(-) diff --git a/src/main/java/com/project/trainingdiary/controller/UserController.java b/src/main/java/com/project/trainingdiary/controller/UserController.java index d2b74401..09fd9cd7 100644 --- a/src/main/java/com/project/trainingdiary/controller/UserController.java +++ b/src/main/java/com/project/trainingdiary/controller/UserController.java @@ -4,10 +4,8 @@ import com.project.trainingdiary.dto.request.SignInRequestDto; import com.project.trainingdiary.dto.request.SignUpRequestDto; import com.project.trainingdiary.dto.request.VerifyCodeRequestDto; -import com.project.trainingdiary.dto.response.CustomResponse; import com.project.trainingdiary.dto.response.MemberInfoResponseDto; import com.project.trainingdiary.dto.response.SignInResponseDto; -import com.project.trainingdiary.model.SuccessMessage; import com.project.trainingdiary.service.UserService; import jakarta.servlet.http.HttpServletRequest; import jakarta.servlet.http.HttpServletResponse; diff --git a/src/main/java/com/project/trainingdiary/service/TrainerService.java b/src/main/java/com/project/trainingdiary/service/TrainerService.java index 64aedf2c..ed2e8b3b 100644 --- a/src/main/java/com/project/trainingdiary/service/TrainerService.java +++ b/src/main/java/com/project/trainingdiary/service/TrainerService.java @@ -43,10 +43,10 @@ public TraineeInfoResponseDto getTraineeInfo(Long id) { checkContract(trainer, trainee); - int totalSessions = ptContractRepository.findByTrainee(trainee).stream() + int totalSessions = ptContractRepository.findByTraineeId(trainee.getId()).stream() .mapToInt(PtContractEntity::getTotalSession) .sum(); - int usedSessions = ptContractRepository.findByTrainee(trainee).stream() + int usedSessions = ptContractRepository.findByTraineeId(trainee.getId()).stream() .mapToInt(PtContractEntity::getUsedSession) .sum(); diff --git a/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java b/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java index 2f924351..2dc92cd5 100644 --- a/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java +++ b/src/test/java/com/project/trainingdiary/service/TrainerServiceTest.java @@ -27,7 +27,6 @@ import com.project.trainingdiary.repository.ptContract.PtContractRepository; import java.time.LocalDate; import java.util.ArrayList; -import java.util.List; import java.util.Optional; import org.junit.jupiter.api.BeforeEach; import org.junit.jupiter.api.DisplayName; @@ -130,7 +129,7 @@ void testGetTraineeInfo_Success() { when(trainerRepository.findByEmail(trainerEmail)).thenReturn(Optional.of(trainer)); when(traineeRepository.findById(traineeId)).thenReturn(Optional.of(trainee)); when(ptContractRepository.existsByTrainerIdAndTraineeId(trainerId, traineeId)).thenReturn(true); - when(ptContractRepository.findByTrainee(trainee)).thenReturn(List.of(contract)); + when(ptContractRepository.findByTraineeId(trainee.getId())).thenReturn(Optional.of(contract)); // when TraineeInfoResponseDto responseDto = trainerService.getTraineeInfo(traineeId);