Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

release 4.0.1 #277

Merged
merged 21 commits into from
Sep 24, 2024
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
21 commits
Select commit Hold shift + click to select a range
1eedccd
[feat]: 지원 현황 검색과 조회로직 통합 및 DB레벨 정렬 적용
Profile-exe Sep 18, 2024
d168a5f
[refactor]: 재사용되는 로직 메서드로 추출
Profile-exe Sep 18, 2024
3f06ec4
[fix]: 데이터가 없는 경우 sortHelper의 IndexOutOfBoundsException 방지를 위한 분기 추가
Profile-exe Sep 18, 2024
e27fc6f
[refactor]: 알파벳 역순으로 순서 수정
Profile-exe Sep 21, 2024
9a05ba9
[feat]: AnswerAdaptor 검색과 정렬이 적용된 면접 기록 지원자 조회 구현
Profile-exe Sep 22, 2024
7500caa
[feat]: AnswerService 면접 기록 지원자 조회 구현
Profile-exe Sep 22, 2024
e21cb07
[feat]: AnswerService 면접 기록 지원자 페이지 정보 구현
Profile-exe Sep 22, 2024
f28b145
[feat]: 면접 기록 조회 API 검색 적용 및 정렬 개선
Profile-exe Sep 22, 2024
9a73ce8
[feat]: 페이징 없는 지원자 조회 구현
Profile-exe Sep 23, 2024
005ddc3
[feat]: 어플리케이션 레벨 페이징 유틸 함수 구현
Profile-exe Sep 23, 2024
1bbc0c0
[fix]: 점수 기준 정렬 오류 해결
Profile-exe Sep 23, 2024
6a12907
[refactor]: 검색 쿼리 파라미터 required를 false로 변경
Profile-exe Sep 23, 2024
2600682
[fix]: pageInfo가 올바른 totalCount를 가지도록 수정
Profile-exe Sep 23, 2024
9591722
[fix]: interviewer가 없는 경우 NPE 방지를 위한 분기 처리 추가
Profile-exe Sep 23, 2024
b395075
[style]: spotless
Profile-exe Sep 23, 2024
b4dc8b6
[refactor]: RecordsViewResponseDto 내부 정적 팩토리 메서드 구현
Profile-exe Sep 23, 2024
4b0bf42
[refactor]: getScoreMap 메서드 calculateAverageScoresByApplicant 로 이름 변경
Profile-exe Sep 23, 2024
a613304
[refactor]: 면접 기록 조회 공통로직 메서드로 추출 및 if 분기 하나로 통합
Profile-exe Sep 23, 2024
dad5f5a
[refactor]: 기수별 면접기록 필터링 로직 메서드로 추출
Profile-exe Sep 23, 2024
09c1d32
[style]: spotless
Profile-exe Sep 23, 2024
cefca5f
Merge pull request #274 from JNU-econovation/feature/BE-97
LJH098 Sep 24, 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 @@ -6,9 +6,8 @@
import com.econovation.recruit.api.applicant.command.UpdateApplicantStateCommand;
import com.econovation.recruitdomain.domains.applicant.domain.MongoAnswer;
import com.econovation.recruitdomain.domains.applicant.event.aggregateevent.AnswerCreatedEvent;
import java.util.Map;

import com.econovation.recruitdomain.domains.applicant.event.aggregateevent.ApplicantStateUpdateEvent;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -35,7 +34,7 @@ public AnswerAggregate(CreateAnswerCommand command) {
}

@CommandHandler
public AnswerAggregate(UpdateApplicantStateCommand command){
public AnswerAggregate(UpdateApplicantStateCommand command) {
apply(new ApplicantStateUpdateEvent(command.getId(), command.getAfterState()));
}

Expand All @@ -48,7 +47,7 @@ public void on(AnswerCreatedEvent event) {
}

@EventSourcingHandler
public void on(ApplicantStateUpdateEvent event){
public void on(ApplicantStateUpdateEvent event) {
this.id = event.getId();
log.info("ApplicantID : " + event.getId());
log.info("상태 변경 : " + event.getAfterState());
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@
import com.econovation.recruitdomain.domains.applicant.domain.MongoAnswerAdaptor;
import com.econovation.recruitdomain.domains.applicant.event.aggregateevent.ApplicantStateUpdateEvent;
import com.econovation.recruitdomain.domains.applicant.event.domainevent.ApplicantStateEvents;

import lombok.RequiredArgsConstructor;
import org.axonframework.eventhandling.EventHandler;
import org.springframework.stereotype.Component;
Expand All @@ -20,7 +19,7 @@ public class ApplicantStateUpdateEventListener {

@EventHandler
@Transactional
public String handle(ApplicantStateUpdateEvent event){
public String handle(ApplicantStateUpdateEvent event) {
MongoAnswer answer = answerAdaptor.findById(event.getId()).get();
ApplicantStateEvents command = ApplicantStateEvents.find(event.getAfterState());

Expand All @@ -36,5 +35,4 @@ public String handle(ApplicantStateUpdateEvent event){
answerAdaptor.save(answer);
return answer.getApplicantStateOrDefault().getPassState();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -12,5 +12,4 @@ public class UpdateApplicantStateCommand {

@TargetAggregateIdentifier private String id;
private String afterState;

}
Original file line number Diff line number Diff line change
@@ -1,5 +1,8 @@
package com.econovation.recruit.api.applicant.controller;

import static com.econovation.recruitcommon.consts.RecruitStatic.APPLICANT_SUCCESS_REGISTER_MESSAGE;
import static com.econovation.recruitcommon.consts.RecruitStatic.PASS_STATE_KEY;

import com.econovation.recruit.api.applicant.command.CreateAnswerCommand;
import com.econovation.recruit.api.applicant.docs.CreateApplicantExceptionDocs;
import com.econovation.recruit.api.applicant.dto.AnswersResponseDto;
Expand All @@ -18,6 +21,11 @@
import com.econovation.recruitinfrastructure.apache.CommonsEmailSender;
import io.swagger.v3.oas.annotations.Operation;
import io.swagger.v3.oas.annotations.tags.Tag;
import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;
import lombok.RequiredArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.axonframework.commandhandling.gateway.CommandGateway;
Expand All @@ -27,15 +35,6 @@
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.*;

import java.time.LocalDateTime;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.UUID;

import static com.econovation.recruitcommon.consts.RecruitStatic.APPLICANT_SUCCESS_REGISTER_MESSAGE;
import static com.econovation.recruitcommon.consts.RecruitStatic.PASS_STATE_KEY;

@RestController
@RequestMapping("/api/v1")
@RequiredArgsConstructor
Expand Down Expand Up @@ -82,8 +81,9 @@ public ResponseEntity<Map<String, Object>> getApplicantById(String applicantId)
public ResponseEntity<AnswersResponseDto> getApplicantsByYear(
@PathVariable(value = "year") Integer year,
@PathVariable(value = "page") Integer page,
@ParameterObject String order) {
AnswersResponseDto result = applicantQueryUseCase.execute(year, page, order);
@ParameterObject String order,
@RequestParam(required = false) String searchKeyword) {
AnswersResponseDto result = applicantQueryUseCase.execute(year, page, order, searchKeyword);
return new ResponseEntity<>(result, HttpStatus.OK);
}

Expand Down Expand Up @@ -150,20 +150,22 @@ public ResponseEntity sendEmail(@RequestBody EmailSendDto emailSendDto) {

@Operation(summary = "지원자의 합/불 상태를 변경합니다.")
@PatchMapping("/applicants/{applicant-id}/state")
public ResponseEntity<Map<String, String>> updateStatus(@PathVariable("applicant-id") String applicantId,
@RequestParam("afterState") String afterState){
// commandGateway.send(new UpdateApplicantStateCommand(applicantId, afterState));
public ResponseEntity<Map<String, String>> updateStatus(
@PathVariable("applicant-id") String applicantId,
@RequestParam("afterState") String afterState) {
// commandGateway.send(new UpdateApplicantStateCommand(applicantId, afterState));
String state = applicantCommandUseCase.execute(applicantId, afterState);
Map<String, String> response = new HashMap<>();
response.put(PASS_STATE_KEY, state);
return new ResponseEntity(response,HttpStatus.OK);
return new ResponseEntity(response, HttpStatus.OK);
}

@Operation(summary = "지원서의 합/불 상태를 조회합니다. (합/불 관리자 페이지 전용)")
@GetMapping("year/{year}/applicants/pass-state")
public ResponseEntity<List<GetApplicantsStatusResponse>> getApplicantsStatus(@PathVariable("year") Integer year,
@RequestParam("order") String sortType) {
List<GetApplicantsStatusResponse> result = applicantQueryUseCase.getApplicantsStatus(year, sortType);
public ResponseEntity<List<GetApplicantsStatusResponse>> getApplicantsStatus(
@PathVariable("year") Integer year, @RequestParam("order") String sortType) {
List<GetApplicantsStatusResponse> result =
applicantQueryUseCase.getApplicantsStatus(year, sortType);
return new ResponseEntity<>(result, HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -1,15 +1,14 @@
package com.econovation.recruit.api.applicant.dto;

import static com.econovation.recruitcommon.consts.RecruitStatic.PASS_STATE_KEY;

import com.econovation.recruitdomain.domains.applicant.domain.state.ApplicantState;
import com.econovation.recruitdomain.domains.applicant.exception.ApplicantWrongStateException;
import java.util.Map;
import lombok.AllArgsConstructor;
import lombok.Builder;
import lombok.Data;

import java.util.Map;

import static com.econovation.recruitcommon.consts.RecruitStatic.PASS_STATE_KEY;

@Data
@AllArgsConstructor
@Builder
Expand All @@ -36,4 +35,4 @@ public static GetApplicantsStatusResponse of(Map<String, Object> result) {
}
throw ApplicantWrongStateException.wrongStatusException;
}
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -17,12 +17,12 @@ public class ApplicantStateUpdateEventHandler {
private final MongoAnswerAdaptor answerAdaptor;
private final PeriodCalculator periodCalculator;

// @Async
// @TransactionalEventListener(
// classes = ApplicantRegisterEvent.class,
// phase = TransactionPhase.AFTER_COMMIT)
// @Transactional(propagation = Propagation.REQUIRES_NEW)
public String handle(ApplicantStateModifyEvent event){
// @Async
// @TransactionalEventListener(
// classes = ApplicantRegisterEvent.class,
// phase = TransactionPhase.AFTER_COMMIT)
// @Transactional(propagation = Propagation.REQUIRES_NEW)
public String handle(ApplicantStateModifyEvent event) {
MongoAnswer answer = answerAdaptor.findById(event.getApplicantId()).get();
ApplicantStateEvents command = event.getEvent();
answer.stateEmptyCheckAndInit();
Expand All @@ -38,5 +38,4 @@ public String handle(ApplicantStateModifyEvent event){
answerAdaptor.save(answer);
return answer.getApplicantStateOrDefault().getPassState();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -3,14 +3,13 @@
import com.econovation.recruit.api.applicant.handler.ApplicantStateUpdateEventHandler;
import com.econovation.recruit.api.applicant.usecase.ApplicantCommandUseCase;
import com.econovation.recruitdomain.common.aop.domainEvent.Events;
import com.econovation.recruitdomain.domains.applicant.domain.state.ApplicantState;
import com.econovation.recruitdomain.domains.applicant.domain.MongoAnswer;
import com.econovation.recruitdomain.domains.applicant.domain.MongoAnswerAdaptor;
import com.econovation.recruitdomain.domains.applicant.domain.state.ApplicantState;
import com.econovation.recruitdomain.domains.applicant.event.domainevent.ApplicantRegisterEvent;
import com.econovation.recruitdomain.domains.applicant.event.domainevent.ApplicantStateModifyEvent;
import java.util.Map;
import java.util.UUID;

import com.econovation.recruitdomain.domains.applicant.event.domainevent.ApplicantStateModifyEvent;
import lombok.RequiredArgsConstructor;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
Expand Down Expand Up @@ -44,7 +43,13 @@ public String execute(String applicantId, String afterState) {
@Override
public UUID execute(Map<String, Object> qna, UUID id) {
ApplicantState nonProcessed = new ApplicantState();
MongoAnswer answer = MongoAnswer.builder().id(id.toString()).qna(qna).year(year).applicantState(nonProcessed).build();
MongoAnswer answer =
MongoAnswer.builder()
.id(id.toString())
.qna(qna)
.year(year)
.applicantState(nonProcessed)
.build();
// 학번으로 중복 체크
// validateRegisterApplicant(qna);
answerAdaptor.save(answer);
Expand Down
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
package com.econovation.recruit.api.applicant.service;

import static com.econovation.recruitcommon.consts.RecruitStatic.PASS_STATE_KEY;

import com.econovation.recruit.api.applicant.aggregate.AnswerAggregate;
import com.econovation.recruit.api.applicant.dto.AnswersResponseDto;
import com.econovation.recruit.api.applicant.dto.GetApplicantsStatusResponse;
Expand All @@ -20,8 +22,6 @@
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import static com.econovation.recruitcommon.consts.RecruitStatic.PASS_STATE_KEY;

@Service
@RequiredArgsConstructor
public class ApplicantService implements ApplicantQueryUseCase {
Expand All @@ -45,6 +45,58 @@ public Map<String, Object> execute(String answerId) {
return qna;
}

@Transactional(readOnly = true)
public AnswersResponseDto execute(
Integer year, Integer page, String sortType, String searchKeyword) {
PageInfo pageInfo = getPageInfo(year, page, searchKeyword);
List<MongoAnswer> sortedResult =
answerAdaptor.findByYearAndSearchKeyword(year, page, sortType, searchKeyword);

List<Map<String, Object>> qnaMapList = getQnaMapListWithIdAndPassState(sortedResult);

if (qnaMapList.isEmpty()) {
return AnswersResponseDto.of(Collections.emptyList(), pageInfo);
}
return AnswersResponseDto.of(qnaMapList, pageInfo);
}

private List<Map<String, Object>> getQnaMapListWithIdAndPassState(
List<MongoAnswer> sortedResult) {
return sortedResult.stream()
.map(
answer -> {
Map<String, Object> qna = answer.getQna();
qna.put("id", answer.getId());
qna.put(PASS_STATE_KEY, answer.getApplicantStateOrDefault());
return qna;
})
.toList();
}

@Override
public PageInfo getPageInfo(Integer year, Integer page, String searchKeyword) {
long totalCount = answerAdaptor.getTotalCountByYearAndSearchKeyword(year, searchKeyword);
return new PageInfo(totalCount, page);
}

@Transactional(readOnly = true)
public List<MongoAnswer> execute(
Integer page,
Integer year,
String sortType,
String searchKeyword,
List<String> applicantIds) {
return answerAdaptor.findByYearAndSearchKeywordAndApplicantIds(
page, year, sortType, searchKeyword, applicantIds);
}

@Override
public List<MongoAnswer> execute(
Integer year, String sortType, String searchKeyword, List<String> applicantIds) {
return answerAdaptor.findByYearAndSearchKeywordAndApplicantIds(
year, sortType, searchKeyword, applicantIds);
}

@Transactional(readOnly = true)
public AnswersResponseDto execute(Integer year, Integer page, String sortType) {
PageInfo pageInfo = getPageInfo(year, page);
Expand Down Expand Up @@ -99,7 +151,8 @@ public Map<String, Map<String, Object>> findAllApplicantVo(List<String> fields)
@Override
public AnswersResponseDto search(Integer page, String searchKeyword) {
List<MongoAnswer> answers = answerAdaptor.findBySearchKeyword(page, searchKeyword);
answers.forEach(answer -> answer.getQna().put(PASS_STATE_KEY, answer.getApplicantStateOrDefault()));
answers.forEach(
answer -> answer.getQna().put(PASS_STATE_KEY, answer.getApplicantStateOrDefault()));
return AnswersResponseDto.of(
answers.stream().map(MongoAnswer::getQna).toList(),
new PageInfo(answers.size(), page));
Expand Down Expand Up @@ -182,13 +235,9 @@ public List<GetApplicantsStatusResponse> getApplicantsStatus(Integer year, Strin
}

private List<Map<String, Object>> sortAndAddIds(List<MongoAnswer> result, String sortType) {
sortHelper.sort(result, sortType);
return result.stream().map(
answer -> {
Map<String, Object> qna = answer.getQna();
qna.put("id", answer.getId());
qna.put(PASS_STATE_KEY, answer.getApplicantStateOrDefault());
return qna;
}).toList();
if (!result.isEmpty()) {
sortHelper.sort(result, sortType);
}
return getQnaMapListWithIdAndPassState(result);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -10,48 +10,45 @@

@Configuration
@EnableStateMachine
public class ApplicantStateMachineConfig extends EnumStateMachineConfigurerAdapter<PassStates, ApplicantStateEvents> {

public class ApplicantStateMachineConfig
extends EnumStateMachineConfigurerAdapter<PassStates, ApplicantStateEvents> {

@Override
public void configure(StateMachineStateConfigurer<PassStates, ApplicantStateEvents> states) throws Exception {
states
.withStates()
.initial(PassStates.NON_PROCESSED)
.state(PassStates.FIRST_PASSED)
.end(PassStates.FIRST_FAILED)
.end(PassStates.FINAL_PASSED)
.end(PassStates.FINAL_FAILED);

public void configure(StateMachineStateConfigurer<PassStates, ApplicantStateEvents> states)
throws Exception {
states.withStates()
.initial(PassStates.NON_PROCESSED)
.state(PassStates.FIRST_PASSED)
.end(PassStates.FIRST_FAILED)
.end(PassStates.FINAL_PASSED)
.end(PassStates.FINAL_FAILED);
}

/**
*
* @param transitions
* @throws Exception
*
* States
* NON_PROCESSED : 불합격
* FIRST_PASSED : 1차 합격
* FIRST_FAILED : 1차 불합격
* FINAL_PASSED : 최종 합격
* FINAL_FAILED : 최종 불합격
*
* Events
* NON_PASS : 불합격
* PASS : 합격 (다음 단계로 전환)
* <p>States NON_PROCESSED : 불합격 FIRST_PASSED : 1차 합격 FIRST_FAILED : 1차 불합격 FINAL_PASSED :
* 최종 합격 FINAL_FAILED : 최종 불합격
* <p>Events NON_PASS : 불합격 PASS : 합격 (다음 단계로 전환)
*/

@Override
public void configure(StateMachineTransitionConfigurer<PassStates, ApplicantStateEvents> transitions) throws Exception {
public void configure(
StateMachineTransitionConfigurer<PassStates, ApplicantStateEvents> transitions)
throws Exception {
transitions
.withExternal()
.source(PassStates.NON_PROCESSED).target(PassStates.FIRST_PASSED).event(ApplicantStateEvents.PASS)
.source(PassStates.NON_PROCESSED)
.target(PassStates.FIRST_PASSED)
.event(ApplicantStateEvents.PASS)
.and()
.withExternal()
.source(PassStates.FIRST_PASSED).target(PassStates.FINAL_PASSED).event(ApplicantStateEvents.PASS)
.source(PassStates.FIRST_PASSED)
.target(PassStates.FINAL_PASSED)
.event(ApplicantStateEvents.PASS)
.and()
.withExternal()
.source(PassStates.FIRST_PASSED).target(PassStates.FINAL_FAILED).event(ApplicantStateEvents.NON_PASS);
.source(PassStates.FIRST_PASSED)
.target(PassStates.FINAL_FAILED)
.event(ApplicantStateEvents.NON_PASS);
}
}
Loading
Loading