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 @@ -32,6 +32,7 @@ public enum ExceptionMessage {
RECRUITMENT_NOT_OPEN("현재 모집 진행 중이 아닙니다."),
DUPLICATE_APPLICATION("이미 신청한 봉사 모집 공고입니다."),
UNAUTHORIZED_VOLUNTEER_APPLY("해당 지원에 권한이 없습니다."),
RECRUIT_BOARD_ALREADY_COMPLETED("이미 종료된 봉사 활동입니다."),

;
private final String message;
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.somemore.recruitboard.domain;

import static com.somemore.recruitboard.domain.RecruitStatus.COMPLETED;
import static com.somemore.recruitboard.domain.RecruitStatus.RECRUITING;
import static jakarta.persistence.EnumType.STRING;
import static jakarta.persistence.GenerationType.IDENTITY;
Expand Down Expand Up @@ -106,6 +107,10 @@ private void updateRecruitmentInfo(RecruitBoardUpdateRequestDto dto) {
);
}

public boolean isCompleted() {
return this.recruitStatus == COMPLETED;
}

private void validateStatusChange(RecruitStatus newStatus) {
if (newStatus.isChangeable()) {
return;
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -7,14 +7,14 @@
import com.somemore.recruitboard.dto.response.RecruitBoardResponseDto;
import com.somemore.recruitboard.dto.response.RecruitBoardWithCenterResponseDto;
import com.somemore.recruitboard.dto.response.RecruitBoardWithLocationResponseDto;
import org.springframework.data.domain.Page;

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

public interface RecruitBoardQueryUseCase {

RecruitBoard getById(Long id);

RecruitBoardResponseDto getRecruitBoardById(Long id);

RecruitBoardWithLocationResponseDto getWithLocationById(Long id);
Expand All @@ -25,7 +25,7 @@ Page<RecruitBoardDetailResponseDto> getRecruitBoardsNearby(
RecruitBoardNearByCondition condition);

Page<RecruitBoardResponseDto> getRecruitBoardsByCenterId(UUID centerId,
RecruitBoardSearchCondition condition);
RecruitBoardSearchCondition condition);

List<Long> getNotCompletedIdsByCenterIds(UUID centerId);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package com.somemore.volunteerapply.controller;

import com.somemore.auth.annotation.CurrentUser;
import com.somemore.global.common.response.ApiResponse;
import com.somemore.volunteerapply.usecase.ApproveVolunteerApplyUseCase;
import com.somemore.volunteerapply.usecase.RejectVolunteerApplyUseCase;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "Center Volunteer Apply Command API", description = "봉사 활동 지원 승인, 거절, 정산 API")
@RequiredArgsConstructor
@RequestMapping("/api")
@RestController
public class CenterVolunteerApplyCommandApiController {

private final ApproveVolunteerApplyUseCase approveVolunteerApplyUseCase;
private final RejectVolunteerApplyUseCase rejectVolunteerApplyUseCase;
Comment on lines +23 to +24
Copy link
Collaborator

Choose a reason for hiding this comment

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

좋아요~~


@Secured("ROLE_CENTER")
@Operation(summary = "봉사 활동 지원 승인", description = "봉사 활동 지원을 승인합니다.")
@PatchMapping("/volunteer-apply/{id}/approve")
public ApiResponse<String> approve(
@CurrentUser UUID centerId,
@PathVariable Long id
) {

approveVolunteerApplyUseCase.approve(id, centerId);
return ApiResponse.ok("봉사 활동 지원 승인 성공");
}

@Secured("ROLE_CENTER")
@Operation(summary = "봉사 활동 지원 거절", description = "봉사 활동 지원을 거절합니다.")
@PatchMapping("/volunteer-apply/{id}/reject")
public ApiResponse<String> reject(
@CurrentUser UUID centerId,
@PathVariable Long id
) {

rejectVolunteerApplyUseCase.reject(id, centerId);
return ApiResponse.ok("봉사 활동 지원 거절 성공");
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.somemore.volunteerapply.service;

import static com.somemore.global.exception.ExceptionMessage.NOT_EXISTS_VOLUNTEER_APPLY;
import static com.somemore.global.exception.ExceptionMessage.RECRUIT_BOARD_ALREADY_COMPLETED;
import static com.somemore.global.exception.ExceptionMessage.UNAUTHORIZED_RECRUIT_BOARD;
import static com.somemore.volunteerapply.domain.ApplyStatus.APPROVED;

import com.somemore.global.exception.BadRequestException;
import com.somemore.recruitboard.domain.RecruitBoard;
import com.somemore.recruitboard.usecase.query.RecruitBoardQueryUseCase;
import com.somemore.volunteerapply.domain.VolunteerApply;
import com.somemore.volunteerapply.repository.VolunteerApplyRepository;
import com.somemore.volunteerapply.usecase.ApproveVolunteerApplyUseCase;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@RequiredArgsConstructor
@Transactional
@Service
public class ApproveVolunteerApplyService implements ApproveVolunteerApplyUseCase {

private final VolunteerApplyRepository volunteerApplyRepository;

private final RecruitBoardQueryUseCase recruitBoardQueryUseCase;

@Override
public void approve(Long id, UUID centerId) {
VolunteerApply apply = getVolunteerApply(id);
RecruitBoard recruitBoard = recruitBoardQueryUseCase.getById(apply.getRecruitBoardId());

validateWriter(recruitBoard, centerId);
validateBoardStatus(recruitBoard);

apply.changeStatus(APPROVED);
volunteerApplyRepository.save(apply);
}

private VolunteerApply getVolunteerApply(Long id) {
return volunteerApplyRepository.findById(id).orElseThrow(
() -> new BadRequestException(NOT_EXISTS_VOLUNTEER_APPLY)
);
}

private void validateWriter(RecruitBoard recruitBoard, UUID centerId) {
if (recruitBoard.isWriter(centerId)) {
return;
}
throw new BadRequestException(UNAUTHORIZED_RECRUIT_BOARD);
}

private void validateBoardStatus(RecruitBoard recruitBoard) {
if (recruitBoard.isCompleted()) {
throw new BadRequestException(RECRUIT_BOARD_ALREADY_COMPLETED);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
package com.somemore.volunteerapply.service;

import static com.somemore.global.exception.ExceptionMessage.NOT_EXISTS_VOLUNTEER_APPLY;
import static com.somemore.global.exception.ExceptionMessage.RECRUIT_BOARD_ALREADY_COMPLETED;
import static com.somemore.global.exception.ExceptionMessage.UNAUTHORIZED_RECRUIT_BOARD;
import static com.somemore.volunteerapply.domain.ApplyStatus.REJECTED;

import com.somemore.global.exception.BadRequestException;
import com.somemore.recruitboard.domain.RecruitBoard;
import com.somemore.recruitboard.usecase.query.RecruitBoardQueryUseCase;
import com.somemore.volunteerapply.domain.VolunteerApply;
import com.somemore.volunteerapply.repository.VolunteerApplyRepository;
import com.somemore.volunteerapply.usecase.RejectVolunteerApplyUseCase;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@RequiredArgsConstructor
@Transactional
@Service
public class RejectVolunteerApplyService implements RejectVolunteerApplyUseCase {

private final VolunteerApplyRepository volunteerApplyRepository;
private final RecruitBoardQueryUseCase recruitBoardQueryUseCase;

@Override
public void reject(Long id, UUID centerId) {
VolunteerApply apply = getApply(id);
RecruitBoard recruitBoard = recruitBoardQueryUseCase.getById(apply.getRecruitBoardId());

validateWriter(recruitBoard, centerId);
validateBoardStatus(recruitBoard);

apply.changeStatus(REJECTED);
volunteerApplyRepository.save(apply);
}

private VolunteerApply getApply(Long id) {
return volunteerApplyRepository.findById(id).orElseThrow(
() -> new BadRequestException(NOT_EXISTS_VOLUNTEER_APPLY)
);
}

private void validateWriter(RecruitBoard recruitBoard, UUID centerId) {
if (recruitBoard.isWriter(centerId)) {
return;
}
throw new BadRequestException(UNAUTHORIZED_RECRUIT_BOARD);
}

private void validateBoardStatus(RecruitBoard recruitBoard) {
if (recruitBoard.isCompleted()) {
throw new BadRequestException(RECRUIT_BOARD_ALREADY_COMPLETED);
}
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.somemore.volunteerapply.usecase;


import java.util.UUID;

public interface ApproveVolunteerApplyUseCase {

void approve(Long id, UUID centerId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
package com.somemore.volunteerapply.usecase;

import java.util.UUID;

public interface RejectVolunteerApplyUseCase {

void reject(Long id, UUID centerId);

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,68 @@
package com.somemore.volunteerapply.controller;

import static org.mockito.ArgumentMatchers.any;
import static org.mockito.BDDMockito.willDoNothing;
import static org.springframework.test.web.servlet.request.MockMvcRequestBuilders.patch;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.jsonPath;
import static org.springframework.test.web.servlet.result.MockMvcResultMatchers.status;

import com.somemore.ControllerTestSupport;
import com.somemore.WithMockCustomUser;
import com.somemore.volunteerapply.usecase.ApproveVolunteerApplyUseCase;
import com.somemore.volunteerapply.usecase.RejectVolunteerApplyUseCase;
import java.util.UUID;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.mock.mockito.MockBean;
import org.springframework.test.web.servlet.MockMvc;

class CenterVolunteerApplyCommandApiControllerTest extends ControllerTestSupport {

@Autowired
private MockMvc mockMvc;

@MockBean
private ApproveVolunteerApplyUseCase approveVolunteerApplyUseCase;

@MockBean
private RejectVolunteerApplyUseCase rejectVolunteerApplyUseCase;

@Test
@DisplayName("봉사 활동 지원 승인 성공 테스트")
@WithMockCustomUser(role = "CENTER")
void approve() throws Exception {
// given
Long id = 1L;

willDoNothing().given(approveVolunteerApplyUseCase)
.approve(any(), any(UUID.class));
// when
mockMvc.perform(patch("/api/volunteer-apply/{id}/approve", id)
.header("Authorization", "Bearer access-token"))
// then
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(200))
.andExpect(jsonPath("$.data").value(""))
.andExpect(jsonPath("$.message").value("봉사 활동 지원 승인 성공"));
}

@Test
@DisplayName("봉사 활동 지원 거절 성공 테스트")
@WithMockCustomUser(role = "CENTER")
void reject() throws Exception {
// given
Long id = 1L;

willDoNothing().given(rejectVolunteerApplyUseCase)
.reject(any(), any(UUID.class));
// when
mockMvc.perform(patch("/api/volunteer-apply/{id}/reject", id)
.header("Authorization", "Bearer access-token"))
// then
.andExpect(status().isOk())
.andExpect(jsonPath("$.code").value(200))
.andExpect(jsonPath("$.data").value(""))
.andExpect(jsonPath("$.message").value("봉사 활동 지원 거절 성공"));
}
}
Loading
Loading