중도 포기는 챌린지 등록 관련 로직이라 생각하여 ChallengeEnrollment 도메인에 챌린지 중도 포기 컨트롤러를 추가했습니다.
@PostMapping("/challenges/{id}/give-up")
public SuccessResponse<Void> giveUpChallenge(@PathVariable("id") Long id, @AuthenticationPrincipal CustomUserDetails user) {
return challengeEnrollmentCancellationService.giveUp(id, user.getMember());
}
서비스
챌린지 중도 포기 시, DB 에서 수행되는 작업은 다음과 같이 3가지 입니다.
ChallengeEnrollment 엔티티의 isGivenUp 을 true 로 설정합니다.
Challenge 엔티티의 전체 참여자 수를 1만큼 감소시킵니다.
challenge_participation_record 테이블에서 오늘을 포함한 이후 날짜에 해당하는 모든 참여 기록을 삭제합니다.
@Transactional
public SuccessResponse<Void> giveUp(Long challengeId, Member member) {
Challenge challenge = challengeSearchService.getChallengeById(challengeId);
ChallengeEnrollment challengeEnrollment = challengeEnrollmentSearchService.getByMemberAndChallenge(member, challenge);
ZonedDateTime now = TimeZoneConverter.convertEtcToLocalTimeZone(ZonedDateTime.now());
if (now.isBefore(challenge.getStartDate())) {
throw new BadRequestException(ErrorCode.TOO_EARLY_GIVEN_UP_CHALLENGE);
}
if (challengeEnrollment.isGivenUp()) {
throw new AlreadyGivenUpChallengeException(member.getId(), challengeId);
}
challengeEnrollment.setGivenUp(true);
challenge.setNumberOfParticipants(challenge.getNumberOfParticipants() - 1);
challengeParticipationRecordRepository.deleteAllByChallengeEnrollmentAndTargetDateAfterOrTargetDate(challengeEnrollment, now.with(LocalTime.MIDNIGHT));
return SuccessResponse.of(SuccessCode.GIVING_UP_CHALLENGE);
}
deleteAllByChallengeEnrollmentAndTargetDateAfterOrTargetDate 메서드는 다음과 같이 JPQL 을 활용하여 작성하였습니다.
@Modifying
@Query("DELETE FROM ChallengeParticipationRecord r WHERE r.challengeEnrollment = :challengeEnrollment AND (r.targetDate > :date OR r.targetDate = :date)")
void deleteAllByChallengeEnrollmentAndTargetDateAfterOrTargetDate(@Param("challengeEnrollment") ChallengeEnrollment challengeEnrollment, @Param("date") ZonedDateTime date);
데이터를 삭제하기 위해 @Modifying 어노테이션을 사용했습니다.
date 매개변수는 targetDate 와 시간 형식을 맞추기 위해 00시 00분으로 전달됩니다.
ChallengeEnrollment 엔티티의 isGivenUp 값에 따라 예외를 처리하도록 구현했습니다.
@Transactional
public SuccessResponse<List<String>> createPost(AddPostRequest request, Long challengeId, Member member) {
// <-- 추가한 부분
Challenge challenge = challengeSearchService.getChallengeById(challengeId);
ChallengeEnrollment enrollment = challengeEnrollmentSearchService.getByMemberAndChallenge(member, challenge);
if (enrollment.isGivenUp()) {
throw new ForbiddenException(ErrorCode.POST_CREATION_FORBIDDEN_DUE_TO_GIVE_UP);
}
// -->
challengePostUtilService.checkChallengePeriodForPost(challenge);
ChallengePost challengePost = this.savePost(request, challenge, member, enrollment);
if (!challengePost.getIsAnnouncement()) {
challengePostUtilService.verifyChallengePostForRecord(challengePost);
}
List<String> presignedUrlList = postPhotoCreationService.createPhotoUrlList(challengePost, request.getPhotos());
return SuccessResponse.of(
SuccessCode.CREATE_POST_SUCCESS,
presignedUrlList
);
}
테스트
챌린지 생성, 수정, 삭제에서 추가한 예외처리 테스트 코드를 작성했습니다.
3. 챌린지 게시물 생성 컨트롤러의 @PathVariable 어노테이션 필드명 추가
챌린지 게시물 생성 테스트 중, @PathVariable 어노테이션 필드명이 명시되지 않아 다음과 같은 오류가 발생했습니다.
Name for argument of type [java.lang.Long] not specified, and parameter name information not available via reflection. Ensure that the compiler uses the '-parameters' flag.
이를 해결하기 위해 @PathVariable("id") Long id 와 같이 필드명을 명시했습니다.
@PostMapping("/challenges/{id}/post")
public SuccessResponse<List<String>> createPost(
@RequestBody AddPostRequest request,
@PathVariable("id") Long id,
@AuthenticationPrincipal CustomUserDetails user) {
return challengePostCreationService.createPost(request, id, user.getMember());
}
챌린지 게시물 생성 컨트롤러 뿐만 아니라 다른 컨트롤러에서도 동일한 오류가 발생하여 일부 수정했습니다.
추후 챌린지 게시물 관련 컨트롤러 작업할 때 추가 확인이 필요합니다.
개요
챌린지 시작 이후 중도 포기를 위한 API 를 구현했습니다.
상세 작업 내용
1. 챌린지 중도 포기 API 구현
컨트롤러
중도 포기는 챌린지 등록 관련 로직이라 생각하여
ChallengeEnrollment
도메인에 챌린지 중도 포기 컨트롤러를 추가했습니다.서비스
챌린지 중도 포기 시, DB 에서 수행되는 작업은 다음과 같이 3가지 입니다.
ChallengeEnrollment
엔티티의isGivenUp
을 true 로 설정합니다.Challenge
엔티티의 전체 참여자 수를 1만큼 감소시킵니다.challenge_participation_record
테이블에서 오늘을 포함한 이후 날짜에 해당하는 모든 참여 기록을 삭제합니다.deleteAllByChallengeEnrollmentAndTargetDateAfterOrTargetDate
메서드는 다음과 같이 JPQL 을 활용하여 작성하였습니다.@Modifying
어노테이션을 사용했습니다.date
매개변수는targetDate
와 시간 형식을 맞추기 위해 00시 00분으로 전달됩니다.테스트
챌린지 중도 포기 테스트 코드와 이에 해당하는 예외처리 테스트 코드를 작성했습니다.
2. 챌린지 중도 포기 이후 게시물 생성, 수정, 삭제 요청 예외처리 추가
중도 포기한 챌린지에서는 게시물 생성, 수정, 삭제가 불가능하도록 예외처리를 추가했습니다.
서비스
ChallengeEnrollment
엔티티의isGivenUp
값에 따라 예외를 처리하도록 구현했습니다.테스트
챌린지 생성, 수정, 삭제에서 추가한 예외처리 테스트 코드를 작성했습니다.
3. 챌린지 게시물 생성 컨트롤러의
@PathVariable
어노테이션 필드명 추가챌린지 게시물 생성 테스트 중,
@PathVariable
어노테이션 필드명이 명시되지 않아 다음과 같은 오류가 발생했습니다.이를 해결하기 위해
@PathVariable("id") Long id
와 같이 필드명을 명시했습니다.챌린지 게시물 생성 컨트롤러 뿐만 아니라 다른 컨트롤러에서도 동일한 오류가 발생하여 일부 수정했습니다. 추후 챌린지 게시물 관련 컨트롤러 작업할 때 추가 확인이 필요합니다.