package kr.wisestone.owl.service.impl; import com.google.common.collect.Lists; import kr.wisestone.owl.common.ExcelConditionCheck; import kr.wisestone.owl.common.IssueCustomFieldValueFormComparator; import kr.wisestone.owl.config.CommonConfiguration; import kr.wisestone.owl.constant.*; import kr.wisestone.owl.data.CheckIssueData; import kr.wisestone.owl.domain.*; import kr.wisestone.owl.domain.enumType.CustomFieldType; import kr.wisestone.owl.domain.enumType.EmailType; import kr.wisestone.owl.domain.enumType.IssueHistoryType; import kr.wisestone.owl.domain.enumType.IssueStatusType; import kr.wisestone.owl.exception.ApiAuthException; import kr.wisestone.owl.exception.ApiParameterException; import kr.wisestone.owl.exception.OwlRuntimeException; import kr.wisestone.owl.mapper.DepartmentMapper; import kr.wisestone.owl.mapper.IssueMapper; import kr.wisestone.owl.mapper.IssueRelationMapper; import kr.wisestone.owl.mapper.ProjectMapper; import kr.wisestone.owl.repository.*; import kr.wisestone.owl.service.*; import kr.wisestone.owl.util.*; import kr.wisestone.owl.util.DateUtil; import kr.wisestone.owl.vo.*; import kr.wisestone.owl.web.condition.*; import kr.wisestone.owl.web.form.*; import kr.wisestone.owl.web.view.ExcelView; import org.apache.commons.lang3.StringUtils; import org.apache.commons.lang3.time.StopWatch; import org.apache.poi.ss.usermodel.*; import org.jsoup.Jsoup; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import org.springframework.beans.factory.annotation.Autowired; import org.springframework.data.domain.Page; import org.springframework.data.domain.PageRequest; import org.springframework.data.domain.Pageable; import org.springframework.data.jpa.repository.JpaRepository; import org.springframework.messaging.simp.SimpMessagingTemplate; import org.springframework.stereotype.Service; import org.springframework.transaction.annotation.Transactional; import org.springframework.ui.Model; import org.springframework.web.multipart.MultipartFile; import org.springframework.web.servlet.ModelAndView; import javax.servlet.http.HttpServletRequest; import java.io.IOException; import java.text.ParseException; import java.util.*; import java.util.regex.Pattern; import static kr.wisestone.owl.domain.enumType.CustomFieldType.*; @Service public class IssueServiceImpl extends AbstractServiceImpl> implements IssueService { private static final Logger log = LoggerFactory.getLogger(IssueServiceImpl.class); @Autowired private IssueRepository issueRepository; @Autowired private ProjectService projectService; @Autowired private IssueTableConfigService issueTableConfigService; @Autowired private IssueStatusService issueStatusService; @Autowired private IssueTypeService issueTypeService; @Autowired private PriorityService priorityService; @Autowired private SeverityService severityService; @Autowired private CustomFieldApiOverlapService customFieldApiOverlapService; @Autowired private IssueApiDefaultService issueApiDefaultService; @Autowired private ApiTokenService apiTokenService; @Autowired private CompanyFieldService companyFieldService; @Autowired private CompanyFieldCategoryService companyFieldCategoryService; @Autowired private IspFieldService ispFieldService; @Autowired private HostingFieldService hostingFieldService; @Autowired private CommonConfiguration configuration; @Autowired private IssueNumberGeneratorService issueNumberGeneratorService; @Autowired private AttachedFileService attachedFileService; @Autowired private IssueCustomFieldValueService issueCustomFieldValueService; @Autowired private IssueCompanyService issueCompanyService; @Autowired private IssueIspService issueIspService; @Autowired private IssueHostingService issueHostingService; @Autowired private IssueUserService issueUserService; @Autowired private IssueDepartmentService issueDepartmentService; @Autowired private CustomFieldService customFieldService; @Autowired private IssueTypeCustomFieldService issueTypeCustomFieldService; @Autowired private UserService userService; @Autowired private DepartmentService departmentService; @Autowired private IssueCommentService issueCommentService; @Autowired private IssueHistoryService issueHistoryService; @Autowired private ProjectRoleUserService projectRoleUserService; @Autowired private ProjectRoleDepartmentService projectRoleDepartmentService; @Autowired private IssueRiskService issueRiskService; @Autowired private WorkspaceService workspaceService; @Autowired private SystemEmailService systemEmailService; @Autowired private IssueVersionService issueVersionService; @Autowired private IssueReservationService issueReservationService; @Autowired private UserWorkspaceService userWorkspaceService; @Autowired private UserLevelService userLevelService; @Autowired private WorkflowDepartmentService workflowDepartmentService; @Autowired private IssueRelationService issueRelationService; @Autowired private IssueRelationRepository issueRelationRepository; @Autowired private ExcelView excelView; @Autowired private IssueMapper issueMapper; @Autowired private IssueCompanyRepository issueCompanyRepository; @Autowired private IssueIspRepository issueIspRepository; @Autowired private IssueHostingRepository issueHostingRepository; @Autowired private ExcelConditionCheck excelConditionCheck; @Autowired private SimpMessagingTemplate simpMessagingTemplate; @Autowired private UserDepartmentService userDepartmentService; @Autowired private UserDepartmentRepository userDepartmentRepository; @Autowired private DepartmentMapper departmentMapper; @Autowired private WorkflowDepartmentRepository workflowDepartmentRepository; @Autowired private IssueRelationMapper issueRelationMapper; @Autowired private WorkflowTransitionService workflowTransitionService; @Override protected JpaRepository getRepository() { return this.issueRepository; } private static final int EXCEL_DOWNLOAD_MAX_ROWS = 10000; // excel download 제한 private static final int EXCEL_IMPORT_MAX_ROWS = 10000; // excel import 제한 @Override @Transactional public void addIssueVersion(Long id) { Issue issue = this.getIssue(id); // 이슈 버전 생성 this.issueVersionService.addIssueVersion(issue); } @Override @Transactional public void addIssueVersion(Long id, Long userId) { Issue issue = this.getIssue(id); User user = this.userService.getUser(userId); // 이슈 버전 생성 this.issueVersionService.addIssueVersion(issue, user); } private IssueForm convertToIssueForm(IssueApiForm issueApiForm, User user) { if (issueApiForm.getIssueTypeId() == null) { throw new ApiParameterException(this.messageAccessor.getMessage(MsgConstants.API_PARAMETER_ISSUE_TYPE_ERROR)); } IssueForm issueForm = ConvertUtil.copyProperties(issueApiForm, IssueForm.class); // issueForm.setFiles(issueApiForm.getFiles()); IssueType issueType = this.issueTypeService.getIssueType(issueApiForm.getIssueTypeId()); if (issueType == null){ throw new ApiParameterException(this.messageAccessor.getMessage(MsgConstants.API_PARAMETER_ISSUE_TYPE_ERROR)); } Workflow workflow = issueType.getWorkflow(); if (issueApiForm.getApiType().equals(IssueApiForm.ApiType.add)) { // 이슈 상태가 지정되어 있지 않을 경우 워크플로우 대기 상태 값으로 지정 List departmentIds = this.workflowDepartmentService.findFirstDepartmentIds(workflow); if (departmentIds != null && departmentIds.size() > 0) { for (Long departmentId : departmentIds) { issueForm.addDepartmentId(departmentId); } } } else if (issueApiForm.getIssueStatusId() == null){ throw new ApiParameterException(this.messageAccessor.getMessage(MsgConstants.API_ISSUE_STATUS_NOT_EXIST)); } else if (!this.workflowTransitionService.contains(issueApiForm.getIssueStatusId(), workflow.getId())) { //이슈 상태 유효성 확인 throw new ApiParameterException(this.messageAccessor.getMessage(MsgConstants.API_ISSUE_STATUS_NOT_EXIST_IN_WORKFLOW)); } // 프로젝트 입력 Project project = issueType.getProject(); if (project == null){ throw new ApiParameterException(this.messageAccessor.getMessage(MsgConstants.API_PARAMETER_PROJECT_ERROR)); } issueForm.setProjectId(project.getId()); if (user != null) { // 기본값 입력하기 IssueApiDefaultForm issueApiDefaultForm = new IssueApiDefaultForm(); issueApiDefaultForm.setUserId(user.getId()); issueApiDefaultForm.setIssueTypeId(issueForm.getIssueTypeId()); IssueApiDefault issueApiDefault = this.issueApiDefaultService.find(issueApiDefaultForm); if (issueApiDefault != null) { ConvertUtil.copyProperties(issueApiDefault, issueForm); issueForm.setId(null); issueForm.setPriorityId(issueApiDefault.getPriority().getId()); issueForm.setSeverityId(issueApiDefault.getSeverity().getId()); } // 중복 값 상위 이슈의 하위 이슈로 처리하기 CustomFieldApiOverlapForm customFieldApiOverlapForm = new CustomFieldApiOverlapForm(); customFieldApiOverlapForm.setUserId(user.getId()); customFieldApiOverlapForm.setIssueTypeId(issueForm.getIssueTypeId()); // 상위일감에 사용할 중복값 설정 List customFieldApiOverlaps = this.customFieldApiOverlapService.find(user.getId(), issueApiForm.getIssueTypeId()); // if (customFieldApiOverlaps == null || customFieldApiOverlaps.size() == 0){ // throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_OVERLAP_SETTING_NOT_EXIST)); // } if (customFieldApiOverlaps != null && customFieldApiOverlaps.size() > 0) { for (int i = 0; i < customFieldApiOverlaps.size(); i++) { CustomFieldApiOverlap customFieldApiOverlap = customFieldApiOverlaps.get(i); issueApiForm.addUseIssueCustomFieldId(customFieldApiOverlap.getCustomField().getId()); } // 중복된 상위 이슈검색 List issues = this.findIssue(issueApiForm, issueForm, customFieldApiOverlaps, user.getId()); int size = issues.size(); if (size > 0) { Issue targetIssue = issues.get(0); if (targetIssue.getParentIssue() != null) { issueForm.setParentIssueId(targetIssue.getParentIssue().getId()); } else { issueForm.setParentIssueId(targetIssue.getId()); } } } issueForm.setIsApi(Issue.IS_API_YES); // 사용자 정의 필드 설정 issueForm.setIssueCustomFields(issueApiForm.getCustomFieldValues()); // 같은 도메인 업체 찾기 // api 입력값 적용 ConvertUtil.copyProperties(issueApiForm, issueForm); return issueForm; } else { throw new ApiAuthException(this.messageAccessor.getMessage(MsgConstants.API_USER_ERROR)); } } /** * 도메인이 동일한 업체 찾기 * @param issueForm IssueForm * @return IssueForm */ private IssueForm findCompanyField(IssueForm issueForm) { if(issueForm.getIssueCustomFields() != null && issueForm.getIssueCustomFields().size() > 0) { CompanyFieldCondition condition = new CompanyFieldCondition(); List> companyFields = Lists.newArrayList(); List> issueCompanyFields = Lists.newArrayList(); List> issueIspFields = Lists.newArrayList(); List> issueHostingFields = Lists.newArrayList(); for (Map issueCustomField : issueForm.getIssueCustomFields()) { Long customFieldId = MapUtil.getLong(issueCustomField, "customFieldId"); CustomField customField = this.customFieldService.getCustomField(customFieldId); if(customField != null && customField.getCustomFieldType().equals(SITE) && customField.getName().equals("도메인")) { String useValue = issueCustomField.get("useValue").toString(); String[] urlArr = null; List urls = Lists.newArrayList(); if (useValue.contains(",")) { urlArr = useValue.split(","); urls.addAll(Arrays.asList(urlArr)); } else { urls.add(useValue); } condition.setUrls(urls); companyFields = this.companyFieldService.find(condition); if(companyFields != null && companyFields.size() > 0) { for (Map companyField : companyFields) { CompanyFieldVo companyFieldVo = ConvertUtil.convertMapToClass(companyField, CompanyFieldVo.class); companyField.put("companyId", companyField.get("id")); issueCompanyFields.add(companyField); if(companyFieldVo.getIspId() != null && companyFieldVo.getIspId() != -1) { Map ispField = this.ispFieldService.find(companyFieldVo.getIspId()); if (ispField != null) { ispField.put("ispId", ispField.get("id")); issueIspFields.add(ispField); } } if(companyFieldVo.getHostingId() != null && companyFieldVo.getHostingId() != -1) { Map hostingField = this.hostingFieldService.find(companyFieldVo.getHostingId()); if (hostingField != null) { hostingField.put("hostingId", hostingField.get("id")); issueHostingFields.add(hostingField); } } } } } issueForm.setIssueCompanyFields(issueCompanyFields); issueForm.setIssueIspFields(issueIspFields); issueForm.setIssueHostingFields(issueHostingFields); } } return issueForm; } /** * 조건에 맞는 파트너 정보 찾기 * @param condition CompanyFieldCondition * @param issueCompanyFields List> * @param issueIspFields List> * @param issueHostingFields List> */ private void findPartner(CompanyFieldCondition condition, List> issueCompanyFields , List> issueIspFields, List> issueHostingFields) { List> companyFields = this.companyFieldService.find(condition); if(companyFields != null && companyFields.size() > 0) { for (Map companyField : companyFields) { CompanyFieldVo companyFieldVo = ConvertUtil.convertMapToClass(companyField, CompanyFieldVo.class); companyField.put("companyId", companyField.get("id")); issueCompanyFields.add(companyField); if(companyFieldVo.getIspId() != null && companyFieldVo.getIspId() != -1) { Map ispField = this.ispFieldService.find(companyFieldVo.getIspId()); if (ispField != null) { ispField.put("ispId", ispField.get("id")); issueIspFields.add(ispField); } } if(companyFieldVo.getHostingId() != null && companyFieldVo.getHostingId() != -1) { Map hostingField = this.hostingFieldService.find(companyFieldVo.getHostingId()); if (hostingField != null) { hostingField.put("hostingId", hostingField.get("id")); issueHostingFields.add(hostingField); } } } } } private User convertToUser(String token) { // 토큰으로 유저 정보 가져오기 UserVo userVo = this.apiTokenService.certification(token); // 해당 유저 정보가 현재 db에 있는지 확인 return this.userService.getUser(userVo.getId()); } // API 를 통해 이슈 추가. @Override @Transactional public List addApiIssue(IssueApiForm issueApiForm) throws CloneNotSupportedException { User user = convertToUser(issueApiForm.getToken()); IssueForm issueForm = this.convertToIssueForm(issueApiForm, user); List issues = Lists.newArrayList(); if (issueForm.getParentIssueId() != null // 기존 추가된 상위 일감이 없거나 설정된 중복 이슈 id가 없을때 || issueApiForm.getUseIssueCustomFieldIds().size() == 0) { issues.add(addIssue(user, issueForm, issueApiForm.getMultipartFiles())); } else { // 가상 상위 이슈 추가 IssueForm parentIssueForm = issueForm.clone(); // 가상 상위 이슈 추가 parentIssueForm.setUseIssueCustomFields(issueApiForm.getUseIssueCustomFieldIds()); // 같은 도메인 업체 찾기 if (parentIssueForm.getIssueCompanyFields() == null) { IssueForm partners = this.findCompanyField(parentIssueForm); parentIssueForm.setIssueCompanyFields(partners.getIssueCompanyFields()); parentIssueForm.setIssueIspFields(partners.getIssueIspFields()); parentIssueForm.setIssueHostingFields(partners.getIssueHostingFields()); } Issue issue = addIssue(user, parentIssueForm, null); issues.add(issue); // 하위 이슈 추가 issueForm.setParentIssueId(issue.getId()); issues.add(addIssue(user, issueForm, issueApiForm.getMultipartFiles())); } return issues; } // 상위 이슈 가져오기 private IssueVo getParentIssueVo(Long parentIssueId) { if (parentIssueId != null) { Issue parentIssue = this.getIssue(parentIssueId); return ConvertUtil.copyProperties(parentIssue, IssueVo.class); } return null; } /** * url 정규식 표현 * @param url String */ private boolean verifyUrl(String url) { boolean urlChk = false; if (!StringUtils.isEmpty(url)) { String reg = "^((http|https)://)?(www.)?([a-zA-Z0-9]+)\\.[a-z]+([a-zA-z0-9.?#]+)?"; if(Pattern.matches(reg, url)) { urlChk = true; } } return urlChk; } /** * ip 정규식 표현 * @param ip String * @return boolean */ private boolean verifyIp(String ip) { boolean ipChk = false; if (!StringUtils.isEmpty(ip)) { if (Pattern.matches("^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\" + ".(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\.(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$", ip)) { ipChk = true; } } return ipChk; } // 중복된 상위 이슈 검색 private List findIssue(IssueApiForm issueApiform, IssueForm issueForm, List customFieldApiOverlaps, Long userId) { List issueCustomFieldValueForms = issueApiform.getIssueCustomFieldValues(); List resultIssueVos = Lists.newArrayList(); String comma = ","; if (issueCustomFieldValueForms.size() > 0) { String concatUseValue = ""; String customFieldType = ""; int useIdx = 0; int cntIp = 0; int cntSite = 0; IssueCustomFieldValueFormComparator comp = new IssueCustomFieldValueFormComparator(); Collections.sort(issueCustomFieldValueForms, comp); List userValues = Lists.newArrayList(); CompanyFieldCondition condition = new CompanyFieldCondition(); List> issueCompanyFields = Lists.newArrayList(); List> issueIspFields = Lists.newArrayList(); List> issueHostingFields = Lists.newArrayList(); for (IssueCustomFieldValueForm issueCustomFieldValueForm : issueCustomFieldValueForms) { userValues.add(issueCustomFieldValueForm.getUseValue()); for(CustomFieldApiOverlap customFieldApiOverlap : customFieldApiOverlaps) { if (customFieldApiOverlap.getCustomField().getId().equals(issueCustomFieldValueForm.getCustomFieldId())) { String useValue = issueCustomFieldValueForm.getUseValue(); if (useValue.contains(" ")) { useValue = useValue.replace(" ",""); } if (this.verifyIp(useValue)) { long ip = ConvertUtil.ipToLong(useValue); customFieldType = IP_ADDRESS.toString(); if (cntIp == 0){ condition.setIp(ip); } cntIp ++; } if (this.verifyUrl(useValue)) { customFieldType = SITE.toString(); if (cntSite == 0) { condition.setUrl(useValue); } cntSite ++; } /*if (customFieldApiOverlap.getCustomField().getCustomFieldType().equals(IP_ADDRESS)) { long ip = ConvertUtil.ipToLong(useValue); customFieldType = IP_ADDRESS.toString(); if (cntIp == 0){ condition.setIp(ip); } cntIp ++; }*/ /*if(customFieldApiOverlap.getCustomField().getCustomFieldType().equals(SITE)) { customFieldType = SITE.toString(); String[] urlArr = null; List urls = Lists.newArrayList(); if (useValue.contains(",")) { urlArr = useValue.split(","); urls.addAll(Arrays.asList(urlArr)); } else { urls.add(useValue); } if (cntSite == 0) { condition.setUrl(urls); } cntSite ++; }*/ if (useIdx > 0) { concatUseValue = concatUseValue.concat(comma); } concatUseValue = concatUseValue.concat(issueCustomFieldValueForm.getUseValue()); useIdx++; } } } // 추가 할 url or ip에 포함되어있는 파트너 찾기 if ((condition.getIp() > 0) || (condition.getUrl() != null && !condition.getUrl().equals(""))) { this.findPartner(condition, issueCompanyFields, issueIspFields, issueHostingFields); } issueForm.setIssueCompanyFields(issueCompanyFields); issueForm.setIssueIspFields(issueIspFields); issueForm.setIssueHostingFields(issueHostingFields); IssueCustomFieldValueCondition issueCustomFieldValueCondition = new IssueCustomFieldValueCondition(); issueCustomFieldValueCondition.setUseValue(concatUseValue); issueCustomFieldValueCondition.setUseValues(userValues); issueCustomFieldValueCondition.setIssueTypeId(issueApiform.getIssueTypeId()); issueCustomFieldValueCondition.setCustomFieldType(customFieldType); // issueCustomFieldValueCondition.setIssueStatusType("CLOSE"); List> results = Lists.newArrayList(); if (customFieldType.equals(IP_ADDRESS.toString()) && issueForm.getIssueCompanyFields() != null && issueForm.getIssueCompanyFields().size() > 0) { long ipValue = 0; if (concatUseValue.contains(",")) { String[] arr = concatUseValue.split(","); for (String str : arr) { ipValue = ConvertUtil.ipToLong(str); } } else { ipValue = ConvertUtil.ipToLong(concatUseValue); } issueCustomFieldValueCondition.setUseValue(String.valueOf(ipValue)); } if (issueCustomFieldValueCondition.getCustomFieldType() != null && !issueCustomFieldValueCondition.getCustomFieldType().equals("")) { // 사용자정의필드 타입이 IP_ADDRESS 또는 SITE 일 경우 results = this.issueMapper.findByCustomFieldValueByCompany(issueCustomFieldValueCondition); } else { results = this.issueMapper.findByCustomFieldValue(issueCustomFieldValueCondition); } if (results != null && results.size() > 0) { for (Map result : results) { resultIssueVos.add(this.getIssue(MapUtil.getLong(result, "id"))); } } } return resultIssueVos; } // 이슈를 생성한다. @Override @Transactional public Issue addIssue(IssueForm issueForm, List multipartFiles) { User user = this.webAppUtil.getLoginUserObject(); return addIssue(user, issueForm, multipartFiles); } // 이슈를 생성한다. @Override @Transactional public Issue addIssue(User user, IssueForm issueForm, List multipartFiles) { StringBuilder detectIssueChange = new StringBuilder(); // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. Workspace workspace = this.workspaceService.checkUseWorkspace(user, user.getLastWorkspaceId()); // 프로젝트 유효성 체크 Project project = this.projectService.getProject(issueForm.getProjectId()); // 이슈 유형 유효성 체크 IssueType issueType = this.issueTypeService.getIssueType(issueForm.getIssueTypeId()); // 우선순위 유효성 체크 Priority priority = this.priorityService.getPriority(issueForm.getPriorityId()); // 중요도 유효성 체크 Severity severity = this.severityService.getSeverity(issueForm.getSeverityId()); // 제목 유효성 체크 this.verifyTitle(issueForm.getTitle()); // 날짜 유효성 체크 this.checkStartCompleteDate(issueForm.getStartDate(), issueForm.getCompleteDate()); // 담당 부서 유효성 체크 //this.verifyIssueDepartment(project, issueForm); // 이슈 상태 유형이 '대기' 인 이슈 상태 가져오기 IssueStatus issueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(issueType.getWorkflow()); Issue issue = ConvertUtil.copyProperties(issueForm, Issue.class); issue.setProject(project); issue.setIssueStatus(issueStatus); issue.setIssueType(issueType); issue.setPriority(priority); issue.setSeverity(severity); if (issueForm.getParentIssueId() != null){ Issue parentIssue = this.getIssue(issueForm.getParentIssueId()); issue.setParentIssue(parentIssue); // 상위 이슈가 종료일경우 대기로 변경 IssueStatus parentIssueStatus = parentIssue.getIssueStatus(); if (parentIssueStatus.getIssueStatusType().equals(IssueStatusType.CLOSE)) { parentIssue.setIssueStatus(issueStatus); } } issue.setIssueNumber(this.issueNumberGeneratorService.generateIssueNumber(project)); // 각 프로젝트의 고유 이슈 번호 생성 issue = this.issueRepository.saveAndFlush(issue); issue.setReverseIndex(issue.getId() * -1); // 쿼리 속도 개선을 위해 리버스 인덱스 생성 if (issueForm.getParentIssueId() != null){ Issue parentIssue = this.getIssue(issueForm.getParentIssueId()); if (issueForm.getIsApi().equals(Issue.IS_API_YES) || (issueForm.getInheritYn() != null && issueForm.getInheritYn())) { // 하위이슈에 상위이슈의 파트너 정보 적용 this.inheritPartners(issue, parentIssue); } } // 담당자 지정 //this.issueUserService.modifyIssueUser(issue, project.getWorkspace(), issueForm.getUserIds()); // 담당부서 지정 this.issueDepartmentService.modifyIssueDepartment(issue, user, project.getWorkspace(), issueForm.getDepartmentIds()); // 업체 정보 저장 this.issueCompanyService.modifyIssueCompanyField(issue, issueForm, detectIssueChange); // ISP 정보 저장 this.issueIspService.modifyIssueIspField(issue, issueForm, detectIssueChange); // HOSTING 정보 저장 this.issueHostingService.modifyIssueHostingField(issue, issueForm, detectIssueChange); // 첨부 파일 저장 // multipartFile 을 file Map List 객체로 변경한다. List> convertFileMaps = this.convertMultipartFileToFile(multipartFiles); this.attachedFileService.addAttachedFile(convertFileMaps, issue, user.getAccount()); // 텍스트 에디터에 첨부한 파일을 이슈와 연결 this.checkNotHaveIssueIdAttachedFile(issue, issueForm); // 사용자 정의 필드 저장 this.issueCustomFieldValueService.modifyIssueCustomFieldValue(issue, issueForm.getIssueCustomFields()); // 이슈 이력 생성 this.issueHistoryService.addIssueHistory(issue, user, IssueHistoryType.ADD, null); // 이슈 위험 관리 생성 this.issueRiskService.addIssueRisk(issue, project.getWorkspace()); // 영속성 컨텍스트 비우기 this.clear(); // 이슈 생성, 삭제시 예약 이메일에 등록해놓는다. this.reservationIssueEmail(issue, EmailType.ISSUE_ADD); // 사용자 시스템 기능 사용 정보 수집 UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class); log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(userVo, ElasticSearchConstants.ISSUE_ADD)); return issue; } // 연관이슈를 생성한다. @Override @Transactional public Issue addRelIssue(IssueForm issueForm, List multipartFiles) { User user = this.webAppUtil.getLoginUserObject(); return addRelIssue(user, issueForm, multipartFiles); } // 하위이슈를 생성한다. @Override @Transactional public Issue addDownIssue(Map resJsonData, IssueForm issueForm, List multipartFiles) { User user = this.webAppUtil.getLoginUserObject(); return addDownIssue(resJsonData, user, issueForm, multipartFiles); } // 하위이슈를 생성한다. @Override @Transactional public Issue addDownIssue(Map resJsonData, User user, IssueForm issueForm, List multipartFiles) { StringBuilder detectIssueChange = new StringBuilder(); // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. Workspace workspace = this.workspaceService.checkUseWorkspace(user, user.getLastWorkspaceId()); // 프로젝트 유효성 체크 Project project = this.projectService.getProject(issueForm.getProjectId()); // 이슈 유형 유효성 체크 IssueType issueType = this.issueTypeService.getIssueType(issueForm.getIssueTypeId()); // 우선순위 유효성 체크 Priority priority = this.priorityService.getPriority(issueForm.getPriorityId()); // 중요도 유효성 체크 Severity severity = this.severityService.getSeverity(issueForm.getSeverityId()); // 제목 유효성 체크 this.verifyTitle(issueForm.getTitle()); // 날짜 유효성 체크 this.checkStartCompleteDate(issueForm.getStartDate(), issueForm.getCompleteDate()); // 담당 부서 유효성 체크 //this.verifyIssueDepartment(project, issueForm); // 이슈 상태 유형이 '대기' 인 이슈 상태 가져오기 IssueStatus issueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(issueType.getWorkflow()); Issue issue = ConvertUtil.copyProperties(issueForm, Issue.class); issue.setProject(project); issue.setIssueStatus(issueStatus); issue.setIssueType(issueType); issue.setPriority(priority); issue.setSeverity(severity); if (issueForm.getParentIssueId() != null){ Issue parentIssue = this.getIssue(issueForm.getParentIssueId()); issue.setParentIssue(parentIssue); } issue.setIssueNumber(this.issueNumberGeneratorService.generateIssueNumber(project)); // 각 프로젝트의 고유 이슈 번호 생성 issue = this.issueRepository.saveAndFlush(issue); issue.setReverseIndex(issue.getId() * -1); // 쿼리 속도 개선을 위해 리버스 인덱스 생성 // 담당자 지정 //this.issueUserService.modifyIssueUser(issue, project.getWorkspace(), issueForm.getUserIds()); // 담당부서 지정 this.issueDepartmentService.modifyIssueDepartment(issue, user, project.getWorkspace(), issueForm.getDepartmentIds()); // 업체 정보 저장 this.issueCompanyService.modifyIssueCompanyField(issue, issueForm, detectIssueChange); // ISP 정보 저장 this.issueIspService.modifyIssueIspField(issue, issueForm, detectIssueChange); // HOSTING 정보 저장 this.issueHostingService.modifyIssueHostingField(issue, issueForm, detectIssueChange); // 첨부 파일 저장 // multipartFile 을 file Map List 객체로 변경한다. List> convertFileMaps = this.convertMultipartFileToFile(multipartFiles); this.attachedFileService.addAttachedFile(convertFileMaps, issue, user.getAccount()); // 텍스트 에디터에 첨부한 파일을 이슈와 연결 this.checkNotHaveIssueIdAttachedFile(issue, issueForm); // 사용자 정의 필드 저장 this.issueCustomFieldValueService.modifyIssueCustomFieldValue(issue, issueForm.getIssueCustomFields()); // 이슈 이력 생성 this.issueHistoryService.addIssueHistory(issue, user, IssueHistoryType.ADD, null); // 이슈 위험 관리 생성 this.issueRiskService.addIssueRisk(issue, project.getWorkspace()); // 영속성 컨텍스트 비우기 this.clear(); // 이슈 생성, 삭제시 예약 이메일에 등록해놓는다. this.reservationIssueEmail(issue, EmailType.ISSUE_ADD); // 사용자 시스템 기능 사용 정보 수집 UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class); log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(userVo, ElasticSearchConstants.ISSUE_ADD)); IssueVo issueVo = this.convertToIssueVo(issue); resJsonData.put(Constants.RES_KEY_CONTENTS, issueVo); return issue; } /** * Issue를 IssueVo로 변환(하위이슈의 파트너 정보 상속 시 필요) * @param issue Issue * @return IssueVo */ private IssueVo convertToIssueVo(Issue issue) { IssueVo issueVo = ConvertUtil.copyProperties(issue, IssueVo.class); issueVo.setInheritPartners(issue.getIssueType().getInheritPartners()); issueVo.setUsePartner(issue.getIssueType().getUsePartner()); return issueVo; } // 연관이슈를 생성한다. @Override @Transactional public Issue addRelIssue(User user, IssueForm issueForm, List multipartFiles) { StringBuilder detectIssueChange = new StringBuilder(); // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. Workspace workspace = this.workspaceService.checkUseWorkspace(user, user.getLastWorkspaceId()); // 프로젝트 유효성 체크 Project project = this.projectService.getProject(issueForm.getProjectId()); // 이슈 유형 유효성 체크 IssueType issueType = this.issueTypeService.getIssueType(issueForm.getIssueTypeId()); // 우선순위 유효성 체크 Priority priority = this.priorityService.getPriority(issueForm.getPriorityId()); // 중요도 유효성 체크 Severity severity = this.severityService.getSeverity(issueForm.getSeverityId()); // 제목 유효성 체크 this.verifyTitle(issueForm.getTitle()); // 날짜 유효성 체크 this.checkStartCompleteDate(issueForm.getStartDate(), issueForm.getCompleteDate()); // 담당 부서 유효성 체크 //this.verifyIssueDepartment(project, issueForm); // 이슈 상태 유형이 '대기' 인 이슈 상태 가져오기 IssueStatus issueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(issueType.getWorkflow()); Issue issue = ConvertUtil.copyProperties(issueForm, Issue.class); issue.setProject(project); issue.setIssueStatus(issueStatus); issue.setIssueType(issueType); issue.setPriority(priority); issue.setSeverity(severity); if (issueForm.getParentIssueId() != null){ Issue parentIssue = this.getIssue(issueForm.getParentIssueId()); issue.setParentIssue(parentIssue); } issue.setIssueNumber(this.issueNumberGeneratorService.generateIssueNumber(project)); // 각 프로젝트의 고유 이슈 번호 생성 issue = this.issueRepository.saveAndFlush(issue); issue.setReverseIndex(issue.getId() * -1); // 쿼리 속도 개선을 위해 리버스 인덱스 생성 // 담당자 지정 //this.issueUserService.modifyIssueUser(issue, project.getWorkspace(), issueForm.getUserIds()); // 담당부서 지정 this.issueDepartmentService.modifyIssueDepartment(issue, user, project.getWorkspace(), issueForm.getDepartmentIds()); // 업체 정보 저장 this.issueCompanyService.modifyIssueCompanyField(issue, issueForm, detectIssueChange); // ISP 정보 저장 this.issueIspService.modifyIssueIspField(issue, issueForm, detectIssueChange); // HOSTING 정보 저장 this.issueHostingService.modifyIssueHostingField(issue, issueForm, detectIssueChange); // 첨부 파일 저장 // multipartFile 을 file Map List 객체로 변경한다. List> convertFileMaps = this.convertMultipartFileToFile(multipartFiles); this.attachedFileService.addAttachedFile(convertFileMaps, issue, user.getAccount()); // 텍스트 에디터에 첨부한 파일을 이슈와 연결 this.checkNotHaveIssueIdAttachedFile(issue, issueForm); // 사용자 정의 필드 저장 this.issueCustomFieldValueService.modifyIssueCustomFieldValue(issue, issueForm.getIssueCustomFields()); // 이슈 이력 생성 this.issueHistoryService.addIssueHistory(issue, user, IssueHistoryType.ADD, null); // 이슈 위험 관리 생성 this.issueRiskService.addIssueRisk(issue, project.getWorkspace()); // 영속성 컨텍스트 비우기 this.clear(); // 이슈 생성, 삭제시 예약 이메일에 등록해놓는다. this.reservationIssueEmail(issue, EmailType.ISSUE_ADD); // 사용자 시스템 기능 사용 정보 수집 UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class); log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(userVo, ElasticSearchConstants.ISSUE_ADD)); return issue; } // 이슈 생성, 삭제시 예약 이메일에 등록해놓는다. private void reservationIssueEmail(Issue issue, EmailType emailType) { Map issueMap = new HashMap<>(); // 이슈 정보를 이메일 전송에 사용하기 위해 Map 형태로 변환한다. this.makeIssueMapToIssue(issue, issueMap); Map projectRoleUserMap = new HashMap<>(); projectRoleUserMap.put("id", issue.getProject().getId()); projectRoleUserMap.put("statuses", Lists.newArrayList("02")); // 관리자 조회 // 관리자 정보 셋팅 List> projectRoleUsers = this.projectRoleUserService.findProjectRoleUser(projectRoleUserMap); if (projectRoleUsers != null && !projectRoleUsers.isEmpty()) { for (Map projectRoleUser : projectRoleUsers) { UserVo userVo = ConvertUtil.convertMapToClass(projectRoleUser, UserVo.class); // 이슈 생성 알림 메일 전송 this.systemEmailService.reservationEmail(new String[]{userVo.getAccount()}, emailType, issueMap); } } } // 이슈 정보를 이메일 전송에 사용하기 위해 Map 형태로 변환한다. public void makeIssueMapToIssue(Issue issue, Map issueMap) { issueMap.put("title", issue.getTitle()); issueMap.put("issueNumber", issue.getIssueNumber()); issueMap.put("issueTypeName", issue.getIssueType().getName()); issueMap.put("issueStatusName", issue.getIssueStatus().getName()); // 담당자 StringBuilder assigneeBuilder = new StringBuilder(); for (IssueUser issueUser : issue.getIssueUsers()) { assigneeBuilder.append(issueUser.getUser().getName()); assigneeBuilder.append("("); assigneeBuilder.append(CommonUtil.decryptAES128(issueUser.getUser().getAccount())); assigneeBuilder.append(")"); assigneeBuilder.append("\n"); } issueMap.put("assignees", assigneeBuilder.toString()); // 담당부서 StringBuilder departsBuilder = new StringBuilder(); for (IssueDepartment issueDepartment : issue.getIssueDepartments()) { departsBuilder.append(issueDepartment.getDepartment().getDepartmentName()); departsBuilder.append("\n"); } issueMap.put("departments", departsBuilder.toString()); // 기간 if (!StringUtils.isEmpty(issue.getStartDate())) { issueMap.put("period", issue.getStartDate() + " ~ " + issue.getCompleteDate()); } issueMap.put("severityName", issue.getSeverity().getName()); issueMap.put("priorityName", issue.getPriority().getName()); issueMap.put("projectName", issue.getProject().getName()); issueMap.put("projectKey", issue.getProject().getProjectKey()); User user = this.userService.getUser(issue.getRegisterId()); StringBuilder registerBuilder = new StringBuilder(); registerBuilder.append(user.getName()); registerBuilder.append("("); registerBuilder.append(CommonUtil.decryptAES128(user.getAccount())); registerBuilder.append(")"); issueMap.put("register", registerBuilder.toString()); Map customField = new HashMap<>(); List issueCustomFieldValueVos = this.issueCustomFieldValueService.findByIssueId(issue.getId()); for (IssueCustomFieldValueVo issueCustomFieldValueVo : issueCustomFieldValueVos) { // 이미 데이터가 존재 if (customField.get(issueCustomFieldValueVo.getCustomFieldVo().getName()) != null) { List useValues = (List) customField.get(issueCustomFieldValueVo.getCustomFieldVo().getName()); useValues.add(issueCustomFieldValueVo.getUseValue()); customField.put(issueCustomFieldValueVo.getCustomFieldVo().getName(), useValues); } else { if (issueCustomFieldValueVo.getCustomFieldVo().getCustomFieldType().equals(INPUT.toString())) { customField.put(issueCustomFieldValueVo.getCustomFieldVo().getName(), issueCustomFieldValueVo.getUseValue()); } else { customField.put(issueCustomFieldValueVo.getCustomFieldVo().getName(), Lists.newArrayList(issueCustomFieldValueVo.getUseValue())); } } } List> customFields = Lists.newArrayList(); Iterator iterator = customField.keySet().iterator(); while (iterator.hasNext()) { String key = iterator.next(); Map result = new HashMap<>(); result.put("name", key); result.put("useValue", customField.get(key)); customFields.add(result); } issueMap.put("customFields", customFields); issueMap.put("description", issue.getDescription()); //업체,ISP,HOSTING 추가 StringBuilder attachedFileBuilder = new StringBuilder(); List attachedFiles = this.attachedFileService.findByIssueId(issue.getId()); for (AttachedFile attachedFile : attachedFiles) { attachedFileBuilder.append(""); attachedFileBuilder.append(attachedFile.getName()); attachedFileBuilder.append(""); attachedFileBuilder.append("\n"); } issueMap.put("attachedFiles", attachedFileBuilder.toString()); } // 제목 유효성 체크 private void verifyTitle(String title) { if (StringUtils.isEmpty(title)) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_NO_TITLE)); } if (title.length() > 300) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_TITLE_MAX_LENGTH_OUT)); } } /** * 날짜 유효성 체크 * @param startDate 시작 일자(문자) * @param completeDate 종료 일자(문자) */ private void checkStartCompleteDate(String startDate, String completeDate) { if (!StringUtils.isEmpty(startDate) && !StringUtils.isEmpty(completeDate)) { Date start = DateUtil.convertStrToDate(startDate, "yy-MM-dd"); Date end = DateUtil.convertStrToDate(completeDate, "yy-MM-dd"); checkStartCompleteDate(start, end); } } /** * 날짜 유효성 체크 * @param start 시작 일자 * @param end 종료 일자 */ private void checkStartCompleteDate(Date start, Date end) { if (start.getTime() > end.getTime()) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.DATE_PICKER_NOT_AVAILABLE)); } } // 텍스트 에디터에 첨부한 파일을 이슈와 연결 private void checkNotHaveIssueIdAttachedFile(Issue issue, IssueForm issueForm) { if (!issueForm.getAttachedFileIds().isEmpty()) { this.attachedFileService.connectIssueIdAttachedFile(issue, issueForm); } } void SetMyDepartmentId(IssueCondition issueCondition){ Long loginId = issueCondition.getLoginUserId(); List myDepartmentIds = Lists.newArrayList(); List myDepartments = this.userDepartmentRepository.findByUserId(loginId); if(myDepartments != null && myDepartments.size() > 0){ for(UserDepartment myDepartment : myDepartments){ myDepartmentIds.add(myDepartment.getDepartmentId()); } } else { myDepartmentIds.add(-1L); } issueCondition.setMyDepartmentIds(myDepartmentIds); } void SetAllDepartmentId(IssueCondition issueCondition){ List departmentIds = Lists.newArrayList(); List> departmentList = this.departmentMapper.find(null); if(departmentList != null && departmentList.size() > 0){ for(Map department : departmentList){ departmentIds.add((Long) department.get("id")); } } issueCondition.setMyDepartmentIds(departmentIds); } void SetWorkflowDepartment(List issueVos){ for(IssueVo issueVo : issueVos){ Long issueTypeId = issueVo.getIssueTypeId(); IssueType issueType = this.issueTypeService.getIssueType(issueTypeId); Long workflowId = issueType.getWorkflow().getId(); List workflowDepartmentList = this.workflowDepartmentRepository.findByWorkflowId(workflowId); List workflowDepartmentIds = Lists.newArrayList(); if(workflowDepartmentList != null && workflowDepartmentList.size()>0){ for(WorkflowDepartment workflowDepartment : workflowDepartmentList){ workflowDepartmentIds.add(workflowDepartment.getDepartment().getId()); } } if(issueVo.getIssueTypeId().equals(issueTypeId)){ issueVo.setWorkflowDepartmentIds(workflowDepartmentIds); } } } // 이슈 목록을 조회한다. @Override @Transactional(readOnly = true) public List findIssue(Map resJsonData, IssueCondition issueCondition, Pageable pageable) { // 검색 조건을 만든다 if (!this.makeIssueSearchCondition(issueCondition, Lists.newArrayList("01", "02", "03"), pageable)) { // 이슈 목록을 찾지 못할 경우 기본 정보로 리턴한다. this.notFoundIssueList(resJsonData, pageable); return Lists.newArrayList(); } Set issueIds = new HashSet<>(); // 사용자 정의 필드 검색시 나오는 이슈 아이디 저장 컬렉션 // 사용자 정의 필드를 사용한 이슈를 찾는다. 만약 이슈가 없다면 여기서 이슈 조회가 끝난다. if (!this.searchUseCustomFields(issueCondition, issueIds, resJsonData, pageable)) { // 이슈 목록을 찾지 못할 경우 기본 정보로 리턴한다. this.notFoundIssueList(resJsonData, pageable); return Lists.newArrayList(); } List issueVos = Lists.newArrayList(); // 이슈 목록 데이터 저장 컬렉션 // 사용자 정의 필드로 검색한 이슈 아이디 값 List issueKeys = Lists.newArrayList(issueIds); issueCondition.setIssueIds(issueKeys); User user = this.webAppUtil.getLoginUserObject(); issueCondition.setLoginUserId(user.getId()); issueCondition.setWorkspaceId(user.getLastWorkspaceId()); List> results = Lists.newArrayList(); Long totalCount = 0L; // UserLevel userLevel = this.userLevelService.getUserLevel(user.getUserLevel().getId()); // if (!this.userWorkspaceService.checkWorkspaceManager(user) // && !MngPermission.checkMngPermission(userLevel.getPermission(), MngPermission.USER_PERMISSION_MNG_ISSUE)) { //최고관리자 & 프로젝트,이슈 관리자 일 경우 모든 이슈 보기 // this.SetMyDepartmentId(issueCondition); //this.SetAllDepartmentId(issueCondition); // } /*else{ // results = this.issueMapper.findByDepartment(issueCondition); // totalCount = this.issueMapper.countByDepartment(issueCondition); // }*/ // StopWatch serviceStart = new StopWatch(); // serviceStart.start(); results = this.issueMapper.find(issueCondition); // serviceStart.stop(); // log.error("result : " + serviceStart.toString()); // serviceStart = new StopWatch(); // serviceStart.start(); totalCount = this.issueMapper.count(issueCondition); // serviceStart.stop(); // log.error("totalCount : " + serviceStart.toString()); int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1; // 이슈 아이디 초기화 issueCondition.setIsApi(issueCondition.getIsApi()); issueCondition.setIssueIds(Lists.newArrayList()); // Map 에 있는 데이터를 IssueVo 데이터로 변환한다. this.setMapToIssueVo(results, issueVos, issueCondition, user); if (issueCondition.getTree()) { this.setParentIssue(issueVos); this.setDownIssues(user, issueVos); this.setRelationIssues(issueVos); } this.setCountDownIssues(issueVos); this.SetWorkflowDepartment(issueVos); //워크플로우에 설정한 담당부서 가져오기 resJsonData.put(Constants.RES_KEY_CONTENTS, issueVos); resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(), totalPage, totalCount)); // 사용자 시스템 기능 사용 정보 수집 log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_FIND)); return issueVos; } // 하위 이슈 세팅(재귀) private void setDownIssues(User user, List issueVos) { for(IssueVo issueVo : issueVos) { List downIssues = this.issueRepository.findByParentIssueId(issueVo.getId()); List downIssueVos = Lists.newArrayList(); IssueCondition issueCondition = new IssueCondition(); issueCondition.addIssueIds(String.valueOf(issueVo.getId())); for(Issue downIssue : downIssues){ IssueVo addIssueVo = ConvertUtil.copyProperties(downIssue, IssueVo.class); addIssueVo.setIssueTypeId(downIssue.getIssueType().getId()); downIssueVos.add(addIssueVo); } issueVo.setIssueDownVos(downIssueVos); if (downIssueVos.size() > 0) { this.setDownIssues(user, downIssueVos); } // 이슈 사용자 정보 추가 //this.setIssueUserList(issueVos, issueCondition); this.setIssueDepartmentList(issueVos, issueCondition, user); // 등록자 정보 추가 this.setRegister(issueVos); // 담당자 정보 셋팅 // 사용자 정의 필드 정보 추가 this.setIssueCustomFieldValue(issueVos, issueCondition); //워크플로우에 설정한 담당부서 가져오기 this.SetWorkflowDepartment(issueVos); } } // 연관 이슈 세팅 private void setRelationIssues(List issueVos) { for(IssueVo issueVo : issueVos) { List relationIssues = this.issueRelationService.findRelationIssue(issueVo.getId()); for(IssueVo relationIssue : relationIssues){ issueVo.addRelationIssueVo(ConvertUtil.copyProperties(relationIssue, IssueVo.class)); } } } // 상위 이슈 체크 private void setParentIssue(List issueVos) { for(IssueVo issueVo : issueVos) { if(issueVo.getParentIssueId() != null) { Issue parentIssue = this.getIssue(issueVo.getParentIssueId()); //issueVo.setParentIssueVo(ConvertUtil.copyProperties(parentIssue, IssueVo.class)); if(parentIssue.getIssueCustomFieldValues() == null || parentIssue.getIssueCustomFieldValues().size() == 0){ issueVo.setIssueCustomFieldValueVos(null); } ConvertUtil.copyProperties(parentIssue, issueVo); } } } @Override @Transactional(readOnly = true) public void setCountDownIssues(List issueVos) { for (IssueVo issueVo : issueVos){ List downIssues = this.issueRepository.findByParentIssueId(issueVo.getId()); //하위이슈 가져오기 if(downIssues != null && downIssues.size() > 0){ //상위이슈 가지고 있는 애들이 있으면 int downIssueAllCount = 0;// 하위이슈 전체 카운트 int downIssueCount = 0;// 하위이슈 미완료 카운트 for(Issue downIssue : downIssues){ downIssueAllCount ++; Long parentIssueId = downIssue.getParentIssue().getId(); Issue parentIssue = this.getIssue(parentIssueId); IssueVo parentIssueVo = ConvertUtil.copyProperties(parentIssue, IssueVo.class); parentIssueVo.setDownIssueAllCount(downIssueAllCount); IssueStatus downIssueStatus = this.issueStatusService.getIssueStatus(downIssue.getIssueStatus().getId()); IssueVo downIssueVo = ConvertUtil.copyProperties(downIssue, IssueVo.class); downIssueVo.setIssueStatusType(downIssueStatus.getIssueStatusType().toString()); if(!downIssueVo.getIssueStatusType().equals("CLOSE")){ //미완료 하위이슈 체크 downIssueCount ++; } issueVo.setDownIssueCount(downIssueCount); issueVo.setDownIssueAllCount(parentIssueVo.getDownIssueAllCount()); } } } } // 이슈 목록을 조회한다(차트용 - 연관일감포함) @Override @Transactional(readOnly = true) public void findApiIssue(ApiMonitorCondition apiMonitorCondition, Map resJsonData) { IssueTypeCondition issueTypeCondition = new IssueTypeCondition(); issueTypeCondition.setIsApi(Issue.IS_API_YES); List issueTypes = this.issueTypeService.findIssueType(issueTypeCondition); // 검색 일자를 구한다. List searchDates = Lists.newArrayList(); if (apiMonitorCondition.getSearchPeriod().equals(DateUtil.CUSTOM_INPUT)) { Date startDate = DateUtil.convertStrToDate(apiMonitorCondition.getSearchStartDate(), "yyyy-MM-dd"); Date endDate = DateUtil.addDays(DateUtil.convertStrToDate(apiMonitorCondition.getSearchEndDate(), "yyyy-MM-dd"), 1); searchDates = CommonUtil.findSearchPeriod(startDate, endDate); } else { searchDates = CommonUtil.findSearchPeriod(apiMonitorCondition.getSearchPeriod()); } // 날짜가 검색되지 않았으면 오류 if (searchDates.size() < 1) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.WIDGET_SEARCH_DATE_NOT_FOUND)); } Long index = 1L; List apiMonitorVos = Lists.newArrayList(); for (Date date : searchDates) { String onlyDate = DateUtil.convertDateToYYYYMMDD(date); issueTypeCondition.setStartDate(onlyDate + " 00:00:00"); issueTypeCondition.setEndDate(onlyDate + " 23:59:59"); ApiMonitorVo apiMonitorVo = new ApiMonitorVo(); apiMonitorVo.setId(index); for (IssueTypeVo issueTypeVo : issueTypes) { issueTypeCondition.setId(issueTypeVo.getId()); apiMonitorVo.addIssueTypeCount(this.issueMapper.countByIssueTypeIdAndDate(issueTypeCondition)); apiMonitorVo.setIsApi(Issue.IS_API_YES); apiMonitorVos.add(apiMonitorVo); } index++; } Map data = new HashMap<>(); data.put("issueTypeVos", issueTypes); data.put("apiMonitorVos", apiMonitorVos); resJsonData.put(Constants.RES_KEY_CONTENTS, data); } // 이슈 목록을 조회한다(차트용 - 연관일감포함) @Override @Transactional(readOnly = true) public List findChartIssue(Map resJsonData, IssueCondition issueCondition, Pageable pageable) { // 검색 조건을 만든다 if (!this.makeIssueSearchCondition(issueCondition,Lists.newArrayList("01", "02", "03"), pageable)) { // 이슈 목록을 찾지 못할 경우 기본 정보로 리턴한다. this.notFoundIssueList(resJsonData, pageable); return Lists.newArrayList(); } Set issueIds = new HashSet<>(); // 사용자 정의 필드 검색시 나오는 이슈 아이디 저장 컬렉션 // 사용자 정의 필드를 사용한 이슈를 찾는다. 만약 이슈가 없다면 여기서 이슈 조회가 끝난다. if (!this.searchUseCustomFields(issueCondition, issueIds, resJsonData, pageable)) { // 이슈 목록을 찾지 못할 경우 기본 정보로 리턴한다. this.notFoundIssueList(resJsonData, pageable); return Lists.newArrayList(); } // 튜닝 전 - 1.3 / 1.2 / 1.1 // 튜닝 후 (단일/다중 검색 조건 3개 기준) - 0.49 / 0.41 / 0.47 / 0.41 List issueVos = Lists.newArrayList(); // 이슈 목록 데이터 저장 컬렉션 // 사용자 정의 필드로 검색한 이슈 아이디 값 List issueKeys = Lists.newArrayList(issueIds); issueCondition.setIssueIds(issueKeys); List> results = this.issueMapper.find(issueCondition); int totalCount = results.size(); int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1; // 이슈 아이디 초기화 issueCondition.setIssueIds(Lists.newArrayList()); // Map 에 있는 데이터를 IssueVo 데이터로 변환한다. this.setMapToIssueVoForChart(results, issueVos, issueCondition); resJsonData.put(Constants.RES_KEY_CONTENTS, issueVos); resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(), totalPage, totalCount)); // 사용자 시스템 기능 사용 정보 수집 log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_FIND)); return issueVos; } // 이슈 목록을 조회한다(차트용 - 연관일감) @Override @Transactional(readOnly = true) public List findChartIssue(Map resJsonData, ProjectCondition projectCondition, Pageable pageable) { IssueCondition issueCondition = new IssueCondition(); // 검색 조건을 만든다 User user = this.webAppUtil.getLoginUserObject(); if (!this.makeIssueSearchCondition(user, issueCondition, projectCondition, pageable)) { // 이슈 목록을 찾지 못할 경우 기본 정보로 리턴한다. this.notFoundIssueList(resJsonData, pageable); return Lists.newArrayList(); } Set issueIds = new HashSet<>(); // 사용자 정의 필드 검색시 나오는 이슈 아이디 저장 컬렉션 List issueVos = Lists.newArrayList(); // 이슈 목록 데이터 저장 컬렉션 // 사용자 정의 필드로 검색한 이슈 아이디 값 List issueKeys = Lists.newArrayList(issueIds); issueCondition.setIssueIds(issueKeys); List> results = this.issueMapper.find(issueCondition); int totalCount = results.size(); int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1; // 이슈 아이디 초기화 issueCondition.setIssueIds(Lists.newArrayList()); // Map 에 있는 데이터를 IssueVo 데이터로 변환한다. this.setMapToIssueVoForChart(results, issueVos, issueCondition); resJsonData.put(Constants.RES_KEY_CONTENTS, issueVos); resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(), totalPage, totalCount)); // 사용자 시스템 기능 사용 정보 수집 log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_FIND)); return issueVos; } // Map 에 있는 데이터를 IssueVo 데이터로 변환한다. 차트용 private void setMapToIssueVoForChart(List> results, List issueVos, IssueCondition issueCondition) { for (Map result : results) { IssueVo issueVo = ConvertUtil.convertMapToClass(result, IssueVo.class); issueVos.add(issueVo); issueCondition.addIssueIds(String.valueOf(issueVo.getId())); } for (IssueVo issueVo : issueVos) { this.setRelationIssue(issueVo, issueVo.getId()); } } // Map 에 있는 데이터를 IssueVo 데이터로 변환한다. private void setMapToIssueVo(List> results, List issueVos, IssueCondition issueCondition, User user) { for (Map result : results) { IssueVo issueVo = ConvertUtil.convertMapToClass(result, IssueVo.class); if (MapUtil.getString(result, "inheritPartners") != null && MapUtil.getString(result, "inheritPartners").equals("1")) { issueVo.setInheritPartners(true); } issueVos.add(issueVo); issueCondition.addIssueIds(String.valueOf(issueVo.getId())); } // 이슈 사용자 정보 추가 //this.setIssueUserList(issueVos, issueCondition); this.setIssueDepartmentList(issueVos, issueCondition, user); // 등록자 정보 추가 this.setRegister(issueVos); // 담당자 정보 셋팅 // 사용자 정의 필드 정보 추가 this.setIssueCustomFieldValue(issueVos, issueCondition); } // 검색 조건을 만든다 private boolean makeIssueSearchCondition(IssueCondition condition, List projectStatues, Pageable pageable) { if (pageable != null) { condition.setPage(pageable.getPageNumber() * pageable.getPageSize()); condition.setPageSize(pageable.getPageSize()); } condition.setLoginUserId(this.webAppUtil.getLoginId()); condition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId()); User user = this.webAppUtil.getLoginUserObject(); UserLevel userLevel = this.userLevelService.getUserLevel(user.getUserLevel().getId()); // 프로젝트 키가 존재할 경우 프로젝트 키에 해당하는 프로젝트를 조회하고 검색 조건에 셋팅한다. if (!this.getProjectByProjectKey(condition.getProjectKey(), condition)) { return false; } // 프로젝트를 선택하지 않았으면 해당 업무 공간에서 참여하고 있는 프로젝트를 찾는다. if (condition.getProjectIds().size() < 1) { List> projects = Lists.newArrayList(); if (this.userWorkspaceService.checkWorkspaceManager(user) || MngPermission.checkMngPermission(userLevel.getPermission(), MngPermission.USER_PERMISSION_MNG_PROJECT)){ return true; }/*else if (MngPermission.checkMngPermission(userLevel.getPermission(), MngPermission.USER_PERMISSION_MNG_ISSUE)){ projects = this.projectService.findByWorkspaceIdAndIncludeProjectAll(projectStatues, condition.getProjectType()); }*/ else { projects = this.projectService.findByWorkspaceIdAndIncludeProject(projectStatues, condition.getProjectType()); } List projectIds = Lists.newArrayList(); for (Map result : projects) { Long projectId = MapUtil.getLong(result, "id"); if (projectId != null) { projectIds.add(projectId); } } condition.setProjectIds(projectIds); if (projectIds.size() < 1) { return false; } } return true; } // 검색 조건을 만든다 private boolean makeIssueSearchCondition(User user, IssueCondition condition, ProjectCondition projectCondition, Pageable pageable) { if (pageable != null) { condition.setPage(pageable.getPageNumber() * pageable.getPageSize()); condition.setPageSize(pageable.getPageSize()); } condition.setLoginUserId(this.webAppUtil.getLoginId()); condition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId()); projectCondition.setWorkspaceId(condition.getWorkspaceId()); // 프로젝트 키가 존재할 경우 프로젝트 키에 해당하는 프로젝트를 조회하고 검색 조건에 셋팅한다. if (!this.getProjectByProjectKey(condition.getProjectKey(), condition)) { return false; } // 프로젝트를 선택하지 않았으면 해당 업무 공간에서 참여하고 있는 프로젝트를 찾는다. if (condition.getProjectIds().size() < 1) { List> projects = null; UserLevel userLevel = this.userLevelService.getUserLevel(user.getUserLevel().getId()); if (this.userWorkspaceService.checkWorkspaceManager(user) || (MngPermission.checkMngPermission(userLevel.getPermission(), MngPermission.USER_PERMISSION_MNG_PROJECT) && MngPermission.checkMngPermission(userLevel.getPermission(), MngPermission.USER_PERMISSION_MNG_ISSUE))) { projects = this.projectMapper.findByWorkspaceManagerAll(projectCondition); } else { projects = this.projectService.findByWorkspaceIdAndIncludeProjectAll(projectCondition); } List projectIds = Lists.newArrayList(); for (Map result : projects) { Long projectId = MapUtil.getLong(result, "id"); if (projectId != null) { projectIds.add(projectId); } } condition.setProjectIds(projectIds); if (projectIds.size() < 1) { return false; } } return true; } // 프로젝트 키가 존재할 경우 프로젝트 키에 해당하는 프로젝트를 조회하고 검색 조건에 셋팅한다. private boolean getProjectByProjectKey(String projectKey, IssueCondition condition) { if (!StringUtils.isEmpty(projectKey)) { Project project = this.projectService.findByProjectKey(projectKey); if (project != null) { // 이미 프로젝트를 선택했을 경우에 프로젝트키로 검색한 프로젝트가 포함되어 있지 않으면 false if (condition.getProjectIds().size() > 0) { if (condition.getProjectIds().contains(project.getId())) { condition.setProjectIds(Lists.newArrayList()); } else { return false; } } condition.addProjectIds(project.getId()); } else { return false; } } return true; } // 이슈 목록을 찾지 못할 경우 기본 정보로 리턴한다. private void notFoundIssueList(Map resJsonData, Pageable pageable) { resJsonData.put(Constants.RES_KEY_CONTENTS, Lists.newArrayList()); resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(), 0, 0)); } // 사용자 정의 필드를 사용한 이슈를 찾는다. 만약 이슈가 없다면 여기서 이슈 조회가 끝난다. private boolean searchUseCustomFields(IssueCondition condition, Set issueIds, Map resJsonData, Pageable pageable) { // 사용자 정의 필드 값이 검색 옵션으로 있었을 때 조회된 이슈가 없으면 조회를 더 이상 할 필요가 없다. // 사용자 정의 필드를 사용한 이슈를 찾는다. boolean customFieldSearch = this.issueCustomFieldValueService.find(condition, issueIds); // 사용자 정의 필드 값이 존재하여 검색을 했을 때 검색된 이슈가 없으면 여기서 종료한다. if (customFieldSearch && issueIds.size() < 1) { resJsonData.put(Constants.RES_KEY_CONTENTS, Lists.newArrayList()); if (pageable != null) { resJsonData.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(), 0, 0)); } return false; } return true; } // 이슈 담당자 정보를 셋팅한다. private void setIssueUserList(List issueVos, IssueCondition issueCondition, User user) { if (issueVos.size() < 1) { return; } List> issueUsers = this.issueMapper.findIssueUser(issueCondition); Map issueConverterUsers = new HashMap<>(); // 이슈에 해당하는 이슈 담당자 정보 셋팅 for (Map issueUser : issueUsers) { String issueId = MapUtil.getString(issueUser, "issueId"); if (MapUtil.getObject(issueConverterUsers, issueId) != null) { List users = (List) MapUtil.getObject(issueConverterUsers, issueId); users.add(new UserVo(MapUtil.getLong(issueUser, "id"), MapUtil.getString(issueUser, "name"), CommonUtil.decryptAES128(MapUtil.getString(issueUser, "account")), MapUtil.getString(issueUser, "profile"))); } else { List users = Lists.newArrayList(new UserVo(MapUtil.getLong(issueUser, "id"), MapUtil.getString(issueUser, "name"), CommonUtil.decryptAES128(MapUtil.getString(issueUser, "account")), MapUtil.getString(issueUser, "profile"))); issueConverterUsers.put(issueId, users); } } // 이슈Vo에 담당자 정보를 셋팅 for (IssueVo issueVo : issueVos) { if (MapUtil.getObject(issueConverterUsers, String.valueOf(issueVo.getId())) != null) { List userVos = (List) MapUtil.getObject(issueConverterUsers, String.valueOf(issueVo.getId())); issueVo.setUserVos(userVos); } // 이슈 수정 권한을 갖고 있는지 확인 if (this.checkHasPermission(issueVo, issueVo.getUserVos(), user, null)) { issueVo.setModifyPermissionCheck(Boolean.TRUE); } } } // 이슈 담당부서 정보를 셋팅한다. private void setIssueDepartmentList(List issueVos, IssueCondition issueCondition, User user) { if (issueVos.size() < 1) { return; } List> issueDepartments = this.issueMapper.findIssueDepartment(issueCondition); Map issueConverterDepartments = new HashMap<>(); // 이슈에 해당하는 이슈 담당부서 정보 셋팅 for (Map issueDepartment : issueDepartments) { String issueId = MapUtil.getString(issueDepartment, "issueId"); if (MapUtil.getObject(issueConverterDepartments, issueId) != null) { List departments = (List) MapUtil.getObject(issueConverterDepartments, issueId); departments.add(new DepartmentVo(MapUtil.getLong(issueDepartment, "id"), MapUtil.getString(issueDepartment, "departmentName"), MapUtil.getString(issueDepartment, "departmentDescription"))); } else { List departments = Lists.newArrayList(new DepartmentVo(MapUtil.getLong(issueDepartment, "id"), MapUtil.getString(issueDepartment, "departmentName"), MapUtil.getString(issueDepartment, "departmentDescription"))); issueConverterDepartments.put(issueId, departments); } } // 이슈Vo에 담당부서 정보를 셋팅 for (IssueVo issueVo : issueVos) { if (MapUtil.getObject(issueConverterDepartments, String.valueOf(issueVo.getId())) != null) { List departmentVos = (List) MapUtil.getObject(issueConverterDepartments, String.valueOf(issueVo.getId())); issueVo.setDepartmentVos(departmentVos); } // 이슈 수정 권한을 갖고 있는지 확인 if (this.checkHasPermission(issueVo, issueVo.getUserVos(), user, issueVo.getDepartmentVos())) { issueVo.setModifyPermissionCheck(Boolean.TRUE); } } } // 이슈 상세 정보를 조회한다. @Override @Transactional(readOnly = true) public void detailIssue(Map resJsonData, IssueCondition issueCondition) { IssueVo issueVo = new IssueVo(); Pageable relPageable = issueCondition.getRelPageable(); Pageable downPageable = issueCondition.getDownPageable(); if (issueCondition.getId() != null) { Issue issue = this.getIssue(issueCondition.getId()); issueVo = ConvertUtil.copyProperties(issue, IssueVo.class); User user = this.webAppUtil.getLoginUserObject(); if (relPageable != null) { issueVo.setRelPageNumber(relPageable.getPageNumber()); issueVo.setRelPageSize(relPageable.getPageSize()); issueVo.setRelPage(relPageable.getPageNumber() * relPageable.getPageSize()); } if (downPageable != null) { issueVo.setDownPageNumber(downPageable.getPageNumber()); issueVo.setDownPage(downPageable.getPageNumber() * downPageable.getPageSize()); issueVo.setDownPageSize(downPageable.getPageSize()); } switch (issueCondition.getDeep()) { case "01": // 프로젝트, 이슈 유형, 이슈 상태, 우선순위, 중요도, 담당부서, 첨부파일, 사용자 정의 필드 정보를 셋팅한다. issueVo.setProjectVo(ConvertUtil.copyProperties(issue.getProject(), ProjectVo.class)); issueVo.setIssueTypeVo(ConvertUtil.copyProperties(issue.getIssueType(), IssueTypeVo.class)); issueVo.setIssueStatusVo(ConvertUtil.copyProperties(issue.getIssueStatus(), IssueStatusVo.class)); issueVo.setPriorityVo(ConvertUtil.copyProperties(issue.getPriority(), PriorityVo.class)); issueVo.setSeverityVo(ConvertUtil.copyProperties(issue.getSeverity(), SeverityVo.class)); this.setRegister(issue, issueVo); // 등록자 정보 셋팅 //this.setIssueUser(issue, issueVo); // 담당자 정보 셋팅 this.setIssueDepartment(issue, issueVo); // 담당부서 정보 셋팅 this.setAttachedFiles(issue, issueVo); // 첨부 파일 정보 셋팅 this.setIssueCustomFields(issue, issueVo); // 사용자 정의 필드 값 정보 셋팅 this.setRelationIssue(issue, issueVo); //연관 일감 셋팅 this.setDownIssues(issue, issueVo, issueCondition.getHideCompleteIssue()); //하위 이슈 세팅 break; case "02": // 프로젝트, 이슈 유형, 이슈 상태, 우선순위, 중요도, 담당자, 첨부파일, 사용자 정의 필드 정보, 댓글, 기록을 셋팅한다. if (issueCondition.getHideCompleteIssue() == null) { issueCondition.setHideCompleteIssue(true); } this.setIssueDetail(issueVo, issue, user, issueCondition.getHideCompleteIssue()); // 이슈 상세 정보를 셋팅한다. this.setIssueTableConfigs(issue, issueVo, issueCondition); issueVo.setProjectVo(ConvertUtil.copyProperties(issue.getProject(), ProjectVo.class)); break; } } // 사용자 시스템 기능 사용 정보 수집 log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_DETAIL)); resJsonData.put(Constants.RES_KEY_CONTENTS, issueVo); } // 테이블 설정 셋팅 private void setIssueTableConfigs(Issue issue, IssueVo issueVo, IssueCondition issueCondition) { //Long IssueTypeId = issue.getIssueType().getId(); Long IssueTypeId = issueCondition.getIssueTypeId(); for (int tableConfigType : IssueTableConfig.IssueTableTypes) { if (tableConfigType != IssueTableConfig.ISSUE_TABLE_TYPE_MAIN) { issueVo.addIssueTableConfigVo(createIssueTableConfigVo(IssueTypeId, tableConfigType)); } } } private IssueTableConfigVo createIssueTableConfigVo(Long issueTypeId, int tableConfigType) { IssueTableConfig issueTableConfig = this.issueTableConfigService.findByUserIdAndWorkspaceIdAndIssueTypeIdAndIssueTableType(issueTypeId, tableConfigType); if (issueTableConfig != null) { return ConvertUtil.copyProperties(issueTableConfig, IssueTableConfigVo.class); } return new IssueTableConfigVo(); } // 하위 이슈 정보를 셋팅한다 private void setDownIssues(Issue issue, IssueVo issueVo, boolean hideCompleteIssue) { Page downIssues = null; List downIssueList = this.issueRepository.findByParentIssueId(issue.getId()); if(downIssueList != null && downIssueList.size() > 0) { int startPage = 0; if (issueVo.getDownPage() != 0) { startPage = (int) Math.floor(issueVo.getDownPage()/issueVo.getDownPageSize()); } Pageable pageable = PageRequest.of(startPage, issueVo.getDownPageSize()); if(hideCompleteIssue){ downIssues = this.issueRepository.findByParentIssueId(issue.getId(), IssueStatusType.CLOSE, pageable); }else { downIssues = this.issueRepository.findByParentIssueId(issue.getId(), pageable); } } if(downIssues != null){ issueVo.setDownTotalPage(downIssues.getTotalPages()); issueVo.setDownTotalCount(downIssues.getTotalElements()); List resultList = new ArrayList<>(); for(Issue downIssue : downIssues){ IssueVo downIssueVo = ConvertUtil.copyProperties(downIssue, IssueVo.class); downIssueVo.setIssueTypeVo(ConvertUtil.copyProperties(downIssue.getIssueType(), IssueTypeVo.class)); downIssueVo.setPriorityVo(ConvertUtil.copyProperties(downIssue.getPriority(), PriorityVo.class)); downIssueVo.setSeverityVo(ConvertUtil.copyProperties(downIssue.getSeverity(), SeverityVo.class)); //이슈 상태 추가 IssueStatusVo issueStatusVo = ConvertUtil.copyProperties(downIssue.getIssueStatus(), IssueStatusVo.class, "issueStatusType"); issueStatusVo.setIssueStatusType(downIssue.getIssueStatus().getIssueStatusType().toString()); downIssueVo.setIssueStatusVo(issueStatusVo); this.setRegister(downIssue, downIssueVo); // 등록자 this.setIssueDepartment(downIssue, downIssueVo); // 담당부서 정보 셋팅 this.setIssueCustomFields(downIssue, downIssueVo); // 사용자정의필드 정보 세팅 this.setIssueHistory(downIssue, downIssueVo); // 이슈 기록 정보 셋팅 this.setIssueComments(downIssue, downIssueVo); // 댓글 정보 셋팅 this.setIssueCompanyField(downIssue, downIssueVo); //업체 정보 세팅 this.setIssueIspField(downIssue, downIssueVo); //ISP 정보 세팅 this.setIssueHostingField(downIssue, downIssueVo); //HOSTING 정보 세팅 downIssueVo.setModifyPermissionCheck(issueVo.getModifyPermissionCheck()); resultList.add(downIssueVo); } issueVo.setIssueDownVos(resultList); } } // 이슈 상세 정보를 셋팅한다. @Override @Transactional(readOnly = true) public void setIssueDetail(IssueVo issueVo, Issue issue, User user) { setIssueDetail(issueVo, issue, user, false); } @Override @Transactional(readOnly = true) public void setIssueDetail(IssueVo issueVo, Issue issue, User user, boolean hideCompleteIssue) { // 이슈 수정 권한을 갖고 있는지 확인 if (this.checkHasPermission(issueVo, issueVo.getUserVos(), user, issueVo.getDepartmentVos())) { issueVo.setModifyPermissionCheck(Boolean.TRUE); } issueVo.setProjectVo(ConvertUtil.copyProperties(issue.getProject(), ProjectVo.class)); issueVo.setIssueTypeVo(ConvertUtil.copyProperties(issue.getIssueType(), IssueTypeVo.class)); IssueStatusVo issueStatusVo = ConvertUtil.copyProperties(issue.getIssueStatus(), IssueStatusVo.class, "issueStatusType"); issueStatusVo.setIssueStatusType(issue.getIssueStatus().getIssueStatusType().toString()); issueVo.setIssueStatusVo(issueStatusVo); issueVo.setPriorityVo(ConvertUtil.copyProperties(issue.getPriority(), PriorityVo.class)); issueVo.setSeverityVo(ConvertUtil.copyProperties(issue.getSeverity(), SeverityVo.class)); this.setRegister(issue, issueVo); // 등록자 정보 셋팅 //this.setIssueUser(issue, issueVo); // 담당자 정보 셋팅 this.setIssueDepartment(issue, issueVo); // 담당부서 정보 셋팅 this.setAttachedFiles(issue, issueVo); // 첨부 파일 정보 셋팅 this.setIssueCustomFields(issue, issueVo); // 사용자 정의 필드 값 정보 셋팅 this.setRelationIssue(issue, issueVo); //연관 일감 셋팅 this.setDownIssues(issue, issueVo, hideCompleteIssue); //하위 일감 세팅 this.setIssueComments(issue, issueVo); // 댓글 정보 셋팅 this.setIssueHistory(issue, issueVo); // 이슈 기록 정보 셋팅 IssueType issueType = this.issueTypeService.getIssueType(issueVo.getIssueTypeVo().getId()); // 이슈의 이슈유형 객체 Integer using = issueType.getUsePartner() != null ? issueType.getUsePartner().intValue() : 0; // 이슈유형별로 사용중인 업체/ISP/호스팅 값 List usePartnerVos = Lists.newArrayList(); for (Integer usePartner : UsePartner.partners) { //1(업체), 2(ISP), 4(호스팅) UsePartnerVo usePartnerVo = UsePartner.checkUsePartner(using, usePartner); if (usePartnerVo != null) { usePartnerVos.add(usePartnerVo); //useCompanyVo.setValues(); } issueVo.setUsePartnerVos(usePartnerVos); } this.setIssueCompanyField(issue, issueVo); //업체 정보 세팅 this.setIssueIspField(issue, issueVo); //ISP 정보 세팅 this.setIssueHostingField(issue, issueVo); //HOSTING 정보 세팅 this.setParentIssue(issue,issueVo); //상위 이슈 정보 세팅 } // 상위일감 정보 추가 private void setParentIssue(Issue issue, IssueVo issueVo) { if(issue.getParentIssue() != null){ issueVo.setParentIssueVo(ConvertUtil.copyProperties(issue.getParentIssue(), IssueVo.class)); } } // 등록자 정보 추가 private void setRegister(List issueVos) { for (IssueVo issueVo : issueVos) { // 사용자 패스워드를 삭제하고 사용자 계정을 복호화한다. issueVo.setRegisterVo(this.userService.removeSensitiveUser(issueVo.getRegisterId())); } } // 이슈 등록자 정보를 셋팅한다. private void setRegister(Issue issue, IssueVo issueVo) { UserVo userVo = this.userService.removeSensitiveUser(issue.getRegisterId()); issueVo.setRegisterVo(userVo); // 등록자는 항상 수정 가능. if (userVo.getId().equals(this.webAppUtil.getLoginId())) { issueVo.setModifyPermissionCheck(Boolean.TRUE); } } // 연관 이슈 정보를 셋팅한다 private void setRelationIssue(Issue issue, IssueVo issueVo) { //Set issueRelations = issue.getIssueRelations(); List> results = this.issueRelationMapper.findByIssueId(issueVo); Long totalCount = this.issueRelationMapper.count(issueVo); if (issue != null && issueVo != null && results.size() > 0) { int totalPage = (int) Math.ceil((totalCount - 1) / issueVo.getRelPageSize()) + 1; issueVo.setRelTotalPage(totalPage); issueVo.setRelTotalCount(totalCount); for (Map result : results) { IssueRelationVo issueRelationVo = ConvertUtil.convertMapToClass(result, IssueRelationVo.class); Issue relationIssue = this.findOne(MapUtil.getLong(result, "relationIssueId")); IssueVo relIssueVo = ConvertUtil.copyProperties(relationIssue, IssueVo.class); Project project = this.projectService.getProject(relationIssue.getProject().getId()); relIssueVo.setProjectId(project.getId()); relIssueVo.setProjectKey(project.getProjectKey()); relIssueVo.setIssueNumber(relationIssue.getIssueNumber()); issueRelationVo.setIssueRelation(relIssueVo); issueRelationVo.setTitle(relationIssue.getTitle()); issueRelationVo.setIssueTypeVo(ConvertUtil.copyProperties(relationIssue.getIssueType(), IssueTypeVo.class)); issueRelationVo.setPriorityVo(ConvertUtil.copyProperties(relationIssue.getPriority(), PriorityVo.class)); issueRelationVo.setSeverityVo(ConvertUtil.copyProperties(relationIssue.getSeverity(), SeverityVo.class)); //이슈 상태 추가 IssueStatusVo issueStatusVo = ConvertUtil.copyProperties(relationIssue.getIssueStatus(), IssueStatusVo.class, "issueStatusType"); issueStatusVo.setIssueStatusType(relationIssue.getIssueStatus().getIssueStatusType().toString()); issueRelationVo.setIssueStatusVo(issueStatusVo); issueRelationVo.setModifyPermissionCheck(issueVo.getModifyPermissionCheck()); this.setRegister(relationIssue, relIssueVo); // 등록자 this.setIssueDepartment(relationIssue, relIssueVo); // 담당부서 정보 셋팅 this.setIssueCustomFields(relationIssue, relIssueVo); // 사용자정의필드 정보 세팅 Set issueCompanies = relationIssue.getIssueCompanies(); Iterator itrCompany = issueCompanies.iterator(); while (itrCompany.hasNext()) { issueRelationVo.addIssueCompanyVo(ConvertUtil.copyProperties(itrCompany.next(), IssueCompanyVo.class)); } Set issueIsps = relationIssue.getIssueIspFields(); Iterator itrIsp = issueIsps.iterator(); while (itrIsp.hasNext()) { issueRelationVo.addIssueIspVo(ConvertUtil.copyProperties(itrIsp.next(), IssueIspVo.class)); } Set issueHostings = relationIssue.getIssueHostingFields(); Iterator itrHosting = issueHostings.iterator(); while (itrHosting.hasNext()) { issueRelationVo.addIssueHostingVo(ConvertUtil.copyProperties(itrHosting.next(), IssueHostingVo.class)); } issueVo.addIssueRelationVo(issueRelationVo); } } else { issue.clearIssueRelations(); } } // 이슈 담당자 정보를 셋팅한다. /*private void setIssueUser(Issue issue, IssueVo issueVo) { List userVos = Lists.newArrayList(); for (IssueUser issueUser : issue.getIssueUsers()) { UserVo userVo = ConvertUtil.copyProperties(issueUser.getUser(), UserVo.class, "password"); userVo.setByName(userVo.getName() + "(" + CommonUtil.decryptAES128(userVo.getAccount()) + ")"); userVo.setAccount(CommonUtil.decryptAES128(userVo.getAccount())); userVos.add(userVo); // 담당자가 있을 경우 담당자만 수정 가능. if (userVo.getId().equals(this.webAppUtil.getLoginId())) { issueVo.setModifyPermissionCheck(Boolean.TRUE); } } // 스케쥴러에서 실행될 경우 오류 발생하므로 권한 체크하지 않는다. if (this.webAppUtil.getLoginId() != null) { // 업무 공간 관리자일 경우 수정 권한을 갖는다. if (this.userWorkspaceService.checkWorkspaceManager()) { issueVo.setModifyPermissionCheck(Boolean.TRUE); } // 프로젝트 관리자일 경우 해당 프로젝트에 등록된 이슈는 수정 권한을 갖는다. if (this.projectRoleUserService.checkProjectManager(issue.getProject())) { issueVo.setModifyPermissionCheck(Boolean.TRUE); } } // 담당자가 없으면 모든 사용자가 수정 가능. if (issue.getIssueUsers().size() < 1) { issueVo.setModifyPermissionCheck(Boolean.TRUE); } issueVo.setUserVos(userVos); }*/ // 이슈 담당부서 정보를 셋팅한다. private void setIssueDepartment(Issue issue, IssueVo issueVo) { List departmentVos = Lists.newArrayList(); for (IssueDepartment issueDepartment : issue.getIssueDepartments()) { DepartmentVo departmentVo = ConvertUtil.copyProperties(issueDepartment.getDepartment(), DepartmentVo.class); departmentVo.setByName(departmentVo.getDepartmentName()); departmentVos.add(departmentVo); List userDepartments = this.userDepartmentRepository.findByDepartmentId(departmentVo.getId()); if (userDepartments != null && userDepartments.size() > 0) { for (UserDepartment userDepartment : userDepartments) { if (userDepartment.getUserId().equals(this.webAppUtil.getLoginId())){ issueVo.setModifyPermissionCheck(Boolean.TRUE); } } } } issueVo.setDepartmentVos(departmentVos); } // 이슈 첨부파일 정보를 셋팅한다. private void setAttachedFiles(Issue issue, IssueVo issueVo) { List attachedFileVos = Lists.newArrayList(); for (AttachedFile attachedFile : issue.getAttachedFiles()) { AttachedFileVo attachedFileVo = ConvertUtil.copyProperties(attachedFile, AttachedFileVo.class, "fileType"); attachedFileVo.setFileType(attachedFile.getFileType().toString()); attachedFileVos.add(attachedFileVo); } issueVo.setAttachedFileVos(attachedFileVos); } // 이슈(이슈 유형)에 연결된 사용자 정의 필드 정보를 셋팅한다. private IssueVo setIssueCustomFields(Issue issue, IssueVo issueVo) { // 해당 프로젝트의 이슈 유형에 연결된 사용자 정의 필드 정보를 가져온다. IssueTypeCustomFieldCondition issueTypeCustomFieldCondition = new IssueTypeCustomFieldCondition(); issueTypeCustomFieldCondition.setProjectId(issue.getProject().getId()); issueTypeCustomFieldCondition.setIssueTypeId(issue.getIssueType().getId()); List issueTypeCustomFieldVos = this.issueTypeCustomFieldService.findIssueTypeCustomField(new HashMap<>(), issueTypeCustomFieldCondition); issueVo.setIssueTypeCustomFieldVos(issueTypeCustomFieldVos); // 이슈에서 사용된 사용자 정의 필드 값을 가져온다. List issueCustomFieldValueVos = this.issueCustomFieldValueService.findByIssueId(issue.getId()); issueVo.setIssueCustomFieldValueVos(issueCustomFieldValueVos); return issueVo; } // 이슈에 등록된 댓글 정보를 셋팅한다. private void setIssueComments(Issue issue, IssueVo issueVo) { issueVo.setIssueCommentVos(this.issueCommentService.findIssueComment(issue.getId())); } // 이슈 기록 정보를 셋팅한다. private void setIssueHistory(Issue issue, IssueVo issueVo) { issueVo.setIssueHistoryVos(this.issueHistoryService.findIssueHistory(issue)); } // 사용자 정의 필드 값이 같은 이슈 찾기 @Override @Transactional public List findIssue(IssueApiForm issueApiform) { List issueCustomFieldValueForms = issueApiform.getIssueCustomFieldValues(); List resultIssueVos = Lists.newArrayList(); String comma = ","; List userValues = Lists.newArrayList(); if (issueCustomFieldValueForms.size() > 0) { IssueCustomFieldValueFormComparator comp = new IssueCustomFieldValueFormComparator(); Collections.sort(issueCustomFieldValueForms, comp); String concatUseValue = ""; for (int i = 0; i < issueCustomFieldValueForms.size(); i++) { IssueCustomFieldValueForm issueCustomFieldValueForm = issueCustomFieldValueForms.get(i); userValues.add(issueCustomFieldValueForm.getUseValue()); if (i > 0) { concatUseValue = concatUseValue.concat(comma); } concatUseValue = concatUseValue.concat(issueCustomFieldValueForm.getUseValue()); } IssueCustomFieldValueCondition issueCustomFieldValueCondition = new IssueCustomFieldValueCondition(); issueCustomFieldValueCondition.setUseValue(concatUseValue); issueCustomFieldValueCondition.setUseValues(userValues); issueCustomFieldValueCondition.setIssueTypeId(issueApiform.getIssueTypeId()); issueCustomFieldValueCondition.setIssueStatusType("CLOSE"); List> results = this.issueMapper.findByCustomFieldValue(issueCustomFieldValueCondition); if (results != null && results.size() > 0) { for (Map result : results) { resultIssueVos.add(this.getIssue(MapUtil.getLong(result, "id"))); } } } return resultIssueVos; } // 리스트에서 해당 아이디를 가지고 있는 이슈 검색 private IssueVo findIssueVo(List list, Long id) { for (IssueVo issueVo : list) { if (id.equals(issueVo.getId())) { return issueVo; } } return null; } // 이슈를 수정한다(api용) @Override @Transactional public List modifyIssue(IssueApiForm issueApiForm, List files) { User user = this.convertToUser(issueApiForm.getToken()); IssueForm issueForm = this.convertToIssueForm(issueApiForm, user); List issue = this.findIssue(issueApiForm); if (issue != null && issue.size() > 0) { List issues = Lists.newArrayList(); for (Issue issueVo : issue) { issueForm.setId(issueVo.getId()); issueForm.setTitle(issueVo.getTitle()); // 자동 종료 상태 설정이 되어 있지 않으면 오류발생 Issue modifyIssue = this.modifyIssueForApi(user, issueForm, files); Issue parentIssue = modifyIssue.getParentIssue(); IssueType issueType = modifyIssue.getIssueType(); Set issueTypeApiEndStatuses = issueType.getIssueTypeApiEndStatuses(); IssueTypeApiEndStatus issueStatus = null; if (issueTypeApiEndStatuses != null && issueTypeApiEndStatuses.size() > 0) { issueStatus = issueTypeApiEndStatuses.iterator().next(); } else { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_COMPLETE_ISSUE_STATUS_NOT_EXIST)); } if (parentIssue != null) { IssueCondition issueCondition = new IssueCondition(issueVo.getId(), parentIssue.getId()); List> results = this.issueMapper.findNotCompleteByParentIssueId(issueCondition); // 하위 일감이 모두 종료 상태일때 상위 일감도 종료 처리 if (results == null || results.size() == 0) { parentIssue.setIssueStatus(issueStatus.getIssueStatus()); this.issueRepository.saveAndFlush(parentIssue); } } issues.add(modifyIssue); } return issues; } else { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.API_ISSUE_NOT_EXIST)); } } // 이슈를 수정한다. @Override @Transactional public Issue modifyIssue(IssueForm issueForm, List multipartFiles) { User user = this.webAppUtil.getLoginUserObject(); return modifyIssue(user, issueForm, multipartFiles); } // 수정 데이터가 유효한지 확인 private CheckIssueData checkIssue(User user, IssueForm issueForm) { // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. this.workspaceService.checkUseWorkspace(user, user.getLastWorkspaceId()); Issue issue = this.getIssue(issueForm.getId()); IssueStatus oldIssueStatus = issue.getIssueStatus(); // 이슈 수정 권한 체크 this.verifyIssueModifyPermission(issue, user); // 프로젝트 유효성 체크 Project project = this.projectService.getProject(issueForm.getProjectId()); // 이슈 상태 유효성 체크 IssueStatus issueStatus = this.issueStatusService.getIssueStatus(issueForm.getIssueStatusId()); // 이슈 유형 유효성 체크 IssueType issueType = this.issueTypeService.getIssueType(issueForm.getIssueTypeId()); // 우선순위 유효성 체크 Priority priority = this.priorityService.getPriority(issueForm.getPriorityId()); // 중요도 유효성 체크 Severity severity = this.severityService.getSeverity(issueForm.getSeverityId()); // 제목 유효성 체크 this.verifyTitle(issueForm.getTitle()); // 날짜 유효성 체크 this.checkStartCompleteDate(issueForm.getStartDate(), issueForm.getCompleteDate()); // 담당자 유효성 체크 //this.verifyIssueAssignee(project, issueForm); // 담당부서 유효성 체크 //this.verifyIssueDepartment(project, issueForm); CheckIssueData checkIssueData = new CheckIssueData(); checkIssueData.setIssue(issue); checkIssueData.setProject(project); checkIssueData.setOldIssueStatus(oldIssueStatus); checkIssueData.setNewIssueStatus(issueStatus); checkIssueData.setIssueType(issueType); checkIssueData.setPriority(priority); checkIssueData.setSeverity(severity); return checkIssueData; } // 이슈 수정(API용) private Issue modifyIssueForApi(User user, IssueForm issueForm, List multipartFiles) { CheckIssueData checkIssueData = this.checkIssue(user, issueForm); if (issueForm.getComment() != null && !issueForm.getComment().equals("")) { //댓글 추가 IssueCommentForm issueCommentForm = new IssueCommentForm(); issueCommentForm.setDescription(issueForm.getComment()); issueCommentForm.setIssueId(issueForm.getId()); this.issueCommentService.addIssueComment(issueCommentForm, user); } // 이슈 이력 남기기 this.addIssueHistoryModify(user, issueForm, checkIssueData, multipartFiles); // db에 저장 return this.saveIssue(issueForm, checkIssueData); } private void addIssueHistoryModify(User user, IssueForm issueForm, CheckIssueData checkIssueData, List multipartFiles) { // 변경 이력 정보 추출 StringBuilder detectIssueChange = this.issueHistoryService.detectIssueChange(issueForm, checkIssueData, multipartFiles); // 프로젝트가 변경되면 이슈 넘버를 새로 따야 한다. this.checkChangeProject(checkIssueData.getProject(), checkIssueData.getIssue()); // 이슈 유형이 변경되었는지 확인하고 변경되었다면 이슈 상태 속성이 '대기' 인 이슈 상태로 교체한다. if (this.checkChangeIssueType(checkIssueData.getIssueType(), checkIssueData.getNewIssueStatus(), checkIssueData.getIssue())) { checkIssueData.setNewIssueStatus(this.issueStatusService.findByIssueStatusTypeIsReady(checkIssueData.getIssueType().getWorkflow())); // 이슈 상태 변경 이력 남기기 - 이력을 남기기 위해 issueForm 에 issueStatus Id 값을 저장. issueForm.setIssueStatusId(checkIssueData.getNewIssueStatus().getId()); this.issueHistoryService.detectIssueStatus(checkIssueData.getIssue(), issueForm, detectIssueChange, checkIssueData.getOldIssueStatus(), checkIssueData.getNewIssueStatus()); } // db에 저장 // checkIssueData.setIssue(this.saveIssue(issueForm, checkIssueData)); // 이슈 이력 등록 if (!StringUtils.isEmpty(detectIssueChange.toString())) { this.issueHistoryService.addIssueHistory(checkIssueData.getIssue(), user, IssueHistoryType.MODIFY, detectIssueChange.toString()); } // 사용자 시스템 기능 사용 정보 수집 UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class); log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(userVo, ElasticSearchConstants.ISSUE_MODIFY)); } private Issue saveIssue(IssueForm issueForm, CheckIssueData checkIssueData) { Issue issue = checkIssueData.getIssue(); ConvertUtil.copyProperties(issueForm, issue, "id"); issue.setProject(checkIssueData.getProject()); issue.setIssueStatus(checkIssueData.getNewIssueStatus()); issue.setIssueType(checkIssueData.getIssueType()); issue.setPriority(checkIssueData.getPriority()); issue.setSeverity(checkIssueData.getSeverity()); issue.setStartDate(issueForm.getStartDate()); issue.setCompleteDate(issueForm.getCompleteDate()); return this.issueRepository.saveAndFlush(issue); } // 이슈를 수정한다. @Override @Transactional public Issue modifyIssue(User user, IssueForm issueForm, List multipartFiles) { CheckIssueData checkIssueData = this.checkIssue(user, issueForm); Issue issue = checkIssueData.getIssue(); IssueStatus oldIssueStatus = checkIssueData.getOldIssueStatus(); Project project = checkIssueData.getProject(); IssueStatus issueStatus = checkIssueData.getNewIssueStatus(); IssueType issueType = checkIssueData.getIssueType(); // 변경 이력 정보 추출 StringBuilder detectIssueChange = this.issueHistoryService.detectIssueChange(issueForm, checkIssueData, multipartFiles); // 프로젝트가 변경되면 이슈 넘버를 새로 따야 한다. this.checkChangeProject(project, issue); // 이슈 유형이 변경되었는지 확인하고 변경되었다면 이슈 상태 속성이 '대기' 인 이슈 상태로 교체한다. if (this.checkChangeIssueType(issueType, issueStatus, issue)) { issueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(issueType.getWorkflow()); // 이슈 상태 변경 이력 남기기 - 이력을 남기기 위해 issueForm 에 issueStatus Id 값을 저장. issueForm.setIssueStatusId(issueStatus.getId()); this.issueHistoryService.detectIssueStatus(issue, issueForm, detectIssueChange, oldIssueStatus, issueStatus); } issue = this.saveIssue(issueForm, checkIssueData); //this.issueUserService.modifyIssueUser(issue, project.getWorkspace(), issueForm.getUserIds()); // 담당부서 지정 if(issueForm.getDepartmentIds().size()>0){ this.issueDepartmentService.modifyIssueDepartment(issue, user, project.getWorkspace(), issueForm.getDepartmentIds()); } // multipartFile 을 file Map List 객체로 변경한다. List> convertFileMaps = this.convertMultipartFileToFile(multipartFiles); // 첨부 파일 저장 - 비동기로 작동 this.attachedFileService.addAttachedFile(convertFileMaps, issue, user.getAccount()); // 삭제된 첨부파일 처리 this.attachedFileService.removeAttachedFiles(issueForm.getRemoveFiles()); // 텍스트 에디터에 첨부한 파일을 이슈와 연결 this.checkNotHaveIssueIdAttachedFile(issue, issueForm); // 사용자 정의 필드 저장 this.issueCustomFieldValueService.modifyIssueCustomFieldValue(issue, issueForm.getIssueCustomFields()); // 사용자 시스템 기능 사용 정보 수집 UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class); log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(userVo, ElasticSearchConstants.ISSUE_MODIFY)); // 업체 정보 저장 this.issueCompanyService.modifyIssueCompanyField(issue, issueForm, detectIssueChange); // ISP 정보 저장 this.issueIspService.modifyIssueIspField(issue, issueForm, detectIssueChange); // HOSTING 정보 저장 this.issueHostingService.modifyIssueHostingField(issue, issueForm, detectIssueChange); // 파트너정보 하위이슈 상속 List downIssues = this.issueRepository.findByParentIssueId(issue.getId()); if (issueForm.getInheritYn() != null && issueForm.getInheritYn() && downIssues != null && downIssues.size() > 0) { for (Issue downIssue : downIssues) { this.inheritPartners(downIssue, issue); } } // 이슈 이력 등록 if (!StringUtils.isEmpty(detectIssueChange.toString())) { this.issueHistoryService.addIssueHistory(issue, user, IssueHistoryType.MODIFY, detectIssueChange.toString()); } return issue; } // multipartFile 을 file Map 객체로 변경한다. private List> convertMultipartFileToFile(List multipartFiles) { List> convertFileMaps = Lists.newArrayList(); if (multipartFiles != null && multipartFiles.size() > 0) { for (MultipartFile multipartFile : multipartFiles) { try { Map fileMap = CommonUtil.makeFileMap(multipartFile); convertFileMaps.add(fileMap); } catch (Exception e) { log.debug("multipartFile -> file 변환 오류" + e.getMessage()); } } } return convertFileMaps; } // 프로젝트가 변경되었는지 확인한다. private void checkChangeProject(Project newProject, Issue issue) { if (!issue.getProject().getId().equals(newProject.getId())) { // 각 프로젝트의 고유 이슈 번호 생성 issue.setIssueNumber(this.issueNumberGeneratorService.generateIssueNumber(newProject)); } } // 이슈 유형이 변경되었는지 확인하고 변경되었으면 이슈 상태를 대기 속성인 이슈 상태로 셋팅한다. private Boolean checkChangeIssueType(IssueType newIssueType, IssueStatus issueStatus, Issue issue) { if (!issue.getIssueType().getId().equals(newIssueType.getId())) { // 이슈 상태를 선택하지 않았을 때 if (issueStatus == null) { return true; } // 이슈 상태의 속성이 '대기' 가 아닌 경우 if (!issueStatus.getIssueStatusType().equals(IssueStatusType.READY)) { return true; } else { // 변경하는 이슈 유형의 워크플로우에 존재하는 상태 속성 '대기'와 동일한 상태인지 확인 IssueStatus newReadyIssueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(newIssueType.getWorkflow()); if (!newReadyIssueStatus.getId().equals(issueStatus.getId())) { return true; } } } return false; } // 이슈 담당부서로 지정될 부서가 해당 프로젝트에 참여 하고 있는 부서인지 확인 private void verifyIssueDepartment(Project project, IssueForm issueForm) { if (issueForm.getDepartmentIds().size() > 0) { List trustDepartmentIds = Lists.newArrayList(); // 참여 확인된 부서 for (Long departmentId : issueForm.getDepartmentIds()) { boolean includeProject = false; for (ProjectRole projectRole : project.getProjectRoles()) { ProjectRoleDepartment projectRoleDepartment = this.projectRoleDepartmentService.findByProjectRoleIdAndDepartmentId(projectRole.getId(), departmentId); if (projectRoleDepartment != null) { includeProject = true; trustDepartmentIds.add(departmentId); break; } } // 데이터 보정 작업 - 프로젝트에서 제외된 사용자는 담당자에서 제외 될 수 있도록 처리 if (!includeProject) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.PROJECT_NOT_INCLUDE_DEPARTMENT)); } } // 참여 확인된 부서로 담당부서 변경 issueForm.setDepartmentIds(trustDepartmentIds); } } // 이슈 담당자로 지정될 사용자가 해당 프로젝트에 참여 하고 있는 사용자 인지 확인 private void verifyIssueAssignee(Project project, IssueForm issueForm) { if (issueForm.getUserIds().size() > 0) { List trustUserIds = Lists.newArrayList(); // 참여 확인된 사용자 for (Long userId : issueForm.getUserIds()) { boolean includeProject = false; for (ProjectRole projectRole : project.getProjectRoles()) { ProjectRoleUser projectRoleUser = this.projectRoleUserService.findByProjectRoleIdAndUserId(projectRole.getId(), userId); if (projectRoleUser != null) { includeProject = true; trustUserIds.add(userId); break; } } // 데이터 보정 작업 - 프로젝트에서 제외된 사용자는 담당자에서 제외 될 수 있도록 처리 if (!includeProject) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.PROJECT_NOT_INCLUDE_USER)); } } // 참여 확인된 사용자로 담당자 변경 issueForm.setUserIds(trustUserIds); } } // 이슈 수정 권한 체크 private void verifyIssueModifyPermission(Issue issue, User user) { // 이슈 수정 권한을 갖고 있는지 확인 if (!this.checkHasPermission(ConvertUtil.copyProperties(issue, IssueVo.class), this.getIssueUserVos(issue), user, this.getIssueDepartmentVos(issue))) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_MODIFY_PERMISSION)); } } // 이슈에서 담당자 정보를 추출한다. private List getIssueUserVos(Issue issue) { List userVos = Lists.newArrayList(); Set issueUsers = issue.getIssueUsers(); try { for (IssueUser issueUser : issueUsers) { User user = issueUser.getUser(); UserVo userVo = ConvertUtil.copyProperties(user, UserVo.class, "password"); userVos.add(userVo); } } catch (Exception ex) { } return userVos; } // 이슈에서 담당자 정보를 추출한다. private List getIssueDepartmentVos(Issue issue) { List departmentVos = Lists.newArrayList(); Set issueDepartments = issue.getIssueDepartments(); try { for (IssueDepartment issueDepartment : issueDepartments) { Department department = issueDepartment.getDepartment(); DepartmentVo departmentVo = ConvertUtil.copyProperties(department, DepartmentVo.class); departmentVos.add(departmentVo); } } catch (Exception ex) { } return departmentVos; } // 이슈 수정 권한을 갖고 있는지 확인 private boolean checkHasPermission(IssueVo issueVo, List issueUserVos, User user, List departmentVos) { boolean hasPermission = false; // 업무 공간 관리자일 경우 수정 권한을 갖는다. hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.WORKSPACE_MANAGER, issueVo, null, null, user); // 프로젝트 관리자일 경우 해당 프로젝트에 등록된 이슈는 수정 권한을 갖는다. hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.PROJECT_MANAGER, issueVo, null, null, user); // 이슈 관리자일 경우 수정 권한을 갖는다. hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.ISSUE_MANAGER, issueVo, null, null, user); // 이슈 등록자일 경우 수정 권한을 갖는다. hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.REGISTER, issueVo, null, null, user); // 이슈 담당자일 경우 수정 권한을 갖는다. //hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.ASSIGNEE, issueVo, issueUserVos); // 이슈 담당부서일 경우 수정 권한을 갖는다. hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.DEPARTMENT, issueVo, null, departmentVos, user); // 담당자가 없으면 모든 사용자가 수정 권한을 갖는다. //hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.ALL_ISSUE_MANAGER, issueVo, null, null, user); //hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.ALL_PROJECT_MANAGER, issueVo, null, null, user); return hasPermission; } // 이슈 수정 권한을 확인한다. private boolean checkIssueModifyPermission(Boolean hasPermission, String checkType, IssueVo issueVo, List issueUserVos, List departmentVos, User user) { if (!hasPermission) { switch (checkType) { case Issue.WORKSPACE_MANAGER: // 업무 공간 관리자 // 업무 공간 관리자일 경우 수정 권한을 갖는다. hasPermission = this.userWorkspaceService.checkWorkspaceManager(user); break; case Issue.PROJECT_MANAGER: // 프로젝트 관리자 Issue issue = this.getIssue(issueVo.getId()); // 프로젝트 관리자일 경우 해당 프로젝트에 등록된 이슈는 수정 권한을 갖는다. hasPermission = this.projectRoleUserService.checkProjectManager(issue.getProject(), user); break; case Issue.ISSUE_MANAGER: // 이슈 관리자 UserLevel userLevel = this.userLevelService.getUserLevel(user.getUserLevel().getId()); hasPermission = MngPermission.checkMngPermission(userLevel.getPermission(), MngPermission.USER_PERMISSION_MNG_ISSUE); break; case Issue.REGISTER: // 이슈 등록자 hasPermission = issueVo.getRegisterId().equals(user.getId()); break; case Issue.ASSIGNEE: // 담당자가 없으면 모든 사용자가 수정 권한을 갖는다. if (issueUserVos.size() < 1) { hasPermission = true; break; } // 이슈 담당자 여부 확인 for (UserVo issueUserVo : issueUserVos) { if (issueUserVo.getId().equals(user.getId())) { hasPermission = true; break; } } break; case Issue.DEPARTMENT: // 담당부서가 없으면 모든 사용자가 수정 권한을 갖는다. /*if (userDepartmentVos.size() < 1) { hasPermission = true; break; }*/ // 이슈 담당부서 여부 확인 for (DepartmentVo departmentVo : departmentVos) { List userDepartments = this.userDepartmentService.findByDepartmentId(departmentVo.getId()); if(userDepartments != null && userDepartments.size() > 0) { for (UserDepartment userDepartment : userDepartments) { if (userDepartment.getUserId().equals(user.getId())){ hasPermission = true; break; } } } } break; } } return hasPermission; } // 이슈 상태 변경 @Override @Transactional public void modifyIssueStatus(IssueForm issueForm, User user) { // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. this.workspaceService.checkUseWorkspace(); // 변경 이력 정보 추출 StringBuilder detectIssueChange = new StringBuilder(); // 이슈 수정 권한 체크 Issue issue = this.getIssue(issueForm.getId()); IssueStatus oldIssueStatus = issue.getIssueStatus(); this.verifyIssueModifyPermission(issue, user); IssueStatus issueStatus = this.issueStatusService.getIssueStatus(issueForm.getIssueStatusId()); if (issueStatus.getIssueStatusType().toString().equals("CLOSE")) { List downIssuesStatus = issueForm.getDownIssuesStatus(); if (downIssuesStatus != null && downIssuesStatus.size() > 0) { for (String downIssueStatus : downIssuesStatus) { if (!downIssueStatus.equals("CLOSE")) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_MODIFY_STATUS)); } } } } // 이슈 상태를 변경할 때 선택한 이슈 상태로 변경할 수 있는지 확인한다. this.issueStatusService.checkNextIssueStatus(issue, issueStatus); // 변경 이력 정보 추출 this.issueHistoryService.detectIssueStatus(issue, issueForm, detectIssueChange, oldIssueStatus, issueStatus); issue.setIssueStatus(issueStatus); this.issueRepository.saveAndFlush(issue); // 코멘트 등록 if (!StringUtils.isEmpty(issueForm.getComment())) { IssueCommentForm issueCommentForm = new IssueCommentForm(); issueCommentForm.setIssueId(issue.getId()); issueCommentForm.setDescription(issueForm.getComment()); this.issueCommentService.addIssueComment(issueCommentForm); } // 이슈 이력 등록 if (!StringUtils.isEmpty(detectIssueChange.toString())) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("
    "); stringBuilder.append(detectIssueChange.toString()); stringBuilder.append("
"); this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, stringBuilder.toString()); } // 이슈 버전 생성 this.issueVersionService.addIssueVersion(issue); // 사용자 시스템 기능 사용 정보 수집 log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_STATUS_CHANGE)); } // 이슈 담당자 변경 @Override @Transactional public void modifyIssueUser(IssueForm issueForm) { User user = this.webAppUtil.getLoginUserObject(); // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. this.workspaceService.checkUseWorkspace(); // 변경 이력 정보 추출 StringBuilder detectIssueChange = new StringBuilder(); // 이슈 수정 권한 체크 Issue issue = this.getIssue(issueForm.getId()); this.verifyIssueModifyPermission(issue, user); issue.setProject(this.projectService.getProject(issueForm.getProjectId())); // 변경 이력 정보 추출 this.issueHistoryService.detectIssueManager(issue, issueForm, detectIssueChange); this.issueUserService.modifyIssueUser(issue, issue.getProject().getWorkspace(), issueForm.getDepartmentIds()); //getUserIds -> getDepartmentIds this.issueRepository.saveAndFlush(issue); // 이슈 이력 등록 if (!StringUtils.isEmpty(detectIssueChange.toString())) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("
    "); stringBuilder.append(detectIssueChange.toString()); stringBuilder.append("
"); this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, stringBuilder.toString()); } // 이슈 버전 생성 this.issueVersionService.addIssueVersion(issue); // 사용자 시스템 기능 사용 정보 수집 log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_USER_CHANGE)); } @Override @Transactional public void modifyIssueDepartment(IssueForm issueForm) { User user = this.webAppUtil.getLoginUserObject(); // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. this.workspaceService.checkUseWorkspace(); // 변경 이력 정보 추출 StringBuilder detectIssueChange = new StringBuilder(); // 이슈 수정 권한 체크 Issue issue = this.getIssue(issueForm.getId()); this.verifyIssueModifyPermission(issue, user); issue.setProject(this.projectService.getProject(issueForm.getProjectId())); // 변경 이력 정보 추출 this.issueHistoryService.detectIssueDepartment(issue, issueForm, detectIssueChange); //this.issueUserService.modifyIssueUser(issue, issue.getProject().getWorkspace(), issueForm.getUserIds()); this.issueDepartmentService.modifyIssueDepartment(issue, issue.getProject().getWorkspace(), issueForm.getDepartmentIds()); this.issueRepository.saveAndFlush(issue); // 이슈 이력 등록 if (!StringUtils.isEmpty(detectIssueChange.toString())) { StringBuilder stringBuilder = new StringBuilder(); stringBuilder.append("
    "); stringBuilder.append(detectIssueChange.toString()); stringBuilder.append("
"); this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, stringBuilder.toString()); } // 이슈 버전 생성 this.issueVersionService.addIssueVersion(issue); // 사용자 시스템 기능 사용 정보 수집 log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_USER_CHANGE)); } // 이슈를 삭제한다. @Override @Transactional public void removeIssues(IssueForm issueForm) { // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. User user = this.webAppUtil.getLoginUserObject(); this.workspaceService.checkUseWorkspace(); if (issueForm.getRemoveIds().size() < 1) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_REMOVE_NOT_SELECT)); } List removeIssues = Lists.newArrayList(); for (Long issueId : issueForm.getRemoveIds()) { //하위이슈 체크 List downIssues = this.issueRepository.findByParentIssueId(issueId); if(downIssues != null && downIssues.size() > 0){ for(Issue downIssue : downIssues){ if(downIssue.getParentIssue() != null){ downIssue.setParentIssue(null); } } } Issue issue = this.issueRemoves(issueId, user); removeIssues.add(issue); } /*if (removeIssues.size() > 0) { this.issueRepository.deleteAll(removeIssues); }*/ // 사용자 시스템 기능 사용 정보 수집 log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_REMOVE)); } // 이슈를 삭제한다. @Override @Transactional public void removeAllIssues(IssueForm issueForm) { // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. User user = this.webAppUtil.getLoginUserObject(); this.workspaceService.checkUseWorkspace(); if (issueForm.getRemoveIds().size() < 1) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_REMOVE_NOT_SELECT)); } Set removeIds = new HashSet<>(); for (Long issueId : issueForm.getRemoveIds()) { removeIds.add(issueId); //하위이슈 체크 List downIssues = this.issueRepository.findByParentIssueId(issueId); if(downIssues != null && downIssues.size() > 0){ for(Issue downIssue : downIssues){ Long downIssueId = downIssue.getId(); removeIds.add(downIssueId); } } } for (Long removeId : removeIds) { this.issueRemoves(removeId, user); } // 사용자 시스템 기능 사용 정보 수집 log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_REMOVE)); } // 하위이슈를 삭제한다. @Override @Transactional public void removeDownIssues(IssueForm issueForm) { // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. User user = this.webAppUtil.getLoginUserObject(); this.workspaceService.checkUseWorkspace(); if (issueForm.getRemoveIds().size() < 1) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_REMOVE_NOT_SELECT)); } List removeIssues = Lists.newArrayList(); Long downIssueId = 0L; for (Long issueId : issueForm.getRemoveIds()) { //삭제 할 이슈의 하위이슈 체크 List downIssues = this.issueRepository.findByParentIssueId(issueId); if(downIssues != null && downIssues.size() > 0){ for(Issue downIssue : downIssues){ downIssueId = downIssue.getId(); } } Issue issue = this.issueRemoves(downIssueId, user); removeIssues.add(issue); } // 사용자 시스템 기능 사용 정보 수집 log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_REMOVE)); } private Issue issueRemoves(Long issueId, User user) { Issue issue = null; if(issueId != null){ issue = this.getIssue(issueId); } // 이슈 수정 권한을 갖고 있는지 확인 this.verifyIssueModifyPermission(issue, user); // 이슈 첨부 파일을 삭제한다. if (issue.getAttachedFiles().size() > 0) { List attachedFileIds = Lists.newArrayList(); for (AttachedFile attachedFile : issue.getAttachedFiles()) { attachedFileIds.add(attachedFile.getId()); } // 첨부파일 삭제 this.attachedFileService.removeAttachedFiles(attachedFileIds); } // 지울 이슈가 연관이슈인지 체크 후 연관이슈 테이블에서도 삭제한다. List issueRelationList = this.issueRelationRepository.findByRelationIssueId(issueId); if (issueRelationList != null && issueRelationList.size() > 0) { for(IssueRelation issueRelation : issueRelationList){ StringBuilder sb = new StringBuilder(); issueHistoryService.detectRelationIssue(IssueHistoryType.DELETE, issueRelation, sb); issueHistoryService.addIssueHistory(issueRelation.getIssue(), IssueHistoryType.MODIFY, sb.toString()); this.issueRelationRepository.delete(issueRelation); } } // 이슈 생성, 삭제시 예약 이메일에 등록해놓는다. this.reservationIssueEmail(issue, EmailType.ISSUE_REMOVE); // 이슈 삭제 this.issueRepository.delete(issue); return issue; } @Override @Transactional(readOnly = true) public Issue getIssue(Long id) { if (id == null) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_EXIST)); } Issue issue = this.findOne(id); if (issue == null) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_EXIST)); } return issue; } // 이슈 유형을 사용하는 이슈 갯수를 조회한다. @Override @Transactional(readOnly = true) public long countByIssueTypeId(Long issueTypeId) { return this.issueMapper.countByIssueTypeId(issueTypeId); } // 이슈 상태를 사용하는 이슈 갯수를 조회한다. @Override @Transactional(readOnly = true) public long countByIssueStatus(Long issueStatusId) { return this.issueMapper.countByIssueStatusId(issueStatusId); } // 이슈 유형에서 워크플로우가 변경되었을 때 해당 워크플로우에 현재 이슈 상태가 존재하지 않으면 대기(생성) 로 변경한다. @Override @Transactional public void changeWorkflows(Workflow workflow, IssueType issueType) { List> issueMaps = this.issueMapper.findByIssueTypeId(issueType.getId()); List issueStatusVos = this.issueStatusService.findByWorkflowId(workflow.getId()); IssueStatus readyIssueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(workflow); List updateIssues = Lists.newArrayList(); for (Map issueMap : issueMaps) { boolean exist = false; for (IssueStatusVo issueStatusVo : issueStatusVos) { Long issueStatusId = MapUtil.getLong(issueMap, "issueStatusId"); if (issueStatusId == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_NOT_EXIST)); } if (issueStatusId.equals(issueStatusVo.getId())) { exist = true; break; } } // 존재하지 않는 대상은 if (!exist) { StringBuilder stringBuilder = new StringBuilder(); String issueStatusName = MapUtil.getString(issueMap, "issueStatusName"); if (issueStatusName == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_STATUS_NOT_EXIST)); } Issue issue = this.getIssue(MapUtil.getLong(issueMap, "issueId")); // 워크플로우에서 이슈 상태가 삭제되어 상태가 변경된 정보를 기록한다. this.issueHistoryService.recordRemoveWorkflowToIssueStatus(issueStatusName, readyIssueStatus.getName(), stringBuilder); this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, stringBuilder.toString()); issue.setIssueStatus(readyIssueStatus); updateIssues.add(issue); } } if (updateIssues.size() > 0) { this.issueRepository.saveAll(updateIssues); for (Issue issue : updateIssues) { // 이슈 버전 생성 this.issueVersionService.addIssueVersion(issue); } } } // 이슈 목록을 엑셀로 다운로드 한다. @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; } IssueCondition issueCondition = IssueCondition.make(conditions); // 검색 조건을 만든다 this.makeIssueSearchCondition(issueCondition, Lists.newArrayList("01", "02", "03"), null); Set issueIds = new HashSet<>(); // 사용자 정의 필드 검색시 나오는 이슈 아이디 저장 컬렉션 Map resJsonData = new HashMap<>(); ExportExcelVo excelInfo = new ExportExcelVo(); excelInfo.setFileName(this.messageAccessor.message("common.issueList")); // 이슈 목록 excelInfo.addAttrInfos(new ExportExcelAttrVo("issueStatusName", this.messageAccessor.message("common.status"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // 상태 excelInfo.addAttrInfos(new ExportExcelAttrVo("issueKey", this.messageAccessor.message("common.issueKey"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // 이슈 번호 excelInfo.addAttrInfos(new ExportExcelAttrVo("title", this.messageAccessor.message("common.issueTitle"), 40, ExportExcelAttrVo.ALIGN_LEFT)); // 이슈 제목 excelInfo.addAttrInfos(new ExportExcelAttrVo("description", this.messageAccessor.message("common.content"), 60, ExportExcelAttrVo.ALIGN_LEFT)); // 내용 excelInfo.addAttrInfos(new ExportExcelAttrVo("issueTypeName", this.messageAccessor.message("common.issueType"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // 이슈 타입 excelInfo.addAttrInfos(new ExportExcelAttrVo("departments", this.messageAccessor.message("common.department"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // 담당부서 excelInfo.addAttrInfos(new ExportExcelAttrVo("priorityName", this.messageAccessor.message("common.priority"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // 우선순위 excelInfo.addAttrInfos(new ExportExcelAttrVo("severityName", this.messageAccessor.message("common.importance"), 6, ExportExcelAttrVo.ALIGN_CENTER)); // 중요도 excelInfo.addAttrInfos(new ExportExcelAttrVo("register", this.messageAccessor.message("common.register"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // 등록자 excelInfo.addAttrInfos(new ExportExcelAttrVo("period", this.messageAccessor.message("common.period"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // 기간 excelInfo.addAttrInfos(new ExportExcelAttrVo("modifyDate", this.messageAccessor.message("common.modifyDate"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // 최종 변경일 excelInfo.addAttrInfos(new ExportExcelAttrVo("companyName", this.messageAccessor.message("common.company"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // 업체 excelInfo.addAttrInfos(new ExportExcelAttrVo("ispName", this.messageAccessor.message("common.isp"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // ISP excelInfo.addAttrInfos(new ExportExcelAttrVo("hostingName", this.messageAccessor.message("common.hosting"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // 호스팅 // 사용자 정의 필드를 사용한 이슈를 찾는다. 만약 이슈가 없다면 여기서 이슈 조회가 끝난다. if (!this.searchUseCustomFields(issueCondition, issueIds, resJsonData, null)) { model.addAttribute(Constants.EXCEL, excelInfo); return new ModelAndView(this.excelView); } List issueVos = Lists.newArrayList(); // 이슈 목록 데이터 저장 컬렉션 // 사용자 정의 필드로 검색한 이슈 아이디 값 List issueKeys = Lists.newArrayList(issueIds); issueCondition.setIssueIds(issueKeys); List> results = this.issueMapper.find(issueCondition); // 엑셀 다운로드를 진행할 때 전체 사용자 정의 필드를 표시한다. this.makeIssueExcelDownloadCustomFields(excelInfo); // 이슈 아이디 초기화 issueCondition.setIssueIds(Lists.newArrayList()); // Map 에 있는 데이터를 IssueVo 데이터로 변환한다. User user = this.webAppUtil.getLoginUserObject(); this.setMapToIssueVo(results, issueVos, issueCondition, user); // IssueVos 데이터를 엑셀에서 표시할 수 있는 데이터로 변경한다. List> convertExcelViewToIssueMaps = this.convertExcelViewToIssueVos(issueVos); if (results.size() > EXCEL_DOWNLOAD_MAX_ROWS) { // 엑셀 1만건 초과 알림 this.simpMessagingTemplate.convertAndSendToUser(this.webAppUtil.getLoginUser().getAccount(), "/notification/system-alert", this.messageAccessor.getMessage(MsgConstants.EXCEL_DOWNLOAD_MAX_ROWS_OVER)); // 1만 건만 출력해준다. excelInfo.setDatas(convertExcelViewToIssueMaps.subList(0, EXCEL_DOWNLOAD_MAX_ROWS)); model.addAttribute(Constants.EXCEL, excelInfo); return new ModelAndView(this.excelView); } // 엑셀에 넣을 데이터 - IssueVos 데이터를 엑셀에서 표시할 수 있는 데이터로 변경한다. excelInfo.setDatas(convertExcelViewToIssueMaps); model.addAttribute(Constants.EXCEL, excelInfo); return new ModelAndView(this.excelView); } // 엑셀 다운로드를 진행할 때 전체 사용자 정의 필드를 표시한다. private void makeIssueExcelDownloadCustomFields(ExportExcelVo excelInfo) { List customFields = this.customFieldService.findByWorkspaceId(); for (CustomField customField : customFields) { excelInfo.addAttrInfos(new ExportExcelAttrVo("customField_" + customField.getId(), customField.getName(), 10, ExportExcelAttrVo.ALIGN_CENTER)); } } // 사용자 정의 필드 정보 추가 private void setIssueCustomFieldValue(List issueVos, IssueCondition issueCondition) { // 이슈에서 저장한 사용자 정의 필드 값을 조회한다. List> issueCustomFieldValues = this.issueCustomFieldValueService.findInIssueIds(issueCondition); for (IssueVo issueVo : issueVos) { for (Map issueCustomFieldValue : issueCustomFieldValues) { int count = 0; Map useValues = new HashMap<>(); if (issueVo.getId().equals(MapUtil.getLong(issueCustomFieldValue, "issueId"))) { IssueCustomFieldValueVo issueCustomFieldValueVo = new IssueCustomFieldValueVo(); useValues.put("useValue"+count, MapUtil.getString(issueCustomFieldValue, "useValue")); useValues.put("customFieldId", MapUtil.getLong(issueCustomFieldValue, "customFieldId")); issueCustomFieldValueVo.setUseValues(useValues); issueCustomFieldValueVo.setUseValue(MapUtil.getString(issueCustomFieldValue, "useValue")); CustomFieldVo customFieldVo = new CustomFieldVo(); customFieldVo.setId(MapUtil.getLong(issueCustomFieldValue, "customFieldId")); issueCustomFieldValueVo.setCustomFieldVo(customFieldVo); issueVo.addIssueCustomFieldValueVo(issueCustomFieldValueVo); } } } } // 업체 정보 추가 private void setIssueCompanyField(Issue issue, IssueVo issueVo) { List issueCompanyVos = Lists.newArrayList(); for(IssueCompany issueCompany : issue.getIssueCompanies()){ IssueCompanyVo issueCompanyVo = ConvertUtil.copyProperties(issueCompany, IssueCompanyVo.class); issueCompanyVo.setId(issueCompany.getId()); CompanyField companyField = issueCompany.getCompanyField(); if (companyField != null) { issueCompanyVo.setCompanyId(issueCompany.getCompanyField().getId()); if (issueCompany.getCompanyTypeId() != null && issueCompany.getCompanyTypeId() != -1) { CompanyFieldCategory companyType = this.companyFieldCategoryService.find(issueCompany.getCompanyTypeId()); issueCompanyVo.setCompanyTypeName(companyType.getUseValue()); } if (issueCompany.getParentSectorId() != null && issueCompany.getParentSectorId() != -1) { CompanyFieldCategory parentSector = this.companyFieldCategoryService.find(issueCompany.getParentSectorId()); issueCompanyVo.setParentSectorName(parentSector.getUseValue()); } if (issueCompany.getChildSectorId() != null && issueCompany.getChildSectorId() != -1) { CompanyFieldCategory childSector = this.companyFieldCategoryService.find(issueCompany.getChildSectorId()); issueCompanyVo.setChildSectorName(childSector.getUseValue()); } if (issueCompany.getRegionId() != null && issueCompany.getRegionId() != -1) { CompanyFieldCategory region = this.companyFieldCategoryService.find(issueCompany.getRegionId()); issueCompanyVo.setRegionName(region.getUseValue()); } if (issueCompany.getStatusId() != null && issueCompany.getStatusId() != -1) { if (issueCompany.getStatusName() != null && !issueCompany.getStatusName().equals("")) { issueCompanyVo.setStatusName(issueCompany.getStatusName()); } else { CompanyFieldCategory status = this.companyFieldCategoryService.find(issueCompany.getStatusId()); issueCompanyVo.setStatusName(status.getUseValue()); } } } issueCompanyVos.add(issueCompanyVo); } issueVo.setIssueCompanyVos(issueCompanyVos); } // Isp 정보 추가 private void setIssueIspField(Issue issue, IssueVo issueVo) { List issueIspVos = Lists.newArrayList(); for(IssueIsp issueIsp : issue.getIssueIspFields()){ IssueIspVo issueIspVo = ConvertUtil.copyProperties(issueIsp, IssueIspVo.class); issueIspVo.setId(issueIsp.getId()); IspField ispField = issueIsp.getIspField(); if (ispField != null) { issueIspVo.setIspId(ispField.getId()); } issueIspVos.add(issueIspVo); } issueVo.setIssueIspVos(issueIspVos); } // Hosting 정보 추가 private void setIssueHostingField(Issue issue, IssueVo issueVo) { List issueHostingVos = Lists.newArrayList(); for(IssueHosting issueHosting : issue.getIssueHostingFields()){ IssueHostingVo issueHostingVo = ConvertUtil.copyProperties(issueHosting, IssueHostingVo.class); issueHostingVo.setId(issueHosting.getId()); HostingField hostingField = issueHosting.getHostingField(); if (hostingField != null) { issueHostingVo.setHostingId(hostingField.getId()); } issueHostingVos.add(issueHostingVo); } issueVo.setIssueHostingVos(issueHostingVos); } // 연관일감 정보 추가 private void setRelationIssue(IssueVo issueVo, Long issueId) { List relationIssues = this.issueRelationService.findRelationIssue(issueId); issueVo.setIssueRelationIssueVos(relationIssues); } // IssueVos 데이터를 엑셀에서 표시할 수 있는 데이터로 변경한다. private List> convertExcelViewToIssueVos(List issueVos) { List> results = Lists.newArrayList(); for (IssueVo issueVo : issueVos) { try { Map result = new HashMap<>(); result.put("issueStatusName", issueVo.getIssueStatusName()); result.put("issueKey", issueVo.getProjectKey() + "-" + issueVo.getIssueNumber()); result.put("title", issueVo.getTitle()); String description = ""; if (issueVo.getDescription() != null) { description = Jsoup.parse(issueVo.getDescription()).text(); // HTML 태그 제거 description = description.replaceAll("\\<.*?>", ""); // 공백 제거 } result.put("description", description); result.put("issueTypeName", issueVo.getIssueTypeName()); result.put("assignees", CommonUtil.convertUserVosToString(issueVo.getUserVos())); result.put("departments", CommonUtil.convertDepartmentVosToString(issueVo.getDepartmentVos())); result.put("priorityName", issueVo.getPriorityName()); result.put("severityName", issueVo.getSeverityName()); result.put("companyName", issueVo.getCompanyName()); result.put("ispName", issueVo.getIspName()); result.put("hostingName", issueVo.getHostingName()); UserVo register = this.userService.removeSensitiveUser(issueVo.getRegisterId()); // 등록자 result.put("register", register.getByName()); // 최종 변경일 result.put("modifyDate", issueVo.getModifyDate()); if (StringUtils.isEmpty(issueVo.getStartDate())) { result.put("period", ""); } else { result.put("period", issueVo.getStartDate() + " ~ " + issueVo.getCompleteDate()); } for (IssueCustomFieldValueVo issueCustomFieldValueVo : issueVo.getIssueCustomFieldValueVos()) { // 사용자 정의 필드 값 저장이 아직 안되어 있는 경우 if (result.get("customField_" + issueCustomFieldValueVo.getCustomFieldVo().getId()) == null) { result.put("customField_" + issueCustomFieldValueVo.getCustomFieldVo().getId().toString(), issueCustomFieldValueVo.getUseValue()); } else { // 이미 저장되어 있으면 다중 선택 사용자 정의 필드 값이다. String useValue = result.get("customField_" + issueCustomFieldValueVo.getCustomFieldVo().getId()); result.put("customField_" + issueCustomFieldValueVo.getCustomFieldVo().getId().toString(), useValue + ", " + issueCustomFieldValueVo.getUseValue()); } } results.add(result); } catch (Exception e) { log.error("엑셀 다운로드 오류 발생"); } } return results; } // 이슈 다중 상태 변경 @Override @Transactional public void modifyMultiIssueStatus(IssueForm issueForm) { // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. this.workspaceService.checkUseWorkspace(); User user = this.webAppUtil.getLoginUserObject(); for (Long issueId : issueForm.getIds()) { issueForm.setId(issueId); // 이슈 상태 변경 this.modifyIssueStatus(issueForm, user); } // 담당 부서 수정 if (issueForm.getDepartmentIds().size() > 0) { Issue issue = this.getIssue(issueForm.getId()); Project project = this.projectService.getProject(issueForm.getProjectId()); this.issueDepartmentService.modifyIssueDepartment(issue, project.getWorkspace(), issueForm.getDepartmentIds()); } } // 이슈 Import 용 엑셀 템플릿 다운로드 @Override @Transactional public ModelAndView downloadExcelTemplate(HttpServletRequest request, Model model) { Map conditions; try { // 엑셀 다운로드에 필요한 검색 조건 정보를 추출한다. conditions = CommonUtil.getSearchConditions(request); } catch (IOException e) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EXCEL_CONDITIONS_NOT_EXIST)); } ExportExcelVo excelInfo = new ExportExcelVo(); excelInfo.setHideCount(true); excelInfo.setFileName(this.messageAccessor.message("common.registerExcelIssue")); // 엑셀로 이슈 등록하기 excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.title"), 20, ExportExcelAttrVo.ALIGN_CENTER)); // 제목 excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.content"), 40, ExportExcelAttrVo.ALIGN_CENTER)); // 내용 excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.priority"), 5, ExportExcelAttrVo.ALIGN_CENTER)); // 우선순위 excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.importance"), 5, ExportExcelAttrVo.ALIGN_CENTER)); // 중요도 excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.startDate"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // 시작일 excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.endDate"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // 종료일 excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.company"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // 업체 excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.isp"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // ISP excelInfo.addAttrInfos(new ExportExcelAttrVo("id", this.messageAccessor.message("common.hosting"), 10, ExportExcelAttrVo.ALIGN_CENTER)); // 호스팅 // 프로젝트에 연결된 사용자 정의 필드 정보를 추출하여 엑셀 download 템플릿을 만든다. this.makeIssueExcelTemplateCustomFields(excelInfo, conditions); // 엑셀에 넣을 데이터 - IssueVos 데이터를 엑셀에서 표시할 수 있는 데이터로 변경한다. excelInfo.setDatas(Lists.newArrayList(new IssueVo())); model.addAttribute(Constants.EXCEL, excelInfo); return new ModelAndView(this.excelView); } // 프로젝트에 연결된 사용자 정의 필드 정보를 추출하여 엑셀 download 템플릿을 만든다. private void makeIssueExcelTemplateCustomFields(ExportExcelVo excelInfo, Map conditions) { List issueTypeCustomFields = this.issueTypeCustomFieldService.findByProjectIdAndIssueTypeId(MapUtil.getLong(conditions, "projectId"), MapUtil.getLong(conditions, "issueTypeId")); for (IssueTypeCustomField issueTypeCustomField : issueTypeCustomFields) { excelInfo.addAttrInfos(new ExportExcelAttrVo("id", issueTypeCustomField.getCustomField().getName(), 10, ExportExcelAttrVo.ALIGN_CENTER)); } } // 엑셀 import 로 이슈를 등록한다. @Override @Transactional public void importExcel(IssueForm issueForm, MultipartFile multipartFile) throws Exception { /*StopWatch serviceStart = new StopWatch(); serviceStart.start();*/ // 사용하고 있는 업무 공간이 활성 상태인지 확인한다. 사용 공간에서 로그인한 사용자가 비활성인지 확인한다. this.workspaceService.checkUseWorkspace(); if (multipartFile != null) { // 업로드 파일 확장자 체크 this.verifyMultipartFileExtension(multipartFile); Map priorityMaps = new HashMap<>(); // 우선 순위 모음 Map severityMaps = new HashMap<>(); // 중요도 모음 Map departmentMaps = new HashMap<>(); // 부서 모음 Map customFieldMaps = new HashMap<>(); Map issueNumberMaps = new HashMap<>(); // 이슈 번호 모음 Map issueTypeCustomFieldMaps = new HashMap<>(); // 이슈 타입 + 사용자 정의 필드 연결 정보 Map companyFieldMaps = new HashMap<>(); //업체 모음 Map ispFieldMaps = new HashMap<>(); //isp 모음 Map hostingFieldMaps = new HashMap<>(); //호스팅 모음 Workspace workspace = this.workspaceService.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId()); // 이슈를 넣으려는 업무 공간 // 이슈의 주요 속성을 map 에 저장하여 엑셀 import 에서 지정한 대상(이슈 속성)을 빠르게 찾을 수 있게 한다. this.IssueAttributeMapToList(issueForm, priorityMaps, severityMaps, departmentMaps, customFieldMaps, issueTypeCustomFieldMaps, companyFieldMaps, ispFieldMaps, hostingFieldMaps); // 0.237 - 0.230 List issueForms = Lists.newArrayList(); List headers = Lists.newArrayList(); Workbook workbook; IssueType issueType = new IssueType(); Workflow workflow = new Workflow(); workbook = WorkbookFactory.create(multipartFile.getInputStream()); Sheet sheet = workbook.getSheetAt(0); int lastRowNum = sheet.getLastRowNum() + 1; // 2건 - 제목, 헤더 - 성능을 위해 최대 1만건으로 제한 if (lastRowNum > (EXCEL_IMPORT_MAX_ROWS + 2)) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_MAX_ROWS_OVER)); } for (int rowIndex = 0; rowIndex < lastRowNum; rowIndex++) { // 0번은 헤더는 무시한다. Row row = sheet.getRow(rowIndex); // 헤더 정보를 추출한다 - 사용자 정의 필드 정보를 가져오기 위해 if (rowIndex == 1) { for (int cellIndex = 0; cellIndex < row.getLastCellNum(); cellIndex++) { Cell cell = row.getCell(cellIndex); if (cell == null) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EXCEL_EMPTY_CELL)); } // 엑셀 import 데이터에서 cell 값을 문자열로 변환한다. String cellValue = CommonUtil.convertExcelStringToCell(cell); if (StringUtils.isEmpty(cellValue)) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EXCEL_HEADER_EMPTY_CELL)); } headers.add(cellValue); } } // 1번 헤더부터 데이터 영역 if (rowIndex > 1) { // 이슈로 등록하기 위해 IssueForm 에 데이터를 셋팅한다. IssueForm newIssueForm = this.setIssueFormToExcelField(row, (rowIndex + 1), priorityMaps, severityMaps, customFieldMaps, companyFieldMaps, ispFieldMaps, hostingFieldMaps, headers); ConvertUtil.copyProperties(issueForm, newIssueForm); Project project = this.projectService.getProject(newIssueForm.getProjectId()); Long issueNumber = this.issueNumberGeneratorService.generateIssueNumber(project); newIssueForm.setIssueNumber(issueNumber); issueType = this.issueTypeService.getIssueType(newIssueForm.getIssueTypeId()); workflow = issueType.getWorkflow(); IssueStatus issueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(workflow); newIssueForm.setIssueStatusId(issueStatus.getId()); issueForms.add(newIssueForm); } } if (issueForms.size() < 1) { return; } // 1.176 // 이슈 등록 this.issueMapper.insertBatch(issueForms); for (IssueForm saveIssueForm : issueForms) { Issue issue = new Issue(); ConvertUtil.copyProperties(saveIssueForm, issue); if (issueForm.getInheritYn() != null && issueForm.getInheritYn() && issueForm.getParentIssueId() != null) { Issue parentIssue = this.getIssue(issueForm.getParentIssueId()); // 상위이슈의 파트너 정보 상속 this.inheritPartners(issue, parentIssue); } saveIssueForm.setId(issue.getId()); // 워크플로우 대기 상태의 부서 추가 List departmentsIds = this.workflowDepartmentService.findFirstDepartmentIds(workflow); if (departmentsIds != null && departmentsIds.size() > 0) { this.issueDepartmentService.add(departmentsIds, workspace, issue); } this.setIssuePartners(saveIssueForm, issue); } // 0.416 - 0.439 // 1.373 ~ 1.394 // TODO - 이슈 이력 벌크 등록을 할 경우 프로필 이력 조회에서 부하가 심해서 넣을지 고민해야함. // this.bulkInsertIssueHistory(issueForms); // 이슈 담당자 벌크 등록 this.bulkInsertIssueAssignee(issueForms, workspace); // 0.361 - 0.705 // 1.816 /*StopWatch serviceStart = new StopWatch(); serviceStart.start();*/ // 이슈 사용자 정의 값 필드 벌크 등록 this.bulkInsertIssueCustomFieldValue(issueForms, issueTypeCustomFieldMaps); // 3.628 - 3.445 /*serviceStart.stop(); log.debug("2차 저장 시간 : " + serviceStart.getTime());*/ // 이슈 리스크 벌크 등록 this.bulkInsertIssueRisk(issueForms, workspace); // reverse index 업데이트 this.issueMapper.updateBatch(issueForms); // 증가된 이슈 번호를 업데이트 한다. // issueNumberMaps.put(issueForm.getProjectId(), issueForm.getProjectId()); // this.issueNumberGeneratorService.updateIssueNumber(issueNumberMaps); } } /** * 엑셀로 입력한 파트너 정보 저장 * @param issueForm IssueForm */ private void setIssuePartners(IssueForm issueForm, Issue issue) { //issueCompany 등록 if (issueForm.getIssueCompanyFields() != null && issueForm.getIssueCompanyFields().size() > 0) { for (Map issueCompanyMap : issueForm.getIssueCompanyFields()) { CompanyField companyField = ConvertUtil.convertMapToClass(issueCompanyMap, CompanyField.class); IssueCompany issueCompany = ConvertUtil.convertMapToClass(issueCompanyMap, IssueCompany.class, "id", "registerDate", "modifyDate"); issueCompany.setCompanyField(companyField); issueCompany.setIssue(issue); this.issueCompanyRepository.saveAndFlush(issueCompany); // 사용자가 ISP를 직접 입력하지 않았을 경우 업체에 등록되어있는 ISP 설정 if (issueForm.getIssueIspFields() == null || issueForm.getIssueIspFields().size() < 1) { // 업체의 ISP가 있는 경우 issueISP 등록 if (companyField.getIspId() != null && companyField.getIspId() != -1) { IspField ispField = this.ispFieldService.getIsp(companyField.getIspId()); IssueIsp issueIsp = ConvertUtil.copyProperties(ispField, IssueIsp.class, "id", "registerDate", "modifyDate"); issueIsp.setIspField(ispField); issueIsp.setIssue(issue); this.issueIspRepository.saveAndFlush(issueIsp); } } // 사용자가 호스팅을 직접 입력하지 않았을 경우 업체에 등록되어있는 호스팅 설정 if (issueForm.getIssueHostingFields() == null || issueForm.getIssueHostingFields().size() < 1) { // 업체의 호스팅이 있는 경우 issueHosting 등록 if (companyField.getHostingId() != null && companyField.getHostingId() != -1) { HostingField hostingField = this.hostingFieldService.getHosting(companyField.getHostingId()); IssueHosting issueHosting = ConvertUtil.copyProperties(hostingField, IssueHosting.class, "id", "registerDate", "modifyDate"); issueHosting.setHostingField(hostingField); issueHosting.setIssue(issue); this.issueHostingRepository.saveAndFlush(issueHosting); } } } } //issueIsp 등록 if (issueForm.getIssueIspFields() != null && issueForm.getIssueIspFields().size() > 0) { for (Map issueIspMap : issueForm.getIssueIspFields()) { IssueIsp issueIsp = ConvertUtil.convertMapToClass(issueIspMap, IssueIsp.class, "id", "registerDate", "modifyDate"); IspField ispField = ConvertUtil.convertMapToClass(issueIspMap, IspField.class); issueIsp.setIspField(ispField); issueIsp.setIssue(issue); this.issueIspRepository.saveAndFlush(issueIsp); } } //issueHosting 등록 if (issueForm.getIssueHostingFields() != null && issueForm.getIssueHostingFields().size() > 0) { for (Map issueHostingMap : issueForm.getIssueHostingFields()) { IssueHosting issueHosting = ConvertUtil.convertMapToClass(issueHostingMap, IssueHosting.class, "id", "registerDate", "modifyDate"); HostingField hostingField = ConvertUtil.convertMapToClass(issueHostingMap, HostingField.class); issueHosting.setHostingField(hostingField); issueHosting.setIssue(issue); this.issueHostingRepository.saveAndFlush(issueHosting); } } } // 업로드 파일 확장자 체크 private void verifyMultipartFileExtension(MultipartFile multipartFile) { multipartFile.getOriginalFilename(); int pos = multipartFile.getOriginalFilename().lastIndexOf("."); String ext = multipartFile.getOriginalFilename().substring(pos + 1); if (!ext.equals("xlsx")) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.EXCEL_NOT_EXTENSION)); } } // 이슈 이력 벌크 등록 private void bulkInsertIssueHistory(List issueForms) { List> issueHistoryMaps = Lists.newArrayList(); for (IssueForm issueForm : issueForms) { Map issueHistoryMap = new HashMap<>(); issueHistoryMap.put("issueId", issueForm.getId()); issueHistoryMap.put("projectId", issueForm.getProjectId()); issueHistoryMap.put("registerId", this.webAppUtil.getLoginId()); issueHistoryMap.put("issueHistoryType", IssueHistoryType.ADD.toString()); StringBuilder description = new StringBuilder(); // 이력 테스트 추출 this.issueHistoryService.makeDescription(description, IssueHistoryType.ADD, null); issueHistoryMap.put("description", description.toString()); issueHistoryMaps.add(issueHistoryMap); } // 이슈 이력 벌크 등록 this.issueMapper.insertHistoryBatch(issueHistoryMaps); } // 이슈 담당자 벌크 등록 private void bulkInsertIssueAssignee(List issueForms, Workspace workspace) { List> issueAssigneeMaps = Lists.newArrayList(); for (IssueForm issueForm : issueForms) { for (Long departmentId : issueForm.getDepartmentIds()) { Map issueAssigneeMap = new HashMap<>(); issueAssigneeMap.put("issueId", issueForm.getId()); //issueAssigneeMap.put("userId", userId); issueAssigneeMap.put("departmentId", departmentId); issueAssigneeMap.put("workspaceId", workspace.getId()); issueAssigneeMap.put("registerId", this.webAppUtil.getLoginId()); issueAssigneeMaps.add(issueAssigneeMap); } } if (issueAssigneeMaps.size() > 0) { // 이슈 담당자 벌크 등록 //this.issueUserService.insertIssueUser(issueAssigneeMaps); this.issueDepartmentService.insertIssueDepartment(issueAssigneeMaps); } } // 이슈 리스크 벌크 등록 private void bulkInsertIssueRisk(List issueForms, Workspace workspace) { List> issueRiskMaps = Lists.newArrayList(); for (IssueForm issueForm : issueForms) { Map issueRiskMap = new HashMap<>(); issueRiskMap.put("issueId", issueForm.getId()); issueRiskMap.put("changeAssigneeCount", 0L); issueRiskMap.put("changeDepartmentCount", 0L); issueRiskMap.put("changeIssueStatusCount", 0L); issueRiskMap.put("workspaceId", workspace.getId()); issueRiskMap.put("issueStatusIds", issueForm.getIssueStatusId()); issueRiskMap.put("registerId", this.webAppUtil.getLoginId()); issueRiskMaps.add(issueRiskMap); } if (issueRiskMaps.size() > 0) { // 이슈 리스크 벌크 등록 this.issueMapper.insertIssueRiskBatch(issueRiskMaps); } } // 이슈 사용자 정의 필드 선택 값 벌크 등록 private void bulkInsertIssueCustomFieldValue(List issueForms, Map issueTypeCustomFieldMaps) { List> issueCustomFieldValueMaps = Lists.newArrayList(); for (IssueForm issueForm : issueForms) { for (Map issueCustomField : issueForm.getIssueCustomFields()) { String findKey = issueForm.getIssueTypeId().toString() + MapUtil.getString(issueCustomField, "customFieldId"); // 이슈 타입 + 사용자 정의 필드 연결정보가 없다면 저장하지 않는다. if (issueTypeCustomFieldMaps.get(findKey) == null) { continue; } issueCustomField.put("issueTypeCustomFieldId", issueTypeCustomFieldMaps.get(findKey)); issueCustomField.put("issueId", issueForm.getId()); issueCustomField.put("registerId", this.webAppUtil.getLoginId()); issueCustomFieldValueMaps.add(issueCustomField); } // 엑셀에 업체명을 입력하지 않았을 경우 같은 도메인 업체 찾기 this.findPartnerByDomain(issueForm); } if (issueCustomFieldValueMaps.size() > 0) { this.issueMapper.insertIssueCustomFieldValueBatch(issueCustomFieldValueMaps); } } /** * 엑셀에 업체명을 입력하지 않았을 경우 같은 도메인 업체 찾기 * @param issueForm IssueForm */ private void findPartnerByDomain(IssueForm issueForm) { if (issueForm.getIssueCompanyFields() == null || issueForm.getIssueCompanyFields().size() < 1) { // 같은 도메인 업체 찾기 IssueForm partners = this.findCompanyField(issueForm); Issue issue = this.findOne(issueForm.getId()); if (partners.getIssueCompanyFields() != null && partners.getIssueCompanyFields().size() > 0) { for (Map company : partners.getIssueCompanyFields()) { IssueCompany issueCompany = ConvertUtil.convertMapToClass(company, IssueCompany.class); CompanyField companyField = ConvertUtil.convertMapToClass(company, CompanyField.class); issueCompany.setCompanyField(companyField); issueCompany.setIssue(issue); this.issueCompanyRepository.saveAndFlush(issueCompany); } } if (partners.getIssueIspFields() != null && partners.getIssueIspFields().size() > 0) { for (Map isp : partners.getIssueIspFields()) { IssueIsp issueIsp = ConvertUtil.convertMapToClass(isp, IssueIsp.class); IspField ispField = ConvertUtil.convertMapToClass(isp, IspField.class); issueIsp.setIspField(ispField); issueIsp.setIssue(issue); this.issueIspRepository.saveAndFlush(issueIsp); } } if (partners.getIssueHostingFields() != null && partners.getIssueHostingFields().size() > 0) { for (Map hosting : partners.getIssueHostingFields()) { IssueHosting issueHosting = ConvertUtil.convertMapToClass(hosting, IssueHosting.class); HostingField hostingField = ConvertUtil.convertMapToClass(hosting, HostingField.class); issueHosting.setHostingField(hostingField); issueHosting.setIssue(issue); this.issueHostingRepository.saveAndFlush(issueHosting); } } } } // 이슈의 주요 속성을 map 에 저장하여 엑셀 import 에서 지정한 대상(이슈 속성)을 빠르게 찾을 수 있게 한다. private void IssueAttributeMapToList(IssueForm issueForm, Map priorityMaps, Map severityMaps, Map departmentMaps, Map customFieldMaps,Map issueTypeCustomFieldMaps, Map companyFieldMaps, Map ispFieldMaps, Map hostingFieldMaps) { Project project = this.projectService.getProject(issueForm.getProjectId()); for (IssueTypeCustomField issueTypeCustomField : project.getIssueTypeCustomFields()) { // 빠르게 찾기 위해 이슈 타입 아이디 + 사용자 정의 필드 아이디를 키로 한다. String makeKey = issueTypeCustomField.getIssueType().getId().toString() + issueTypeCustomField.getCustomField().getId().toString(); issueTypeCustomFieldMaps.put(makeKey, issueTypeCustomField.getId()); } // 우선순위를 바로 찾을 수 있게 준비 List priorities = this.priorityService.findByWorkspaceId(); for (Priority priority : priorities) { priorityMaps.put(priority.getName(), priority); } // 중요도를 바로 찾을 수 있게 준비 List severities = this.severityService.findByWorkspaceId(); for (Severity severity : severities) { severityMaps.put(severity.getName(), severity); } // 사용자 정의 필드를 바로 찾을 수 있게 준비 List customFields = this.customFieldService.findByWorkspaceId(); for (CustomField customField : customFields) { customFieldMaps.put(customField.getName(), customField); } // 업체 정보를 바로 찾을 수 있게 준비 List companyFields = this.companyFieldService.findAll(); for (CompanyField companyField : companyFields) { companyFieldMaps.put(companyField.getName(), companyField); } // ISP 정보를 바로 찾을 수 있게 준비 List ispFields = this.ispFieldService.findAll(); for (IspField ispField : ispFields) { ispFieldMaps.put(ispField.getName(), ispField); } // 호스팅 정보를 바로 찾을 수 있게 준비 List hostingFields = this.hostingFieldService.findAll(); for (HostingField hostingField : hostingFields) { hostingFieldMaps.put(hostingField.getName(), hostingField); } } /** * cell String으로 변환 함수 * @param cell Cell * @param isNull boolean * @return String */ private String stringToCell (Cell cell, boolean isNull) { String cellStr = ""; if (!isNull) { cellStr = CommonUtil.convertExcelStringToCell(cell); // 공백 제거 cell.setCellValue(cellStr.trim()); } else { cell.setCellValue(cellStr); } return cellStr; } /** * cell NULL 체크 함수 * 빈 값이 아닌 cell 체크 * @param cell Cell * @return boolean */ private Boolean cellNullCheck (Cell cell) { int cellType = cell.getCellType(); if (cellType < Cell.CELL_TYPE_BLANK) { if (cellType == Cell.CELL_TYPE_STRING) { if (cell.getStringCellValue() != null && !cell.getStringCellValue().equals("")) { return false; } } else { return false; } } return true; } // 엑셀 필드에 있는 정보를 이슈 form 으로 옮긴다. private IssueForm setIssueFormToExcelField(Row row, int rowIndex, Map priorityMaps, Map severityMaps, Map customFieldMaps, Map companyFieldMaps, Map ispFieldMaps, Map hostingFieldMaps, List headers) throws ParseException { IssueForm issueForm = new IssueForm(); issueForm.setRegisterId(this.webAppUtil.getLoginId()); // 제목, 내용, 프로젝트 키, 이슈 타입, 우선순위, 중요도, 담당자, 시작일, 종료일, 사용자 정의 필드 for (int cellIndex = 0; cellIndex < headers.size(); cellIndex++) { Cell cell = row.getCell(cellIndex); String cellStr = ""; boolean isNull = true; if (cell != null) { isNull = cellNullCheck(cell); cellStr = stringToCell(cell, isNull); //cell을 String으로 변환 } switch (cellIndex) { case 0: // 이슈 제목을 IssueForm 에 저장한다. if (isNull) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_ISSUE_TITLE_IS_NULL, rowIndex)); } this.setIssueFormTitle(cellStr, issueForm, rowIndex); break; case 1: // 내용 issueForm.setDescription(cellStr); break; case 2: // 우선순위를 IssueForm 에 저장한다. if (isNull) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_PRIORITY_IS_NULL, rowIndex)); } this.setIssueFormPriority(cellStr, priorityMaps, issueForm, rowIndex); break; case 3: // 중요도를 IssueForm 에 저장한다. if (isNull) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_SEVERITY_IS_NULL, rowIndex)); } this.setIssueFormSeverity(cellStr, severityMaps, issueForm, rowIndex); break; /*case 6: // 담당자를 IssueForm 에 저장한다. this.setIssueFormAssignee(cell, userMaps, issueForm, project); break;*/ case 4: // 시작일을 IssueForm 에 저장한다. this.setIssueFormPeriod(cellStr, issueForm, true, rowIndex, isNull); break; case 5: // 종료일을 IssueForm 에 저장한다. this.setIssueFormPeriod(cellStr, issueForm, false, rowIndex, isNull); break; case 6: // 업체를 IssueForm 에 저장한다. this.setIssueFormCompanyField(cellStr, companyFieldMaps, issueForm, rowIndex); break; case 7: // ISP를 IssueForm 에 저장한다. this.setIssueFormIspField(cellStr, ispFieldMaps, issueForm, rowIndex); break; case 8: // 호스팅을 IssueForm 에 저장한다. this.setIssueFormHostingField(cellStr, hostingFieldMaps, issueForm, rowIndex); break; default: // 9번 부터는 사용자 정의 필드. 사용자 정의 필드 정보를 IssueForm 에 저장한다. this.setIssueFormCustomFieldValue(cellStr, customFieldMaps, issueForm, headers.get(cellIndex), rowIndex); } } return issueForm; } private void setIssueFormHostingField(String cell, Map hostingFieldMaps, IssueForm issueForm, int rowIndex) { if (cell.length() > 0) { Map issueHostingFields = new HashMap<>(); HostingField hostingFieldMap = hostingFieldMaps.get(cell); if (hostingFieldMap == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_HOSTING_NOT_EXIST, rowIndex)); } ConvertUtil.copyProperties(hostingFieldMap, issueHostingFields); issueForm.addIssueHostingField(issueHostingFields); } } private void setIssueFormIspField(String cell, Map ispFieldMaps, IssueForm issueForm, int rowIndex) { if (cell.length() > 0) { Map issueIspFields = new HashMap<>(); IspField ispFieldMap = ispFieldMaps.get(cell); if (ispFieldMap == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_ISP_NOT_EXIST, rowIndex)); } ConvertUtil.copyProperties(ispFieldMap, issueIspFields); issueForm.addIssueIspField(issueIspFields); } } private void setIssueFormCompanyField(String cell, Map companyFieldMaps, IssueForm issueForm, int rowIndex) { if (cell.length() > 0) { Map issueCompanyFields = new HashMap<>(); CompanyField companyFieldMap = companyFieldMaps.get(cell); if (companyFieldMap == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_COMPANY_NOT_EXIST, rowIndex)); } ConvertUtil.copyProperties(companyFieldMap, issueCompanyFields); issueForm.addissueCompanyField(issueCompanyFields); } } // 이슈 제목을 IssueForm 에 저장한다. private void setIssueFormTitle(String title, IssueForm issueForm, int rowIndex) { // 제목 유효성 체크 this.verifyTitle(title); issueForm.setTitle(title); } // 프로젝트 아이디를 IssueForm 에 저장한다. private Project setIssueFormProject(Cell cell, Map projectMaps, IssueForm issueForm, int rowIndex) { if (cell == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_PROJECT_KEY_IS_NULL, rowIndex)); } Project project = projectMaps.get(CommonUtil.convertExcelStringToCell(cell).toUpperCase()); if (project == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_PROJECT_NOT_EXIST, rowIndex)); } issueForm.setProjectId(project.getId()); return project; } // 이슈 고유 번호를 IssueForm 에 저장한다. private void setIssueFormIssueNumber(IssueForm issueForm, Map issueNumberMaps, Project project, int rowIndex) { // 이슈 고유 번호 Long issueNumber = issueNumberMaps.get(project.getId()); if (issueNumber == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_NUMBER_NOT_EXIST, rowIndex)); } issueForm.setIssueNumber(issueNumber); issueNumberMaps.put(project.getId(), ++issueNumber); // 이슈 번호를 1씩 증가 시킨다. } // 우선순위를 IssueForm 에 저장한다. private void setIssueFormPriority(String priorityStr, Map priorityMaps, IssueForm issueForm, int rowIndex) { Priority priority = priorityMaps.get(priorityStr); if (priority == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_PRIORITY_NOT_EXIST, rowIndex)); } issueForm.setPriorityId(priority.getId()); } // 중요도를 IssueForm 에 저장한다. private void setIssueFormSeverity(String strSeverity, Map severityMaps, IssueForm issueForm, int rowIndex) { Severity severity = severityMaps.get(strSeverity); if (severity == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_SEVERITY_NOT_EXIST, rowIndex)); } issueForm.setSeverityId(severity.getId()); } private void setIssueFormAssignee(Cell cell, Map userMaps, IssueForm issueForm, Project project) { if (cell != null) { String[] splitAssignee = CommonUtil.convertExcelStringToCell(cell).split("#"); Map userMap = (Map) MapUtil.getObject(userMaps, project.getProjectKey()); List userIds = Lists.newArrayList(); for (String account : splitAssignee) { if (MapUtil.getLong(userMap, account) != null) { userIds.add(MapUtil.getLong(userMap, account)); } } issueForm.setUserIds(userIds); } } // 시작일, 종료일을 IssueForm 에 저장한다. private void setIssueFormPeriod(String periodDate, IssueForm issueForm, Boolean checkStartDate, int rowIndex, boolean isNull) throws ParseException { if (!isNull) { Date startDate = DateUtil.convertStrToDateOnly(periodDate); if (startDate == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_PERIOD_NOT_VALIDITY_EMPTY, rowIndex)); } if (checkStartDate) { issueForm.setStartDate(DateUtil.convertDateToStr(startDate, "yyyy-MM-dd")); } else { issueForm.setCompleteDate(DateUtil.convertDateToStr(startDate, "yyyy-MM-dd")); // 종료일만 입력 했을 경우 if (issueForm.getCompleteDate() != null && issueForm.getStartDate() == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_PERIOD_NOT_VALIDITY_EMPTY_START, rowIndex)); } try { // 날짜 유효성 체크 this.checkStartCompleteDate(issueForm.getStartDate(), issueForm.getCompleteDate()); } catch (OwlRuntimeException e) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_PERIOD_NOT_VALID, rowIndex)); } } } } // 사용자 정의 필드 정보를 IssueForm 에 저장한다.- private void setIssueFormCustomFieldValue(String cellValue, Map customFieldMaps, IssueForm issueForm, String customFieldName, int rowIndex) { Map issueCustomFieldMap = new HashMap<>(); CustomField customField = customFieldMaps.get(customFieldName); if (customField == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_HEADER_CUSTOM_FIELD_NOT_EXIST, rowIndex)); } // 사용자 정의 필드 값이 공백이면 중지 if (StringUtils.isEmpty(cellValue)) { return; } boolean validity = false; switch (customField.getCustomFieldType()) { case INPUT: case NUMBER: case DATETIME: case IP_ADDRESS: case EMAIL: case SITE: case TEL: if (customField.getCustomFieldType() != INPUT && cellValue.length() > 100) { //INPUT 타입은 100자 제한 없음 throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.CUSTOM_FIELD_TEXT_TYPE_MAX_LENGTH_OUT)); } //DATETIME일 경우 format 변경 if (customField.getCustomFieldType() == DATETIME) { Date date = DateUtil.convertStrToDate(cellValue); if (date == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_DATETIME_NOT_DASH, rowIndex)); } } //IP_ADDRESS일 경우 정규표현식 체크 if (customField.getCustomFieldType() == IP_ADDRESS) { String regExp = "^(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)\\." + "(25[0-5]|2[0-4][0-9]|[01]?[0-9][0-9]?)$"; if (!cellValue.matches(regExp)) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_IMPORT_IP_ADDRESS_NOT_VALIDITY, rowIndex)); } } issueCustomFieldMap.put("customFieldId", customField.getId()); issueCustomFieldMap.put("useValue", cellValue); issueForm.addIssueCustomFields(issueCustomFieldMap); break; case SINGLE_SELECT: // 값 유효성 체크 for (CustomFieldValue customFieldValue : customField.getCustomFieldValues()) { if (customFieldValue.getValue().equals(cellValue)) { validity = true; break; } } if (!validity) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_CUSTOM_FIELD_VALUE_NOT_VALIDITY, rowIndex)); } issueCustomFieldMap.put("customFieldId", customField.getId()); issueCustomFieldMap.put("useValue", cellValue); issueForm.addIssueCustomFields(issueCustomFieldMap); break; case MULTI_SELECT: // 값 유효성 체크 String[] useValues = cellValue.split("#"); // 해, 달 for (String useValue : useValues) { for (CustomFieldValue customFieldValue : customField.getCustomFieldValues()) { if (customFieldValue.getValue().equals(useValue)) { validity = true; Map multiValueMap = new HashMap<>(); multiValueMap.put("customFieldId", customField.getId()); multiValueMap.put("useValue", useValue); issueForm.addIssueCustomFields(multiValueMap); } } } if (!validity) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.EXCEL_CUSTOM_FIELD_VALUE_NOT_VALIDITY, rowIndex)); } break; } } // 프로젝트에 있는 모든 이슈 정보를 조회한다. @Override @Transactional(readOnly = true) public List findByProjectId(Long projectId) { List> issueMaps = this.issueMapper.findByProjectId(projectId); List issueIds = Lists.newArrayList(); for (Map issueMap : issueMaps) { Long issueId = MapUtil.getLong(issueMap, "id"); if (issueId != null) { issueIds.add(issueId); } } return issueIds; } // 이슈를 대상자들에게 메일로 발송한다. @Override @Transactional(readOnly = true) public void sendIssueEmail(IssueForm issueForm) { if (issueForm.getSendEmails().size() < 1) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_SEND_USER)); }else if (issueForm.getTemplate() != null){ throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_SELECT_TEMPLATE)); } Issue issue = this.getIssue(issueForm.getId()); Map issueMap = new HashMap<>(); // 이슈 정보를 이메일 전송에 사용하기 위해 Map 형태로 변환한다. this.makeIssueMapToIssue(issue, issueMap); // 발신자 표시 UserVo toUser = this.webAppUtil.getLoginUser(); issueMap.put("toUser", toUser.getName() + "(" + CommonUtil.decryptAES128(toUser.getAccount()) + ")"); // 이슈 링크 String projectKey = issue.getProject().getProjectKey(); Long IssueNumber = issue.getIssueNumber(); String link = this.configuration.getEmailSendUrl() + "/#/issues/issueList?projectKey=" + projectKey + "&issueNumber=" + IssueNumber.toString(); issueMap.put("issueLink", link); issueMap.put("projectLink", link); // 사용자 시스템 기능 사용 정보 수집 log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_ANOTHER_USER_SEND_EMAIL)); this.systemEmailService.directEmail(issueForm.getSendEmails().toArray(new String[issueForm.getSendEmails().size()]), EmailType.ISSUE_SEND, issueMap, null); } // 이슈를 템플릿에 따라 파트너 담당자에게 메일로 발송한다. @Override @Transactional(readOnly = true) public void sendIssueEmailPartners(EmailTemplateForm emailTemplateForm, List multipartFiles) { if (emailTemplateForm.getSendEmails().size() < 1) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_SEND_USER)); }else if (emailTemplateForm.getTemplate() == null){ throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_SELECT_TEMPLATE)); } else if (emailTemplateForm.getIssueId() == null) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_EXIST)); } Issue issue = this.getIssue(emailTemplateForm.getIssueId()); // 발신자 표시 User user = this.webAppUtil.getLoginUserObject(); UserVo toUser = this.webAppUtil.getLoginUser(); // 사용자 시스템 기능 사용 정보 수집 log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_ANOTHER_USER_SEND_EMAIL)); StringBuilder sb = new StringBuilder(); Locale locale = CommonUtil.getUserLanguage(user.getLanguage()); String[] sendMails = ConvertUtil.ToArray(emailTemplateForm.getSendEmails()); for(int i=0; i < sendMails.length; i++) { sendMails[i] = CommonUtil.decryptAES128(sendMails[i]); } this.systemEmailService.sendEmail(emailTemplateForm.getTitle(), emailTemplateForm.getTemplate(), sendMails, null, multipartFiles); this.issueHistoryService.detectSendIssueMail(IssueHistoryType.SEND, emailTemplateForm.getSendEmails(), sb); this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.SEND, sb.toString()); } @Override public void sendCommonEmail(EmailCommonForm emailCommonForm, List multipartFiles) { if (emailCommonForm.getSendEmails().size() < 1) { throw new OwlRuntimeException( this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_SEND_USER)); } Issue issue = null; if(emailCommonForm.getIssueId() != null) { issue = this.getIssue(emailCommonForm.getIssueId()); } // 발신자 표시 User user = this.webAppUtil.getLoginUserObject(); UserVo toUser = this.webAppUtil.getLoginUser(); // 사용자 시스템 기능 사용 정보 수집 log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_ANOTHER_USER_SEND_EMAIL)); StringBuilder sb = new StringBuilder(); Locale locale = CommonUtil.getUserLanguage(user.getLanguage()); String[] sendMails = ConvertUtil.ToArray(emailCommonForm.getSendEmails()); for(int i=0; i < sendMails.length; i++) { sendMails[i] = CommonUtil.decryptAES128(sendMails[i]); } this.systemEmailService.sendEmail(emailCommonForm.getTitle(), emailCommonForm.getDescription(), sendMails, null, multipartFiles); if (issue != null) { this.issueHistoryService.detectSendIssueMail(IssueHistoryType.SEND, emailCommonForm.getSendEmails(), sb); this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.SEND, sb.toString()); } } // 예약 발생 이슈를 실행한다 @Override @Transactional public void reservationIssue() { List issueReservations = this.issueReservationService.findByIssueReservationTypeNotNull(); Calendar calendar = Calendar.getInstance(); int dayOfWeek = calendar.get(Calendar.DAY_OF_WEEK); int month = calendar.get(Calendar.MONTH) + 1; int date = calendar.get(Calendar.DATE); for (IssueReservation issueReservation : issueReservations) { switch (issueReservation.getIssueReservationType()) { case DAY: // 이슈를 다시 생성 상태로 변경시킨다. this.occurReservationIssue(issueReservation.getIssue()); break; case WEEK: if (dayOfWeek == Integer.parseInt(issueReservation.getReservation())) { // 이슈를 다시 생성 상태로 변경시킨다. this.occurReservationIssue(issueReservation.getIssue()); } break; case MONTH: if (date == Integer.parseInt(issueReservation.getReservation())) { // 이슈를 다시 생성 상태로 변경시킨다. this.occurReservationIssue(issueReservation.getIssue()); } break; case YEAR: String[] reservations = issueReservation.getReservation().split("-"); if (reservations.length > 1) { if ((Integer.parseInt(reservations[0]) == month) && Integer.parseInt(reservations[1]) == date) { // 이슈를 다시 생성 상태로 변경시킨다. this.occurReservationIssue(issueReservation.getIssue()); } } break; } } } // 이슈를 다시 생성 상태로 변경시킨다. private void occurReservationIssue(Issue issue) { if (!issue.getIssueStatus().getIssueStatusType().equals(IssueStatusType.READY)) { // 이슈를 생성 상태로 변경시킨다. IssueStatus issueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(issue.getIssueType().getWorkflow()); if (issueStatus != null) { StringBuilder detectIssueChange = new StringBuilder(); // 예약 발생으로 이슈 상태 변경된 정보를 기록한다. this.issueHistoryService.detectReservationIssueStatus(issue, detectIssueChange, issueStatus); issue.setIssueStatus(issueStatus); this.issueRepository.saveAndFlush(issue); // 이슈 상태 변경 정보를 이력으로 남긴다. this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, detectIssueChange.toString()); // 이슈 버전 생성 this.issueVersionService.addIssueVersion(issue); } } } @Autowired private ProjectMapper projectMapper; @Override @Transactional(readOnly = true) public Map findTask(IssueCondition taskCondition) { StopWatch serviceStart = new StopWatch(); serviceStart.start(); ProjectCondition projectCondition = new ProjectCondition(this.webAppUtil.getLoginUser().getLastProjectId(), this.webAppUtil.getLoginId()); List> checkProjectRole = this.projectMapper.checkIncludeProject(projectCondition); if (checkProjectRole.size() < 1) { throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.PROJECT_NOT_INCLUDE_USER)); } // 이중으로 역할 체크를 해서 참여하지 않은 사용자가 해당 프로젝트에 접근 못하도록 한다. taskCondition.setLoginUserId(this.webAppUtil.getLoginId()); // List> results = this.taskMapper.find(taskCondition); List> results = this.issueMapper.find(taskCondition); serviceStart.stop(); long executeTime = serviceStart.getTime(); Map tasks = new HashMap<>(); // 워크플로우 상태 id 를 키로 만든다. for (Long workflowStatusId : taskCondition.getStatusIds()) { tasks.put(workflowStatusId.toString(), Lists.newArrayList()); } Map taskUserSave = new HashMap<>(); // 전체 task_id를 모은다. for (Map result : results) { Long taskId = MapUtil.getLong(result, "id"); taskCondition.addIssueIds(taskId.toString()); // 각 task_id 별로 사용자 정보를 저장할 공간을 미리 확보한다. taskUserSave.put(taskId.toString(), Lists.newArrayList()); } List> taskDepartments = Lists.newArrayList(); // task 가 하나도 없을 경우에는 조회를 하지 않는다. if (!taskCondition.getIssueIds().isEmpty()) { //taskUsers = this.issueMapper.getAllTaskUser(taskCondition); taskDepartments = this.issueMapper.getAllTaskUser(taskCondition); } // task_id 에 매칭되는 담당자 정보를 준비한다. /*for (Map taskUser : taskUsers) { Long taskId = MapUtil.getLong(taskUser, "taskId"); List userVos = (List)taskUserSave.get(taskId.toString()); userVos.add(ConvertUtil.convertMapToClass(taskUser, UserVo.class)); }*/ for (Map taskDepartment : taskDepartments) { Long taskId = MapUtil.getLong(taskDepartment, "taskId"); List departmentVos = (List)taskUserSave.get(taskId.toString()); departmentVos.add(ConvertUtil.convertMapToClass(taskDepartment, DepartmentVo.class)); } for (Map result : results) { IssueVo taskVo = ConvertUtil.convertMapToClass(result, IssueVo.class); // 중요도 셋팅 if (MapUtil.getLong(result, "priorityId") != null) { PriorityVo priorityVo = new PriorityVo(); priorityVo.setId(MapUtil.getLong(result, "priorityId")); priorityVo.setName(MapUtil.getString(result, "priorityName")); priorityVo.setColor(MapUtil.getString(result, "priorityColor")); taskVo.setPriorityVo(priorityVo); } // 워크플로우 상태 셋팅 if (MapUtil.getLong(result, "workflowStatusId") != null) { WorkflowStatusVo workflowStatusVo = new WorkflowStatusVo(); workflowStatusVo.setId(MapUtil.getLong(result, "workflowStatusId")); workflowStatusVo.setName(MapUtil.getString(result, "workflowStatusName")); workflowStatusVo.setColor(MapUtil.getString(result, "workflowStatusColor")); taskVo.setWorkflowStatusVo(workflowStatusVo); } // 담당자 셋팅 //List userVos = (List)taskUserSave.get(taskVo.getId().toString()); //taskVo.setUserVos(userVos); // 담당부서 세팅 List departmentVos = (List)taskUserSave.get(taskVo.getId().toString()); taskVo.setDepartmentVos(departmentVos); List taskVos = (List)tasks.get(MapUtil.getString(result, "workflowStatusId")); taskVos.add(taskVo); tasks.put(MapUtil.getString(result, "workflowStatusId"), taskVos); } /*serviceStart.stop(); long executeTime = serviceStart.getTime();*/ return tasks; } @Transactional @Override public void modifyParentIssue(IssueForm issueDownForm) { //Issue issue = this.getIssue(issueDownForm.getId()); //하위 이슈 Long newParentIssueId = issueDownForm.getParentIssueId(); //변경할 하위이슈의 상위이슈 StringBuilder sb = new StringBuilder(); for (Long downId : issueDownForm.getIds()) { Issue issue = this.getIssue(downId); Issue parentIssue = issue.getParentIssue(); //변경 전 하위이슈의 상위이슈 if(parentIssue != null && parentIssue.getId().equals(newParentIssueId)){ //변경 전 하위이슈의 상위이슈가 존재 할 경우 this.issueHistoryService.detectDownIssues(IssueHistoryType.DELETE, issue, sb); this.issueHistoryService.addIssueHistory(parentIssue, IssueHistoryType.MODIFY, sb.toString()); } if (newParentIssueId != null) { // 추가 할 경우 parentIssue = this.getIssue(newParentIssueId); //상위이슈(myIssue) issue.setParentIssue(parentIssue); //myIssue를 하위이슈의 상위이슈로 set this.issueHistoryService.detectDownIssues(IssueHistoryType.ADD, issue, sb); //issue = 하위이슈 } else{ // 삭제 할 경우 this.issueHistoryService.detectDownIssues(IssueHistoryType.DELETE, issue, sb); issue.setParentIssue(null); } if (issueDownForm.getInheritYn() != null && issueDownForm.getInheritYn() && issue.getParentIssue() != null) { // 상위이슈의 파트너 정보 상속받기 issue = this.inheritPartners(issue, parentIssue); } this.issueHistoryService.addIssueHistory(parentIssue, IssueHistoryType.MODIFY, sb.toString()); //parentIssue = myIssue(기록은 현재 상세페이지에 해야하니까) this.issueRepository.saveAndFlush(issue); } } /** * 상위이슈의 파트너 정보 상속받기 * @param issue Issue * @param parentIssue Issue * @return Issue */ private Issue inheritPartners(Issue issue, Issue parentIssue) { if (parentIssue != null) { if (parentIssue.getIssueType().getInheritPartners()) { IssueCompany issueCompany = new IssueCompany(); IssueIsp issueIsp = new IssueIsp(); IssueHosting issueHosting = new IssueHosting(); if (parentIssue.getIssueCompanies() != null && parentIssue.getIssueCompanies().size() > 0) { issue.getIssueCompanies().clear(); issue.getIssueCompanies().addAll(parentIssue.getIssueCompanies()); Iterator itrCompany = issue.getIssueCompanies().iterator(); ConvertUtil.copyProperties(itrCompany.next(), issueCompany, "id"); issueCompany.setIssue(issue); issueCompany.setCompanyField(parentIssue.getIssueCompanies().iterator().next().getCompanyField()); this.issueCompanyRepository.saveAndFlush(issueCompany); } else { this.issueCompanyRepository.deleteByIssueId(issue.getId()); this.issueCompanyRepository.flush(); } if (parentIssue.getIssueIspFields() != null && parentIssue.getIssueIspFields().size() > 0) { issue.getIssueIspFields().clear(); issue.getIssueIspFields().addAll(parentIssue.getIssueIspFields()); Iterator itrIsp = issue.getIssueIspFields().iterator(); ConvertUtil.copyProperties(itrIsp.next(), issueIsp, "id"); issueIsp.setIssue(issue); issueIsp.setIspField(parentIssue.getIssueIspFields().iterator().next().getIspField()); this.issueIspRepository.saveAndFlush(issueIsp); } else { this.issueIspRepository.deleteByIssueId(issue.getId()); this.issueIspRepository.flush(); } if (parentIssue.getIssueHostingFields() != null && parentIssue.getIssueHostingFields().size() > 0) { issue.getIssueHostingFields().clear(); issue.getIssueHostingFields().addAll(parentIssue.getIssueHostingFields()); Iterator itrHosting = issue.getIssueHostingFields().iterator(); ConvertUtil.copyProperties(itrHosting.next(), issueHosting, "id"); issueHosting.setIssue(issue); issueHosting.setHostingField(parentIssue.getIssueHostingFields().iterator().next().getHostingField()); this.issueHostingRepository.saveAndFlush(issueHosting); } else { this.issueHostingRepository.deleteByIssueId(issue.getId()); this.issueHostingRepository.flush(); } } } return issue; } @Override public void findPartner(Map resJsonData, Map params) { Long issueTypeId = MapUtil.getLong(params, "issueTypeId"); List usePartnerVos = Lists.newArrayList(); Integer using = 0; if (issueTypeId != null) { IssueType issueType = this.issueTypeService.getIssueType(issueTypeId); // 이슈의 이슈유형 객체 using = issueType.getUsePartner() != null ? issueType.getUsePartner().intValue() : 0; // 이슈유형별로 사용중인 업체/ISP/호스팅 값 } else { for (int partner : UsePartner.partners) { using += partner; } } for (Integer usePartner : UsePartner.partners) { //1(업체), 2(ISP), 4(호스팅) UsePartnerVo usePartnerVo = UsePartner.checkUsePartner(using, usePartner); if (usePartnerVo != null) { usePartnerVos.add(usePartnerVo); } } resJsonData.put(Constants.RES_KEY_CONTENTS, usePartnerVos); } @Override public void findReadyDepartments(Map resJsonData, DepartmentCondition condition, Pageable pageable) { IssueType issueType = this.issueTypeService.getIssueType(condition.getIssueTypeId()); if (issueType != null) { // 이슈 상태 유형이 '대기' 인 이슈 상태 가져오기 IssueStatus issueStatus = this.issueStatusService.findByIssueStatusTypeIsReady(issueType.getWorkflow()); condition.setIssueStatusId(issueStatus.getId()); condition.setWorkflowId(issueType.getWorkflow().getId()); } List> departmentVos = this.departmentMapper.findByIssueStatusId(condition); resJsonData.put(Constants.RES_KEY_CONTENTS, departmentVos); } }