Skip to content

Commit

Permalink
Merge pull request #162 from TrainingDiary/feature/trainee-view-train…
Browse files Browse the repository at this point in the history
…ee-info

트레이너의 트레이니 조회 API 트레이니 본인도 조회할 수 있도록 구현
  • Loading branch information
kyoo0115 authored Jul 27, 2024
2 parents 804a240 + aa9d4e9 commit 66dc5f5
Show file tree
Hide file tree
Showing 5 changed files with 385 additions and 106 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import com.project.trainingdiary.dto.response.trainer.TraineeInfoResponseDto;
import com.project.trainingdiary.service.TrainerService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import io.swagger.v3.oas.annotations.tags.Tag;
Expand All @@ -32,12 +33,14 @@ public class TrainerController {

@Operation(
summary = "트레이니 정보 조회",
description = "트레이너가 트레이니 정보를 조회합니다."
description = "트레이너 및 트레이니가 트레이니 정보를 조회합니다."
)
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "성공")
@ApiResponse(responseCode = "200", description = "성공"),
@ApiResponse(responseCode = "400", description = "다른 트레이니의 정보를 볼 없습니다.", content = @Content),
@ApiResponse(responseCode = "404", description = "계약이 없습니다.", content = @Content)
})
@PreAuthorize("hasRole('TRAINER')")
@PreAuthorize("hasRole('TRAINER') or hasRole('TRAINEE')")
@GetMapping("/trainees/{id}")
public ResponseEntity<TraineeInfoResponseDto> getTraineeInfo(
@PathVariable Long id
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,11 @@
package com.project.trainingdiary.exception.user;

import com.project.trainingdiary.exception.GlobalException;
import org.springframework.http.HttpStatus;

public class UnauthorizedTraineeException extends GlobalException {

public UnauthorizedTraineeException() {
super(HttpStatus.BAD_REQUEST, "다른 트레이니 정보를 볼 수 없습니다.");
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@
import java.util.Optional;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;
import org.springframework.data.repository.query.Param;

public interface PtContractRepository extends JpaRepository<PtContractEntity, Long>,
PtContractRepositoryCustom {
Expand Down Expand Up @@ -41,4 +42,18 @@ public interface PtContractRepository extends JpaRepository<PtContractEntity, Lo
+ "where p.trainee.id = ?1 "
+ "and p.isTerminated = false")
Optional<PtContractEntity> findByTraineeId(long traineeId);

@Query("SELECT ptc FROM pt_contract ptc " +
"LEFT JOIN FETCH ptc.trainee t " +
"LEFT JOIN FETCH t.inBodyRecords ir " +
"LEFT JOIN FETCH ptc.trainer tr " +
"WHERE t.id = :traineeId AND tr.id = :trainerId AND ptc.isTerminated = false")
Optional<PtContractEntity> findWithTraineeAndTrainer(@Param("traineeId") Long traineeId,
@Param("trainerId") Long trainerId);

@Query("SELECT ptc FROM pt_contract ptc " +
"LEFT JOIN FETCH ptc.trainee t " +
"LEFT JOIN FETCH t.inBodyRecords ir " +
"WHERE t.id = :traineeId AND ptc.isTerminated = false")
Optional<PtContractEntity> findByTraineeIdWithInBodyRecords(@Param("traineeId") Long traineeId);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.project.trainingdiary.service;

import com.github.benmanes.caffeine.cache.Cache;
import com.project.trainingdiary.dto.request.trainer.AddInBodyInfoRequestDto;
import com.project.trainingdiary.dto.request.trainer.EditTraineeInfoRequestDto;
import com.project.trainingdiary.dto.response.trainer.AddInBodyInfoResponseDto;
Expand All @@ -12,11 +13,17 @@
import com.project.trainingdiary.exception.ptcontract.PtContractNotExistException;
import com.project.trainingdiary.exception.user.TraineeNotFoundException;
import com.project.trainingdiary.exception.user.TrainerNotFoundException;
import com.project.trainingdiary.exception.user.UnauthorizedTraineeException;
import com.project.trainingdiary.exception.user.UserNotFoundException;
import com.project.trainingdiary.model.UserPrincipal;
import com.project.trainingdiary.model.type.UserRoleType;
import com.project.trainingdiary.repository.InBodyRecordHistoryRepository;
import com.project.trainingdiary.repository.TraineeRepository;
import com.project.trainingdiary.repository.TrainerRepository;
import com.project.trainingdiary.repository.ptContract.PtContractRepository;
import lombok.RequiredArgsConstructor;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import org.springframework.security.core.Authentication;
import org.springframework.security.core.context.SecurityContextHolder;
import org.springframework.stereotype.Service;
Expand All @@ -26,25 +33,55 @@
@RequiredArgsConstructor
public class TrainerService {

private static final Logger log = LoggerFactory.getLogger(TrainerService.class);
private final TraineeRepository traineeRepository;
private final TrainerRepository trainerRepository;
private final InBodyRecordHistoryRepository inBodyRecordHistoryRepository;
private final PtContractRepository ptContractRepository;

private final Cache<String, UserPrincipal> userCache;

/**
* 트레이니 정보를 조회합니다.
*
* @param id 트레이니의 ID
* @return TraineeInfoResponseDto 트레이니의 정보와 남은 세션 수를 포함한 응답 DTO
*/
public TraineeInfoResponseDto getTraineeInfo(Long id) {
UserRoleType role = getMyRole();
if (role.equals(UserRoleType.TRAINER)) {
return trainerGetTraineeInfo(id);
} else {
return traineeGetTraineeInfo(id);
}
}

public TraineeInfoResponseDto trainerGetTraineeInfo(Long id) {
TrainerEntity trainer = getAuthenticatedTrainer();
TraineeEntity trainee = getTraineeById(id);
PtContractEntity ptContract = getPtContract(trainer, trainee);

PtContractEntity ptContract = ptContractRepository.findWithTraineeAndTrainer(id,
trainer.getId())
.orElseThrow(PtContractNotExistException::new);

TraineeEntity trainee = ptContract.getTrainee();

return TraineeInfoResponseDto.fromEntity(trainee, ptContract.getRemainingSession());
}

public TraineeInfoResponseDto traineeGetTraineeInfo(Long id) {
TraineeEntity trainee = getAuthenticatedTrainee();

if (!trainee.getId().equals(id)) {
throw new UnauthorizedTraineeException();
}
PtContractEntity ptContract = ptContractRepository.findByTraineeIdWithInBodyRecords(id)
.orElseThrow(PtContractNotExistException::new);

TraineeEntity fetchedTrainee = ptContract.getTrainee();

return TraineeInfoResponseDto.fromEntity(fetchedTrainee, ptContract.getRemainingSession());
}

/**
* 새로운 인바디 기록을 추가합니다.
*
Expand Down Expand Up @@ -101,13 +138,42 @@ private TraineeEntity getTraineeById(Long id) {
*/
private TrainerEntity getAuthenticatedTrainer() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null || authentication.getName() == null) {
if (authentication == null
|| !(authentication.getPrincipal() instanceof UserPrincipal userPrincipal)) {
throw new TrainerNotFoundException();
}
return trainerRepository.findByEmail(authentication.getName())

UserPrincipal cachedUser = userCache.getIfPresent(userPrincipal.getEmail());
if (cachedUser != null && cachedUser.getTrainer() != null) {
return cachedUser.getTrainer();
}

return trainerRepository.findByEmail(userPrincipal.getEmail())
.orElseThrow(TrainerNotFoundException::new);
}

/**
* 인증된 트레이니를 조회합니다.
*
* @return 트레이니 엔티티
* @throws TraineeNotFoundException 트레이니가 존재하지 않을 경우 예외 발생
*/
private TraineeEntity getAuthenticatedTrainee() {
Authentication authentication = SecurityContextHolder.getContext().getAuthentication();
if (authentication == null
|| !(authentication.getPrincipal() instanceof UserPrincipal userPrincipal)) {
throw new TraineeNotFoundException();
}

UserPrincipal cachedUser = userCache.getIfPresent(userPrincipal.getEmail());
if (cachedUser != null && cachedUser.getTrainee() != null) {
return cachedUser.getTrainee();
}

return traineeRepository.findByEmail(userPrincipal.getEmail())
.orElseThrow(TraineeNotFoundException::new);
}

/**
* 트레이너와 트레이니 간의 PT 계약을 조회합니다.
*
Expand Down Expand Up @@ -159,4 +225,19 @@ private void updateRemainingSession(PtContractEntity ptContract, int remainingSe
int addition = remainingSession - ptContract.getRemainingSession();
ptContract.addSession(addition);
}

/**
* 현재 인증된 사용자의 역할을 반환합니다.
*
* @return 인증된 사용자의 역할
*/
private UserRoleType getMyRole() {
Authentication auth = SecurityContextHolder.getContext().getAuthentication();
if (auth == null || auth.getAuthorities() == null) {
throw new UserNotFoundException();
}
return auth.getAuthorities().stream()
.anyMatch(a -> a.getAuthority().equals("ROLE_TRAINER")) ? UserRoleType.TRAINER
: UserRoleType.TRAINEE;
}
}
Loading

0 comments on commit 66dc5f5

Please sign in to comment.