package kr.wisestone.owl.service.impl; import com.google.common.collect.Lists; import kr.wisestone.owl.constant.MsgConstants; import kr.wisestone.owl.domain.IssueStatus; import kr.wisestone.owl.domain.Workflow; import kr.wisestone.owl.domain.WorkflowTransition; import kr.wisestone.owl.domain.enumType.IssueStatusType; import kr.wisestone.owl.domain.enumType.ProjectType; import kr.wisestone.owl.exception.OwlRuntimeException; import kr.wisestone.owl.repository.WorkflowTransitionRepository; import kr.wisestone.owl.service.IssueStatusService; import kr.wisestone.owl.service.WorkflowTransitionService; import kr.wisestone.owl.util.MapUtil; import kr.wisestone.owl.vo.IssueStatusVo; import kr.wisestone.owl.vo.WorkflowTransitionVo; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import java.util.HashMap; import java.util.List; import java.util.Map; @Service public class WorkflowTransitionServiceImpl extends AbstractServiceImpl> implements WorkflowTransitionService { private static final Logger log = LoggerFactory.getLogger(WorkflowTransitionServiceImpl.class); @Autowired private WorkflowTransitionRepository workflowTransitionRepository; @Autowired private IssueStatusService issueStatusService; @Override protected JpaRepository getRepository() { return this.workflowTransitionRepository; } // 워크스페이스 생성시 기본 제공되는 이슈 상태를 연결하여 기본 제공되는 워크플로우을 생성한다. @Override @Transactional public void addDefaultWorkflowTransition(Workflow workflow, List issueStatuses, ProjectType projectType) { switch (projectType) { case BTS_PROJECT: // 생성 -> 진행 (1 -> 2) // 진행 -> 확인 (2 -> 4) // 확인 -> 재진행 (4 -> 3) // 확인 -> 종료 (4 -> 5) // 재진행 -> 진행 (3 -> 2) // 재진행 -> 확인 (3 -> 4) // 종료 -> 재진행 (5 -> 3) this.makeWorkflowTransition(1L, 2L, issueStatuses, workflow, 47L, 186L, 173L, 186L, 77L, 183L); this.makeWorkflowTransition(2L, 4L, issueStatuses, workflow, 173L, 186L, 313L, 185L, 139L, 186L); this.makeWorkflowTransition(4L, 3L, issueStatuses, workflow, 313L, 185L, 312L, 34L, 219L, 89L); this.makeWorkflowTransition(4L, 5L, issueStatuses, workflow, 313L, 185L, 493L, 185L, 380L, 186L); this.makeWorkflowTransition(3L, 2L, issueStatuses, workflow, 312L, 34L, 173L, 186L, 78L, 43L); this.makeWorkflowTransition(5L, 3L, issueStatuses, workflow, 493L, 185L, 312L, 34L, 490L, 40L); break; case RMS_PROJECT: // 생성 -> 진행 (1 -> 2) // 진행 -> 확인 (2 -> 4) // 확인 -> 승인 불가 (4 -> 6) // 확인 -> 승인 (4 -> 7) // 승인 불가 -> 진행 (6 -> 2) // 승인 불가 -> 종료 (6 -> 5) // 승인 -> 종료 (7 -> 5) // 종료 -> 진행 (5 -> 2) this.makeWorkflowTransition(1L, 2L, issueStatuses, workflow, 73L, 45L, 297L, 42L, 73L, 45L); this.makeWorkflowTransition(2L, 4L, issueStatuses, workflow, 297L, 42L, 297L, 141L, 244L, 95L); this.makeWorkflowTransition(4L, 6L, issueStatuses, workflow, 297L, 141L, 79L, 153L, 292L, 132L); this.makeWorkflowTransition(4L, 7L, issueStatuses, workflow, 297L, 141L, 515L, 150L, 292L, 132L); this.makeWorkflowTransition(6L, 2L, issueStatuses, workflow, 79L, 153L, 297L, 42L, 79L, 153L); this.makeWorkflowTransition(6L, 5L, issueStatuses, workflow, 79L, 153L, 298L, 223L, 79L, 153L); this.makeWorkflowTransition(7L, 5L, issueStatuses, workflow, 515L, 150L, 298L, 223L, 515L, 150L); this.makeWorkflowTransition(5L, 2L, issueStatuses, workflow, 298L, 223L, 297L, 42L, 388L, 118L); break; case TCM_PROJECT: // 생성 -> 확인 (1 -> 4) // 확인 -> 종료 (4 -> 5) // 종료 -> 재진행 (5 -> 3) // 재진행 -> 확인(3 -> 4) this.makeWorkflowTransition(1L, 4L, issueStatuses, workflow, 121L, 133L, 298L, 132L, 179L, 132L); this.makeWorkflowTransition(4L, 5L, issueStatuses, workflow, 298L, 133L, 466L, 133L, 272L, 131L); this.makeWorkflowTransition(5L, 3L, issueStatuses, workflow, 466L, 133L, 298L, 38L, 466L, 133L); this.makeWorkflowTransition(3L, 4L, issueStatuses, workflow, 298L, 38L, 298L, 132L, 219L, 68L); break; } } private void makeWorkflowTransition(Long startPosition, Long endPosition, List issueStatuses, Workflow workflow, Long sourceX, Long sourceY, Long targetX, Long targetY, Long correctX, Long correctY) { IssueStatus start = null; IssueStatus end = null; for (IssueStatus issueStatus : issueStatuses) { if (startPosition.equals(issueStatus.getPosition())) { start = issueStatus; } if (endPosition.equals(issueStatus.getPosition())) { end = issueStatus; } } if (start == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.START_ISSUE_STATUS_NOT_EXIST)); } if (end == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.END_ISSUE_STATUS_NOT_EXIST)); } WorkflowTransition workflowTransition = new WorkflowTransition(workflow, start, end, sourceX, sourceY, targetX, targetY, correctX, correctY); this.workflowTransitionRepository.saveAndFlush(workflowTransition); } // 워크플로우에서 사용되는 전체 전이선을 조회한다. @Override @Transactional(readOnly = true) public List findByWorkflowId(Long workflowId) { return this.workflowTransitionRepository.findByWorkflowId(workflowId); } // 워크플로우에 사용되는 이슈 상태인지 확인한다 @Override public boolean contains(Long issueStatusId, Long workflowId) { List workflowTransitionVos = workflowTransitionRepository.findBySourceIssueStatusIdAndTargetIssueStatusIdAndWorkflowId(issueStatusId, workflowId); return workflowTransitionVos != null && workflowTransitionVos.size() > 0; } // 이슈 상태와 연결된 전이선 정보를 조회한다. @Override @Transactional(readOnly = true) public List findBySourceIssueStatusIdAndWorkflowId(Long sourceStatusId, Long workflowId) { return this.workflowTransitionRepository.findBySourceIssueStatusIdAndWorkflowId(sourceStatusId, workflowId); } // 전이선의 정보를 업데이트한다. 이미 존재하던 전이선이면 변경을 새로 생성된 전이선은 생성을 한다. @Override @Transactional public void modify(Workflow workflow, List issueStatusVos) { Map issueStatusMaps = new HashMap<>(); // 상태 속성 별로 1개씩 존재하는지 확인한다. this.checkRequireIssueStatusType(issueStatusVos); // 고립된 이슈 상태가 없는 워크플로우인지 확인한다. this.checkIsolationWorkflow(issueStatusVos); for (IssueStatusVo issueStatusVo : issueStatusVos) { issueStatusMaps.put(issueStatusVo.getId().toString(), issueStatusVo); } // 해당 워크플로우의 전체 전이선 삭제 List removeWorkflowTransitions = this.workflowTransitionRepository.findByWorkflowId(workflow.getId()); if (removeWorkflowTransitions.size() > 0) { this.removeWorkflowTransition(removeWorkflowTransitions); } for (IssueStatusVo issueStatusVo : issueStatusVos) { for (WorkflowTransitionVo workflowTransitionVo : issueStatusVo.getWorkflowTransitionVos()) { IssueStatus sourceIssueStatus = this.issueStatusService.getIssueStatus(workflowTransitionVo.getSourceStatusId()); IssueStatus targetIssueStatus = this.issueStatusService.getIssueStatus(workflowTransitionVo.getTargetStatusId()); IssueStatusVo sourceIssueStatusVo = (IssueStatusVo) MapUtil.getObject(issueStatusMaps, sourceIssueStatus.getId().toString()); IssueStatusVo targetIssueStatusVo = (IssueStatusVo) MapUtil.getObject(issueStatusMaps, targetIssueStatus.getId().toString()); WorkflowTransition workflowTransition = new WorkflowTransition(workflow, sourceIssueStatus, targetIssueStatus, sourceIssueStatusVo.getxLocation(), sourceIssueStatusVo.getyLocation(), targetIssueStatusVo.getxLocation(), targetIssueStatusVo.getyLocation(), workflowTransitionVo.getCorrectX(), workflowTransitionVo.getCorrectY(), workflowTransitionVo.getDirect()); this.workflowTransitionRepository.save(workflowTransition); } } } // 상태 속성 별로 1개씩 존재하는지 확인한다. private void checkRequireIssueStatusType(List issueStatusVos) { boolean firstStatusExist = true; boolean middleStatusExist = true; boolean lastStatusExist = true; for (IssueStatusVo issueStatusVo : issueStatusVos) { switch (IssueStatusType.valueOf(issueStatusVo.getIssueStatusType())) { case READY: firstStatusExist = false; break; case OPEN: middleStatusExist = false; break; case CLOSE: lastStatusExist = false; break; } } if (firstStatusExist) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WORKFLOW_REQUIRE_ISSUE_STATUS_TYPE_TO_READY)); } if (middleStatusExist) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WORKFLOW_REQUIRE_ISSUE_STATUS_TYPE_TO_OPEN)); } if (lastStatusExist) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WORKFLOW_REQUIRE_ISSUE_STATUS_TYPE_TO_CLOSE)); } } // 고립된 이슈 상태가 없는 워크플로우인지 확인한다. private void checkIsolationWorkflow(List issueStatusVos) { boolean firstCheck = false; boolean middleCheck = false; boolean lastCheck = true; boolean connectCheck = false; List workflowTransitionVos = Lists.newArrayList(); // 전체 전이선 추출 for (IssueStatusVo issueStatusVo : issueStatusVos) { for (WorkflowTransitionVo workflowTransitionVo : issueStatusVo.getWorkflowTransitionVos()) { workflowTransitionVos.add(workflowTransitionVo); } } // 시작 상태 정상 확인 for (IssueStatusVo issueStatusVo : issueStatusVos) { switch (IssueStatusType.valueOf(issueStatusVo.getIssueStatusType())) { case READY: for (WorkflowTransitionVo workflowTransitionVo : workflowTransitionVos) { if (workflowTransitionVo.getSourceStatusId().equals(issueStatusVo.getId())) { firstCheck = true; break; } } break; case OPEN: boolean normalSourceCheck = false; boolean normalTargetCheck = false; middleCheck = true; for (WorkflowTransitionVo workflowTransitionVo : workflowTransitionVos) { if (workflowTransitionVo.getSourceStatusId().equals(issueStatusVo.getId())) { normalSourceCheck = true; } if (workflowTransitionVo.getTargetStatusId().equals(issueStatusVo.getId())) { normalTargetCheck = true; } } if (!normalSourceCheck || !normalTargetCheck) { connectCheck = true; } break; case CLOSE: boolean tempLastCheck = false; for (WorkflowTransitionVo workflowTransitionVo : workflowTransitionVos) { if (workflowTransitionVo.getTargetStatusId().equals(issueStatusVo.getId())) { tempLastCheck = true; break; } } if (!tempLastCheck) { lastCheck = false; } break; } } if (!firstCheck || !middleCheck || !lastCheck || connectCheck) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WORKFLOW_ISOLATION)); } } @Override @Transactional(readOnly = true) public WorkflowTransition getWorkflowTransition(Long id) { if (id == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WORKFLOW_TRANSITION_NOT_EXIST)); } WorkflowTransition workflowTransition = this.findOne(id); if (workflowTransition == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WORKFLOW_TRANSITION_NOT_EXIST)); } return workflowTransition; } private void removeWorkflowTransition(List workflowTransitions) { for (WorkflowTransition workflowTransition : workflowTransitions) { this.workflowTransitionRepository.delete(workflowTransition); } this.workflowTransitionRepository.flush(); } }