Skip to content
Merged
Show file tree
Hide file tree
Changes from 8 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
@@ -0,0 +1,43 @@
package dmu.dasom.api.domain.applicant.dto;

import dmu.dasom.api.domain.applicant.enums.ApplicantStatus;
import io.swagger.v3.oas.annotations.media.Schema;
import lombok.*;

import java.time.LocalDateTime;

@AllArgsConstructor
@Builder
@Getter
@NoArgsConstructor(access = AccessLevel.PROTECTED)
@Schema(name = "ApplicantResponseDto", description = "지원자 응답 DTO")
public class ApplicantResponseDto {

@Schema(description = "id", example = "1")
private Long id;

@Schema(description = "학번", example = "20210000")
private String studentNo;

@Schema(description = "연락처", example = "010-1234-5678")
private String contact;

@Schema(description = "이메일", example = "[email protected]")
private String email;

@Schema(description = "학년", example = "3")
private int grade;

@Schema(description = "지원 동기", example = "부원 지원 동기")
private String reasonForApply;

@Schema(description = "희망 활동", example = "스터디")
private String activityWish;

@Schema(description = "상태", example = "PENDING")
private ApplicantStatus status;

@Schema(description = "생성일", example = "2025-02-10 22:42:21.871801")
private LocalDateTime createdAt;

}
15 changes: 15 additions & 0 deletions src/main/java/dmu/dasom/api/domain/applicant/entity/Applicant.java
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
package dmu.dasom.api.domain.applicant.entity;

import dmu.dasom.api.domain.applicant.dto.ApplicantResponseDto;
import dmu.dasom.api.domain.applicant.enums.ApplicantStatus;
import jakarta.persistence.*;
import jakarta.validation.constraints.*;
Expand Down Expand Up @@ -73,4 +74,18 @@ public void updateStatus(final ApplicantStatus status) {
this.status = status;
}

public ApplicantResponseDto toApplicantResponse() {
return ApplicantResponseDto.builder()
.id(this.id)
.studentNo(this.studentNo)
.contact(this.contact)
.email(this.email)
.grade(this.grade)
.reasonForApply(this.reasonForApply)
.activityWish(this.activityWish)
.status(this.status)
.createdAt(this.createdAt)
.build();
}

}
Original file line number Diff line number Diff line change
@@ -1,7 +1,14 @@
package dmu.dasom.api.domain.applicant.repository;

import dmu.dasom.api.domain.applicant.entity.Applicant;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.Pageable;
import org.springframework.data.jpa.repository.JpaRepository;
import org.springframework.data.jpa.repository.Query;

public interface ApplicantRepository extends JpaRepository<Applicant, Long> {

@Query("SELECT a FROM Applicant a ORDER BY a.id DESC")
Page<Applicant> findAllWithPageRequest(final Pageable pageable);

}
Original file line number Diff line number Diff line change
@@ -1,9 +1,13 @@
package dmu.dasom.api.domain.applicant.service;

import dmu.dasom.api.domain.applicant.dto.ApplicantCreateRequestDto;
import dmu.dasom.api.domain.applicant.dto.ApplicantResponseDto;
import dmu.dasom.api.global.dto.PageResponse;

public interface ApplicantService {

void apply(final ApplicantCreateRequestDto request);

PageResponse<ApplicantResponseDto> getApplicants(final int page);

}
Original file line number Diff line number Diff line change
@@ -1,14 +1,23 @@
package dmu.dasom.api.domain.applicant.service;

import dmu.dasom.api.domain.applicant.dto.ApplicantCreateRequestDto;
import dmu.dasom.api.domain.applicant.dto.ApplicantResponseDto;
import dmu.dasom.api.domain.applicant.entity.Applicant;
import dmu.dasom.api.domain.applicant.repository.ApplicantRepository;
import dmu.dasom.api.domain.common.exception.CustomException;
import dmu.dasom.api.domain.common.exception.ErrorCode;
import dmu.dasom.api.global.dto.PageResponse;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageRequest;
import org.springframework.stereotype.Service;

@RequiredArgsConstructor
@Service
public class ApplicantServiceImpl implements ApplicantService {

private final static int DEFAULT_PAGE_SIZE = 20;

private final ApplicantRepository applicantRepository;

// 지원자 저장
Expand All @@ -17,4 +26,16 @@ public void apply(final ApplicantCreateRequestDto request) {
applicantRepository.save(request.toEntity());
}

// 지원자 조회
@Override
public PageResponse<ApplicantResponseDto> getApplicants(final int page) {
final Page<Applicant> applicants = applicantRepository.findAllWithPageRequest(PageRequest.of(page, DEFAULT_PAGE_SIZE));

// 조회 조건에 따라 결과가 없을 수 있음
if (applicants.isEmpty())
throw new CustomException(ErrorCode.EMPTY_RESULT);

return PageResponse.from(applicants.map(Applicant::toApplicantResponse));
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -16,7 +16,8 @@ public enum ErrorCode {
ARGUMENT_NOT_VALID(400, "C007", "요청한 값이 올바르지 않습니다."),
TOKEN_NOT_VALID(400, "C008", "토큰이 올바르지 않습니다."),
INTERNAL_SERVER_ERROR(500, "C009", "서버에 문제가 발생하였습니다."),
NOT_FOUND(404, "C010", "해당 리소스를 찾을 수 없습니다.")
NOT_FOUND(404, "C010", "해당 리소스를 찾을 수 없습니다."),
EMPTY_RESULT(400, "C011", "조회 결과가 없습니다.")
;

private final int status;
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,34 @@
package dmu.dasom.api.domain.recruit.controller;

import dmu.dasom.api.domain.applicant.dto.ApplicantCreateRequestDto;
import dmu.dasom.api.domain.applicant.service.ApplicantService;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
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;

@RestController
@RequestMapping("/api/recruit")
@RequiredArgsConstructor
public class RecruitController {

private final ApplicantService applicantService;

// 지원하기
@Operation(summary = "부원 지원하기")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "지원 성공")
})
@PostMapping("/apply")
public ResponseEntity<Void> apply(@Valid @RequestBody final ApplicantCreateRequestDto request) {
applicantService.apply(request);
return ResponseEntity.ok().build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package dmu.dasom.api.global.admin.controller;

import dmu.dasom.api.domain.applicant.dto.ApplicantResponseDto;
import dmu.dasom.api.domain.applicant.service.ApplicantService;
import dmu.dasom.api.global.dto.PageResponse;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.media.Content;
import io.swagger.v3.oas.annotations.media.ExampleObject;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.responses.ApiResponse;
import io.swagger.v3.oas.annotations.responses.ApiResponses;
import jakarta.validation.constraints.Min;
import lombok.RequiredArgsConstructor;
import org.springframework.http.ResponseEntity;
import org.springframework.web.ErrorResponse;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/api/admin")
@RequiredArgsConstructor
public class AdminController {

private final ApplicantService applicantService;

// 지원자 조회
@Operation(summary = "지원자 조회")
@ApiResponses(value = {
@ApiResponse(responseCode = "200", description = "지원자 조회 성공"),
@ApiResponse(responseCode = "400", description = "잘못된 요청",
content = @Content(
mediaType = "application/json",
schema = @Schema(implementation = ErrorResponse.class),
examples = {
@ExampleObject(
name = "조회 결과 없음",
value = "{ \"code\": \"C011\", \"message\": \"조회 결과가 없습니다.\" }"
)
}
)
)
})
@GetMapping("/applicants")
public ResponseEntity<PageResponse<ApplicantResponseDto>> getApplicants(
@RequestParam(value = "page", defaultValue = "0") @Min(0) final int page
) {
return ResponseEntity.ok(applicantService.getApplicants(page));
}

}
50 changes: 50 additions & 0 deletions src/main/java/dmu/dasom/api/global/dto/PageResponse.java
Original file line number Diff line number Diff line change
@@ -0,0 +1,50 @@
package dmu.dasom.api.global.dto;

import io.swagger.v3.oas.annotations.media.Schema;
import jakarta.validation.constraints.NotEmpty;
import lombok.Builder;
import lombok.Getter;
import org.springframework.data.domain.Page;

import java.util.List;

@Getter
@Builder
@Schema(name = "PageResponse", description = "페이징 응답 DTO")
public class PageResponse<T> {

@Schema(description = "데이터")
@NotEmpty
private List<T> content;

@Schema(description = "현재 페이지 번호")
private int number;

@Schema(description = "페이지 크기")
private int size;

@Schema(description = "전체 데이터 개수")
private long totalElements;

@Schema(description = "전체 페이지 개수")
private int totalPages;

@Schema(description = "첫 페이지 여부")
private boolean first;

@Schema(description = "마지막 페이지 여부")
private boolean last;

public static <T> PageResponse<T> from(Page<T> page) {
return PageResponse.<T>builder()
.content(page.getContent())
.number(page.getNumber())
.size(page.getSize())
.totalElements(page.getTotalElements())
.totalPages(page.getTotalPages())
.first(page.isFirst())
.last(page.isLast())
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
package dmu.dasom.api.domain.applicant;

import dmu.dasom.api.domain.applicant.dto.ApplicantCreateRequestDto;
import dmu.dasom.api.domain.applicant.dto.ApplicantResponseDto;
import dmu.dasom.api.domain.applicant.entity.Applicant;
import dmu.dasom.api.domain.applicant.repository.ApplicantRepository;
import dmu.dasom.api.domain.applicant.service.ApplicantServiceImpl;
import dmu.dasom.api.domain.common.exception.CustomException;
import dmu.dasom.api.domain.common.exception.ErrorCode;
import dmu.dasom.api.global.dto.PageResponse;
import org.junit.jupiter.api.DisplayName;
import org.junit.jupiter.api.Test;
import org.junit.jupiter.api.extension.ExtendWith;
import org.mockito.InjectMocks;
import org.mockito.Mock;
import org.mockito.junit.jupiter.MockitoExtension;
import org.springframework.data.domain.Page;
import org.springframework.data.domain.PageImpl;
import org.springframework.data.domain.PageRequest;

import java.util.Collections;

import static org.junit.jupiter.api.Assertions.*;
import static org.mockito.Mockito.*;

@ExtendWith(MockitoExtension.class)
class ApplicantServiceTest {

@Mock
private ApplicantRepository applicantRepository;

@InjectMocks
private ApplicantServiceImpl applicantService;

@Test
@DisplayName("지원자 저장 - 성공")
void apply_success() {
// given
ApplicantCreateRequestDto request = mock(ApplicantCreateRequestDto.class);

// when
applicantService.apply(request);

// then
verify(applicantRepository).save(request.toEntity());
}

@Test
@DisplayName("지원자 저장 - 실패")
void apply_fail() {
}

@Test
@DisplayName("지원자 조회 - 성공")
void getApplicants_success() {
// given
int page = 0;
PageRequest pageRequest = PageRequest.of(page, 20);
Applicant applicant = mock(Applicant.class);
Page<Applicant> applicants = new PageImpl<>(Collections.singletonList(applicant), pageRequest, 1);
when(applicantRepository.findAllWithPageRequest(pageRequest)).thenReturn(applicants);
when(applicant.toApplicantResponse()).thenReturn(mock(ApplicantResponseDto.class));

// when
PageResponse<ApplicantResponseDto> response = applicantService.getApplicants(page);

// then
assertNotNull(response);
assertEquals(1, response.getContent().size());
verify(applicantRepository).findAllWithPageRequest(pageRequest);
}

@Test
@DisplayName("지원자 조회 - 실패 (결과 없음)")
void getApplicants_fail_emptyResult() {
// given
int page = 0;
PageRequest pageRequest = PageRequest.of(page, 20);
Page<Applicant> applicants = new PageImpl<>(Collections.emptyList(), pageRequest, 0);
when(applicantRepository.findAllWithPageRequest(pageRequest)).thenReturn(applicants);

// when
CustomException exception = assertThrows(CustomException.class, () -> {
applicantService.getApplicants(page);
});

// then
assertEquals(ErrorCode.EMPTY_RESULT, exception.getErrorCode());
verify(applicantRepository).findAllWithPageRequest(pageRequest);
}
}