package kr.wisestone.owl.service.impl; import com.google.common.collect.Lists; import kr.wisestone.owl.common.ExcelConditionCheck; import kr.wisestone.owl.constant.Constants; import kr.wisestone.owl.constant.MsgConstants; import kr.wisestone.owl.domain.*; import kr.wisestone.owl.domain.enumType.ProjectType; import kr.wisestone.owl.exception.OwlRuntimeException; import kr.wisestone.owl.mapper.WorkflowMapper; import kr.wisestone.owl.repository.WorkflowRepository; import kr.wisestone.owl.service.*; import kr.wisestone.owl.util.ConvertUtil; import kr.wisestone.owl.vo.*; import kr.wisestone.owl.web.condition.WorkflowCondition; import kr.wisestone.owl.web.condition.WorkflowDepartmentCondition; import kr.wisestone.owl.web.form.WorkflowForm; import kr.wisestone.owl.web.view.ExcelView; import org.apache.commons.lang3.StringUtils; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.ui.Model; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import java.util.*; @Service public class WorkflowServiceImpl extends AbstractServiceImpl> implements WorkflowService { private static final Logger log = LoggerFactory.getLogger(WorkflowServiceImpl.class); @Autowired private WorkflowRepository workflowRepository; @Autowired private IssueStatusService issueStatusService; @Autowired private WorkflowTransitionService workflowTransitionService; @Autowired private WorkflowDepartmentService workflowDepartmentService; @Autowired private WorkspaceService workspaceService; @Autowired private IssueService issueService; @Autowired private UserService userService; @Autowired private WorkflowMapper workflowMapper; @Autowired private ExcelView excelView; @Autowired private ExcelConditionCheck excelConditionCheck; @Override protected JpaRepository getRepository() { return this.workflowRepository; } // 기본 프로젝트에서 사용되는 기본 워크플로우 @Override @Transactional public void addDefaultWorkflow(Workspace workspace, List projectTypes) { for (ProjectType projectType : projectTypes) { List issueStatuses = Lists.newArrayList(); // 워크플로우 생성 Workflow workflow = new Workflow(); workflow.setWorkspace(workspace); workflow.setProjectType(projectType); // project 생성시 project type 에 맞는 워크플로우/이슈 유형을 자동 생성할 수 있게 해준다. - 프로젝트 템플릿 유형 별로 해당 project type 에 맞는 워크플로우로 생성해준다. switch(projectType) { case BTS_PROJECT: workflow.setName(this.messageAccessor.message("common.btsWorkflow")); // BTS 워크플로우 workflow.setDescription(this.messageAccessor.message("common.usedToManageThisWorkflow")); // 버그를 관리하는데 사용하는 워크플로우 입니다. issueStatuses = this.issueStatusService.findByWorkspaceId(workspace.getId()); break; case RMS_PROJECT: workflow.setName(this.messageAccessor.message("common.rmsWorkflow")); // RMS 워크플로우 workflow.setDescription(this.messageAccessor.message("common.userToManageRequirementsThisWorkflow")); // 요구 사항을 관리하는데 사용하는 워크플로우 입니다. issueStatuses = this.issueStatusService.findByWorkspaceId(workspace.getId()); break; case TCM_PROJECT: workflow.setName(this.messageAccessor.message("common.tcmWorkflow")); // TCM 워크플로우 workflow.setDescription(this.messageAccessor.message("common.userToManageTestCasesThisWorkflow")); // 테스트 케이스를 관리하는데 사용하는 워크플로우 입니다. issueStatuses = this.issueStatusService.findByWorkspaceId(workspace.getId()); break; } // 워크스페이스에 기본 제공되는 이슈 상태가 없을 경우에는 오류 if (issueStatuses.isEmpty()) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_NOT_EXIST)); } this.workflowRepository.saveAndFlush(workflow); // 워크플로우 전이 연결. this.workflowTransitionService.addDefaultWorkflowTransition(workflow, issueStatuses, projectType); } } // 워크플로우를 생성한다. @Override @Transactional public Workflow addWorkflow(WorkflowForm workflowForm) { // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. this.workspaceService.checkUseWorkspace(); // 이름 유효성 체크 this.verifyName(workflowForm.getName(), null); Workflow workflow = ConvertUtil.copyProperties(workflowForm, Workflow.class, "projectType"); Workspace workspace = this.workspaceService.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId()); workflow.setWorkspace(workspace); workflow = this.workflowRepository.saveAndFlush(workflow); this.workflowTransitionService.modify(workflow, workflowForm.getIssueStatusVos()); this.workflowDepartmentService.modify(workflow, workflowForm.getIssueStatusVos()); return workflow; } // 이름 유효성 체크 private void verifyName(String name, Long id) { if (StringUtils.isEmpty(name)) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WORKFLOW_NOT_NAME)); } if (name.length() > 20) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WORKFLOW_NAME_MAX_LENGTH_OUT)); } Workflow workflow; Long workspaceId = this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId(); if (id == null) { workflow = this.workflowRepository.findByNameAndWorkspaceId(name, workspaceId); } else { workflow = this.workflowRepository.findByNameAndWorkspaceIdAndIdNot(name, workspaceId, id); } if (workflow != null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WORKFLOW_USED_NAME)); } } // 워크플로우 목록을 조회한다. @Override @Transactional(readOnly = true) public List findWorkflow(Map resJsonData, WorkflowCondition condition, Pageable pageable) { condition.setPage(pageable.getPageNumber() * pageable.getPageSize()); condition.setPageSize(pageable.getPageSize()); condition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId()); List> results = this.workflowMapper.find(condition); Long totalCount = this.workflowMapper.count(condition); int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1; List workflowVos = ConvertUtil.convertListToListClass(results, WorkflowVo.class); //workflow 리스트 // 워크플로우를 사용하는 이슈 유형 정보를 추가한다. this.setIssueTypeVos(workflowVos); // 워크플로우 담당부서 정보를 추가한다. this.setDepartmentVos(workflowVos); resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(), totalPage, totalCount)); resJsonData.put(Constants.RES_KEY_CONTENTS, workflowVos); return workflowVos; } // 워크플로우를 사용하는 이슈 유형 정보를 추가한다. private void setIssueTypeVos(List workflowVos) { for (WorkflowVo workflowVo : workflowVos) { Workflow workflow = this.getWorkflow(workflowVo.getId()); List issueTypeVos = ConvertUtil.convertObjectsToClasses(workflow.getIssueTypes(), IssueTypeVo.class); workflowVo.setIssueTypeVos(issueTypeVos); } } // 워크플로우를 사용하는 부서 정보를 추가한다. private void setDepartmentVos(List workflowVos) { for (WorkflowVo workflowVo : workflowVos) { Workflow workflow = this.getWorkflow(workflowVo.getId()); List workflowDepartmentVos = this.workflowDepartmentService.find(workflow.getId(), null); if (workflowDepartmentVos != null && workflowDepartmentVos.size() > 0) { IssueStatusVo issueStatusVo = new IssueStatusVo(); issueStatusVo.setWorkflowDepartmentVos(workflowDepartmentVos); workflowVo.addIssueStatusVos(issueStatusVo); } } } // 워크플로우 상세 정보를 조회한다. @Override @Transactional(readOnly = true) public void detailWorkflow(Map resJsonData, WorkflowCondition workflowCondition) { WorkflowVo workflowVo = new WorkflowVo(); if (workflowCondition.getId() != null) { Workflow workflow = this.getWorkflow(workflowCondition.getId()); workflowVo = ConvertUtil.copyProperties(workflow, WorkflowVo.class); switch (workflowCondition.getDeep()) { case "01" : // 연관된 이슈 상태와 전이선 정보를 가져온다. List issueStatusVos = this.issueStatusService.findByWorkflowId(workflowCondition.getId()); for (IssueStatusVo issueStatusVo : issueStatusVos) { List workflowDepartmentVos = this.workflowDepartmentService.find(workflowVo.getId(), issueStatusVo.getId()); issueStatusVo.setWorkflowDepartmentVos(workflowDepartmentVos); } workflowVo.setIssueStatusVos(issueStatusVos); break; } } resJsonData.put(Constants.RES_KEY_CONTENTS, workflowVo); } // 워크플로우를 수정한다. @Override @Transactional public Workflow modifyWorkflow(WorkflowForm workflowForm) { // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. this.workspaceService.checkUseWorkspace(); Workflow workflow = this.getWorkflow(workflowForm.getId()); // 이름 유효성 체크 this.verifyName(workflowForm.getName(), workflowForm.getId()); workflow.setName(workflowForm.getName()); // 담당 부서 정보 변경 this.workflowDepartmentService.modify(workflow, workflowForm.getIssueStatusVos()); this.workflowTransitionService.modify(workflow, workflowForm.getIssueStatusVos()); // 워크플로우가 변경되었는지 확인하고 변경되었을 경우 이슈 상태가 없는 이슈는 '생성' 인 이슈 상태로 이동한다. this.checkWorkflowChange(workflow); return workflow; } // 워크플로우가 변경되었는지 확인하고 변경되었을 경우 이슈 상태가 없는 이슈는 '생성' 인 이슈 상태로 이동한다. private void checkWorkflowChange(Workflow workflow) { for (IssueType issueType : workflow.getIssueTypes()) { this.issueService.changeWorkflows(workflow, issueType); } } @Override @Transactional(readOnly = true) public Workflow getWorkflow(Long id) { if (id == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WORKFLOW_NOT_EXIST)); } Workflow workflow = this.findOne(id); if (workflow == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WORKFLOW_NOT_EXIST)); } return workflow; } // 워크플로우를 삭제한다. @Override @Transactional public void removeWorkflows(WorkflowForm workflowForm) { // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. this.workspaceService.checkUseWorkspace(); if (workflowForm.getRemoveIds().size() < 1) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WORKFLOW_REMOVE_NOT_SELECT)); } for (Long workflowId : workflowForm.getRemoveIds()) { this.removeWorkflow(workflowId); } this.workflowRepository.flush(); } private void removeWorkflow(Long workflowId) { Workflow workflow = this.getWorkflow(workflowId); // 워크플로우 담당부서 삭제 this.workflowDepartmentService.remove(workflowId); // 워크플로우를 이슈 타입에서 사용하고 있는지 확인 this.checkIssueTypeWorkflow(workflow); this.workflowRepository.delete(workflow); } // 워크플로우를 이슈 타입에서 사용하고 있는지 확인 private void checkIssueTypeWorkflow(Workflow workflow) { if (workflow.getIssueTypes().size() > 0) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WORKFLOW_USED_ISSUE_TYPE)); } } // 워크스페이스의 해당 프로젝트 유형에서 사용되는 워크플로우를 찾아온다. @Override @Transactional(readOnly = true) public Workflow findByWorkspaceIdAndProjectType(Long workspaceId, ProjectType projectType) { Workflow workflow = this.workflowRepository.findByWorkspaceIdAndProjectType(workspaceId, projectType); if (workflow == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WORKFLOW_NOT_EXIST)); } return workflow; } // 워크스페이스에 있는 모든 워크플로우 목록을 조회한다. @Override @Transactional(readOnly = true) public List findByWorkspaceId(Long workspaceId) { return this.workflowRepository.findByWorkspaceId(workspaceId); } // 워크플로우 목록을 엑셀로 다운로드 한다. @Override @Transactional public ModelAndView downloadExcel(HttpServletRequest request, Model model) { // 사용 공간에서 로그인한 사용자가 비활성인지 확인하고 비활성일 경우 엑셀 다운로드를 금지한다. ModelAndView modelAndView = this.workspaceService.checkUseExcelDownload(model); if (modelAndView != null) { return modelAndView; } Map conditions = new HashMap<>(); // 엑셀 다운로드에 필요한 검색 조건 정보를 추출하고 검색 조건 추출에 오류가 발생하면 경고를 표시해준다. modelAndView = this.excelConditionCheck.checkCondition(conditions, request, model); if (modelAndView != null) { return modelAndView; } WorkflowCondition workflowCondition = WorkflowCondition.make(conditions); workflowCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId()); List> results = this.workflowMapper.find(workflowCondition); List workflowVos = ConvertUtil.convertListToListClass(results, WorkflowVo.class); ExportExcelVo excelInfo = new ExportExcelVo(); excelInfo.setFileName(this.messageAccessor.message("common.workflowList")); // 워크플로우 목록 excelInfo.addAttrInfos(new ExportExcelAttrVo("name", this.messageAccessor.message("common.workflow"), 15, ExportExcelAttrVo.ALIGN_LEFT)); // 워크플로우 // 엑셀에 넣을 데이터 excelInfo.setDatas(workflowVos); model.addAttribute(Constants.EXCEL, excelInfo); return new ModelAndView(this.excelView); } }