Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyRecruitInfoResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplySummaryResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyVolunteerInfoResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyWithReviewStatusResponseDto;
import com.somemore.domains.volunteerapply.usecase.VolunteerApplyQueryFacadeUseCase;
import com.somemore.domains.volunteerapply.usecase.VolunteerApplyQueryUseCase;
import com.somemore.global.auth.annotation.CurrentUser;
Expand Down Expand Up @@ -34,19 +35,18 @@ public class VolunteerApplyQueryApiController {

@Operation(summary = "특정 모집글 봉사자 지원 단건 조회", description = "특정 모집글에 대한 봉사자 지원을 조회합니다.")
@GetMapping("/volunteer-apply/recruit-board/{recruitBoardId}/volunteer/{volunteerId}")
public ApiResponse<?> getVolunteerApplyByRecruitIdAndVolunteerId(
public ApiResponse<VolunteerApplyWithReviewStatusResponseDto> getVolunteerApplyByRecruitIdAndVolunteerId(
@PathVariable Long recruitBoardId,
@PathVariable UUID volunteerId
) {
try {
return ApiResponse.ok(
200,
volunteerApplyQueryUseCase.getVolunteerApplyByRecruitIdAndVolunteerId(
recruitBoardId, volunteerId),
volunteerApplyQueryFacadeUseCase.getVolunteerApplyByRecruitIdAndVolunteerId(recruitBoardId, volunteerId),
"특정 모집글에 대한 봉사자 지원 단건 조회 성공"
);
} catch (NoSuchElementException e) {
return ApiResponse.ok(210, "지원 내역이 없습니다.");
return ApiResponse.ok(210, null, "지원 내역이 없습니다.");
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
package com.somemore.domains.volunteerapply.dto.response;

import com.fasterxml.jackson.databind.PropertyNamingStrategies;
import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.somemore.domains.volunteerapply.domain.ApplyStatus;
import com.somemore.domains.volunteerapply.domain.VolunteerApply;
Expand All @@ -9,33 +9,35 @@
import java.util.UUID;
import lombok.Builder;

@JsonNaming(PropertyNamingStrategies.SnakeCaseStrategy.class)
@JsonNaming(SnakeCaseStrategy.class)
@Builder
public record VolunteerApplyResponseDto(
public record VolunteerApplyWithReviewStatusResponseDto(
@Schema(description = "봉사 지원 ID", example = "1")
Long id,
@Schema(description = "봉사자 UUID", example = "f5a8779a-bcc9-4fc5-b8a1-7b2a383054a9")
UUID volunteerId,
@Schema(description = "모집글 ID", example = "101")
Long recruitBoardId,
@Schema(description = "지원 상태", example = "WAITING", allowableValues = {"WAITING",
"APPROVED", "REJECTED"})
@Schema(description = "지원 상태", example = "WAITING", allowableValues = {"WAITING", "APPROVED", "REJECTED"})
ApplyStatus status,
@Schema(description = "봉사 참여 여부", example = "false")
Boolean attended,
@Schema(description = "리뷰 작성 여부", example = "false")
Boolean isReviewed,
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

제가 이겼군요...

@Schema(description = "지원서 생성 일시", example = "2024-11-01T12:00:00")
LocalDateTime createdAt,
@Schema(description = "지원서 수정 일시", example = "2024-11-01T12:30:00")
LocalDateTime updatedAt
) {

public static VolunteerApplyResponseDto from(VolunteerApply volunteerApply) {
return VolunteerApplyResponseDto.builder()
public static VolunteerApplyWithReviewStatusResponseDto of(VolunteerApply volunteerApply, boolean isReviewed) {
return VolunteerApplyWithReviewStatusResponseDto.builder()
.id(volunteerApply.getId())
.volunteerId(volunteerApply.getVolunteerId())
.recruitBoardId(volunteerApply.getRecruitBoardId())
.status(volunteerApply.getStatus())
.attended(volunteerApply.getAttended())
.isReviewed(isReviewed)
.createdAt(volunteerApply.getCreatedAt())
.updatedAt(volunteerApply.getUpdatedAt())
.build();
Expand Down
Original file line number Diff line number Diff line change
@@ -1,16 +1,18 @@
package com.somemore.domains.volunteerapply.service;

import com.somemore.domains.recruitboard.domain.RecruitBoard;
import com.somemore.domains.recruitboard.service.validator.RecruitBoardValidator;
import com.somemore.domains.recruitboard.usecase.RecruitBoardQueryUseCase;
import com.somemore.domains.review.usecase.ReviewQueryUseCase;
import com.somemore.domains.volunteer.repository.mapper.VolunteerSimpleInfo;
import com.somemore.domains.volunteer.usecase.VolunteerQueryUseCase;
import com.somemore.domains.volunteerapply.domain.VolunteerApply;
import com.somemore.domains.volunteerapply.dto.condition.VolunteerApplySearchCondition;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyRecruitInfoResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyWithReviewStatusResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyVolunteerInfoResponseDto;
import com.somemore.domains.volunteerapply.usecase.VolunteerApplyQueryFacadeUseCase;
import com.somemore.domains.volunteerapply.usecase.VolunteerApplyQueryUseCase;
import com.somemore.global.exception.BadRequestException;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.stereotype.Service;
Expand All @@ -21,8 +23,6 @@
import java.util.UUID;
import java.util.stream.Collectors;

import static com.somemore.global.exception.ExceptionMessage.UNAUTHORIZED_RECRUIT_BOARD;

@RequiredArgsConstructor
@Transactional(readOnly = true)
@Service
Expand All @@ -31,52 +31,49 @@ public class VolunteerApplyQueryFacadeService implements VolunteerApplyQueryFaca
private final VolunteerApplyQueryUseCase volunteerApplyQueryUseCase;
private final RecruitBoardQueryUseCase recruitBoardQueryUseCase;
private final VolunteerQueryUseCase volunteerQueryUseCase;
private final ReviewQueryUseCase reviewQueryUseCase;
private final RecruitBoardValidator recruitBoardValidator;

@Override
public VolunteerApplyWithReviewStatusResponseDto getVolunteerApplyByRecruitIdAndVolunteerId(Long recruitId, UUID volunteerId) {
VolunteerApply apply = volunteerApplyQueryUseCase.getByRecruitIdAndVolunteerId(recruitId, volunteerId);
boolean isReviewed = checkIfReviewed(apply);
Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

리뷰가 작성되어있을 때, 체크한다 이런 느낌인가요?

Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

음.. 봉사 지원과 리뷰의 관계를 생각해봤을때, 봉사지원(상태가 APPROVED이고 참석여부가 TRUE일때)일때, 리뷰가 있을수도(작성했을 수도)없을 수도 있어서 위 메서드로 빼놨습니다.

즉, 리뷰 작성 가능성이 있을때, ReviewQueryUseCase로 조회하도록 했습니다.


return VolunteerApplyWithReviewStatusResponseDto.of(apply, isReviewed);
}

@Override
public Page<VolunteerApplyVolunteerInfoResponseDto> getVolunteerAppliesByRecruitIdAndCenterId(
Long recruitId, UUID centerId, VolunteerApplySearchCondition condition) {
validateAuthorization(recruitId, centerId);

Page<VolunteerApply> applies = volunteerApplyQueryUseCase.getAllByRecruitId(recruitId,
condition);
RecruitBoard recruitBoard = recruitBoardQueryUseCase.getById(recruitId);
recruitBoardValidator.validateWriter(recruitBoard, centerId);

Map<UUID, VolunteerSimpleInfo> volunteerMap = getVolunteerInfoMap(
applies);
Page<VolunteerApply> applies = volunteerApplyQueryUseCase.getAllByRecruitId(recruitId, condition);
Map<UUID, VolunteerSimpleInfo> volunteerMap = getVolunteerInfoMap(applies);

return applies.map(
apply -> VolunteerApplyVolunteerInfoResponseDto.of(
apply,
volunteerMap.getOrDefault(apply.getVolunteerId(), null)
));
apply -> VolunteerApplyVolunteerInfoResponseDto.of(apply, volunteerMap.getOrDefault(apply.getVolunteerId(), null)));
}

@Override
public Page<VolunteerApplyRecruitInfoResponseDto> getVolunteerAppliesByVolunteerId(
UUID volunteerId, VolunteerApplySearchCondition condition) {

Page<VolunteerApply> applies = volunteerApplyQueryUseCase.getAllByVolunteerId(volunteerId,
condition);

Page<VolunteerApply> applies = volunteerApplyQueryUseCase.getAllByVolunteerId(volunteerId, condition);
Map<Long, RecruitBoard> boardMap = getRecruitBoardMap(applies);

return applies.map(
apply -> VolunteerApplyRecruitInfoResponseDto.of(
apply,
boardMap.getOrDefault(apply.getRecruitBoardId(), null)
));
apply -> VolunteerApplyRecruitInfoResponseDto.of(apply, boardMap.getOrDefault(apply.getRecruitBoardId(), null)));
}

private void validateAuthorization(Long recruitId, UUID centerId) {
RecruitBoard recruitBoard = recruitBoardQueryUseCase.getById(recruitId);
if (recruitBoard.isWriter(centerId)) {
return;
}

throw new BadRequestException(UNAUTHORIZED_RECRUIT_BOARD);
private boolean checkIfReviewed(VolunteerApply apply) {
return apply.isVolunteerActivityCompleted()
&& reviewQueryUseCase.existsByVolunteerApplyId(apply.getId());
Copy link
Collaborator Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

@m-a-king
apply.isVolunteerActivityCompleted() == true 이면
reviewQueryUseCase.existsByVolunteerApplyId(apply.getId()) 이부분이 호출되면서 리뷰 작성 여부를 반환합니다.

반대로 전자가 false면 후자는 실행을 안하니까 조회 한번을 아낄수 있어요

Copy link
Collaborator

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

감사합니다. 이해했습니다~

}

private Map<Long, RecruitBoard> getRecruitBoardMap(Page<VolunteerApply> applies) {
List<Long> boardIds = applies.getContent().stream().map(VolunteerApply::getRecruitBoardId)
List<Long> boardIds = applies.getContent().stream()
.map(VolunteerApply::getRecruitBoardId)
.toList();
List<RecruitBoard> boards = recruitBoardQueryUseCase.getAllByIds(boardIds);

Expand All @@ -86,15 +83,14 @@ private Map<Long, RecruitBoard> getRecruitBoardMap(Page<VolunteerApply> applies)
}

private Map<UUID, VolunteerSimpleInfo> getVolunteerInfoMap(Page<VolunteerApply> applies) {
List<UUID> volunteerIds = applies.getContent().stream().map(VolunteerApply::getVolunteerId)
List<UUID> volunteerIds = applies.getContent().stream()
.map(VolunteerApply::getVolunteerId)
.toList();

List<VolunteerSimpleInfo> volunteers = volunteerQueryUseCase.getVolunteerSimpleInfosByIds(
volunteerIds);
List<VolunteerSimpleInfo> volunteers = volunteerQueryUseCase.getVolunteerSimpleInfosByIds(volunteerIds);

return volunteers.stream()
.collect(Collectors.toMap(VolunteerSimpleInfo::id,
volunteer -> volunteer));
.collect(Collectors.toMap(VolunteerSimpleInfo::id, volunteer -> volunteer));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.somemore.domains.volunteerapply.domain.VolunteerApply;
import com.somemore.domains.volunteerapply.dto.condition.VolunteerApplySearchCondition;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplySummaryResponseDto;
import com.somemore.domains.volunteerapply.repository.VolunteerApplyRepository;
import com.somemore.domains.volunteerapply.usecase.VolunteerApplyQueryUseCase;
Expand Down Expand Up @@ -46,13 +45,6 @@ public VolunteerApplySummaryResponseDto getSummaryByRecruitId(Long recruitId) {
return VolunteerApplySummaryResponseDto.from(applies);
}

@Override
public VolunteerApplyResponseDto getVolunteerApplyByRecruitIdAndVolunteerId(Long recruitId, UUID volunteerId) {
VolunteerApply apply = getByRecruitIdAndVolunteerId(recruitId, volunteerId);

return VolunteerApplyResponseDto.from(apply);
}

@Override
public Page<VolunteerApply> getAllByRecruitId(Long recruitId, VolunteerApplySearchCondition condition) {
return volunteerApplyRepository.findAllByRecruitId(recruitId, condition);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,18 +2,18 @@

import com.somemore.domains.volunteerapply.dto.condition.VolunteerApplySearchCondition;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyRecruitInfoResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyWithReviewStatusResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyVolunteerInfoResponseDto;
import org.springframework.data.domain.Page;

import java.util.UUID;
import org.springframework.data.domain.Page;

public interface VolunteerApplyQueryFacadeUseCase {

Page<VolunteerApplyVolunteerInfoResponseDto> getVolunteerAppliesByRecruitIdAndCenterId(
Long recruitId,
UUID centerId, VolunteerApplySearchCondition condition);
VolunteerApplyWithReviewStatusResponseDto getVolunteerApplyByRecruitIdAndVolunteerId(Long recruitId, UUID volunteerId);

Page<VolunteerApplyVolunteerInfoResponseDto> getVolunteerAppliesByRecruitIdAndCenterId(Long recruitId, UUID centerId, VolunteerApplySearchCondition condition);

Page<VolunteerApplyRecruitInfoResponseDto> getVolunteerAppliesByVolunteerId(UUID volunteerId,
VolunteerApplySearchCondition condition);
Page<VolunteerApplyRecruitInfoResponseDto> getVolunteerAppliesByVolunteerId(UUID volunteerId, VolunteerApplySearchCondition condition);

}
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,6 @@

import com.somemore.domains.volunteerapply.domain.VolunteerApply;
import com.somemore.domains.volunteerapply.dto.condition.VolunteerApplySearchCondition;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplySummaryResponseDto;
import org.springframework.data.domain.Page;

Expand All @@ -17,8 +16,6 @@ public interface VolunteerApplyQueryUseCase {

VolunteerApplySummaryResponseDto getSummaryByRecruitId(Long recruitId);

VolunteerApplyResponseDto getVolunteerApplyByRecruitIdAndVolunteerId(Long recruitId, UUID volunteerId);

List<UUID> getVolunteerIdsByRecruitIds(List<Long> recruitIds);

List<VolunteerApply> getAllByIds(List<Long> ids);
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@

import com.somemore.domains.volunteerapply.dto.condition.VolunteerApplySearchCondition;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyRecruitInfoResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyWithReviewStatusResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplySummaryResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyVolunteerInfoResponseDto;
import com.somemore.domains.volunteerapply.usecase.VolunteerApplyQueryFacadeUseCase;
Expand Down Expand Up @@ -44,15 +44,15 @@ void getVolunteerApplyByRecruitIdAndVolunteerId() throws Exception {
Long recruitBoardId = 1L;
UUID volunteerId = UUID.randomUUID();

VolunteerApplyResponseDto response = VolunteerApplyResponseDto.builder()
VolunteerApplyWithReviewStatusResponseDto response = VolunteerApplyWithReviewStatusResponseDto.builder()
.id(1L)
.volunteerId(volunteerId)
.recruitBoardId(recruitBoardId)
.status(WAITING)
.attended(false)
.build();

given(volunteerApplyQueryUseCase.getVolunteerApplyByRecruitIdAndVolunteerId(recruitBoardId,
given(volunteerApplyQueryFacadeUseCase.getVolunteerApplyByRecruitIdAndVolunteerId(recruitBoardId,
volunteerId))
.willReturn(response);

Expand All @@ -78,7 +78,7 @@ void getVolunteerApplyByRecruitIdAndVolunteerIdWhenDoesNotExist() throws Excepti
Long recruitBoardId = 1L;
UUID volunteerId = UUID.randomUUID();

given(volunteerApplyQueryUseCase.getVolunteerApplyByRecruitIdAndVolunteerId(recruitBoardId,
given(volunteerApplyQueryFacadeUseCase.getVolunteerApplyByRecruitIdAndVolunteerId(recruitBoardId,
volunteerId))
.willThrow(new NoSuchElementException(NOT_EXISTS_VOLUNTEER_APPLY));

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,8 @@

import com.somemore.domains.recruitboard.domain.RecruitBoard;
import com.somemore.domains.recruitboard.repository.RecruitBoardRepository;
import com.somemore.domains.review.domain.Review;
import com.somemore.domains.review.repository.ReviewRepository;
import com.somemore.domains.volunteer.domain.Volunteer;
import com.somemore.domains.volunteer.domain.VolunteerDetail;
import com.somemore.domains.volunteer.dto.request.VolunteerRegisterRequestDto;
Expand All @@ -11,6 +13,7 @@
import com.somemore.domains.volunteerapply.dto.condition.VolunteerApplySearchCondition;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyRecruitInfoResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyVolunteerInfoResponseDto;
import com.somemore.domains.volunteerapply.dto.response.VolunteerApplyWithReviewStatusResponseDto;
import com.somemore.domains.volunteerapply.repository.VolunteerApplyRepository;
import com.somemore.support.IntegrationTestSupport;
import org.junit.jupiter.api.DisplayName;
Expand Down Expand Up @@ -42,7 +45,30 @@ class VolunteerApplyQueryFacadeServiceTest extends IntegrationTestSupport {
private VolunteerDetailRepository volunteerDetailRepository;
@Autowired
private VolunteerApplyRepository volunteerApplyRepository;
@Autowired
private ReviewRepository reviewRepository;

@DisplayName("모집글 아이디와 봉사자 아이디로 지원 응답 값을 조회할 수 있다.")
@Test
void getVolunteerApplyByRecruitIdAndVolunteerId() {
// given
Long recruitBoardId = 1234L;
UUID volunteerId = UUID.randomUUID();
VolunteerApply apply = createApply(volunteerId, recruitBoardId);
volunteerApplyRepository.save(apply);

Review review = createReview(apply.getId(), volunteerId);
reviewRepository.save(review);

// when
VolunteerApplyWithReviewStatusResponseDto dto = volunteerApplyQueryFacadeService.getVolunteerApplyByRecruitIdAndVolunteerId(
recruitBoardId, volunteerId);

// then
assertThat(dto.recruitBoardId()).isEqualTo(recruitBoardId);
assertThat(dto.volunteerId()).isEqualTo(volunteerId);
assertThat(dto.isReviewed()).isTrue();
}

@DisplayName("모집글 아이디와 기관 아이디로 필터에 맞는 지원자 간단 정보를 조회할 수 있다.")
@Test
Expand Down Expand Up @@ -108,6 +134,16 @@ void getVolunteerAppliesByVolunteerId() {
assertThat(result).hasSize(3);
}

private static Review createReview(Long volunteerApplyId, UUID volunteerId) {
return Review.builder()
.volunteerApplyId(volunteerApplyId)
.volunteerId(volunteerId)
.title("리뷰 제목")
.content("리뷰 내용")
.imgUrl("리뷰 이미지")
.build();
}

private static VolunteerDetail createVolunteerDetail(UUID volunteerId) {

VolunteerRegisterRequestDto volunteerRegisterRequestDto =
Expand All @@ -130,7 +166,7 @@ private static VolunteerApply createApply(UUID volunteerId, Long recruitId) {
.volunteerId(volunteerId)
.recruitBoardId(recruitId)
.status(APPROVED)
.attended(false)
.attended(true)
.build();
}

Expand Down
Loading
Loading