Skip to content

Commit

Permalink
feat: 봉사자가 신청한 봉사 리스트 조회 로직을 구현한다. (#87)
Browse files Browse the repository at this point in the history
* feat: Review 생성시 검증 로직을 추가한다.

* refactor: Review 를 Applicant 와 oneToOne 관계를 설정한다.

* feat: 봉사자가 신청한 봉사 리스트 조회 쿼리를 작성한다.

* test: 봉사자가 신청한 봉사 리스트 조회 레포지토리 테스트 코드를 작성한다.

* feat: 봉사자가 신청한 봉사 리스트 조회 서비스 코드를 작성한다.

* test: 봉사자가 신청한 봉사 리스트 조회 테스트 코드를 작성한다.

* feat: 봉사자가 신청한 봉사 리스트 조회 컨트롤러 코드를 작성한다.

* fix: Review 연관관계 변경으로 인한 빌드 실패를 수정한다.

* fix: Review 연관관계 변경으로 인한 빌드 실패를 수정한다.

* refactor : 후기 작성 가능 여부를 applicant에서 판단한다.

* style: shouldWriteReview를 hasNotReview로 변경한다.

* fix: 충돌을 해결한다.

---------

Co-authored-by: pushedrumex <[email protected]>
Co-authored-by: [email protected] <[email protected]>
Co-authored-by: hseong3243 <[email protected]>
  • Loading branch information
4 people authored Nov 6, 2023
1 parent 975d4ba commit 790ad93
Show file tree
Hide file tree
Showing 16 changed files with 339 additions and 9 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -21,6 +21,7 @@
import jakarta.persistence.OneToOne;
import jakarta.persistence.Table;
import java.time.LocalDateTime;
import java.util.Objects;
import lombok.AccessLevel;
import lombok.NoArgsConstructor;

Expand Down Expand Up @@ -112,6 +113,10 @@ public boolean hasReview() {
return review != null;
}

public boolean hasNotReview() {
return getStatus().equals(ApplicantStatus.ATTENDANCE) && Objects.isNull(review);
}

public void registerReview(Review review) {
this.review = review;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
package com.clova.anifriends.domain.applicant.controller;

import com.clova.anifriends.domain.applicant.dto.FindApplicantsApprovedResponse;
import com.clova.anifriends.domain.applicant.dto.FindApplyingVolunteersResponse;
import com.clova.anifriends.domain.applicant.service.ApplicantService;
import com.clova.anifriends.domain.auth.resolver.LoginUser;
import lombok.RequiredArgsConstructor;
Expand All @@ -27,6 +28,13 @@ public ResponseEntity<Void> registerApplicant(
return ResponseEntity.noContent().build();
}

@GetMapping("/volunteers/applicants")
public ResponseEntity<FindApplyingVolunteersResponse> findApplyingVolunteers(
@LoginUser Long volunteerId
) {
return ResponseEntity.ok(applicantService.findApplyingVolunteers(volunteerId));

}

@GetMapping("/shelters/recruitments/{recruitmentId}/approval")
public ResponseEntity<FindApplicantsApprovedResponse> findApplicantApproved(
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,47 @@
package com.clova.anifriends.domain.applicant.dto;

import com.clova.anifriends.domain.applicant.Applicant;
import com.clova.anifriends.domain.applicant.wrapper.ApplicantStatus;
import java.time.LocalDateTime;
import java.util.List;

public record FindApplyingVolunteersResponse(
List<FindApplyingVolunteerResponse> findApplyingVolunteerResponses
) {

public record FindApplyingVolunteerResponse(
Long recruitmentId,
Long applicantId,
String title,
String shelterName,
ApplicantStatus status,
boolean isWritedReview,
LocalDateTime volunteerDate
) {

public static FindApplyingVolunteerResponse from(
Applicant applicant
) {
return new FindApplyingVolunteerResponse(
applicant.getRecruitment().getRecruitmentId(),
applicant.getApplicantId(),
applicant.getRecruitment().getTitle(),
applicant.getRecruitment().getShelter().getName(),
applicant.getStatus(),
applicant.hasNotReview(),
applicant.getRecruitment().getStartTime()
);
}
}

public static FindApplyingVolunteersResponse from(
List<Applicant> applicants
) {
return new FindApplyingVolunteersResponse(
applicants
.stream()
.map(FindApplyingVolunteerResponse::from)
.toList()
);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,17 @@ public interface ApplicantRepository extends JpaRepository<Applicant, Long> {

boolean existsByRecruitmentAndVolunteer(Recruitment recruitment, Volunteer volunteer);

@Query(
"select a "
+ "from Applicant a "
+ "left join fetch a.recruitment "
+ "left join fetch a.volunteer "
+ "left join fetch a.review "
+ "where a.volunteer = :volunteer"
)
List<Applicant> findApplyingVolunteers(
@Param("volunteer") Volunteer volunteer);

@Query("select a from Applicant a "
+ "where a.applicantId = :applicantId "
+ "and a.volunteer.volunteerId = :volunteerId")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@

import com.clova.anifriends.domain.applicant.Applicant;
import com.clova.anifriends.domain.applicant.dto.FindApplicantsApprovedResponse;
import com.clova.anifriends.domain.applicant.dto.FindApplyingVolunteersResponse;
import com.clova.anifriends.domain.applicant.exception.ApplicantConflictException;
import com.clova.anifriends.domain.applicant.repository.ApplicantRepository;
import com.clova.anifriends.domain.recruitment.Recruitment;
Expand Down Expand Up @@ -35,6 +36,18 @@ public void registerApplicant(Long recruitmentId, Long volunteerId) {
applicantRepository.save(applicant);
}

@Transactional(readOnly = true)
public FindApplyingVolunteersResponse findApplyingVolunteers(
Long volunteerId
) {
Volunteer foundVolunteer = getVolunteer(volunteerId);

List<Applicant> applyingVolunteers = applicantRepository.findApplyingVolunteers(
foundVolunteer);

return FindApplyingVolunteersResponse.from(applyingVolunteers);
}

private Recruitment getRecruitment(Long recruitmentId) {
return recruitmentRepository.findById(recruitmentId)
.orElseThrow(() -> new RecruitmentNotFoundException("존재하지 않는 봉사입니다."));
Expand Down
5 changes: 5 additions & 0 deletions src/main/java/com/clova/anifriends/domain/review/Review.java
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@
import com.clova.anifriends.domain.review.exception.ReviewAuthorizationException;
import com.clova.anifriends.domain.review.exception.ReviewBadRequestException;
import com.clova.anifriends.domain.review.wrapper.ReviewContent;
import com.clova.anifriends.domain.volunteer.Volunteer;
import jakarta.persistence.Column;
import jakarta.persistence.Embedded;
import jakarta.persistence.Entity;
Expand Down Expand Up @@ -87,4 +88,8 @@ public Long getReviewId() {
public Applicant getApplicant() {
return applicant;
}

public Volunteer getVolunteer() {
return applicant.getVolunteer();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -27,6 +27,8 @@ Optional<Review> findByReviewIdAndVolunteerId(
@Query("select r from Review r "
+ "join fetch r.applicant a "
+ "where a.volunteer.volunteerId = :volunteerId")
Page<Review> findAllByVolunteerVolunteerIdOrderByCreatedAtDesc(@Param("volunteerId") Long volunteerId,
Page<Review> findAllByVolunteerVolunteerIdOrderByCreatedAtDesc(
@Param("volunteerId") Long volunteerId,
Pageable pageable);
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -6,9 +6,9 @@
import com.clova.anifriends.domain.review.Review;
import com.clova.anifriends.domain.review.dto.response.FindReviewResponse;
import com.clova.anifriends.domain.review.dto.response.FindShelterReviewsResponse;
import com.clova.anifriends.domain.review.dto.response.FindVolunteerReviewsResponse;
import com.clova.anifriends.domain.review.exception.ApplicantNotFoundException;
import com.clova.anifriends.domain.review.exception.ReviewBadRequestException;
import com.clova.anifriends.domain.review.dto.response.FindVolunteerReviewsResponse;
import com.clova.anifriends.domain.review.exception.ReviewNotFoundException;
import com.clova.anifriends.domain.review.repository.ReviewRepository;
import java.util.List;
Expand Down
12 changes: 12 additions & 0 deletions src/test/java/com/clova/anifriends/base/BaseRepositoryTest.java
Original file line number Diff line number Diff line change
@@ -1,8 +1,11 @@
package com.clova.anifriends.base;

import com.clova.anifriends.base.config.TestQueryDslConfig;
import com.clova.anifriends.domain.applicant.repository.ApplicantRepository;
import com.clova.anifriends.domain.recruitment.repository.RecruitmentRepository;
import com.clova.anifriends.domain.review.repository.ReviewRepository;
import com.clova.anifriends.domain.shelter.repository.ShelterRepository;
import com.clova.anifriends.domain.volunteer.repository.VolunteerRepository;
import jakarta.persistence.EntityManager;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.autoconfigure.orm.jpa.DataJpaTest;
Expand All @@ -15,9 +18,18 @@ public abstract class BaseRepositoryTest {
@Autowired
protected EntityManager entityManager;

@Autowired
protected ApplicantRepository applicantRepository;

@Autowired
protected ReviewRepository reviewRepository;

@Autowired
protected ShelterRepository shelterRepository;

@Autowired
protected RecruitmentRepository recruitmentRepository;

@Autowired
protected VolunteerRepository volunteerRepository;
}
Original file line number Diff line number Diff line change
@@ -1,6 +1,9 @@
package com.clova.anifriends.domain.applicant.controller;

import static com.clova.anifriends.domain.applicant.wrapper.ApplicantStatus.ATTENDANCE;
import static com.clova.anifriends.domain.applicant.wrapper.ApplicantStatus.PENDING;
import static org.mockito.ArgumentMatchers.anyLong;
import static org.mockito.BDDMockito.given;
import static org.mockito.Mockito.doNothing;
import static org.mockito.Mockito.when;
import static org.springframework.restdocs.headers.HeaderDocumentation.headerWithName;
Expand All @@ -14,15 +17,28 @@
import static org.springframework.restdocs.payload.PayloadDocumentation.responseFields;
import static org.springframework.restdocs.request.RequestDocumentation.parameterWithName;
import static org.springframework.restdocs.request.RequestDocumentation.pathParameters;
import static org.springframework.test.util.ReflectionTestUtils.setField;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.post;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.clova.anifriends.base.BaseControllerTest;
import com.clova.anifriends.domain.applicant.Applicant;
import com.clova.anifriends.domain.applicant.dto.FindApplicantsApprovedResponse;
import com.clova.anifriends.domain.applicant.dto.FindApplyingVolunteersResponse;
import com.clova.anifriends.domain.applicant.support.ApplicantFixture;
import com.clova.anifriends.domain.recruitment.Recruitment;
import com.clova.anifriends.domain.recruitment.support.fixture.RecruitmentFixture;
import com.clova.anifriends.domain.review.Review;
import com.clova.anifriends.domain.review.support.ReviewFixture;
import com.clova.anifriends.domain.shelter.Shelter;
import com.clova.anifriends.domain.shelter.support.ShelterFixture;
import com.clova.anifriends.domain.volunteer.Volunteer;
import com.clova.anifriends.domain.volunteer.support.VolunteerFixture;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.http.MediaType;
import org.springframework.restdocs.payload.JsonFieldType;
import org.springframework.test.web.servlet.ResultActions;

class ApplicantControllerTest extends BaseControllerTest {
Expand All @@ -46,6 +62,80 @@ void registerApplicant() throws Exception {
));
}

@Test
@DisplayName("findApplyingVolunteers 실행 시")
void findApplyingVolunteers() throws Exception {
// given
Long volunteerId = 1L;
Long recruitmentId = 1L;
Long applicantShouldWriteReviewId = 1L;
Long applicantShouldNotWriteReviewId = 2L;

Shelter shelter = ShelterFixture.shelter();
Volunteer volunteer = VolunteerFixture.volunteer();
setField(volunteer, "volunteerId", volunteerId);

Recruitment recruitment = RecruitmentFixture.recruitment(shelter);
setField(recruitment, "recruitmentId", recruitmentId);

Applicant applicantShouldWriteReview = ApplicantFixture.applicant(recruitment,
volunteer, ATTENDANCE);
Applicant applicantShouldNotWriteReview = ApplicantFixture.applicant(recruitment,
volunteer, PENDING);

setField(applicantShouldWriteReview, "applicantId", applicantShouldWriteReviewId);
setField(applicantShouldNotWriteReview, "applicantId", applicantShouldNotWriteReviewId);

Review review = ReviewFixture.review(applicantShouldWriteReview);
setField(review, "reviewId", 1L);

FindApplyingVolunteersResponse findApplyingVolunteersResponse = FindApplyingVolunteersResponse.from(
List.of(
applicantShouldWriteReview,
applicantShouldNotWriteReview
)
);

given(applicantService.findApplyingVolunteers(volunteerId)).willReturn(
findApplyingVolunteersResponse);

// when
ResultActions resultActions = mockMvc.perform(
get("/api/volunteers/applicants")
.header(AUTHORIZATION, volunteerAccessToken)
.contentType(MediaType.APPLICATION_JSON));

// then
resultActions.andExpect(status().isOk())
.andDo(restDocs.document(
responseFields(
fieldWithPath("findApplyingVolunteerResponses").type(JsonFieldType.ARRAY)
.description("신청한 봉사 리스트"),
fieldWithPath("findApplyingVolunteerResponses[].recruitmentId").type(
JsonFieldType.NUMBER)
.description("봉사 모집글 ID"),
fieldWithPath("findApplyingVolunteerResponses[].applicantId").type(
JsonFieldType.NUMBER)
.description("봉사 신청자 ID"),
fieldWithPath("findApplyingVolunteerResponses[].title").type(
JsonFieldType.STRING)
.description("모집글 제목"),
fieldWithPath("findApplyingVolunteerResponses[].shelterName").type(
JsonFieldType.STRING)
.description("보호소 이름"),
fieldWithPath("findApplyingVolunteerResponses[].status").type(
JsonFieldType.STRING)
.description("승인 상태"),
fieldWithPath("findApplyingVolunteerResponses[].isWritedReview").type(
JsonFieldType.BOOLEAN)
.description("후기 작성 가능 여부"),
fieldWithPath("findApplyingVolunteerResponses[].volunteerDate").type(
JsonFieldType.STRING)
.description("봉사 날짜")
)
));
}

@Test
@DisplayName("봉사 신청 승인자 조회 API 호출 시")
void findApplicantApproved() throws Exception {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -9,15 +9,23 @@
import static com.clova.anifriends.domain.shelter.support.ShelterFixture.shelter;
import static com.clova.anifriends.domain.volunteer.support.VolunteerFixture.volunteer;
import static org.assertj.core.api.Assertions.assertThat;
import static org.springframework.test.util.ReflectionTestUtils.setField;

import com.clova.anifriends.base.BaseRepositoryTest;
import com.clova.anifriends.domain.applicant.Applicant;
import com.clova.anifriends.domain.applicant.dto.FindApplyingVolunteersResponse;
import com.clova.anifriends.domain.applicant.support.ApplicantFixture;
import com.clova.anifriends.domain.recruitment.Recruitment;
import com.clova.anifriends.domain.recruitment.repository.RecruitmentRepository;
import com.clova.anifriends.domain.recruitment.support.fixture.RecruitmentFixture;
import com.clova.anifriends.domain.review.Review;
import com.clova.anifriends.domain.review.support.ReviewFixture;
import com.clova.anifriends.domain.shelter.Shelter;
import com.clova.anifriends.domain.shelter.repository.ShelterRepository;
import com.clova.anifriends.domain.shelter.support.ShelterFixture;
import com.clova.anifriends.domain.volunteer.Volunteer;
import com.clova.anifriends.domain.volunteer.repository.VolunteerRepository;
import com.clova.anifriends.domain.volunteer.support.VolunteerFixture;
import java.util.List;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Nested;
Expand Down Expand Up @@ -99,4 +107,49 @@ void findApprovedByRecruitmentIdAndShelterId2() {
}
}

@Nested
@DisplayName("findApplyingVolunteers 실행 시")
class FindApplyingVolunteersTest {

@Test
@DisplayName("성공")
void findApplyingVolunteers() {
// given
Volunteer volunteer = VolunteerFixture.volunteer();
Shelter shelter = ShelterFixture.shelter();
Recruitment recruitment = RecruitmentFixture.recruitment(shelter);

shelterRepository.save(shelter);
volunteerRepository.save(volunteer);
recruitmentRepository.save(recruitment);

Applicant applicantShouldWriteReview = ApplicantFixture.applicant(
recruitment, volunteer, ATTENDANCE);
Applicant applicantShouldNotWriteReview1 = ApplicantFixture.applicant(
recruitment, volunteer, PENDING);
Applicant applicantShouldNotWriteReview2 = ApplicantFixture.applicant(
recruitment, volunteer, ATTENDANCE);
Review review = ReviewFixture.review(applicantShouldNotWriteReview2);
setField(review, "reviewId", 1L);

applicantRepository.save(applicantShouldWriteReview);
applicantRepository.save(applicantShouldNotWriteReview1);
applicantRepository.save(applicantShouldNotWriteReview2);

// when
List<Applicant> applyingVolunteers = applicantRepository.findApplyingVolunteers(
volunteer);

FindApplyingVolunteersResponse expected = FindApplyingVolunteersResponse.from(
applyingVolunteers);

// then
assertThat(expected.findApplyingVolunteerResponses().get(0).isWritedReview()).isEqualTo(
true);
assertThat(expected.findApplyingVolunteerResponses().get(1).isWritedReview()).isEqualTo(
false);
assertThat(expected.findApplyingVolunteerResponses().get(2).isWritedReview()).isEqualTo(
false);
}
}
}
Loading

0 comments on commit 790ad93

Please sign in to comment.