Skip to content

Commit

Permalink
�chore: merge into main (#187)
Browse files Browse the repository at this point in the history
* [feat] 지원 서류 삭제 기능 (#177)

* [feat] 지원 서류 삭제

* [chore] update CODEOWNERS

* [feat] 지원서 목록 보기 API required false인 parameter들에 대해 default value 설정

* [test] 지원서 목록 보기 API required false인 parameter들에 대한 테스트 코드 작성

* [feat] 지원서 목록 보기 API enum 타입인 parameter들에 대해 allowable values 설정

* [test] 지원서 목록 보기 API enum 타입인 parameter들에 대해 테스트 코드 작성

* [fix] 지원서 엑셀 파일 생성 시각 API 테스트 status code 수정

* [revert] 코드 수정 내역 되돌림

* [feat] 지원서 목록 보기 API required false인 parameter들에 대해 default value 설정

* [test] 지원서 목록 보기 API required false인 parameter들에 대한 테스트 코드 작성

* [feat] 지원서 목록 보기 API enum 타입인 parameter들에 대해 allowable values 설정

* [test] 지원서 목록 보기 API enum 타입인 parameter들에 대해 테스트 코드 작성

* [fix] 지원서 엑셀 파일 생성 시각 API 테스트 status code 수정

* [revert] 코드 수정 내역 되돌림

* [feat] 지원서 목록 보기 API required false인 parameter들에 대해 default value 설정

* [test] 지원서 목록 보기 API required false인 parameter들에 대한 테스트 코드 작성

* [feat] 지원서 목록 보기 API enum 타입인 parameter들에 대해 allowable values 설정

* [test] 지원서 목록 보기 API enum 타입인 parameter들에 대해 테스트 코드 작성

* [fix] 지원서 엑셀 파일 생성 시각 API 테스트 status code 수정

* [feat] 면접 참여 여부 확인용 DTO 생성 #180

* [feat] 최종 활동 여부 확인용 DTO 생성 #180

* [feat] 불가능 사유 unableReason 필드 및 해당 필드 업데이트 로직 추가 #180

* [feat] 최종 활동 여부 확인용 DTO 생성 #180

* [feat] 면접 참여 가능 여부 확인과 활동 가능 여부 확인 Service 로직 작성 #180

* [feat] 활동 가능 여부 확인 시 최종 합격 여부 검증하는 validator 작성 #180

* feat: 면접 참여 및 활동 가능 여부 확인용 Controller 작성

* feat: 면접 참여 및 활동 가능 여부 확인용 Controller 작성 #180

* [refact] 회고록 목록 보기 response dto 필드 수정

* [refact] 회고록 상세 보기/수정 response dto 필드 수정

* [refact] 리프레시 토큰 발급 로직 수정 (#186)

* [refact] 리프레시토큰 발급 로직 수정 #185

* [refact] 상수 추가

---------

Co-authored-by: yoonsseo <[email protected]>
Co-authored-by: letskuku <[email protected]>
Co-authored-by: Yujeong Lee <[email protected]>
Co-authored-by: 이윤서 <[email protected]>
Co-authored-by: suhhyun <[email protected]>
  • Loading branch information
6 people authored Aug 14, 2024
1 parent f1f3579 commit b57ae66
Show file tree
Hide file tree
Showing 18 changed files with 229 additions and 49 deletions.
2 changes: 1 addition & 1 deletion .github/CODEOWNERS
Validating CODEOWNERS rules …
Original file line number Diff line number Diff line change
@@ -1 +1 @@
* @hyunihs @suhhyun524 @mushroom1324 @haen-su @mirageoasis
* @hyunihs @mushroom1324 @letskuku @yoonsseo @itsme-shawn @YoungGyo-00
13 changes: 9 additions & 4 deletions src/main/java/ceos/backend/domain/admin/AdminController.java
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,7 @@
import ceos.backend.global.config.user.AdminDetails;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.servlet.http.HttpServletRequest;
import jakarta.validation.Valid;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
Expand All @@ -21,6 +22,8 @@
public class AdminController {

private final AdminService adminService;
private static final String MOBILE = "mobile";
private static final String WEB = "web";

@Operation(summary = "닉네임 확인")
@PostMapping("/username")
Expand All @@ -38,9 +41,10 @@ public void signUp(@RequestBody @Valid SignUpRequest signUpRequest) {

@Operation(summary = "로그인")
@PostMapping("/signin")
public TokenResponse signIn(@RequestBody @Valid SignInRequest signInRequest) {
public TokenResponse signIn(HttpServletRequest request, @RequestBody @Valid SignInRequest signInRequest) {
log.info("로그인");
return adminService.signIn(signInRequest);
String device = request.getHeader("User-Agent").contains("mobile") ? MOBILE : WEB;
return adminService.signIn(device, signInRequest);
}

@Operation(summary = "아이디 찾기")
Expand Down Expand Up @@ -68,9 +72,10 @@ public void resetPwd(

@Operation(summary = "로그아웃")
@PostMapping("/logout")
public void logout(@AuthenticationPrincipal AdminDetails adminUser) {
public void logout(HttpServletRequest request, @AuthenticationPrincipal AdminDetails adminUser) {
log.info("로그아웃");
adminService.logout(adminUser);
String device = request.getHeader("User-Agent").contains("mobile") ? MOBILE : WEB;
adminService.logout(device, adminUser);
}

@Operation(summary = "토큰 재발급")
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -51,16 +51,18 @@ public void signUp(SignUpRequest signUpRequest) {
}

@Transactional
public TokenResponse signIn(SignInRequest signInRequest) {
public TokenResponse signIn(String device, SignInRequest signInRequest) {

final Admin admin = adminHelper.findForSignIn(signInRequest);
final Authentication authentication = adminHelper.adminAuthorizationInput(admin);

adminHelper.checkRole(admin);

String redisKey = admin.getId().toString() + ":" + device;

// 토큰 발급
final String accessToken = tokenProvider.createAccessToken(admin.getId(), authentication);
final String refreshToken = tokenProvider.createRefreshToken(admin.getId(), authentication);
final String refreshToken = tokenProvider.createRefreshToken(admin.getId(), authentication, redisKey);

return adminMapper.toTokenResponse(accessToken, refreshToken);
}
Expand Down Expand Up @@ -97,11 +99,13 @@ public void resetPwd(ResetPwdRequest resetPwdRequest, AdminDetails adminUser) {
}

@Transactional
public void logout(AdminDetails adminUser) {
public void logout(String device, AdminDetails adminUser) {
final Admin admin = adminUser.getAdmin();

String redisKey = admin.getId().toString() + ":" + device;

// 레디스 삭제
tokenProvider.deleteRefreshToken(admin.getId());
tokenProvider.deleteRefreshToken(redisKey);
}

@Transactional
Expand Down
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@

package ceos.backend.domain.application;


Expand All @@ -7,16 +8,13 @@
import ceos.backend.domain.application.dto.request.UpdateAttendanceRequest;
import ceos.backend.domain.application.dto.request.UpdateInterviewTime;
import ceos.backend.domain.application.dto.request.UpdatePassStatus;
import ceos.backend.domain.application.dto.response.GetApplication;
import ceos.backend.domain.application.dto.response.GetApplicationQuestion;
import ceos.backend.domain.application.dto.response.GetApplications;
import ceos.backend.domain.application.dto.response.GetCreationTime;
import ceos.backend.domain.application.dto.response.GetInterviewTime;
import ceos.backend.domain.application.dto.response.GetResultResponse;
import ceos.backend.domain.application.dto.response.*;
import ceos.backend.domain.application.service.ApplicationExcelService;
import ceos.backend.domain.application.service.ApplicationService;
import ceos.backend.global.common.entity.Part;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.Parameter;
import io.swagger.v3.oas.annotations.media.Schema;
import io.swagger.v3.oas.annotations.tags.Tag;
import jakarta.validation.Valid;
import java.nio.file.Path;
Expand All @@ -26,15 +24,7 @@
import org.springframework.http.HttpHeaders;
import org.springframework.http.MediaType;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PatchMapping;
import org.springframework.web.bind.annotation.PathVariable;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.PutMapping;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import org.springframework.web.bind.annotation.RestController;
import org.springframework.web.bind.annotation.*;

@Slf4j
@RestController
Expand All @@ -48,10 +38,13 @@ public class ApplicationController {
@Operation(summary = "지원자 목록 보기")
@GetMapping
public GetApplications getApplications(
@RequestParam(value = "part", required = false) Part part,
@RequestParam(value = "docPass", required = false) Pass docPass,
@RequestParam(value = "finalPass", required = false) Pass finalPass,
@RequestParam(value = "applicantName", required = false) String applicantName,
@Parameter(schema = @Schema(allowableValues = {"PRODUCT", "DESIGN", "FRONTEND", "BACKEND"}))
@RequestParam(value = "part", required = false, defaultValue = "") Part part,
@Parameter(schema = @Schema(allowableValues = {"PASS", "FAIL"}))
@RequestParam(value = "docPass", required = false, defaultValue = "") Pass docPass,
@Parameter(schema = @Schema(allowableValues = {"PASS", "FAIL"}))
@RequestParam(value = "finalPass", required = false, defaultValue = "") Pass finalPass,
@RequestParam(value = "applicantName", required = false, defaultValue = "") String applicantName,
@RequestParam("pageNum") int pageNum,
@RequestParam("limit") int limit) {
log.info("지원자 목록 보기");
Expand Down Expand Up @@ -150,6 +143,13 @@ public void updateDocumentPassStatus(
applicationService.updateDocumentPassStatus(applicationId, updatePassStatus);
}

@Operation(summary = "면접 참여 가능 여부 확인", description = "resultDateDoc ~ resultDateFinal 전날")
@GetMapping(value = "/{applicationId}/interview/availability")
public GetInterviewAvailability getInterviewAvailability(@PathVariable("applicationId") Long applicationId) {
log.info("면접 참여 가능 여부 확인");
return applicationService.getInterviewAvailability(applicationId);
}

@Operation(summary = "최종 합격 여부 변경", description = "resultDateDoc ~ ResultDateFinal 전날")
@PatchMapping(value = "/{applicationId}/final")
public void updateFinalPassStatus(
Expand All @@ -159,6 +159,13 @@ public void updateFinalPassStatus(
applicationService.updateFinalPassStatus(applicationId, updatePassStatus);
}

@Operation(summary = "활동 가능 여부 확인", description = "resultDateDoc ~ resultDateFinal 전날")
@GetMapping(value = "/{applicationId}/final/availability")
public GetFinalAvailability getFinalPass(@PathVariable("applicationId") Long applicationId) {
log.info("활동 가능 여부 확인");
return applicationService.getFinalAvailability(applicationId);
}

@Operation(summary = "지원서 엑셀 파일 생성")
@GetMapping(value = "/file/create")
public GetCreationTime createApplicationExcel() {
Expand Down Expand Up @@ -191,4 +198,11 @@ public GetCreationTime getApplicationExcelCreationTime() {
log.info("지원서 엑셀 파일 생성 시각 확인");
return applicationExcelService.getApplicationExcelCreationTime();
}

@Operation(summary = "지원서 전체 삭제")
@DeleteMapping(value = "/delete")
public void deleteAllApplications() {
log.info("지원서 전체 삭제");
applicationService.deleteAllApplications();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,8 @@
import jakarta.validation.constraints.NotNull;
import java.util.ArrayList;
import java.util.List;

import jakarta.validation.constraints.Size;
import lombok.AccessLevel;
import lombok.Builder;
import lombok.Getter;
Expand Down Expand Up @@ -36,17 +38,20 @@ public class Application extends BaseEntity {
@ColumnDefault("false")
private boolean interviewCheck;

@Size(max = 100)
private String unableReason;

@NotNull
@Enumerated(EnumType.STRING)
private Pass documentPass;

@NotNull
@ColumnDefault("false")
private boolean finalCheck;
private boolean finalCheck; // 활동 가능 여부

@NotNull
@Enumerated(EnumType.STRING)
private Pass finalPass;
private Pass finalPass; // 최종 합격 결과

@OneToMany(mappedBy = "application", cascade = CascadeType.ALL)
private List<ApplicationAnswer> applicationAnswers = new ArrayList<>();
Expand Down Expand Up @@ -85,6 +90,8 @@ public void updateInterviewCheck(boolean check) {
this.interviewCheck = check;
}

public void updateUnableReason(String reason) { this.unableReason = reason; }

public void updateFinalCheck(boolean check) {
this.finalCheck = check;
}
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,7 @@ public class ApplicationAnswer extends BaseEntity {
private Long id;

// Question : Answer = 1:1 (단방향)
@OneToOne(cascade = CascadeType.ALL)
@OneToOne
@JoinColumn(name = "application_question_id")
private ApplicationQuestion applicationQuestion;

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
package ceos.backend.domain.application.dto.response;

import ceos.backend.domain.application.domain.Application;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class GetFinalAvailability {
private boolean finalAvailability; // 활동 가능 여부
private String reason; // 활동 불가능 사유

@Builder
public GetFinalAvailability(boolean finalCheck, String reason) {
this.finalAvailability = finalCheck;
this.reason = reason;
}

public static GetFinalAvailability of(Application application) {
return GetFinalAvailability.builder()
.finalAvailability(application.isFinalCheck())
.reason(application.getUnableReason())
.build();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
package ceos.backend.domain.application.dto.response;

import ceos.backend.domain.application.domain.Application;
import lombok.Builder;
import lombok.Getter;

@Getter
@Builder
public class GetInterviewAvailability {
private boolean interviewAvailability; // 참여 가능 여부
private String reason; // 참여 불가능 사유

@Builder
public GetInterviewAvailability(boolean interviewCheck, String reason) {
this.interviewAvailability = interviewCheck;
this.reason = reason;
}

public static GetInterviewAvailability of(Application application) {
return GetInterviewAvailability.builder()
.interviewAvailability(application.isInterviewCheck())
.reason(application.getUnableReason())
.build();
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -22,6 +22,7 @@ public enum ApplicationErrorCode implements BaseErrorCode {
SAME_PASS_STATUS(BAD_REQUEST, "APPLICATION_400_7", "같은 상태로 변경할 수 없습니다."),
NOT_SET_INTERVIEW_TIME(BAD_REQUEST, "APPLICATION_400_8", "면접 시간이 정해지지 않았습니다."),
APPLICATION_STILL_EXIST(BAD_REQUEST, "APPLICATION_400_9", "기존 지원자 데이터가 남아있습니다."),
NOT_DELETABLE_DURING_RECRUITMENT(BAD_REQUEST, "APPLICATION_400_10", "최종 발표 전 지원자를 삭제할 수 없습니다."),

APPLICANT_NOT_FOUND(BAD_REQUEST, "APPLICATION_404_3", "존재하지 않는 지원자입니다."),

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
package ceos.backend.domain.application.exception.exceptions;

import ceos.backend.domain.application.exception.ApplicationErrorCode;
import ceos.backend.global.error.BaseErrorException;

public class NotDeletableDuringRecruitment extends BaseErrorException {
public static final NotDeletableDuringRecruitment EXCEPTION = new NotDeletableDuringRecruitment();

private NotDeletableDuringRecruitment() {
super(ApplicationErrorCode.NOT_DELETABLE_DURING_RECRUITMENT);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -13,11 +13,8 @@
import ceos.backend.domain.application.dto.request.UpdateAttendanceRequest;
import ceos.backend.domain.application.dto.request.UpdateInterviewTime;
import ceos.backend.domain.application.dto.request.UpdatePassStatus;
import ceos.backend.domain.application.dto.response.GetApplication;
import ceos.backend.domain.application.dto.response.GetApplicationQuestion;
import ceos.backend.domain.application.dto.response.GetApplications;
import ceos.backend.domain.application.dto.response.GetInterviewTime;
import ceos.backend.domain.application.dto.response.GetResultResponse;
import ceos.backend.domain.application.dto.response.*;
import ceos.backend.domain.application.exception.exceptions.NotDeletableDuringRecruitment;
import ceos.backend.domain.application.helper.ApplicationHelper;
import ceos.backend.domain.application.mapper.ApplicationMapper;
import ceos.backend.domain.application.repository.ApplicationAnswerRepository;
Expand All @@ -35,6 +32,8 @@
import ceos.backend.global.common.entity.Part;
import ceos.backend.global.util.InterviewDateTimeConvertor;
import ceos.backend.global.util.ParsedDurationConvertor;

import java.time.LocalDateTime;
import java.util.List;
import lombok.RequiredArgsConstructor;
import org.springframework.data.domain.Page;
Expand Down Expand Up @@ -165,6 +164,7 @@ public void updateInterviewAttendance(
application.updateInterviewCheck(true);
applicationRepository.save(application);
} else {
application.updateUnableReason(request.getReason());
applicationHelper.sendSlackUnableReasonMessage(application, request, false);
}
}
Expand Down Expand Up @@ -193,6 +193,7 @@ public void updateParticipationAvailability(
application.updateFinalCheck(true);
applicationRepository.save(application);
} else {
application.updateUnableReason(request.getReason());
applicationHelper.sendSlackUnableReasonMessage(application, request, true);
}
}
Expand Down Expand Up @@ -246,6 +247,16 @@ public void updateInterviewTime(Long applicationId, UpdateInterviewTime updateIn
application.updateInterviewTime(duration);
}

@Transactional(readOnly = true)
public GetInterviewAvailability getInterviewAvailability(Long applicationId) {
applicationValidator.validateExistingApplicant(applicationId); // 유저 검증
final Application application = applicationHelper.getApplicationById(applicationId);
applicationValidator.validateDocumentPassStatus(application); // 서류 통과 검증

return GetInterviewAvailability.of(application);
}


@Transactional
public void updateDocumentPassStatus(Long applicationId, UpdatePassStatus updatePassStatus) {
recruitmentValidator.validateBetweenStartDateDocAndResultDateDoc(); // 기간 검증
Expand All @@ -264,4 +275,26 @@ public void updateFinalPassStatus(Long applicationId, UpdatePassStatus updatePas

application.updateFinalPass(updatePassStatus.getPass());
}

@Transactional(readOnly = true)
public GetFinalAvailability getFinalAvailability(Long applicationId) {
applicationValidator.validateExistingApplicant(applicationId); // 유저 검증
final Application application = applicationHelper.getApplicationById(applicationId);
applicationValidator.validateFinalPassStatus(application); // 최종 합격 검증

return GetFinalAvailability.of(application);
}


@Transactional
public void deleteAllApplications() {
Recruitment recruitment = recruitmentHelper.takeRecruitment();
// 현재 시간이 resultDateFinal 이전이면 삭제 불가
if(LocalDateTime.now().isBefore(recruitment.getResultDateFinal())) {
throw NotDeletableDuringRecruitment.EXCEPTION;
}
// application, applicationAnswer, applicationInterview 삭제 (cascade)
applicationRepository.deleteAll();
}

}
Loading

0 comments on commit b57ae66

Please sign in to comment.