Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
19 commits
Select commit Hold shift + click to select a range
5b63fcf
refactor(recruit-board): 조회 기능 수정
leebs0521 Dec 3, 2024
8671add
test(recruit-board): 조회 기능 수정에 따른 테스트
leebs0521 Dec 3, 2024
191a6a7
feat(recruit-board): 모집 상태에 대한 도메인 비지니스 로직 메서드 작성
leebs0521 Dec 3, 2024
26cb27a
test(recruit-board): 모집 상태에 대한 도메인 비지니스 로직 메서드 테스트
leebs0521 Dec 3, 2024
404c409
test(recruit-board): 모집 상태에 대한 도메인 비지니스 로직 메서드 테스트
leebs0521 Dec 3, 2024
7ccf7dd
feat(volunteer-apply): 봉사 모집글 지원 기능
leebs0521 Dec 3, 2024
012e7a9
test(volunteer-apply): 봉사 모집글 지원 테스트
leebs0521 Dec 3, 2024
793bb34
feat(volunteer-apply): 봉사 모집글 지원 API 연결
leebs0521 Dec 3, 2024
97faef7
test(volunteer-apply): 봉사 모집글 지원 API 연결 테스트
leebs0521 Dec 3, 2024
c464977
refactor(volunteer-apply): 클래스 이름 변경
leebs0521 Dec 3, 2024
452b192
feat(volunteer-apply): 도메인 로직 작성
leebs0521 Dec 3, 2024
0180976
test(volunteer-apply): 도메인 로직 작성 테스트
leebs0521 Dec 3, 2024
697e6aa
feat(volunteer-apply): 봉사 지원 철회 기능
leebs0521 Dec 3, 2024
fcb83a4
test(volunteer-apply): 봉사 지원 철회 기능 테스트
leebs0521 Dec 3, 2024
b487880
feat(volunteer-apply): 봉사 지원 철회 기능 API
leebs0521 Dec 3, 2024
d0511e9
test(volunteer-apply): 봉사 지원 철회 기능 API 테스트
leebs0521 Dec 3, 2024
c6bd639
fix(volunteer-apply): sonar qube 리뷰 반영
leebs0521 Dec 3, 2024
72d09fa
refactor(recruit-board): 도메인 로직 메서드 변경
leebs0521 Dec 3, 2024
95e46bd
test(recruit-board): 도메인 로직 메서드 변경에 따른 테스트 및 부적절한 테스트 제거
leebs0521 Dec 3, 2024
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 @@ -29,7 +29,10 @@ public enum ExceptionMessage {
REVIEW_ALREADY_EXISTS("이미 작성한 리뷰가 존재합니다."),
REVIEW_RESTRICTED_TO_ATTENDED("리뷰는 참석한 봉사에 한해서만 작성할 수 있습니다."),
NOT_EXISTS_REVIEW("존재하지 않는 리뷰입니다."),
;
RECRUITMENT_NOT_OPEN("현재 모집 진행 중이 아닙니다."),
DUPLICATE_APPLICATION("이미 신청한 봉사 모집 공고입니다."),
UNAUTHORIZED_VOLUNTEER_APPLY("해당 지원에 권한이 없습니다."),

;
private final String message;
}
16 changes: 10 additions & 6 deletions src/main/java/com/somemore/recruitboard/domain/RecruitBoard.java
Original file line number Diff line number Diff line change
Expand Up @@ -57,7 +57,7 @@ public class RecruitBoard extends BaseEntity {

@Builder
public RecruitBoard(UUID centerId, Long locationId, String title, String content,
RecruitmentInfo recruitmentInfo, String imgUrl) {
RecruitmentInfo recruitmentInfo, String imgUrl) {
this.centerId = centerId;
this.locationId = locationId;
this.title = title;
Expand Down Expand Up @@ -92,13 +92,17 @@ public void changeRecruitStatus(RecruitStatus newStatus, LocalDateTime currentDa
this.recruitStatus = newStatus;
}

public boolean isRecruitOpen() {
return this.recruitStatus == RECRUITING;
}

private void updateRecruitmentInfo(RecruitBoardUpdateRequestDto dto) {
recruitmentInfo.updateWith(
dto.recruitmentCount(),
dto.volunteerCategory(),
dto.volunteerStartDateTime(),
dto.volunteerEndDateTime(),
dto.admitted()
dto.recruitmentCount(),
dto.volunteerCategory(),
dto.volunteerStartDateTime(),
dto.volunteerEndDateTime(),
dto.admitted()
);
}

Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -33,8 +33,15 @@ public class RecruitBoardQueryService implements RecruitBoardQueryUseCase {
private final CenterQueryUseCase centerQueryUseCase;

@Override
public RecruitBoardResponseDto getById(Long id) {
RecruitBoard recruitBoard = getRecruitBoard(id);
public RecruitBoard getById(Long id) {
return recruitBoardRepository.findById(id).orElseThrow(
() -> new BadRequestException(NOT_EXISTS_RECRUIT_BOARD.getMessage())
);
}

@Override
public RecruitBoardResponseDto getRecruitBoardById(Long id) {
RecruitBoard recruitBoard = getById(id);
return RecruitBoardResponseDto.from(recruitBoard);
}

Expand Down Expand Up @@ -75,9 +82,4 @@ public List<Long> getNotCompletedIdsByCenterIds(UUID centerId) {
return recruitBoardRepository.findNotCompletedIdsByCenterId(centerId);
}

private RecruitBoard getRecruitBoard(Long id) {
return recruitBoardRepository.findById(id).orElseThrow(
() -> new BadRequestException(NOT_EXISTS_RECRUIT_BOARD.getMessage())
);
}
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package com.somemore.recruitboard.usecase.query;

import com.somemore.recruitboard.domain.RecruitBoard;
import com.somemore.recruitboard.dto.condition.RecruitBoardNearByCondition;
import com.somemore.recruitboard.dto.condition.RecruitBoardSearchCondition;
import com.somemore.recruitboard.dto.response.RecruitBoardDetailResponseDto;
Expand All @@ -13,7 +14,8 @@

public interface RecruitBoardQueryUseCase {

RecruitBoardResponseDto getById(Long id);
RecruitBoard getById(Long id);
RecruitBoardResponseDto getRecruitBoardById(Long id);

RecruitBoardWithLocationResponseDto getWithLocationById(Long id);

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

import com.somemore.auth.annotation.CurrentUser;
import com.somemore.global.common.response.ApiResponse;
import com.somemore.volunteerapply.dto.VolunteerApplyCreateRequestDto;
import com.somemore.volunteerapply.usecase.ApplyVolunteerApplyUseCase;
import com.somemore.volunteerapply.usecase.WithdrawVolunteerApplyUseCase;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.security.access.annotation.Secured;
import org.springframework.web.bind.annotation.DeleteMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@Tag(name = "Volunteer Apply Command API", description = "봉사 활동 지원, 철회 관련 API")
@RequiredArgsConstructor
@RequestMapping("/api")
@RestController
public class VolunteerApplyCommandApiController {

private final ApplyVolunteerApplyUseCase applyVolunteerApplyUseCase;
private final WithdrawVolunteerApplyUseCase withdrawVolunteerApplyUseCase;

@Secured("ROLE_VOLUNTEER")
@Operation(summary = "봉사 활동 지원", description = "봉사 활동에 지원합니다.")
@PostMapping("/volunteer-apply")
public ApiResponse<Long> apply(
@CurrentUser UUID volunteerId,
@Valid @RequestBody VolunteerApplyCreateRequestDto requestDto
) {
return ApiResponse.ok(
201,
applyVolunteerApplyUseCase.apply(requestDto, volunteerId),
"봉사 활동 지원 성공"
);
}

@Secured("ROLE_VOLUNTEER")
@Operation(summary = "봉사 활동 지원 철회", description = "봉사 활동 지원을 철회합니다.")
@DeleteMapping("/volunteer-apply/{id}")
public ApiResponse<String> withdraw(
@CurrentUser UUID volunteerId,
@PathVariable Long id
) {
withdrawVolunteerApplyUseCase.withdraw(id, volunteerId);
return ApiResponse.ok("봉사 활동 지원 철회 성공");
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -63,6 +63,11 @@ public void changeAttended(Boolean attended) {
this.attended = attended;
}

public boolean isOwnApplication(UUID volunteerId) {
return this.volunteerId.equals(volunteerId);
}


public boolean isVolunteerActivityCompleted() {
return this.attended && this.status == APPROVED;
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,30 @@
package com.somemore.volunteerapply.dto;

import static com.somemore.volunteerapply.domain.ApplyStatus.WAITING;

import com.fasterxml.jackson.databind.PropertyNamingStrategies.SnakeCaseStrategy;
import com.fasterxml.jackson.databind.annotation.JsonNaming;
import com.somemore.volunteerapply.domain.VolunteerApply;
import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotNull;
import java.util.UUID;
import lombok.Builder;

@JsonNaming(SnakeCaseStrategy.class)
@Builder
public record VolunteerApplyCreateRequestDto(
@Schema(description = "봉사 모집글 아이디", example = "1")
@NotNull(message = "모집글 아이디는 필수 값입니다.")
Long recruitBoardId
) {

public VolunteerApply toEntity(UUID volunteerId) {
return VolunteerApply.builder()
.volunteerId(volunteerId)
.recruitBoardId(recruitBoardId)
.status(WAITING)
.attended(false)
.build();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -15,9 +15,12 @@ public interface VolunteerApplyRepository {

Optional<VolunteerApply> findById(Long id);

Optional<VolunteerApply> findByRecruitIdAndVolunteerId(Long recruitId, UUID volunteerId);

boolean existsByRecruitIdAndVolunteerId(Long recruitId, UUID volunteerId);

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

Page<VolunteerApply> findAllByRecruitId(Long recruitId, Pageable pageable);

Optional<VolunteerApply> findByRecruitIdAndVolunteerId(Long recruitId, UUID volunteerId);
}
Original file line number Diff line number Diff line change
Expand Up @@ -83,6 +83,16 @@ public Optional<VolunteerApply> findByRecruitIdAndVolunteerId(Long recruitId,
return findOne(exp);
}

@Override
public boolean existsByRecruitIdAndVolunteerId(Long recruitId, UUID volunteerId) {
return queryFactory
.selectFrom(volunteerApply)
.where(volunteerApply.recruitBoardId.eq(recruitId)
.and(volunteerApply.volunteerId.eq(volunteerId))
.and(isNotDeleted()))
.fetchFirst() != null;
}

private Long getCount(BooleanExpression exp) {
return queryFactory
.select(volunteerApply.count())
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,53 @@
package com.somemore.volunteerapply.service;

import static com.somemore.global.exception.ExceptionMessage.DUPLICATE_APPLICATION;
import static com.somemore.global.exception.ExceptionMessage.RECRUITMENT_NOT_OPEN;

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.dto.VolunteerApplyCreateRequestDto;
import com.somemore.volunteerapply.repository.VolunteerApplyRepository;
import com.somemore.volunteerapply.usecase.ApplyVolunteerApplyUseCase;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

@RequiredArgsConstructor
@Transactional
@Service
public class ApplyVolunteerApplyService implements ApplyVolunteerApplyUseCase {

private final VolunteerApplyRepository volunteerApplyRepository;
private final RecruitBoardQueryUseCase recruitBoardQueryUseCase;

@Override
public Long apply(VolunteerApplyCreateRequestDto requestDto, UUID volunteerId) {

RecruitBoard board = recruitBoardQueryUseCase.getById(requestDto.recruitBoardId());
validateCanApply(board);
validateDuplicatedApply(volunteerId, board);

VolunteerApply apply = requestDto.toEntity(volunteerId);
volunteerApplyRepository.save(apply);

return apply.getId();
}

private void validateCanApply(RecruitBoard board) {
if (board.isRecruitOpen()) {
return;
}
throw new BadRequestException(RECRUITMENT_NOT_OPEN);
}

private void validateDuplicatedApply(UUID volunteerId, RecruitBoard board) {
boolean isDuplicate = volunteerApplyRepository.existsByRecruitIdAndVolunteerId(board.getId(),
volunteerId);
if (isDuplicate) {
throw new BadRequestException(DUPLICATE_APPLICATION);
}
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.somemore.volunteerapply.service;

import static com.somemore.global.exception.ExceptionMessage.NOT_EXISTS_VOLUNTEER_APPLY;
import static com.somemore.global.exception.ExceptionMessage.UNAUTHORIZED_VOLUNTEER_APPLY;

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

@RequiredArgsConstructor
@Transactional
@Service
public class WithdrawVolunteerApplyService implements WithdrawVolunteerApplyUseCase {

private final VolunteerApplyRepository volunteerApplyRepository;

@Override
public void withdraw(Long id, UUID volunteerId) {
VolunteerApply apply = getApply(id);
validateVolunteerIdentity(apply, volunteerId);

apply.markAsDeleted();
volunteerApplyRepository.save(apply);
}

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

private void validateVolunteerIdentity(VolunteerApply apply, UUID volunteerId) {
if (apply.isOwnApplication(volunteerId)) {
return;
}

throw new BadRequestException(UNAUTHORIZED_VOLUNTEER_APPLY);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
package com.somemore.volunteerapply.usecase;

import com.somemore.volunteerapply.dto.VolunteerApplyCreateRequestDto;
import java.util.UUID;

public interface ApplyVolunteerApplyUseCase {

Long apply(VolunteerApplyCreateRequestDto requestDto, UUID volunteerId);

}
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 WithdrawVolunteerApplyUseCase {

void withdraw(Long id, UUID volunteerId);

}
Loading
Loading