HabitPay / backend

https://habitpay.github.io/backend/
0 stars 0 forks source link

[feat] 챌린지 중도포기 api 구현#300 #301

Closed Han-Joon-Hyeok closed 3 weeks ago

Han-Joon-Hyeok commented 3 weeks ago

개요

챌린지 시작 이후 중도 포기를 위한 API 를 구현했습니다.

상세 작업 내용

1. 챌린지 중도 포기 API 구현

컨트롤러

중도 포기는 챌린지 등록 관련 로직이라 생각하여 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가지 입니다.

  1. ChallengeEnrollment 엔티티의 isGivenUp 을 true 로 설정합니다.
  2. Challenge 엔티티의 전체 참여자 수를 1만큼 감소시킵니다.
  3. 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);

테스트

챌린지 중도 포기 테스트 코드와 이에 해당하는 예외처리 테스트 코드를 작성했습니다.

2. 챌린지 중도 포기 이후 게시물 생성, 수정, 삭제 요청 예외처리 추가

중도 포기한 챌린지에서는 게시물 생성, 수정, 삭제가 불가능하도록 예외처리를 추가했습니다.

서비스

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());
}

챌린지 게시물 생성 컨트롤러 뿐만 아니라 다른 컨트롤러에서도 동일한 오류가 발생하여 일부 수정했습니다. 추후 챌린지 게시물 관련 컨트롤러 작업할 때 추가 확인이 필요합니다.

Han-Joon-Hyeok commented 3 weeks ago

하나의 PR 에 작업하다보니 생각보다 코드 양이 많아졌습니다...😅 미리 양해 부탁드립니다...