Skip to content

Commit

Permalink
Merge pull request #233 from rlajm1203/feature/BE-87
Browse files Browse the repository at this point in the history
[BE-87] 지원자 합/불 상태 필드 추가 및 지원서 상태 변경 API 추가
  • Loading branch information
rlajm1203 authored Sep 12, 2024
2 parents 0cee2e6 + ebce934 commit 5bf126b
Show file tree
Hide file tree
Showing 19 changed files with 375 additions and 8 deletions.
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,12 @@
import static org.axonframework.modelling.command.AggregateLifecycle.apply;

import com.econovation.recruit.api.applicant.command.CreateAnswerCommand;
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 lombok.AllArgsConstructor;
import lombok.Getter;
import lombok.NoArgsConstructor;
Expand All @@ -31,6 +34,11 @@ public AnswerAggregate(CreateAnswerCommand command) {
apply(new AnswerCreatedEvent(command.getId(), command.getYear(), command.getQna()));
}

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

// Event handler for AnswerCreatedEvent
@EventSourcingHandler
public void on(AnswerCreatedEvent event) {
Expand All @@ -39,6 +47,13 @@ public void on(AnswerCreatedEvent event) {
this.qna = event.getQna();
}

@EventSourcingHandler
public void on(ApplicantStateUpdateEvent event){
this.id = event.getId();
log.info("ApplicantID : " + event.getId());
log.info("상태 변경 : " + event.getAfterState());
}

public static AnswerAggregate from(MongoAnswer answer) {
return new AnswerAggregate(answer.getId(), answer.getYear(), answer.getQna());
}
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,38 @@
package com.econovation.recruit.api.applicant.aggregate;

import com.econovation.recruitdomain.domains.applicant.domain.MongoAnswer;
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;
import org.springframework.transaction.annotation.Transactional;

@Component
@RequiredArgsConstructor
public class ApplicantStateUpdateEventListener {

private final MongoAnswerAdaptor answerAdaptor;

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

switch (command) {
case PASS:
answer.pass();
break;
case NON_PASS:
answer.nonPass();
break;
}

answerAdaptor.save(answer);
return answer.getApplicantState().getPassState();
}

}
Original file line number Diff line number Diff line change
@@ -0,0 +1,16 @@
package com.econovation.recruit.api.applicant.command;

import lombok.*;
import org.axonframework.modelling.command.TargetAggregateIdentifier;

@AllArgsConstructor
@ToString
@Data
@NoArgsConstructor
@Getter
public class UpdateApplicantStateCommand {

@TargetAggregateIdentifier private String id;
private String afterState;

}
Original file line number Diff line number Diff line change
Expand Up @@ -5,6 +5,7 @@
import com.econovation.recruit.api.applicant.command.CreateAnswerCommand;
import com.econovation.recruit.api.applicant.docs.CreateApplicantExceptionDocs;
import com.econovation.recruit.api.applicant.dto.AnswersResponseDto;
import com.econovation.recruit.api.applicant.usecase.ApplicantCommandUseCase;
import com.econovation.recruit.api.applicant.usecase.ApplicantQueryUseCase;
import com.econovation.recruit.api.applicant.usecase.TimeTableLoadUseCase;
import com.econovation.recruit.api.applicant.usecase.TimeTableRegisterUseCase;
Expand All @@ -29,12 +30,7 @@
import org.springframework.beans.factory.annotation.Value;
import org.springframework.http.HttpStatus;
import org.springframework.http.ResponseEntity;
import org.springframework.web.bind.annotation.GetMapping;
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;
import org.springframework.web.bind.annotation.*;

@RestController
@RequestMapping("/api/v1")
Expand All @@ -49,6 +45,7 @@ public class ApplicantController {
private final CommonsEmailSender commonsEmailSender;
private final CommandGateway commandGateway;
private final ApplicantValidator applicantValidator;
private final ApplicantCommandUseCase applicantCommandUseCase;

@Value("${econovation.year}")
private Integer year;
Expand Down Expand Up @@ -146,4 +143,13 @@ public ResponseEntity sendEmail(@RequestBody EmailSendDto emailSendDto) {
emailSendDto.getEmail(), emailSendDto.getApplicantId(), LocalDateTime.now());
return new ResponseEntity<>(HttpStatus.OK);
}

@Operation(summary = "지원자의 합/불 상태를 변경합니다.")
@PatchMapping("/applicants/{applicant-id}/state")
public ResponseEntity<String> updateStatus(@PathVariable("applicant-id") String applicantId,
@RequestParam("afterState") String afterState){
// commandGateway.send(new UpdateApplicantStateCommand(applicantId, afterState));
String state = applicantCommandUseCase.execute(applicantId, afterState);
return new ResponseEntity(state,HttpStatus.OK);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
package com.econovation.recruit.api.applicant.handler;

import com.econovation.recruitdomain.domains.applicant.domain.MongoAnswer;
import com.econovation.recruitdomain.domains.applicant.domain.MongoAnswerAdaptor;
import com.econovation.recruitdomain.domains.applicant.event.domainevent.ApplicantRegisterEvent;
import com.econovation.recruitdomain.domains.applicant.event.domainevent.ApplicantStateEvents;
import com.econovation.recruitdomain.domains.applicant.event.domainevent.ApplicantStateModifyEvent;
import lombok.RequiredArgsConstructor;
import org.springframework.scheduling.annotation.Async;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Propagation;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.transaction.event.TransactionPhase;
import org.springframework.transaction.event.TransactionalEventListener;

@Component
@RequiredArgsConstructor
public class ApplicantStateUpdateEventHandler {

private final MongoAnswerAdaptor answerAdaptor;

// @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();

switch (command) {
case PASS:
answer.pass();
break;
case NON_PASS:
answer.nonPass();
break;
}

answerAdaptor.save(answer);
return answer.getApplicantState().getPassState();
}

}
Original file line number Diff line number Diff line change
@@ -1,12 +1,17 @@
package com.econovation.recruit.api.applicant.service;

import com.econovation.recruit.api.applicant.aggregate.ApplicantStateUpdateEventListener;
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.ApplicantState;
import com.econovation.recruitdomain.domains.applicant.domain.MongoAnswer;
import com.econovation.recruitdomain.domains.applicant.domain.MongoAnswerAdaptor;
import com.econovation.recruitdomain.domains.applicant.event.domainevent.ApplicantRegisterEvent;
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 All @@ -16,6 +21,7 @@
@RequiredArgsConstructor
public class AnswerCommandService implements ApplicantCommandUseCase {
private final MongoAnswerAdaptor answerAdaptor;
private final ApplicantStateUpdateEventHandler applicantStateUpdateEventHandler;

@Value("${econovation.year}")
private Integer year;
Expand All @@ -24,7 +30,8 @@ public class AnswerCommandService implements ApplicantCommandUseCase {
@Transactional
public UUID execute(Map<String, Object> qna) {
UUID id = UUID.randomUUID();
MongoAnswer answer = MongoAnswer.builder().id(id.toString()).qna(qna).year(year).build();
ApplicantState nonPassed = new ApplicantState();
MongoAnswer answer = MongoAnswer.builder().id(id.toString()).qna(qna).year(year).applicantState(nonPassed).build();
// 학번으로 중복 체크
// validateRegisterApplicant(qna);
answerAdaptor.save(answer);
Expand All @@ -38,4 +45,12 @@ public UUID execute(Map<String, Object> qna) {
Events.raise(applicantRegisterEvent);
return id;
}

@Override
@Transactional
public String execute(String applicantId, String afterState) {
ApplicantStateModifyEvent stateModifyEventEvents =
ApplicantStateModifyEvent.of(applicantId, afterState);
return applicantStateUpdateEventHandler.handle(stateModifyEventEvents); // 동기로 처리
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
package com.econovation.recruit.api.applicant.state.config;

import com.econovation.recruitdomain.domains.applicant.domain.PassStates;
import com.econovation.recruitdomain.domains.applicant.event.domainevent.ApplicantStateEvents;
import org.springframework.context.annotation.Configuration;
import org.springframework.statemachine.config.EnableStateMachine;
import org.springframework.statemachine.config.EnumStateMachineConfigurerAdapter;
import org.springframework.statemachine.config.builders.StateMachineStateConfigurer;
import org.springframework.statemachine.config.builders.StateMachineTransitionConfigurer;

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


@Override
public void configure(StateMachineStateConfigurer<PassStates, ApplicantStateEvents> states) throws Exception {
states
.withStates()
.initial(PassStates.NONPASSED)
.state(PassStates.FIRSTPASSED)
.end(PassStates.FINALPASSED);
}

/**
*
* @param transitions
* @throws Exception
*
* States
* NONPASSED : 불합격
* FIRSTPASSED : 1차 합격
* FINALPASSED : 최종 합격
*
* Events
* NON_PASS : 불합격
* PASS : 합격 (다음 단계로 전환)
*/

@Override
public void configure(StateMachineTransitionConfigurer<PassStates, ApplicantStateEvents> transitions) throws Exception {
transitions
.withExternal()
.source(PassStates.NONPASSED).target(PassStates.FIRSTPASSED).event(ApplicantStateEvents.PASS)
.and()
.withExternal()
.source(PassStates.FIRSTPASSED).target(PassStates.FINALPASSED).event(ApplicantStateEvents.PASS)
.and()
.withExternal()
.source(PassStates.FIRSTPASSED).target(PassStates.NONPASSED).event(ApplicantStateEvents.NON_PASS);
}
}
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
package com.econovation.recruit.api.applicant.state.support;

import com.econovation.recruitdomain.domains.applicant.support.ApplicantStateManger;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Component;

@Component
@RequiredArgsConstructor
public class ApplicantManager implements ApplicantStateManger {

@Override
public void update(String applicantId, String afterState) {
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,4 +7,6 @@
@UseCase
public interface ApplicantCommandUseCase {
UUID execute(Map<String, Object> blocks);

String execute(String applicantId, String state);
}
Original file line number Diff line number Diff line change
Expand Up @@ -76,7 +76,9 @@ public SecurityFilterChain filterChain(HttpSecurity http) throws Exception {
// ( jwt 필터나 , basic auth 필터의 순서는 상관이없다.) --> 왜냐면 jwt는 토큰 여부 파악만하고 있으면 검증이고 없으면 넘김.
// 내부 소스까지 실행을 못함. 권한 문제 때문에.
.mvcMatchers(HttpMethod.DELETE, "/api/v1//interviewers/*")
.hasAnyRole("OPERATION", "PRESIDENT")
.hasAnyRole("ROLE_OPERATION", "ROLE_PRESIDENT")
.mvcMatchers(HttpMethod.PATCH, "/api/v1/applicants/{applicant-id}/status")
.hasAnyRole("ROLE_OPERATION", "ROLE_PRESIDENT")
.anyRequest()
.hasAnyRole(RolePattern);

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,23 @@
package com.econovation.recruitdomain.domains.applicant.domain;

public class ApplicantState {

private PassStates passState;

public ApplicantState(){
this.passState = PassStates.NONPASSED; // 초기 상태
}

public void nextPassState(){
this.passState = this.passState.next();
}

public void prevPassState(){
this.passState = this.passState.prev();
}

public String getPassState(){
return this.passState.getStatus();
}

}
Original file line number Diff line number Diff line change
Expand Up @@ -32,10 +32,23 @@ public class MongoAnswer extends MongoBaseTimeEntity {

@TextIndexed private String qnaSearchIndex;

// 합,불 상태
@Field("state")
private ApplicantState applicantState;

public void pass(){
this.applicantState.nextPassState();
}

public void nonPass(){
this.applicantState.prevPassState();
}

public MongoAnswer(String id, Integer year, Map<String, Object> qna) {
this.id = id;
this.year = year;
this.qna = qna;
this.applicantState = new ApplicantState();
this.qnaSearchIndex =
qna.values().stream().map(Object::toString).collect(Collectors.joining(" "));
}
Expand Down
Loading

0 comments on commit 5bf126b

Please sign in to comment.