OWL ITS + 탐지시스템(인터넷 진흥원)
이민희
2021-11-23 b2bf222751b9bba2315f861c2c5a2511dad86626
이력 남기기
17개 파일 변경됨
487 ■■■■■ 파일 변경됨
src/main/java/kr/wisestone/owl/domain/Issue.java 1 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/domain/IssueRisk.java 9 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/mapper/WidgetMapper.java 3 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/service/IssueHistoryService.java 11 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/service/IssueRiskService.java 3 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/service/impl/IssueCompanyServiceImpl.java 15 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/service/impl/IssueHistoryServiceImpl.java 199 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/service/impl/IssueHostingServiceImpl.java 13 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/service/impl/IssueIspServiceImpl.java 13 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/service/impl/IssueRelationServiceImpl.java 4 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/service/impl/IssueRiskServiceImpl.java 44 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java 69 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/service/impl/WidgetServiceImpl.java 16 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/resources/migration/V1_11__Alter_Table.sql 3 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/resources/mybatis/query-template/issue-template.xml 4 ●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/resources/mybatis/query-template/widget-template.xml 52 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/webapp/i18n/ko/global.json 28 ●●●●● 패치 | 보기 | raw | blame | 히스토리
src/main/java/kr/wisestone/owl/domain/Issue.java
@@ -15,6 +15,7 @@
    public static final String PROJECT_MANAGER = "PROJECT_MANAGER"; //  프로젝트 관리자
    public static final String REGISTER = "REGISTER";   //  이슈 등록자
    public static final String ASSIGNEE = "ASSIGNEE";   //  이슈 담당자
    public static final String DEPARTMENT = "DEPARTMENT";   //  이슈 담당부서
    @Id
    @GeneratedValue(strategy = GenerationType.IDENTITY)
src/main/java/kr/wisestone/owl/domain/IssueRisk.java
@@ -14,6 +14,7 @@
    @GeneratedValue(strategy = GenerationType.IDENTITY)
    private Long id;
    private Long changeAssigneeCount;
    private Long changeDepartmentCount;
    private Long changeIssueStatusCount;
    private String issueStatusIds;
@@ -74,4 +75,12 @@
    public void setWorkspace(Workspace workspace) {
        this.workspace = workspace;
    }
    public Long getChangeDepartmentCount() {
        return changeDepartmentCount;
    }
    public void setChangeDepartmentCount(Long changeDepartmentCount) {
        this.changeDepartmentCount = changeDepartmentCount;
    }
}
src/main/java/kr/wisestone/owl/mapper/WidgetMapper.java
@@ -61,7 +61,8 @@
    //  위험 관리
    List<Map<String, Object>> findRiskIssue(WidgetCondition widgetCondition);
    Map<String, Object> countChangeStatusAndAssigneeIssue(WidgetCondition widgetCondition);
    //Map<String, Object> countChangeStatusAndAssigneeIssue(WidgetCondition widgetCondition);
    Map<String, Object> countChangeStatusAndDepartmentIssue(WidgetCondition widgetCondition);
    Long countRiskIssue(WidgetCondition widgetCondition);
    //  전체 이슈 처리 현황
src/main/java/kr/wisestone/owl/service/IssueHistoryService.java
@@ -4,6 +4,7 @@
import kr.wisestone.owl.domain.enumType.IssueHistoryType;
import kr.wisestone.owl.service.impl.IssueHistoryServiceImpl;
import kr.wisestone.owl.vo.IssueHistoryVo;
import kr.wisestone.owl.vo.IssueVo;
import kr.wisestone.owl.web.condition.IssueHistoryCondition;
import kr.wisestone.owl.web.form.IssueForm;
import org.springframework.data.jpa.repository.JpaRepository;
@@ -43,12 +44,22 @@
    void detectIssueManager(Issue issue, IssueForm issueForm, StringBuilder description);
    void detectIssueDepartment(Issue issue, IssueForm issueForm, StringBuilder description);
    void detectAttachedFile(IssueForm issueForm, StringBuilder description, List<MultipartFile> files);
    void detectCustomField(Issue issue, IssueForm issueForm, StringBuilder description);
    void detectRelationIssue(IssueHistoryType type, IssueRelation issueRelation, StringBuilder description);
    void detectDownIssues(IssueHistoryType type, Issue issue, StringBuilder description);
    void detectIssueCompany(IssueHistoryType type, Map<String, Object> param, IssueCompany issueCompany, StringBuilder description);
    void detectIssueIsp(IssueHistoryType type, Map<String, Object> param, IssueIsp issueIsp, StringBuilder description);
    void detectIssueHosting(IssueHistoryType type, Map<String, Object> param, IssueHosting issueHosting, StringBuilder description);
    void recodeRemoveCustomFieldOptionValue(CustomField customField, String oldValue, String newValue, StringBuilder description);
    void recodeChangeCustomFieldType(CustomField customField, String oldValue, StringBuilder description);
src/main/java/kr/wisestone/owl/service/IssueRiskService.java
@@ -9,5 +9,6 @@
    IssueRisk addIssueRisk(Issue issue, Workspace workspace);
    void modifyIssueRisk(Issue issue, Boolean changeIssueStatus, Boolean changeAssignee, Long issueStatusId);
    //void modifyIssueRisk(Issue issue, Boolean changeIssueStatus, Boolean changeAssignee, Long issueStatusId);
    void modifyIssueRisk(Issue issue, Boolean changeIssueStatus, Boolean changeDepartment, Long issueStatusId);
}
src/main/java/kr/wisestone/owl/service/impl/IssueCompanyServiceImpl.java
@@ -1,6 +1,7 @@
package kr.wisestone.owl.service.impl;
import kr.wisestone.owl.domain.*;
import kr.wisestone.owl.domain.enumType.IssueHistoryType;
import kr.wisestone.owl.mapper.IssueCompanyMapper;
import kr.wisestone.owl.repository.IssueCompanyRepository;
import kr.wisestone.owl.service.*;
@@ -28,6 +29,9 @@
    private CompanyFieldService companyFieldService;
    @Autowired
    private IssueHistoryService issueHistoryService;
    @Autowired
    private IssueCompanyMapper issueCompanyMapper;
    @Override
@@ -48,9 +52,13 @@
                    Set<IssueCompany> issueCompanies = issue.getIssueCompanies();
                    IssueCompany issueCompany = null;
                    if (issueCompanies != null && issueCompanies.size() > 0) { //수정 할 경우
                        issueCompany = issueCompanies.iterator().next();
                        // 변경 이력
                        StringBuilder sb = new StringBuilder();
                        issueHistoryService.detectIssueCompany(IssueHistoryType.MODIFY, param, issueCompany, sb);
                        issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, sb.toString());
                        issueCompany.setCompanyField(companyField);
                        issueCompany.setName(MapUtil.getString(param, "name"));
                        issueCompany.setEmail(MapUtil.getString(param, "email"));
@@ -61,8 +69,13 @@
                        issueCompany = ConvertUtil.convertMapToClass(param, IssueCompany.class);
                        issueCompany.setIssue(issue);
                        issueCompany.setCompanyField(companyField);
                        // 추가 이력
                        StringBuilder sb = new StringBuilder();
                        issueHistoryService.detectIssueCompany(IssueHistoryType.ADD, param, issueCompany, sb);
                        issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, sb.toString());
                    }
                    this.issueCompanyRepository.saveAndFlush(issueCompany);
                }
            }
        }
src/main/java/kr/wisestone/owl/service/impl/IssueHistoryServiceImpl.java
@@ -13,6 +13,7 @@
import kr.wisestone.owl.service.*;
import kr.wisestone.owl.util.*;
import kr.wisestone.owl.vo.IssueHistoryVo;
import kr.wisestone.owl.vo.IssueVo;
import kr.wisestone.owl.web.condition.IssueHistoryCondition;
import kr.wisestone.owl.web.form.IssueForm;
import org.apache.commons.lang3.StringUtils;
@@ -37,6 +38,9 @@
    @Autowired
    private UserService userService;
    @Autowired
    private DepartmentService departmentService;
    @Autowired
    private IssueHistoryMapper issueHistoryMapper;
@@ -292,7 +296,9 @@
        //  이슈 기간 변경 정보를 기록한다.
        this.detectIssuePeriod(issue, issueForm, description);
        //  담당자 변경 정보를 기록한다.
        //this.detectIssueManager(issue, issueForm, description);
        this.detectIssueManager(issue, issueForm, description);
        //  담당부서 변경 정보를 기록한다.
        this.detectIssueDepartment(issue, issueForm, description);
        //  사용자 정의 필드 변경 정보를 기록한다.
        this.detectCustomField(issue, issueForm, description);
@@ -447,10 +453,8 @@
        else {
            recodeIssuePeriod.append("<span translate=\"common.unspecified\">미지정</span>");
        }
        return recodeIssuePeriod.toString();
    }
    // 연관 일감 변경 정보를 기록한다.
    @Override
@@ -462,6 +466,123 @@
           description.append("<span translate=\"issue.relationIssueRemoveHistory\">연관 일감이 삭제되었습니다. " + issueRelation.getRelationIssue().getTitle() + "</span>");
           description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + issueRelation.getRelationIssue().getTitle() + "</span>");
       }
    }
    // 하위 일감 변경 정보를 기록한다.
    @Override
    public void detectDownIssues(IssueHistoryType type, Issue issue, StringBuilder description) {
        if (type == IssueHistoryType.ADD) {
            description.append("<span translate=\"issue.downIssueAddHistory\">하위 일감이 추가되었습니다. </span>");
            description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + issue.getTitle() + "</span>");
        }else {
            description.append("<span translate=\"issue.downIssueRemoveHistory\">하위 일감이 삭제되었습니다. </span>");
            description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + issue.getTitle() + "</span>");
        }
    }
    // 업체 정보 변경 정보를 기록한다.
    @Override
    public void detectIssueCompany(IssueHistoryType type, Map<String, Object> param, IssueCompany issueCompany, StringBuilder description) {
        Long id = MapUtil.getLong(param, "id");
        if (type == IssueHistoryType.ADD) { //추가 할 경우
            description.append("<span translate=\"issue.issueCompanyAddHistory\">업체 정보가 추가되었습니다. </span>");
            description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + issueCompany.getCompanyField().getName() + "</span>");
        } else if (type == IssueHistoryType.MODIFY) { //수정 할 경우
            if (id != null && !issueCompany.getId().equals(id)) {
                description.append("<span translate=\"issue.issueCompanyModifyHistory\">업체 정보가 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + issueCompany.getCompanyField().getName() + "</span>");
            }
            if (param.get("manager") != null && (issueCompany.getManager() == null || !issueCompany.getManager().equals(param.get("manager")))) {
                description.append("<span translate=\"issue.issueCompanyModifyManagerHistory\">&nbsp;>&nbsp;업체 정보의 담당자가 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + param.get("manager") + "</span>");
            }
            if (param.get("tel") != null && (issueCompany.getTel() == null || !issueCompany.getTel().equals(param.get("tel")))) {
                description.append("<span translate=\"issue.issueCompanyModifyTelHistory\">&nbsp;>&nbsp;업체 정보의 전화번호가 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + param.get("tel") + "</span>");
            }
            if (param.get("email") != null && (issueCompany.getEmail() == null || !issueCompany.getEmail().equals(param.get("email")))) {
                description.append("<span translate=\"issue.issueCompanyModifyEmailHistory\">&nbsp;>&nbsp;업체 정보의 이메일이 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + param.get("email") + "</span>");
            }
            if (param.get("memo") != null && (issueCompany.getMemo() == null || !issueCompany.getMemo().equals(param.get("memo")))) {
                description.append("<span translate=\"issue.issueCompanyModifyMemoHistory\">&nbsp;>&nbsp;업체 정보의 비고가 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + param.get("memo") + "</span>");
            }
        } else {
            description.append("<span translate=\"issue.issueCompanyRemoveHistory\">업체 정보가 삭제되었습니다. " + issueCompany.getCompanyField().getName() + "</span>");
            description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + issueCompany.getCompanyField().getName() + "</span>");
        }
    }
    // ISP 정보 변경 정보를 기록한다.
    @Override
    public void detectIssueIsp(IssueHistoryType type, Map<String, Object> param, IssueIsp issueIsp, StringBuilder description) {
        Long id = MapUtil.getLong(param, "id");
        if (type == IssueHistoryType.ADD) {
            description.append("<span translate=\"issue.issueIspAddHistory\">ISP 정보가 추가되었습니다. </span>");
            description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + issueIsp.getIspField().getName() + "</span>");
        } else if (type == IssueHistoryType.MODIFY) {
            if (id != null && !issueIsp.getId().equals(id)) { //수정 할 경우
                description.append("<span translate=\"issue.issueIspModifyHistory\">ISP 정보가 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + issueIsp.getIspField().getName() + "</span>");
            }
            if (param.get("manager") != null && (issueIsp.getManager() == null || !issueIsp.getManager().equals(param.get("manager")))) {
                description.append("<span translate=\"issue.issueIspModifyManagerHistory\">ISP 정보의 담당자가 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + param.get("manager") + "</span>");
            }
            if (param.get("tel") != null && (issueIsp.getTel() == null || !issueIsp.getTel().equals(param.get("tel")))) {
                description.append("<span translate=\"issue.issueIspModifyTelHistory\">ISP 정보의 전화번호가 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + param.get("tel") + "</span>");
            }
            if (param.get("email") != null && (issueIsp.getEmail() == null || !issueIsp.getEmail().equals(param.get("email")))) {
                description.append("<span translate=\"issue.issueIspModifyEmailHistory\">ISP 정보의 이메일이 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + param.get("email") + "</span>");
            }
            if (param.get("memo") != null && (issueIsp.getMemo() == null || !issueIsp.getMemo().equals(param.get("memo")))) {
                description.append("<span translate=\"issue.issueIspModifyMemoHistory\">ISP 정보의 비고가 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + param.get("memo") + "</span>");
            }
        } else {
            description.append("<span translate=\"issue.issueIspRemoveHistory\">ISP 정보가 삭제되었습니다. " + issueIsp.getIspField().getName() + "</span>");
            description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + issueIsp.getIspField().getName() + "</span>");
        }
    }
    // 호스팅 정보 변경 정보를 기록한다.
    @Override
    public void detectIssueHosting(IssueHistoryType type, Map<String, Object> param, IssueHosting issueHosting, StringBuilder description) {
        Long id = MapUtil.getLong(param, "id");
        if (type == IssueHistoryType.ADD) {
            description.append("<span translate=\"issue.issueHostingAddHistory\">호스팅 정보가 추가되었습니다. </span>");
            description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + issueHosting.getHostingField().getName() + "</span>");
        }else if(type == IssueHistoryType.MODIFY){
            if(id != null && !issueHosting.getId().equals(id)){ //수정 할 경우
                description.append("<span translate=\"issue.issueHostingModifyHistory\">호스팅 정보가 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + issueHosting.getHostingField().getName() + "</span>");
            }
            if(param.get("manager") != null && (issueHosting.getManager() == null || !issueHosting.getManager().equals(param.get("manager")))){
                description.append("<span translate=\"issue.issueHostingModifyManagerHistory\">호스팅 정보의 담당자가 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + param.get("manager") + "</span>");
            }
            if(param.get("tel") != null && (issueHosting.getTel() == null || !issueHosting.getTel().equals(param.get("tel")))){
                description.append("<span translate=\"issue.issueHostingModifyTelHistory\">호스팅 정보의 전화번호가 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + param.get("tel") + "</span>");
            }
            if(param.get("email") != null && (issueHosting.getEmail() == null || !issueHosting.getEmail().equals(param.get("email")))){
                description.append("<span translate=\"issue.issueHostingModifyEmailHistory\">호스팅 정보의 이메일이 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + param.get("email") + "</span>");
            }
            if(param.get("memo") != null && (issueHosting.getMemo() == null || !issueHosting.getMemo().equals(param.get("memo")))){
                description.append("<span translate=\"issue.issueHostingModifyMemoHistory\">호스팅 정보의 비고가 변경되었습니다. </span>");
                description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + param.get("memo") + "</span>");
            }
        } else {
            description.append("<span translate=\"issue.issueHostingRemoveHistory\">호스팅 정보가 삭제되었습니다. " + issueHosting.getHostingField().getName() + "</span>");
            description.append("<span class=\"text-primary bold\">&nbsp;>&nbsp;" + issueHosting.getHostingField().getName() + "</span>");
        }
    }
    //  담당자 변경 정보를 기록한다.
@@ -537,6 +658,78 @@
        this.makeIssueHistoryHtml(description, title, beforeUser.toString(), afterUser.toString());
    }
    //  담당부서 변경 정보를 기록한다.
    @Override
    public void detectIssueDepartment(Issue issue, IssueForm issueForm, StringBuilder description) {
        boolean saveIssueRisk = false;  //  이슈 위험 관리에 이중 저장되는 것을 방지하기 위한 구분 값
        //  담당부서 수가 달라졌을 경우
        if(issue.getIssueDepartments().size() != issueForm.getDepartmentIds().size()){
            this.recodeIssueDepartment(issue, issueForm, description);
            // 이슈 위험 관리에 담당부서 변경 정보를 업데이트 한다. - 담당부서 변경
            this.issueRiskService.modifyIssueRisk(issue, false, true, null);
            saveIssueRisk = true;
        }
        //  담당부서 수는 같으나 담당부서가 달라졌을 경우
        if (issue.getIssueDepartments().size() > 0 && issueForm.getDepartmentIds().size() > 0) {
            //  이전 담당자 표시
            for (IssueDepartment issueDepartment : issue.getIssueDepartments()) {
                boolean change = true;
                Department department = issueDepartment.getDepartment();
                for (Long departmentId : issueForm.getDepartmentIds()) {
                    if (department.getId().equals(departmentId)) {
                        change = false;
                        break;
                    }
                }
                if (change) {
                    //  담당부서 변경 정보 기록
                    this.recodeIssueDepartment(issue, issueForm, description);
                    //  담당부서 수가 달라졌을 경우에 저장되지 않았다면 여기서 이슈 위험 관리 저장을 한다.
                    if (!saveIssueRisk) {
                        //  이슈 위험 관리에 담당부서 변경 정보를 업데이트한다. - 담당부서 변경
                        this.issueRiskService.modifyIssueRisk(issue, false, true, null);
                    }
                    break;
                }
            }
        }
    }
    //  담당부서 변경 정보 기록
    private void recodeIssueDepartment(Issue issue, IssueForm issueForm, StringBuilder description) {
        String title = "<span translate=\"common.updateDepartment\">담당부서가 변경되었습니다.</span>";
        StringBuilder beforeDepartment = new StringBuilder();
        //  이전 담당부서 표시
        for (IssueDepartment issueDepartment : issue.getIssueDepartments()) {
            beforeDepartment.append(issueDepartment.getDepartment().getDepartmentName());
            beforeDepartment.append(", ");
        }
        //  담당부서가 없었으면 없음으로 표시
        if (issue.getIssueDepartments().size() < 1) {
            beforeDepartment.append("<span translate=\"common.none\">없음</span>");
        }
        StringBuilder afterDepartment = new StringBuilder();
        for (Long departmentId : issueForm.getDepartmentIds()) {
            Department department = this.departmentService.getDepartment(departmentId);
            afterDepartment.append(department.getDepartmentName());
            afterDepartment.append(", ");
        }
        //  담당부서가 없었으면 없음으로 표시
        if (issueForm.getDepartmentIds().size() < 1) {
            afterDepartment.append("<span translate=\"common.none\">없음</span>");
        }
        //  이력 정보를 html 태그로 만들어 준다.
        this.makeIssueHistoryHtml(description, title, beforeDepartment.toString(), afterDepartment.toString());
    }
    //  이슈에 첨부된 파일에 대해 변경 정보를 기록한다.
    @Override
    public void detectAttachedFile(IssueForm issueForm, StringBuilder description, List<MultipartFile> files) {
src/main/java/kr/wisestone/owl/service/impl/IssueHostingServiceImpl.java
@@ -1,6 +1,7 @@
package kr.wisestone.owl.service.impl;
import kr.wisestone.owl.domain.*;
import kr.wisestone.owl.domain.enumType.IssueHistoryType;
import kr.wisestone.owl.mapper.HostingFieldMapper;
import kr.wisestone.owl.mapper.IssueHostingMapper;
import kr.wisestone.owl.repository.IssueHostingRepository;
@@ -30,6 +31,9 @@
    private HostingFieldService hostingFieldService;
    @Autowired
    private IssueHistoryService issueHistoryService;
    @Autowired
    private IssueHostingMapper issueHostingMapper;
    @Override
@@ -53,6 +57,11 @@
                    if (issueHostings != null && issueHostings.size() >0){//수정 할 경우
                        issueHosting = issueHostings.iterator().next();
                        // 변경 이력
                        StringBuilder sb = new StringBuilder();
                        issueHistoryService.detectIssueHosting(IssueHistoryType.MODIFY, param, issueHosting, sb);
                        issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, sb.toString());
                        issueHosting.setHostingField(hostingField);
                        issueHosting.setName(MapUtil.getString(param, "name"));
                        issueHosting.setEmail(MapUtil.getString(param, "email"));
@@ -64,6 +73,10 @@
                        issueHosting = ConvertUtil.convertMapToClass(param, IssueHosting.class);
                        issueHosting.setIssue(issue);
                        issueHosting.setHostingField(hostingField);
                        // 추가 이력
                        StringBuilder sb = new StringBuilder();
                        issueHistoryService.detectIssueHosting(IssueHistoryType.ADD, param, issueHosting, sb);
                        issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, sb.toString());
                    }
                    this.issueHostingRepository.saveAndFlush(issueHosting);
                }
src/main/java/kr/wisestone/owl/service/impl/IssueIspServiceImpl.java
@@ -1,6 +1,7 @@
package kr.wisestone.owl.service.impl;
import kr.wisestone.owl.domain.*;
import kr.wisestone.owl.domain.enumType.IssueHistoryType;
import kr.wisestone.owl.mapper.IssueIspMapper;
import kr.wisestone.owl.repository.IssueIspRepository;
import kr.wisestone.owl.service.*;
@@ -28,6 +29,9 @@
    private IspFieldService ispFieldService;
    @Autowired
    private IssueHistoryService issueHistoryService;
    @Autowired
    private IssueIspMapper issueIspMapper;
    @Override
@@ -51,6 +55,11 @@
                    if (issueIsps != null && issueIsps.size() > 0) {//수정 할 경우
                        issueIsp = issueIsps.iterator().next();
                        // 변경 이력
                        StringBuilder sb = new StringBuilder();
                        issueHistoryService.detectIssueIsp(IssueHistoryType.MODIFY, param, issueIsp, sb);
                        issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, sb.toString());
                        issueIsp.setIspField(ispField);
                        issueIsp.setName(MapUtil.getString(param, "name"));
                        issueIsp.setEmail(MapUtil.getString(param, "email"));
@@ -62,6 +71,10 @@
                        issueIsp = ConvertUtil.convertMapToClass(param, IssueIsp.class);
                        issueIsp.setIssue(issue);
                        issueIsp.setIspField(ispField);
                        // 추가 이력
                        StringBuilder sb = new StringBuilder();
                        issueHistoryService.detectIssueIsp(IssueHistoryType.ADD, param, issueIsp, sb);
                        issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, sb.toString());
                    }
                    this.issueIspRepository.saveAndFlush(issueIsp);
                }
src/main/java/kr/wisestone/owl/service/impl/IssueRelationServiceImpl.java
@@ -55,10 +55,6 @@
            issueRelation.setRelationIssueType(condition.getRelationIssueType());
            issueRelationRepository.saveAndFlush(issueRelation);
            StringBuilder sb = new StringBuilder();
            issueHistoryService.detectRelationIssue(IssueHistoryType.ADD, issueRelation, sb);
            issueHistoryService.addIssueHistory(issue, IssueHistoryType.MODIFY, sb.toString());
        }
    }
src/main/java/kr/wisestone/owl/service/impl/IssueRiskServiceImpl.java
@@ -34,6 +34,7 @@
        IssueRisk issueRisk = new IssueRisk();
        issueRisk.setChangeIssueStatusCount(0L);
        issueRisk.setChangeAssigneeCount(0L);
        issueRisk.setChangeDepartmentCount(0L);
        issueRisk.setIssue(issue);
        issueRisk.setWorkspace(workspace);
        issueRisk.setIssueStatusIds(issue.getIssueStatus().getId().toString());
@@ -41,7 +42,7 @@
    }
    //  이슈에서 담당자나 상태가 변경될 경우 이슈 위험 관리 정보를 업데이트한다.
    @Override
    /*@Override
    @Transactional
    public void modifyIssueRisk(Issue issue, Boolean changeIssueStatus, Boolean changeAssignee, Long issueStatusId) {
        IssueRisk issueRisk = issue.getIssueRisk();
@@ -80,5 +81,46 @@
        }
        this.issueRiskRepository.saveAndFlush(issueRisk);
    }*/
    @Override
    @Transactional
    public void modifyIssueRisk(Issue issue, Boolean changeIssueStatus, Boolean changeDepartment, Long issueStatusId) {
        IssueRisk issueRisk = issue.getIssueRisk();
        //  상태 변경
        if (changeIssueStatus) {
            String issueStatusIds = issueRisk.getIssueStatusIds();
            issueStatusIds += "&" + issueStatusId;
            Map<String, Integer> issueStatusIdMaps = new HashMap<>();
            for (String key : issueStatusIds.split("&")) {
                if (!issueStatusIdMaps.containsKey(key)) {
                    issueStatusIdMaps.put(key, 1);
                }
                else {
                    issueStatusIdMaps.put(key, issueStatusIdMaps.get(key) + 1);
                }
            }
            Integer saveCount = 0;
            for (Integer value : issueStatusIdMaps.values()) {
                if (value > saveCount) {
                    saveCount = value;
                }
            }
            issueRisk.setChangeIssueStatusCount(saveCount.longValue());
            issueRisk.setIssueStatusIds(issueStatusIds);
        }
        //  담당자 변경
        if (changeDepartment) {
            issueRisk.setChangeDepartmentCount(issueRisk.getChangeDepartmentCount() + 1);
        }
        this.issueRiskRepository.saveAndFlush(issueRisk);
    }
}
src/main/java/kr/wisestone/owl/service/impl/IssueServiceImpl.java
@@ -222,6 +222,12 @@
        //this.issueUserService.modifyIssueUser(issue, project.getWorkspace(), issueForm.getUserIds());
        //  담당부서 지정
        this.issueDepartmentService.modifyIssueDepartment(issue, project.getWorkspace(), issueForm.getDepartmentIds());
        //  업체 정보 저장
        this.issueCompanyService.modifyIssueCompanyField(issue, issueForm.getIssueCompanyFields());
        //  ISP 정보 저장
        this.issueIspService.modifyIssueIspField(issue, issueForm.getIssueIspFields());
        //  HOSTING 정보 저장
        this.issueHostingService.modifyIssueHostingField(issue, issueForm.getIssueHostingFields());
        //  multipartFile 을 file Map List 객체로 변경한다.
        List<Map<String, Object>> convertFileMaps = this.convertMultipartFileToFile(multipartFiles);
@@ -241,12 +247,6 @@
        this.reservationIssueEmail(issue.getId(), EmailType.ISSUE_ADD);
        //  사용자 시스템 기능 사용 정보 수집
        log.info(ElasticSearchUtil.makeUserActiveHistoryMessage(this.webAppUtil.getLoginUser(), ElasticSearchConstants.ISSUE_ADD));
        //  업체 정보 저장
        this.issueCompanyService.modifyIssueCompanyField(issue, issueForm.getIssueCompanyFields());
        //  ISP 정보 저장
        this.issueIspService.modifyIssueIspField(issue, issueForm.getIssueIspFields());
        //  HOSTING 정보 저장
        this.issueHostingService.modifyIssueHostingField(issue, issueForm.getIssueHostingFields());
        return issue;
    }
@@ -865,7 +865,6 @@
    }
    //  상위일감 정보 추가
    private void setParentIssue(Issue issue, IssueVo issueVo) {
        if(issue.getParentIssue() != null){
@@ -1225,11 +1224,11 @@
        boolean hasPermission = false;
        //  업무 공간 관리자일 경우 수정 권한을 갖는다.
        hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.WORKSPACE_MANAGER, issueVo, null);
        hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.WORKSPACE_MANAGER, issueVo, null, null);
        //  프로젝트 관리자일 경우 해당 프로젝트에 등록된 이슈는 수정 권한을 갖는다.
        hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.PROJECT_MANAGER, issueVo, null);
        hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.PROJECT_MANAGER, issueVo, null, null);
        //   이슈 등록자일 경우 수정 권한을 갖는다.
        hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.REGISTER, issueVo, null);
        hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.REGISTER, issueVo, null, null);
        //  이슈 담당자일 경우 수정 권한을 갖는다. => 담당부서로 수정 - 체크
        //hasPermission = this.checkIssueModifyPermission(hasPermission, Issue.ASSIGNEE, issueVo, issueUserVos);
        //  담당자가 없으면 모든 사용자가 수정 권한을 갖는다.
@@ -1238,7 +1237,7 @@
    }
    //  이슈 수정 권한을 확인한다.
    private boolean checkIssueModifyPermission(Boolean hasPermission, String checkType, IssueVo issueVo, List<UserVo> issueUserVos) {
    private boolean checkIssueModifyPermission(Boolean hasPermission, String checkType, IssueVo issueVo, List<UserVo> issueUserVos, List<DepartmentVo> issueDepartmentVos) {
        if (!hasPermission) {
            switch (checkType) {
                case Issue.WORKSPACE_MANAGER:  //  업무 공간 관리자
@@ -1262,15 +1261,28 @@
                        hasPermission = true;
                        break;
                    }
                    //   이슈 담당자 여부 확인 => 담당부서로 수정 - 체크
                    /*for (UserVo issueUserVo : issueUserVos) {
                    //   이슈 담당자 여부 확인
                    for (UserVo issueUserVo : issueUserVos) {
                        if (issueUserVo.getId().equals(this.webAppUtil.getLoginId())) {
                            hasPermission = true;
                            break;
                        }
                    }*/
                    }
                    break;
                case Issue.DEPARTMENT:
                    //  담당부서가 없으면 모든 사용자가 수정 권한을 갖는다.
                    if (issueDepartmentVos.size() < 1) {
                        hasPermission = true;
                        break;
                    }
                    //   이슈 담당부서 여부 확인
                    /*for (DepartmentVo issueDepartmentVo : issueDepartmentVos) {
                        if (issueDepartmentVo.getId().equals()) {
                            hasPermission = true;
                            break;
                        }
                    }*/
                    break;
            }
        }
@@ -1373,7 +1385,7 @@
        issue.setProject(this.projectService.getProject(issueForm.getProjectId()));
        //  변경 이력 정보 추출
        this.issueHistoryService.detectIssueManager(issue, issueForm, detectIssueChange);
        this.issueHistoryService.detectIssueDepartment(issue, issueForm, detectIssueChange);
        //this.issueUserService.modifyIssueUser(issue, issue.getProject().getWorkspace(), issueForm.getUserIds());
        this.issueDepartmentService.modifyIssueDepartment(issue, issue.getProject().getWorkspace(), issueForm.getDepartmentIds());
@@ -1982,6 +1994,7 @@
            Map<String, Long> 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());
@@ -2613,16 +2626,30 @@
    @Transactional
    @Override
    public void modifyParentIssue(IssueForm issueForm) {
        Issue issue = this.issueRepository.getOne(issueForm.getId());
        Long parentIssueId = issueForm.getParentIssueId();
        if (parentIssueId != null) {
            Issue parentIssue = this.issueRepository.getOne(parentIssueId);
    public void modifyParentIssue(IssueForm issueDownForm) {
        System.out.println(issueDownForm.getTitle());
        Issue issue = this.getIssue(issueDownForm.getId());
        Issue parentIssue = issue.getParentIssue();
        Long newParentIssueId = issueDownForm.getParentIssueId();
        StringBuilder sb = new StringBuilder();
        IssueHistoryType type =  IssueHistoryType.ADD;
        if (newParentIssueId != null) {
           // todo 이전 하위 일감 히스토리기록 필요
            type = IssueHistoryType.ADD;
            parentIssue = this.getIssue(newParentIssueId);
            issue.setParentIssue(parentIssue);
            this.issueHistoryService.detectDownIssues(type, issue, sb);
        } else  {
            // remove
            type = IssueHistoryType.DELETE;
            this.issueHistoryService.detectDownIssues(type, issue, sb);
            issue.setParentIssue(null);
        }
        this.issueHistoryService.addIssueHistory(parentIssue, IssueHistoryType.MODIFY, sb.toString());
        this.issueRepository.saveAndFlush(issue);
    }
}
src/main/java/kr/wisestone/owl/service/impl/WidgetServiceImpl.java
@@ -512,9 +512,11 @@
        widgetCondition.setPage(pageable.getPageNumber() * pageable.getPageSize());
        widgetCondition.setPageSize(pageable.getPageSize());
        Map<String, Object> countChangeStatusAndAssigneeIssue = new HashMap<>();
        countChangeStatusAndAssigneeIssue.put("changeAssigneeCount", 0L);
        countChangeStatusAndAssigneeIssue.put("changeIssueStatusCount", 0L);
        //Map<String, Object> countChangeStatusAndAssigneeIssue = new HashMap<>();
        //countChangeStatusAndAssigneeIssue.put("changeAssigneeCount", 0L);
        Map<String, Object> countChangeStatusAndDepartmentIssue = new HashMap<>();
        countChangeStatusAndDepartmentIssue.put("changeDepartmentCount", 0L);
        countChangeStatusAndDepartmentIssue.put("changeIssueStatusCount", 0L);
        //  0.212 - 0.213
        List<Map<String, Object>> riskIssues = Lists.newArrayList();
        //  0.244 - 0.248
@@ -522,7 +524,8 @@
        Long totalCount = 0L;
        if (widgetCondition.getProjectIds().size() > 0) {
            countChangeStatusAndAssigneeIssue = this.widgetMapper.countChangeStatusAndAssigneeIssue(widgetCondition);
            //countChangeStatusAndAssigneeIssue = this.widgetMapper.countChangeStatusAndAssigneeIssue(widgetCondition);
            countChangeStatusAndDepartmentIssue = this.widgetMapper.countChangeStatusAndDepartmentIssue(widgetCondition);
            riskIssues = this.widgetMapper.findRiskIssue(widgetCondition);
            totalCount = this.widgetMapper.countRiskIssue(widgetCondition);
        }
@@ -535,8 +538,9 @@
        int totalPage = (int) Math.ceil((totalCount - 1) / pageable.getPageSize()) + 1;
        Map<String, Object> results = new HashMap<>();
        results.put("changeAssigneeCount", MapUtil.getLong(countChangeStatusAndAssigneeIssue, "changeAssigneeCount"));
        results.put("changeIssueStatusCount", MapUtil.getLong(countChangeStatusAndAssigneeIssue, "changeIssueStatusCount"));
        //results.put("changeAssigneeCount", MapUtil.getLong(countChangeStatusAndAssigneeIssue, "changeAssigneeCount"));
        results.put("changeDepartmentCount", MapUtil.getLong(countChangeStatusAndDepartmentIssue, "changeDepartmentCount"));
        results.put("changeIssueStatusCount", MapUtil.getLong(countChangeStatusAndDepartmentIssue, "changeIssueStatusCount"));
        results.put("issues", riskIssues);
        results.put(Constants.REQ_KEY_PAGE_VO, new ResPage(pageable.getPageNumber(), pageable.getPageSize(),
                totalPage, totalCount));
src/main/resources/migration/V1_11__Alter_Table.sql
@@ -187,3 +187,6 @@
ALTER TABLE `issue_table_config` ADD COLUMN  `issue_id` BIGINT(20) NOT NULL;
ALTER TABLE `issue_table_config` ADD COLUMN  `issue_table_type` INT(11) NOT NULL;
ALTER TABLE `issue_table_config` ADD INDEX `issueTableTypeIndex`(`issue_table_type`);
-- issue_risk 테이블 부서변경 카운트 컬럼 추가
ALTER TABLE `issue_risk` ADD COLUMN `change_department_count` BIGINT(20) NOT NULL DEFAULT '0';
src/main/resources/mybatis/query-template/issue-template.xml
@@ -367,12 +367,12 @@
    <!--    이슈 리스크 bulk insert, import 에서 사용 -->
    <insert id="insertIssueRiskBatch" keyColumn="id" keyProperty="id" useGeneratedKeys="true"
            parameterType="java.util.HashMap">
        INSERT INTO issue_risk(issue_id, workspace_id, change_assignee_count, change_issue_status_count,
        INSERT INTO issue_risk(issue_id, workspace_id, change_assignee_count, change_department_count, change_issue_status_count,
        issue_status_ids,
        register_id, modify_id, register_date, modify_date)
        VALUES
        <foreach collection="list" item="map" index="index" separator="," open="" close="">
            (#{map.issueId}, #{map.workspaceId}, #{map.changeAssigneeCount}, #{map.changeIssueStatusCount},
            (#{map.issueId}, #{map.workspaceId}, #{map.changeAssigneeCount}, #{map.changeDepartmentCount}, #{map.changeIssueStatusCount},
            #{map.issueStatusIds},
            #{map.registerId}, #{map.registerId}, NOW(), NOW())
        </foreach>
src/main/resources/mybatis/query-template/widget-template.xml
@@ -305,7 +305,7 @@
    <!--    5번 위젯 시작 -->
    <!--    번복되는 상태 변경 및 빈번한 담당자 변경 개수   -->
    <select id="countChangeStatusAndAssigneeIssue" resultType="java.util.HashMap"
    <!--<select id="countChangeStatusAndAssigneeIssue" resultType="java.util.HashMap"
            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
        select
        count(case when ir.change_assignee_count > 3 then 1 end) as changeAssigneeCount,
@@ -322,10 +322,29 @@
                </foreach>
            </when>
        </choose>
    </select>-->
    <select id="countChangeStatusAndDepartmentIssue" resultType="java.util.HashMap"
            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
        select
        count(case when ir.change_department_count > 3 then 1 end) as changeDepartmentCount,
        count(case when ir.change_issue_status_count > 3 then 1 end) as changeIssueStatusCount
        from issue i
        inner join issue_risk ir on ir.issue_id = i.id
        inner join issue_status iss on iss.id = i.issue_status_id
        where iss.issue_status_type != 'CLOSE'
        <choose>
            <when test="projectIds.size != 0">
                AND i.project_id IN
                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
                    #{item}
                </foreach>
            </when>
        </choose>
    </select>
    <!--    위험 관리    -->
    <select id="findRiskIssue" resultType="java.util.HashMap"
    <!--<select id="findRiskIssue" resultType="java.util.HashMap"
            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
        select
        DISTINCT i.id, i.title, iss.name as issueStatusName,
@@ -351,6 +370,33 @@
        <if test="page != null and !page.equals('')">
            limit #{pageSize} offset #{page};
        </if>
    </select>-->
    <select id="findRiskIssue" resultType="java.util.HashMap"
            parameterType="kr.wisestone.owl.web.condition.WidgetCondition">
        select
        DISTINCT i.id, i.title, iss.name as issueStatusName,
        (case when ir.change_department_count > 3 then true else false end) as changeDepartmentType,
        (case when ir.change_issue_status_count > 3 then true else false end) as changeIssueStatusType,
        CONCAT(p.project_key, '-', i.issue_number) AS issueKey,
        i.issue_number as issueNumber,
        p.project_key as projectKey
        from issue i
        inner join issue_risk ir on ir.issue_id = i.id
        inner join issue_status iss on iss.id = i.issue_status_id
        inner join project p on p.id = i.project_id
        where iss.issue_status_type != 'CLOSE'
        <choose>
            <when test="projectIds.size != 0">
                AND p.id IN
                <foreach collection="projectIds" item="item" index="index" separator="," open="(" close=")">
                    #{item}
                </foreach>
            </when>
        </choose>
        and (ir.change_department_count > 3 || ir.change_issue_status_count > 3)
        <if test="page != null and !page.equals('')">
            limit #{pageSize} offset #{page};
        </if>
    </select>
    <!--    위험 관리 개수    -->
@@ -370,7 +416,7 @@
                </foreach>
            </when>
        </choose>
        and (ir.change_assignee_count > 3 || ir.change_issue_status_count > 3)
        and (ir.change_department_count > 3 || ir.change_issue_status_count > 3)
    </select>
    <!--    5번 위젯 끝 -->
src/main/webapp/i18n/ko/global.json
@@ -168,6 +168,33 @@
        "relationIssueRemove" : "연관 이슈 삭제",
        "relationIssueRemoveHistory" : "연관 이슈가 삭제되었습니다.",
        "relationIssueAddHistory" : "연관 이슈가 추가되었습니다.",
        "downIssueRemoveHistory" : "하위 이슈가 삭제되었습니다.",
        "downIssueAddHistory" : "하위 이슈가 추가되었습니다.",
        "issueCompanyRemoveHistory" : "업체 정보가 삭제되었습니다.",
        "issueCompanyAddHistory" : "업체 정보가 추가되었습니다.",
        "issueCompanyModifyHistory" : "업체 정보가 변경되었습니다.",
        "issueCompanyModifyManagerHistory" : "업체 정보의 담당자가 변경되었습니다.",
        "issueCompanyModifyTelHistory" : "업체 정보의 전화번호가 변경되었습니다.",
        "issueCompanyModifyEmailHistory" : "업체 정보의 이메일이 변경되었습니다.",
        "issueCompanyModifyMemoHistory" : "업체 정보의 비고가 변경되었습니다.",
        "issueIspRemoveHistory" : "ISP 정보가 삭제되었습니다.",
        "issueIspAddHistory" : "ISP 정보가 추가되었습니다.",
        "issueIspModifyHistory" : "ISP 정보가 변경되었습니다.",
        "issueIspModifyManagerHistory" : "ISP 정보의 담당자가 변경되었습니다.",
        "issueIspModifyTelHistory" : "ISP 정보의 전화번호가 변경되었습니다.",
        "issueIspModifyEmailHistory" : "ISP 정보의 이메일이 변경되었습니다.",
        "issueIspModifyMemoHistory" : "ISP 정보의 비고가 변경되었습니다.",
        "issueHostingRemoveHistory" : "호스팅 정보가 삭제되었습니다.",
        "issueHostingAddHistory" : "호스팅 정보가 추가되었습니다.",
        "issueHostingModifyHistory" : "호스팅 정보가 변경되었습니다.",
        "issueHostingModifyManagerHistory" : "호스팅 정보의 담당자가 변경되었습니다.",
        "issueHostingModifyTelHistory" : "호스팅 정보의 전화번호가 변경되었습니다.",
        "issueHostingModifyEmailHistory" : "호스팅 정보의 이메일이 변경되었습니다.",
        "issueHostingModifyMemoHistory" : "호스팅 정보의 비고가 변경되었습니다.",
        "requireIssueTitle": "이슈 제목을 입력하세요.",
        "issueList": "이슈 목록",
        "summaryIssueActivity": "이슈 활동 내역 요약",
@@ -755,6 +782,7 @@
        "updatePeriod": "기간이 변경되었습니다.",
        "unspecified": "미지정",
        "updateAssignee": "담당자가 변경되었습니다.",
        "updateDepartment": "담당부서가 변경되었습니다.",
        "none": "없음",
        "updateAttachment": "첨부 파일이 변경되었습니다.",
        "deleteAttachment": "첨부 파일이 삭제되었습니다.",