OWL ITS + 탐지시스템(인터넷 진흥원)
이민희
2022-01-13 ebfd37816a332308519b30af5bfb017c5052be69
src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java
@@ -11,14 +11,14 @@
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.IssueRelationRepository;
import kr.wisestone.owl.repository.IssueRepository;
import kr.wisestone.owl.repository.UserDepartmentRepository;
import kr.wisestone.owl.repository.WorkflowDepartmentRepository;
import kr.wisestone.owl.repository.*;
import kr.wisestone.owl.service.*;
import kr.wisestone.owl.util.*;
import kr.wisestone.owl.util.DateUtil;
@@ -33,6 +33,8 @@
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;
@@ -83,6 +85,9 @@
    @Autowired
    private CompanyFieldService companyFieldService;
    @Autowired
    private CompanyFieldCategoryService companyFieldCategoryService;
    @Autowired
    private IspFieldService ispFieldService;
@@ -178,6 +183,15 @@
    private IssueMapper issueMapper;
    @Autowired
    private IssueCompanyRepository issueCompanyRepository;
    @Autowired
    private IssueIspRepository issueIspRepository;
    @Autowired
    private IssueHostingRepository issueHostingRepository;
    @Autowired
    private ExcelConditionCheck excelConditionCheck;
    @Autowired
@@ -194,6 +208,12 @@
    @Autowired
    private WorkflowDepartmentRepository workflowDepartmentRepository;
    @Autowired
    private IssueRelationMapper issueRelationMapper;
    @Autowired
    private WorkflowTransitionService workflowTransitionService;
    @Override
    protected JpaRepository<Issue, Long> getRepository() {
@@ -223,14 +243,14 @@
    private IssueForm convertToIssueForm(IssueApiForm issueApiForm, User user) {
        if (issueApiForm.getIssueTypeId() == null) {
            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_PARAMETER_ISSUE_TYPE_ERROR));
            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 OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_PARAMETER_ISSUE_TYPE_ERROR));
            throw new ApiParameterException(this.messageAccessor.getMessage(MsgConstants.API_PARAMETER_ISSUE_TYPE_ERROR));
        }
        Workflow workflow = issueType.getWorkflow();
@@ -244,13 +264,16 @@
                }
            }
        } else if (issueApiForm.getIssueStatusId() == null){
            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_ISSUE_STATUS_NOT_EXIST));
            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 OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_PARAMETER_PROJECT_ERROR));
            throw new ApiParameterException(this.messageAccessor.getMessage(MsgConstants.API_PARAMETER_PROJECT_ERROR));
        }
        issueForm.setProjectId(project.getId());
@@ -283,7 +306,7 @@
                    issueApiForm.addUseIssueCustomFieldId(customFieldApiOverlap.getCustomField().getId());
                }
                // 중복된 이슈검색
                // 종료상태가 아닌 중복된 상위 이슈검색
                List<Issue> issues = this.findIssue(issueApiForm, customFieldApiOverlaps, user.getId());
                int size = issues.size();
                if (size > 0) {
@@ -309,11 +332,11 @@
            return issueForm;
        } else {
            throw new OwlRuntimeException(this.messageAccessor.getMessage(MsgConstants.API_USER_ERROR));
            throw new ApiAuthException(this.messageAccessor.getMessage(MsgConstants.API_USER_ERROR));
        }
    }
    private void findCompanyField(IssueForm issueForm) {
    private IssueForm findCompanyField(IssueForm issueForm) {
        if(issueForm.getIssueCustomFields() != null && issueForm.getIssueCustomFields().size() > 0) {
            CompanyFieldCondition condition = new CompanyFieldCondition();
            List<Map<String, Object>> companyFields = this.companyFieldService.find(condition);
@@ -322,9 +345,8 @@
            List<Map<String, Object>> issueHostingFields = Lists.newArrayList();
            for (Map<String, Object> issueCustomField : issueForm.getIssueCustomFields()) {
                int customFieldId = (Integer) issueCustomField.get("customFieldId");
                Long customId = (long) customFieldId;
                CustomField customField = this.customFieldService.getCustomField(customId);
                Long customFieldId = MapUtil.getLong(issueCustomField, "customFieldId");
                CustomField customField = this.customFieldService.getCustomField(customFieldId);
                if(customField != null && customField.getCustomFieldType().toString().equals("SITE") && customField.getName().equals("도메인")) {
                    String useValue = issueCustomField.get("useValue").toString();
                    if(companyFields != null && companyFields.size() > 0) {
@@ -356,6 +378,8 @@
                issueForm.setIssueHostingFields(issueHostingFields);
            }
        }
        return issueForm;
    }
    private User convertToUser(String token) {
@@ -381,7 +405,14 @@
        } else {
            // 가상 상위 이슈 추가
            IssueForm parentIssueForm = issueForm.clone();
            // 가상 상위 이슈 추가
            parentIssueForm.setUseIssueCustomFields(issueApiForm.getUseIssueCustomFieldIds());
            //  같은 도메인 업체 찾기
            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);
            // 하위 이슈 추가
@@ -414,7 +445,9 @@
            IssueCustomFieldValueFormComparator comp = new IssueCustomFieldValueFormComparator();
            Collections.sort(issueCustomFieldValueForms, comp);
            List<String> userValues = Lists.newArrayList();
            for (IssueCustomFieldValueForm issueCustomFieldValueForm : issueCustomFieldValueForms) {
                userValues.add(issueCustomFieldValueForm.getUseValue());
                for(CustomFieldApiOverlap customFieldApiOverlap : customFieldApiOverlaps) {
                    if (customFieldApiOverlap.getCustomField().getId().equals(issueCustomFieldValueForm.getCustomFieldId())) {
                        if (useIdx > 0) {
@@ -428,7 +461,9 @@
            IssueCustomFieldValueCondition issueCustomFieldValueCondition = new IssueCustomFieldValueCondition();
            issueCustomFieldValueCondition.setUseValue(concatUseValue);
            issueCustomFieldValueCondition.setUseValues(userValues);
            issueCustomFieldValueCondition.setIssueTypeId(issueApiform.getIssueTypeId());
            issueCustomFieldValueCondition.setIssueStatusType("CLOSE");
            List<Map<String, Object>> results = this.issueMapper.findByCustomFieldValue(issueCustomFieldValueCondition);
            if (results != null && results.size() > 0) {
                for (Map<String, Object> result : results) {
@@ -498,7 +533,6 @@
        this.issueIspService.modifyIssueIspField(issue, issueForm);
        //  HOSTING 정보 저장
        this.issueHostingService.modifyIssueHostingField(issue, issueForm);
        //  첨부 파일 저장
        //  multipartFile 을 file Map List 객체로 변경한다.
@@ -908,42 +942,40 @@
            return Lists.newArrayList();
        }
        //  튜닝 전 - 1.3 / 1.2 / 1.1
        //  튜닝 후 (단일/다중 검색 조건 3개 기준) - 0.49 / 0.41 / 0.47 / 0.41
        List<IssueVo> issueVos = Lists.newArrayList();  //  이슈 목록 데이터 저장 컬렉션
        //  사용자 정의 필드로 검색한 이슈 아이디 값
        List<String> issueKeys = Lists.newArrayList(issueIds);
        issueCondition.setIssueIds(issueKeys);
        issueCondition.setLoginUserId(this.webAppUtil.getLoginId());
        issueCondition.setWorkspaceId(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());
        User user = this.webAppUtil.getLoginUserObject();
        issueCondition.setLoginUserId(user.getId());
        issueCondition.setWorkspaceId(user.getLastWorkspaceId());
        List<Map<String, Object>> results = Lists.newArrayList();
        Long totalCount = 0L;
        UserLevel userLevel = this.userLevelService.getUserLevel(user.getUserLevel().getId());
//        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);
        //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);
        //  튜닝 전 - 0.8, 0.9, 0.9, 0.9, 0.9
        /*StopWatch serviceStart = new StopWatch();
        serviceStart.start();*/
        //  튜닝 전 - 1.1, 1.1, 1.3, 1.2
        /*serviceStart.stop();
        log.debug("serviceENd1 : " + serviceStart.getTime());*/
//        serviceStart.stop();
//        log.error("totalCount : " + serviceStart.toString());
        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
        //  이슈 아이디 초기화
@@ -959,8 +991,8 @@
            this.setDownIssues(user, issueVos);
            this.setRelationIssues(issueVos);
        }
        this.setCountDownIssues(issueVos);
        this.setCountDownIssues(issueVos);
        this.SetWorkflowDepartment(issueVos); //워크플로우에 설정한 담당부서 가져오기
        resJsonData.put(Constants.RES_KEY_CONTENTS, issueVos);
@@ -1457,11 +1489,24 @@
    @Transactional(readOnly = true)
    public void detailIssue(Map<String, Object> 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": //  프로젝트, 이슈 유형, 이슈 상태,  우선순위, 중요도, 담당부서, 첨부파일, 사용자 정의 필드 정보를 셋팅한다.
@@ -1478,17 +1523,15 @@
                    this.setIssueCustomFields(issue, issueVo);  //  사용자 정의 필드 값 정보 셋팅
                    this.setRelationIssue(issue, issueVo);        //연관 일감 셋팅
                    this.setDownIssues(issue, issueVo); //하위 이슈 세팅
                    break;
                case "02": //  프로젝트, 이슈 유형, 이슈 상태,  우선순위, 중요도, 담당자, 첨부파일, 사용자 정의 필드 정보, 댓글, 기록을 셋팅한다.
                    this.setIssueDetail(issueVo, issue, user);    //  이슈 상세 정보를 셋팅한다.
                    this.setIssueTableConfigs(issue, issueVo);
                    this.setIssueTableConfigs(issue, issueVo, issueCondition);
                    issueVo.setProjectVo(ConvertUtil.copyProperties(issue.getProject(), ProjectVo.class));
                    break;
            }
        }
        //  사용자 시스템 기능 사용 정보 수집
        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_DETAIL));
@@ -1496,8 +1539,9 @@
    }
    // 테이블 설정 셋팅
    private void setIssueTableConfigs(Issue issue, IssueVo issueVo) {
        Long IssueTypeId = issue.getIssueType().getId();
    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) {
@@ -1517,8 +1561,21 @@
    // 하위 이슈 정보를 셋팅한다
    private void setDownIssues(Issue issue, IssueVo issueVo) {
        List<Issue> downIssues = this.issueRepository.findByParentIssueId(issue.getId());
        if(downIssues != null && downIssues.size()>0){
        Page<Issue> downIssues = null;
        List<Issue> 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());
            downIssues = this.issueRepository.findByParentIssueId(issue.getId(), pageable);
        }
        if(downIssues != null){
            issueVo.setDownTotalPage(downIssues.getTotalPages());
            issueVo.setDownTotalCount(downIssues.getTotalElements());
            List<IssueVo> resultList = new ArrayList<>();
            for(Issue downIssue : downIssues){
                IssueVo downIssueVo = ConvertUtil.copyProperties(downIssue, IssueVo.class);
@@ -1618,13 +1675,18 @@
    // 연관 이슈 정보를 셋팅한다
    private void setRelationIssue(Issue issue, IssueVo issueVo) {
        Set<IssueRelation> issueRelations = issue.getIssueRelations();
        if (issue != null && issueVo != null && issueRelations.size() > 0) {
            for (IssueRelation issueRelation : issueRelations) {
                IssueRelationVo issueRelationVo = ConvertUtil.copyProperties(issueRelation, IssueRelationVo.class);
        //Set<IssueRelation> issueRelations = issue.getIssueRelations();
                Issue relationIssue = issueRelation.getRelationIssue();
        List<Map<String, Object>> 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<String, Object> 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());
@@ -1759,6 +1821,7 @@
        List<Issue> resultIssueVos = Lists.newArrayList();
        String comma = ",";
        List<String> userValues = Lists.newArrayList();
        if (issueCustomFieldValueForms.size() > 0) {
            IssueCustomFieldValueFormComparator comp = new IssueCustomFieldValueFormComparator();
            Collections.sort(issueCustomFieldValueForms, comp);
@@ -1766,6 +1829,7 @@
            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);
                }
@@ -1774,6 +1838,7 @@
            IssueCustomFieldValueCondition issueCustomFieldValueCondition = new IssueCustomFieldValueCondition();
            issueCustomFieldValueCondition.setUseValue(concatUseValue);
            issueCustomFieldValueCondition.setUseValues(userValues);
            issueCustomFieldValueCondition.setIssueTypeId(issueApiform.getIssueTypeId());
            List<Map<String, Object>> results = this.issueMapper.findByCustomFieldValue(issueCustomFieldValueCondition);
            if (results != null && results.size() > 0) {
@@ -1897,6 +1962,13 @@
    // 이슈 수정(API용)
    private Issue modifyIssueForApi(User user, IssueForm issueForm, List<MultipartFile> 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);
@@ -2733,6 +2805,26 @@
            CompanyField companyField = issueCompany.getCompanyField();
            if (companyField != null) {
                issueCompanyVo.setCompanyId(issueCompany.getCompanyField().getId());
                if (issueCompany.getCompanyTypeId() != null) {
                    CompanyFieldCategory companyType = this.companyFieldCategoryService.find(issueCompany.getCompanyTypeId());
                    issueCompanyVo.setCompanyTypeName(companyType.getUseValue());
                }
                if (issueCompany.getParentSectorId() != null) {
                    CompanyFieldCategory parentSector = this.companyFieldCategoryService.find(issueCompany.getParentSectorId());
                    issueCompanyVo.setParentSectorName(parentSector.getUseValue());
                }
                if (issueCompany.getChildSectorId() != null) {
                    CompanyFieldCategory childSector = this.companyFieldCategoryService.find(issueCompany.getChildSectorId());
                    issueCompanyVo.setChildSectorName(childSector.getUseValue());
                }
                if (issueCompany.getRegionId() != null) {
                    CompanyFieldCategory region = this.companyFieldCategoryService.find(issueCompany.getRegionId());
                    issueCompanyVo.setRegionName(region.getUseValue());
                }
                if (issueCompany.getStatusId() != null) {
                    CompanyFieldCategory status = this.companyFieldCategoryService.find(issueCompany.getStatusId());
                    issueCompanyVo.setStatusName(status.getUseValue());
                }
            }
            issueCompanyVos.add(issueCompanyVo);
        }
@@ -2920,7 +3012,7 @@
            Workspace workspace = this.workspaceService.getWorkspace(this.userService.getUser(this.webAppUtil.getLoginId()).getLastWorkspaceId());  //  이슈를 넣으려는 업무 공간
            //  이슈의 주요 속성을 map 에 저장하여 엑셀 import 에서 지정한 대상(이슈 속성)을 빠르게 찾을 수 있게 한다.
            this.IssueAttributeMapToList(priorityMaps, severityMaps, departmentMaps, customFieldMaps, issueTypeCustomFieldMaps);
            this.IssueAttributeMapToList(issueForm, priorityMaps, severityMaps, departmentMaps, customFieldMaps, issueTypeCustomFieldMaps);
            //  0.237 - 0.230
            List<IssueForm> issueForms = Lists.newArrayList();
@@ -3001,7 +3093,9 @@
                issue.setIssueType(issueType);
                issue.setProject(project);
                issue.setIssueNumber(issueNumber);
                issue.setParentIssue(this.getIssue(saveIssueForm.getParentIssueId()));
                if (saveIssueForm.getParentIssueId() != null && saveIssueForm.getParentIssueId() > -1) {
                    issue.setParentIssue(this.getIssue(saveIssueForm.getParentIssueId()));
                }
                issue = this.issueRepository.saveAndFlush(issue);
@@ -3012,10 +3106,12 @@
                issueDepartment.setWorkspace(workspace);
                List<Long> departmentsIds = this.workflowDepartmentService.findFirstDepartmentIds(workflow);
                for (Long departmentId : departmentsIds) {
                    issueDepartment.setDepartment(this.departmentService.getDepartment(departmentId));
                if (departmentsIds != null && departmentsIds.size() > 0) {
                    for (Long departmentId : departmentsIds) {
                        issueDepartment.setDepartment(this.departmentService.getDepartment(departmentId));
                    }
                    issue.addIssueDepartment(issueDepartment);
                }
                issue.addIssueDepartment(issueDepartment);
                saveIssueForm.setIssueStatusId(issueStatus.getId());
            }
@@ -3038,8 +3134,6 @@
            //  이슈 사용자 정의 값 필드 벌크 등록
            this.bulkInsertIssueCustomFieldValue(issueForms, issueTypeCustomFieldMaps);
            //  3.628 - 3.445
            // 업체,ISP,호스팅 추가
            /*serviceStart.stop();
            log.debug("2차 저장 시간 : " + serviceStart.getTime());*/
@@ -3152,6 +3246,29 @@
                issueCustomField.put("registerId", this.webAppUtil.getLoginId());
                issueCustomFieldValueMaps.add(issueCustomField);
            }
            IssueForm partners = this.findCompanyField(issueForm); // 같은 도메인 업체 찾기
            Issue issue = this.findOne(issueForm.getId());
            if (partners.getIssueCompanyFields() != null && partners.getIssueCompanyFields().size() > 0) {
                for (Map<String, Object> company : partners.getIssueCompanyFields()) {
                    IssueCompany issueCompany = ConvertUtil.convertMapToClass(company, IssueCompany.class);
                    issueCompany.setIssue(issue);
                    this.issueCompanyRepository.saveAndFlush(issueCompany);
                }
            }
            if (partners.getIssueIspFields() != null && partners.getIssueIspFields().size() > 0) {
                for (Map<String, Object> isp : partners.getIssueIspFields()) {
                    IssueIsp issueIsp = ConvertUtil.convertMapToClass(isp, IssueIsp.class);
                    issueIsp.setIssue(issue);
                    this.issueIspRepository.saveAndFlush(issueIsp);
                }
            }
            if (partners.getIssueHostingFields() != null && partners.getIssueHostingFields().size() > 0) {
                for (Map<String, Object> hosting : partners.getIssueHostingFields()) {
                    IssueHosting issueHosting = ConvertUtil.convertMapToClass(hosting, IssueHosting.class);
                    issueHosting.setIssue(issue);
                    this.issueHostingRepository.saveAndFlush(issueHosting);
                }
            }
        }
        if (issueCustomFieldValueMaps.size() > 0) {
@@ -3160,8 +3277,16 @@
    }
    //  이슈의 주요 속성을 map 에 저장하여 엑셀 import 에서 지정한 대상(이슈 속성)을 빠르게 찾을 수 있게 한다.
    private void IssueAttributeMapToList(Map<String, Priority> priorityMaps, Map<String, Severity> severityMaps,
    private void IssueAttributeMapToList(IssueForm issueForm, Map<String, Priority> priorityMaps, Map<String, Severity> severityMaps,
                                         Map<String, DepartmentVo> departmentMaps, Map<String, CustomField> customFieldMaps,Map<String, Long> issueTypeCustomFieldMaps) {
        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<Priority> priorities = this.priorityService.findByWorkspaceId();
@@ -3224,13 +3349,13 @@
                    break;*/
                case 4:
                    //  시작일을 IssueForm 에 저장한다.
                    if (cell != null) {
                    if (cell != null && cell.getCellType() != Cell.CELL_TYPE_BLANK) {
                        this.setIssueFormPeriod(cell, issueForm, true, rowIndex);
                    }
                    break;
                case 5:
                    //  종료일을 IssueForm 에 저장한다.
                    if (cell != null) {
                    if (cell != null && cell.getCellType() != Cell.CELL_TYPE_BLANK) {
                        this.setIssueFormPeriod(cell, issueForm, false, rowIndex);
                    }
                    break;
@@ -3346,7 +3471,7 @@
            //  값이 공백이면 중지
            String cellValue = CommonUtil.convertExcelStringToCell(cell);
            if (StringUtils.isEmpty(cellValue)) {
            if (StringUtils.isEmpty(cellValue) || !cell.toString().equals("null")) {
                return;
            }
@@ -3541,6 +3666,37 @@
        this.systemEmailService.sendEmail(emailTemplateForm.getTitle(), emailTemplateForm.getTemplate(), sendMails, null);
        this.issueHistoryService.detectSendIssueMail(IssueHistoryType.SEND, emailTemplateForm.getSendEmails(), sb);
        this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.SEND, sb.toString());
    }
    @Override
    public void sendCommonEmail(EmailCommonForm emailCommonForm) {
        if (emailCommonForm.getSendEmails().size() < 1) {
            throw new OwlRuntimeException(
                    this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_SEND_USER));
        } else if (emailCommonForm.getIssueId() == null) {
            throw new OwlRuntimeException(
                    this.messageAccessor.getMessage(MsgConstants.ISSUE_NOT_EXIST));
        }
        Issue 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);
        this.issueHistoryService.detectSendIssueMail(IssueHistoryType.SEND, emailCommonForm.getSendEmails(), sb);
        this.issueHistoryService.addIssueHistory(issue, IssueHistoryType.SEND, sb.toString());
    }
@@ -3742,17 +3898,26 @@
    @Override
    public void findPartner(Map<String, Object> resJsonData, Map<String, Object> params) {
        Long issueTypeId = MapUtil.getLong(params, "issueTypeId");
        IssueType issueType = this.issueTypeService.getIssueType(issueTypeId); // 이슈의 이슈유형 객체
        Integer using = issueType.getUsePartner() != null ? issueType.getUsePartner().intValue() : 0; // 이슈유형별로 사용중인 업체/ISP/호스팅 값
        List<UsePartnerVo> 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);
        }
        resJsonData.put(Constants.RES_KEY_CONTENTS, usePartnerVos);
    }
    @Override