package ink.wgink.service.department.service.impl;

import com.alibaba.excel.EasyExcel;
import com.alibaba.fastjson.JSONArray;
import com.alibaba.fastjson.JSONObject;
import com.github.pagehelper.PageHelper;
import com.github.pagehelper.PageInfo;
import ink.wgink.common.base.DefaultBaseService;
import ink.wgink.exceptions.SaveException;
import ink.wgink.exceptions.SearchException;
import ink.wgink.interfaces.consts.ISystemConstant;
import ink.wgink.interfaces.department.IDepartmentDeleteAfterHandler;
import ink.wgink.interfaces.department.IDepartmentSaveAfterHandler;
import ink.wgink.interfaces.department.IDepartmentUpdateAfterHandler;
import ink.wgink.module.file.excel.error.AbstractErrorExcelHandler;
import ink.wgink.module.file.service.IFileService;
import ink.wgink.pojo.ListPage;
import ink.wgink.pojo.dtos.ZTreeDTO;
import ink.wgink.pojo.dtos.department.DepartmentDTO;
import ink.wgink.pojo.dtos.department.DepartmentSimpleDTO;
import ink.wgink.pojo.pos.DepartmentPO;
import ink.wgink.pojo.result.SuccessResultList;
import ink.wgink.pojo.result.UploadExcelResultDTO;
import ink.wgink.service.department.dao.IDepartmentDao;
import ink.wgink.service.department.enmus.DepartmentAdjustmentTypeEnum;
import ink.wgink.service.department.listener.excel.DepartmentExcel;
import ink.wgink.service.department.listener.excel.DepartmentExcelError;
import ink.wgink.service.department.listener.excel.DepartmentExcelListener;
import ink.wgink.service.department.pojo.vos.*;
import ink.wgink.service.department.service.IDepartmentAdjustmentService;
import ink.wgink.service.department.service.IDepartmentService;
import ink.wgink.service.department.service.IDepartmentUserService;
import ink.wgink.util.ArrayListUtil;
import ink.wgink.util.UUIDUtil;
import ink.wgink.util.map.HashMapUtil;
import ink.wgink.util.string.WStringUtil;
import org.apache.commons.lang3.StringUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import org.springframework.web.multipart.MultipartFile;

import java.io.IOException;
import java.util.*;

/**
 * When you feel like quitting. Think about why you started
 * 当你想要放弃的时候，想想当初你为何开始
 *
 * @ClassName: DepartmentServiceImpl
 * @Description: 组织部门
 * @Author: wanggeng
 * @Date: 2021/1/27 3:53 下午
 * @Version: 1.0
 */
@Service
public class DepartmentServiceImpl extends DefaultBaseService implements IDepartmentService {

    @Autowired
    private IDepartmentDao departmentDao;
    @Autowired
    private IDepartmentUserService departmentUserService;
    @Autowired
    private IDepartmentAdjustmentService departmentAdjustmentService;
    @Autowired
    private IFileService fileService;
    @Autowired(required = false)
    private IDepartmentSaveAfterHandler departmentSaveAfterHandler;
    @Autowired(required = false)
    private IDepartmentUpdateAfterHandler departmentUpdateAfterHandler;
    @Autowired(required = false)
    private IDepartmentDeleteAfterHandler departmentDeleteAfterHandler;

    @Override
    public void save(DepartmentVO departmentVO) {
        saveReturnId(departmentVO);
    }

    @Override
    public String saveReturnId(DepartmentVO departmentVO) {
        String parentCode = null;
        String departmentParentId = departmentVO.getDepartmentParentId();
        if (!ISystemConstant.TREE_ROOT_ID.equals(departmentParentId)) {
            DepartmentDTO departmentDTO = get(departmentParentId);
            parentCode = departmentDTO.getDepartmentCode();
        } else {
            if (!isAdmin()) {
                throw new SaveException("非超级管理员无法在根节点添加组织");
            }
        }
        // 同级，同名组织不能添加
        if (isDepartmentExist(null, departmentVO.getDepartmentParentId(), departmentVO.getDepartmentName())) {
            throw new SaveException("同级组织名称不能相同");
        }
        String departmentId = UUIDUtil.getUUID();
        String departmentCode = getCode(parentCode, departmentParentId);
        Map<String, Object> params = HashMapUtil.beanToMap(departmentVO);
        params.put("departmentCode", departmentCode);
        params.put("departmentId", departmentId);
        setSaveInfo(params);
        departmentDao.save(params);
        if (departmentSaveAfterHandler != null) {
            departmentSaveAfterHandler.handle(departmentId,
                    departmentParentId,
                    departmentVO.getDepartmentName(),
                    departmentCode,
                    departmentVO.getDepartmentType(),
                    departmentVO.getDepartmentAreaCode(),
                    departmentVO.getDepartmentAreaName());
        }
        return departmentId;
    }

    @Override
    public void remove(List<String> ids) {
        // 清空组织所在人员组织信息
        for (String departmentId : ids) {
            departmentUserService.delete(departmentId);
        }
        Map<String, Object> params = getHashMap(2);
        params.put("departmentIds", ids);
        setUpdateInfo(params);
        departmentDao.remove(params);
        if (departmentDeleteAfterHandler != null) {
            departmentDeleteAfterHandler.handle(ids);
        }
    }

    @Override
    public void update(String departmentId, DepartmentVO departmentVO) {
        if (isDepartmentExist(departmentId, departmentVO.getDepartmentParentId(), departmentVO.getDepartmentName())) {
            throw new SaveException("同级组织名称不能相同");
        }
        Map<String, Object> params = HashMapUtil.beanToMap(departmentVO);
        params.put("departmentId", departmentId);
        setUpdateInfo(params);
        departmentDao.update(params);
        if (departmentUpdateAfterHandler != null) {
            departmentUpdateAfterHandler.handle(departmentId,
                    departmentVO.getDepartmentParentId(),
                    departmentVO.getDepartmentName(),
                    departmentVO.getDepartmentType(),
                    departmentVO.getDepartmentAreaCode(),
                    departmentVO.getDepartmentAreaName());
        }
    }

    @Override
    public void updateMergeNew(MergeNewDepartmentInfoVO mergeNewDepartmentInfoVO) {
        String departmentId = saveReturnId(mergeNewDepartmentInfoVO);
        String[] mergeDepartmentIdArray = mergeNewDepartmentInfoVO.getMergeDepartmentIds().split("_");
        mergeById(departmentId, mergeNewDepartmentInfoVO.getDepartmentName(), mergeDepartmentIdArray);
    }

    @Override
    public void updateMerge(String departmentId, MergeDepartmentInfoVO mergeDepartmentInfoVO) {
        DepartmentPO departmentPO = getPO(departmentId);
        if (departmentPO == null) {
            throw new SearchException("部门不存在");
        }
        String[] mergeDepartmentIdArray = mergeDepartmentInfoVO.getMergeDepartmentIds().split("_");
        mergeById(departmentId, departmentPO.getDepartmentName(), mergeDepartmentIdArray);
    }

    @Override
    public void updateSplit(String departmentId, SplitDepartmentVO splitDepartmentVO) {
        DepartmentDTO departmentDTO = get(departmentId);
        if (departmentDTO == null) {
            throw new SearchException("机构组织不存在");
        }
        LOG.debug("删除已经拆分的组织");
        removeByIdReturnDeletedUserIds(departmentId, departmentDTO.getDepartmentName());
        LOG.debug("新建拆分的组织");
        for (SplitDepartmentInfoVO splitDepartmentInfoVO : splitDepartmentVO.getSplitDepartments()) {
            String userIds = splitDepartmentInfoVO.getUserIds();
            String newDepartmentId = saveReturnId(splitDepartmentInfoVO);
            departmentAdjustmentService.save(departmentId, departmentDTO.getDepartmentName(), newDepartmentId, splitDepartmentInfoVO.getDepartmentName(), DepartmentAdjustmentTypeEnum.SPLIT);
            if (!StringUtils.isBlank(userIds)) {
                departmentUserService.update(newDepartmentId, Arrays.asList(userIds.split("_")), new ArrayList<>());
            }
        }
    }

    @Override
    public UploadExcelResultDTO importExcel(MultipartFile excel) throws IOException {
        // 错误列表
        List<DepartmentExcelError> departmentExcelErrors = new ArrayList<>(0);
        long startTime = System.currentTimeMillis();
        EasyExcel.read(excel.getInputStream(), DepartmentExcel.class, new DepartmentExcelListener() {
            @Override
            public void listDepartmentExcel(List<DepartmentExcel> departmentExcels) throws Exception {
                JSONArray listTreeDepartmentExcelJsonArray = listTreeExcelJsonArray(0, departmentExcels, departmentExcelErrors);
                saveExcel("0", null, listTreeDepartmentExcelJsonArray, departmentExcelErrors);
            }
        }).headRowNumber(2).sheet().doRead();
        long endTime = System.currentTimeMillis();
        String excelFileId = null;
        if (departmentExcelErrors.size() > 0) {
            excelFileId = new AbstractErrorExcelHandler<DepartmentExcelError>(fileService) {
                @Override
                public List<List<String>> excelHeaderNames() {
                    return simpleExcelHeader(new String[]{"ID", "组织名称", "上级ID", "组织说明", "组织电话", "类型", "错误原因"});
                }
            }.saveErrorExcel(departmentExcelErrors);
        }
        return new UploadExcelResultDTO(departmentExcelErrors.size(), endTime - startTime, excelFileId);
    }

    @Override
    public void resetCode() {
        Map<String, Object> params = getHashMap(2);
        resetCode("0", "", params);
    }

    @Override
    public List<DepartmentDTO> list(Map<String, Object> params) {
        List<DepartmentDTO> srcDepartmentDTOs = departmentDao.list(params);
        List<DepartmentDTO> destDepartmentDTOs = new ArrayList<>(Arrays.asList(new DepartmentDTO[srcDepartmentDTOs.size()]));
        Collections.copy(destDepartmentDTOs, srcDepartmentDTOs);
        return destDepartmentDTOs;
    }

    @Override
    public List<DepartmentDTO> listByIds(List<String> ids) {
        if (ids.isEmpty()) {
            return new ArrayList<>();
        }
        Map<String, Object> params = getHashMap(2);
        params.put("departmentIds", ids);
        return list(params);
    }

    @Override
    public List<DepartmentDTO> listByParentId(String departmentParentId) {
        Map<String, Object> params = getHashMap(2);
        params.put("departmentParentId", departmentParentId);
        return list(params);
    }

    @Override
    public List<DepartmentDTO> listByAreaCode(String departmentAreaCode) {
        Map<String, Object> params = getHashMap(2);
        params.put("departmentAreaCode", departmentAreaCode);
        return list(params);
    }

    @Override
    public List<DepartmentDTO> listByAreaCodeAndOrder(String departmentAreaCode, String order) {
        Map<String, Object> params = getHashMap(6);
        params.put("departmentAreaCode", departmentAreaCode);
        params.put("sort", "departmentAreaCode");
        params.put("order", order);
        return list(params);
    }

    @Override
    public List<DepartmentDTO> listAllByAreaCode(String departmentAreaCode) {
        Map<String, Object> params = getHashMap(2);
        params.put("departmentAreaCodeLike", WStringUtil.cutContinuityRepeatCharDesc(departmentAreaCode, '0', 3));
        return list(params);
    }

    @Override
    public List<DepartmentDTO> listAllByAreaCodeAndOrder(String departmentAreaCode, String order) {
        Map<String, Object> params = getHashMap(6);
        params.put("departmentAreaCodeLike", WStringUtil.cutContinuityRepeatCharDesc(departmentAreaCode, '0', 3));
        params.put("sort", "departmentAreaCode");
        params.put("order", order);
        return list(params);
    }

    @Override
    public List<DepartmentDTO> listSubAllByAreaCode(String departmentAreaCode) {
        Map<String, Object> params = getHashMap(4);
        params.put("departmentAreaCodeLike", WStringUtil.cutContinuityRepeatCharDesc(departmentAreaCode, '0', 3));
        params.put("excludeDepartmentAreaCode", departmentAreaCode);
        return list(params);
    }

    @Override
    public List<DepartmentDTO> listSubAllByAreaCodeAndOrder(String departmentAreaCode, String order) {
        Map<String, Object> params = getHashMap(8);
        params.put("departmentAreaCodeLike", WStringUtil.cutContinuityRepeatCharDesc(departmentAreaCode, '0', 3));
        params.put("excludeDepartmentAreaCode", departmentAreaCode);
        params.put("sort", "departmentAreaCode");
        params.put("order", order);
        return list(params);
    }

    @Override
    public SuccessResultList<List<DepartmentDTO>> listPage(ListPage page) {
        PageHelper.startPage(page.getPage(), page.getRows());
        List<DepartmentDTO> departmentDTOs = departmentDao.list(page.getParams());
        PageInfo<DepartmentDTO> pageInfo = new PageInfo<>(departmentDTOs);
        return new SuccessResultList<>(departmentDTOs, pageInfo.getPageNum(), pageInfo.getTotal());
    }

    @Override
    public SuccessResultList<List<DepartmentDTO>> listPageByParentId(String departmentParentId, ListPage page) {
        if (StringUtils.equals(ISystemConstant.TREE_ROOT_ID, departmentParentId)) {
            List<String> listBaseDepartmentIds = listBaseDepartmentIds();
            if (!Objects.isNull(listBaseDepartmentIds)) {
                page.getParams().put("departmentIds", listBaseDepartmentIds);
            } else {
                page.getParams().put("departmentParentId", departmentParentId);
            }
            return listPage(page);
        }
        page.getParams().put("departmentParentId", departmentParentId);
        return listPage(page);
    }

    @Override
    public SuccessResultList<List<DepartmentDTO>> listPageByAreaCode(String areaCode, ListPage page) {
        page.getParams().put("departmentAreaCode", areaCode);
        return listPage(page);
    }

    @Override
    public SuccessResultList<List<DepartmentDTO>> listPageAllByAreaCode(String areaCode, ListPage page) {
        page.getParams().put("departmentAreaCodeLike", WStringUtil.cutContinuityRepeatCharDesc(areaCode, '0', 3));
        return listPage(page);
    }

    @Override
    public SuccessResultList<List<DepartmentDTO>> listPageSubAllByAreaCode(String departmentAreaCode, ListPage page) {
        page.getParams().put("departmentAreaCodeLike", WStringUtil.cutContinuityRepeatCharDesc(departmentAreaCode, '0', 3));
        page.getParams().put("excludeDepartmentAreaCode", departmentAreaCode);
        return listPage(page);
    }

    @Override
    public SuccessResultList<List<DepartmentDTO>> listPageSubAllByAreaCodeAndType(String departmentAreaCode, Integer departmentType, ListPage page) {
        page.getParams().put("departmentAreaCodeLike", WStringUtil.cutContinuityRepeatCharDesc(departmentAreaCode, '0', 3));
        page.getParams().put("excludeDepartmentAreaCode", departmentAreaCode);
        page.getParams().put("departmentType", departmentType);
        return listPage(page);
    }

    @Override
    public DepartmentDTO get(String departmentId) {
        Map<String, Object> params = getHashMap(1);
        params.put("departmentId", departmentId);
        return departmentDao.get(params);
    }

    @Override
    public List<DepartmentDTO> listAll(Map<String, Object> params) {
        List<DepartmentDTO> departmentDTOs = list(params);
        listSub(departmentDTOs, params);
        return departmentDTOs;
    }

    @Override
    public List<ZTreeDTO> listZTree(Map<String, Object> params) {
        List<ZTreeDTO> zTreeDepartment = departmentDao.listZTree(params);
        List<ZTreeDTO> zTreeDTOs = Arrays.asList(new ZTreeDTO[zTreeDepartment.size()]);
        Collections.copy(zTreeDTOs, zTreeDepartment);
        for (ZTreeDTO zTreeDTO : zTreeDTOs) {
            Integer subCount = departmentDao.countByParentId(zTreeDTO.getId());
            setZTreeInfo(zTreeDTO, subCount);
        }
        return zTreeDTOs;
    }

    @Override
    public List<ZTreeDTO> listZTree(String departmentParentId, Map<String, Object> params) {
        if (securityComponent.getCurrentUser() != null && StringUtils.equals(ISystemConstant.TREE_ROOT_ID, departmentParentId)) {
            List<String> listBaseDepartmentIds = listBaseDepartmentIds();
            if (listBaseDepartmentIds != null && !listBaseDepartmentIds.isEmpty()) {
                params.put("departmentIds", listBaseDepartmentIds);
                return listZTree(params);
            }
        }
        params.put("departmentParentId", departmentParentId);
        return listZTree(params);
    }

    @Override
    public List<ZTreeDTO> listZTree(String departmentParentId, Integer departmentType) {
        Map<String, Object> params = getHashMap(4);
        params.put("departmentParentId", departmentParentId);
        params.put("departmentType", departmentType);
        return listZTree(params);
    }

    @Override
    public List<ZTreeDTO> listZTreeByAreaCodeAndParentIdAndType(String departmentAreaCode, String departmentParentId, Integer departmentType) {
        Map<String, Object> params = getHashMap(8);
        if (StringUtils.isBlank(departmentParentId) || StringUtils.equals(departmentParentId, "0")) {
            // params.put("departmentAreaCode", departmentAreaCode);
            params.put("departmentAreaCodeLike", WStringUtil.cutContinuityRepeatCharDesc(departmentAreaCode, '0', 3));
        } else {
            params.put("departmentAreaCodeLike", WStringUtil.cutContinuityRepeatCharDesc(departmentAreaCode, '0', 3));
            params.put("departmentParentId", departmentParentId);
        }
        params.put("departmentType", departmentType);
        List<ZTreeDTO> sourceZTreeDTO = departmentDao.listZTree(params);
        List<ZTreeDTO> zTreeDTOs = ArrayListUtil.deepClone(sourceZTreeDTO, ZTreeDTO.class);
        for (ZTreeDTO zTreeDTO : zTreeDTOs) {
            Integer subCount = departmentDao.countByParentId(zTreeDTO.getId());
            setZTreeInfo(zTreeDTO, subCount);
        }
        return zTreeDTOs;
    }

    @Override
    public List<ZTreeDTO> listZTreeByAreaCodeAndParentId(String departmentAreaCode, String departmentParentId) {
        Map<String, Object> params = getHashMap(4);
        params.put("departmentAreaCode", departmentAreaCode);
        params.put("departmentParentId", departmentParentId);
        return listZTree(params);
    }

    @Override
    public List<String> listId(Map<String, Object> params) {
        List<String> departmentIds = new ArrayList<>();
        List<DepartmentDTO> departmentDTOs = list(params);
        for (DepartmentDTO departmentDTO : departmentDTOs) {
            departmentIds.add(departmentDTO.getDepartmentId());
        }
        return departmentIds;
    }

    @Override
    public List<String> listRootId(List<DepartmentSimpleDTO> departmentSimpleDTOs) {
        List<String> departmentIds = new ArrayList<>();
        if (Objects.isNull(departmentSimpleDTOs) || departmentSimpleDTOs.isEmpty()) {
            return departmentIds;
        }
        List<DepartmentSimpleDTO> departmentList = new ArrayList<>(departmentSimpleDTOs);
        List<DepartmentSimpleDTO> departmentTempList = new ArrayList<>(departmentSimpleDTOs);
        for (int i = 0; i < departmentList.size(); i++) {
            DepartmentSimpleDTO departmentSimpleDTO = null;
            // 找出CODE
            for (DepartmentSimpleDTO department : departmentList) {
                DepartmentSimpleDTO higherDepartmentSimpleDTO = getHigherLevel(departmentList.get(i), department);
                if (higherDepartmentSimpleDTO != null) {
                    departmentSimpleDTO = higherDepartmentSimpleDTO;
                }
            }
            departmentIds.add(departmentSimpleDTO.getDepartmentId());
            // 删除包含关系的内容
            for (int j = 0; j < departmentList.size(); j++) {
                if (StringUtils.startsWith(departmentList.get(j).getDepartmentCode(), departmentSimpleDTO.getDepartmentCode())) {
                    departmentList.remove(j);
                    j--;
                }
            }
            for (int j = 0; j < departmentTempList.size(); j++) {
                if (StringUtils.startsWith(departmentTempList.get(j).getDepartmentCode(), departmentSimpleDTO.getDepartmentCode())) {
                    departmentTempList.remove(j);
                    j--;
                }
            }
            i = -1;
        }
        return departmentIds;
    }

    @Override
    public DepartmentSimpleDTO getHigherLevel(DepartmentSimpleDTO departmentSimpleDTO1, DepartmentSimpleDTO departmentSimpleDTO2) {
        if (StringUtils.startsWith(departmentSimpleDTO1.getDepartmentCode(), departmentSimpleDTO2.getDepartmentCode())) {
            return departmentSimpleDTO2;
        } else if (StringUtils.startsWith(departmentSimpleDTO2.getDepartmentCode(), departmentSimpleDTO1.getDepartmentCode())) {
            return departmentSimpleDTO1;
        }
        return null;
    }

    @Override
    public Integer count(Map<String, Object> params) {
        Integer count = departmentDao.count(params);
        return count == null ? 0 : count;
    }

    @Override
    public DepartmentPO getPO(String departmentId) {
        Map<String, Object> params = getHashMap(2);
        params.put("departmentId", departmentId);
        return departmentDao.getSimple(params);
    }

    @Override
    public List<DepartmentPO> listPO(Map<String, Object> params) {
        return departmentDao.listPO(params);
    }

    @Override
    public List<DepartmentPO> listPO(List<String> departmentIds) {
        if (departmentIds.isEmpty()) {
            return new ArrayList<>();
        }
        Map<String, Object> params = getHashMap(2);
        params.put("departmentIds", departmentIds);
        return listPO(params);
    }

    @Override
    public List<DepartmentPO> listPOByParentId(String departmentParentId) {
        return listSimpleByParentIdAndAddDateOrUpdateDate(departmentParentId, null, null);
    }

    @Override
    public List<DepartmentPO> listPOByParentIdAndAddDate(String departmentParentId, String addDate) {
        return listSimpleByParentIdAndAddDateOrUpdateDate(departmentParentId, addDate, null);
    }

    @Override
    public List<DepartmentPO> listPOByParentIdAndUpdateDate(String departmentParentId, String updateDate) {
        return listSimpleByParentIdAndAddDateOrUpdateDate(departmentParentId, null, updateDate);
    }

    @Override
    public List<DepartmentPO> listPOByAreaCode(String departmentAreaCode) {
        Map<String, Object> params = getHashMap(2);
        params.put("departmentAreaCode", departmentAreaCode);
        return listPO(params);
    }

    @Override
    public List<DepartmentPO> listPOByAreaCodeAndType(String departmentAreaCode, Integer departmentType) {
        Map<String, Object> params = getHashMap(4);
        params.put("departmentAreaCode", departmentAreaCode);
        params.put("departmentType", departmentType);
        return listPO(params);
    }

    @Override
    public Integer countByAreaCode(String areaCode) {
        Map<String, Object> params = getHashMap(2);
        params.put("departmentAreaCode", areaCode);
        return count(params);
    }

    @Override
    public Integer countAllByAreaCode(String areaCode) {
        Map<String, Object> params = getHashMap(2);
        params.put("departmentAreaCodeLike", WStringUtil.cutContinuityRepeatCharDesc(areaCode, '0', 3));
        return count(params);
    }

    @Override
    public List<DepartmentPO> listPOByNames(List<String> departmentNames) {
        if (departmentNames == null || departmentNames.isEmpty()) {
            return new ArrayList<>();
        }
        Map<String, Object> params = getHashMap(2);
        params.put("departmentNames", departmentNames);
        return listPO(params);
    }

    /**
     * 通过上级ID和新增或修改日期获取组织列表（简单模式）
     *
     * @param departmentParentId 上级ID
     * @param addDate            添加日期
     * @param updateDate         更新日期
     * @return
     */
    private List<DepartmentPO> listSimpleByParentIdAndAddDateOrUpdateDate(String departmentParentId, String addDate, String updateDate) {
        Map<String, Object> params = getHashMap(3);
        if (!StringUtils.equals(ISystemConstant.TREE_ROOT_ID, departmentParentId)) {
            DepartmentPO departmentPO = getPO(departmentParentId);
            if (departmentPO == null) {
                return new ArrayList<>();
            }
            params.put("departmentCode", departmentPO.getDepartmentCode());
        }
        params.put("addDate", addDate);
        params.put("updateDate", updateDate);
        return departmentDao.listPO(params);
    }

    /**
     * 合并组织到指定组织
     *
     * @param departmentId
     * @param mergeDepartmentIdArray
     */
    private void mergeById(String departmentId, String departmentName, String[] mergeDepartmentIdArray) {
        DepartmentDTO departmentDTO = get(departmentId);
        if (departmentDTO == null) {
            throw new SearchException("并入部门不存在");
        }
        List<String> departmentUserIds = new ArrayList<>();
        for (String mergeDepartmentId : mergeDepartmentIdArray) {
            DepartmentDTO mergeDepartmentDTO = get(mergeDepartmentId);
            if (mergeDepartmentDTO == null) {
                throw new SearchException("合并部门不存在");
            }
            LOG.debug("新增合并日志");
            saveAdjustment(mergeDepartmentId, mergeDepartmentDTO.getDepartmentName(), departmentId, departmentDTO.getDepartmentName(), DepartmentAdjustmentTypeEnum.MERGE);
            LOG.debug("删除需要合并的组织人员");
            List<String> oldDepartmentUserIds = removeByIdReturnDeletedUserIds(mergeDepartmentId, mergeDepartmentDTO.getDepartmentName());
            departmentUserIds.addAll(oldDepartmentUserIds);
        }
        LOG.debug("将合并组织的人员添加到新组织");
        departmentUserService.save(departmentId, departmentName, departmentUserIds);
    }

    /**
     * 删除部门的同时移除部门人员，并且返回移除的部门UserIds（通过部门ID）
     *
     * @param departmentId
     * @param departmentName
     * @return
     */
    private List<String> removeByIdReturnDeletedUserIds(String departmentId, String departmentName) {
        // 获取用户ID
        List<String> userIds = departmentUserService.listUserId(departmentId);
        // 删除组织用户
        departmentUserService.delete(departmentId, departmentName, userIds);
        // 删除组织
        Map<String, Object> params = getHashMap(2);
        params.put("departmentIds", Arrays.asList(departmentId));
        setUpdateInfo(params);
        departmentDao.remove(params);
        return userIds;
    }

    /**
     * 新增组织调整日志
     *
     * @param oldDepartmentId
     * @param newDepartmentId
     * @param departmentAdjustmentTypeEnum
     */
    private void saveAdjustment(String oldDepartmentId, String oldDepartmentName, String newDepartmentId, String newDepartmentName, DepartmentAdjustmentTypeEnum departmentAdjustmentTypeEnum) {
        departmentAdjustmentService.save(oldDepartmentId, oldDepartmentName, newDepartmentId, newDepartmentName, departmentAdjustmentTypeEnum);
    }

    /**
     * 新增组织Excel导入，返回ID和编码
     *
     * @param parentCode
     * @param departmentVO
     * @return [0]:uuid, [1]:code
     * @throws Exception
     */
    private String[] saveExcelReturnIdAndCode(String parentCode, DepartmentVO departmentVO) throws Exception {
        String uuid = UUIDUtil.getUUID();
        Map<String, Object> params = HashMapUtil.beanToMap(departmentVO);
        String departmentCode = getCode(parentCode, departmentVO.getDepartmentParentId());
        params.put("departmentCode", departmentCode);
        params.put("departmentId", uuid);
        setSaveInfo(params);
        departmentDao.save(params);
        return new String[]{uuid, departmentCode};
    }

    /**
     * 保存组织导入Excel数据
     *
     * @param departmentParentId
     * @param departmentParentCode
     * @param listTreeDepartmentExcelJsonArray
     * @param departmentExcelErrors
     * @throws Exception
     */
    private void saveExcel(String departmentParentId, String departmentParentCode, JSONArray listTreeDepartmentExcelJsonArray, List<DepartmentExcelError> departmentExcelErrors) throws Exception {
        Map<String, Object> params = getHashMap(2);
        for (int i = 0; i < listTreeDepartmentExcelJsonArray.size(); i++) {
            JSONObject departmentExcelJsonObject = listTreeDepartmentExcelJsonArray.getJSONObject(i);
            JSONArray subDepartmentExcelJsonArray = departmentExcelJsonObject.getJSONArray("subDepartments");
            String departmentName = departmentExcelJsonObject.getString("departmentName");
            String departmentSummary = departmentExcelJsonObject.getString("departmentSummary");
            String departmentTel = departmentExcelJsonObject.getString("departmentTel");
            Integer departmentType = departmentExcelJsonObject.getInteger("departmentType");
            params.put("departmentName", departmentName);
            params.put("departmentParentId", departmentParentId);
            DepartmentDTO departmentDTO = departmentDao.get(params);
            boolean hasSubDepartment = subDepartmentExcelJsonArray != null && subDepartmentExcelJsonArray.size() > 0;
            if (departmentDTO != null && !hasSubDepartment) {
                // 组织存在并且没有下级
                DepartmentExcelError departmentExcelError = new DepartmentExcelError();
                departmentExcelError.setDepartmentId(departmentExcelJsonObject.getInteger("departmentId"));
                departmentExcelError.setDepartmentName(departmentName);
                departmentExcelError.setDepartmentParentId(departmentExcelJsonObject.getInteger("departmentParentId"));
                departmentExcelError.setDepartmentSummary(departmentSummary);
                departmentExcelError.setDepartmentTel(departmentTel);
                departmentExcelError.setDepartmentType(departmentType);
                departmentExcelError.setReason("组织存在，无法添加");
                departmentExcelErrors.add(departmentExcelError);
                continue;
            }
            String departmentId;
            String departmentCode;
            if (departmentDTO == null) {
                // 新增组织
                DepartmentVO departmentVO = new DepartmentVO();
                departmentVO.setDepartmentParentId(departmentParentId);
                departmentVO.setDepartmentName(departmentName);
                departmentVO.setDepartmentSummary(departmentSummary);
                departmentVO.setDepartmentType(departmentType == null ? 2 : departmentType);
                departmentVO.setDepartmentTel(departmentTel);
                departmentVO.setDepartmentState(1);
                departmentVO.setDepartmentOrder(1);
                String[] departmentIdAndCodeArray = saveExcelReturnIdAndCode(departmentParentCode, departmentVO);
                departmentId = departmentIdAndCodeArray[0];
                departmentCode = departmentIdAndCodeArray[1];
            } else {
                departmentId = departmentDTO.getDepartmentId();
                departmentCode = departmentDTO.getDepartmentCode();
            }
            if (hasSubDepartment) {
                // 处理下级
                saveExcel(departmentId, departmentCode, subDepartmentExcelJsonArray, departmentExcelErrors);
            }
        }
    }

    /**
     * 重置编码
     *
     * @param departmentParentId
     * @param departmentParentCode
     * @param params
     */
    private void resetCode(String departmentParentId, String departmentParentCode, Map<String, Object> params) {
        params.clear();
        params.put("departmentParentId", departmentParentId);
        List<DepartmentDTO> departmentDTOs = list(params);
        for (int i = 0; i < departmentDTOs.size(); i++) {
            params.clear();
            int index = i + 1;
            DepartmentDTO departmentDTO = departmentDTOs.get(i);
            String departmentCode = String.format("%s%04d", departmentParentCode, index);
            params.put("departmentId", departmentDTO.getDepartmentId());
            params.put("departmentCode", departmentCode);
            departmentDao.updateCode(params);
            resetCode(departmentDTO.getDepartmentId(), departmentCode, params);
        }
    }

    /**
     * 递归查询子部门
     *
     * @param departmentDTOs
     * @param params
     */
    private void listSub(List<DepartmentDTO> departmentDTOs, Map<String, Object> params) throws SearchException {
        for (DepartmentDTO departmentDTO : departmentDTOs) {
            params.put("departmentParentId", departmentDTO.getDepartmentId());
            List<DepartmentDTO> subDepartmentDTOs = list(params);
            departmentDTO.setSubDepartments(subDepartmentDTOs);
            listSub(subDepartmentDTOs, params);
        }
    }

    /**
     * 组织是否存在
     *
     * @param excludeDepartmentId
     * @param departmentParentId
     * @param departmentName
     * @return
     */
    private boolean isDepartmentExist(String excludeDepartmentId, String departmentParentId, String departmentName) {
        Map<String, Object> params = getHashMap(3);
        params.put("excludeDepartmentId", excludeDepartmentId);
        params.put("departmentParentId", departmentParentId);
        params.put("departmentName", departmentName.trim());
        DepartmentDTO departmentDTO = departmentDao.get(params);
        if (departmentDTO == null) {
            return false;
        }
        return false;
    }

    /**
     * 获取code
     *
     * @param parentCode
     * @param parentId
     * @return
     */
    private String getCode(String parentCode, String parentId) {
        DepartmentDTO departmentDTO = departmentDao.getLastByParentId(parentId);
        String code = departmentDTO != null ? departmentDTO.getDepartmentCode() : "0000";
        return super.getNewCode(code, parentCode);
    }

    /**
     * 构建导入组织Excel树形结构数据，将列表结构转换为树形结构
     *
     * @param departmentParentId
     * @param departmentExcels
     * @return
     */
    private JSONArray listTreeExcelJsonArray(Integer departmentParentId, List<DepartmentExcel> departmentExcels, List<DepartmentExcelError> departmentExcelErrors) {
        JSONArray treeDepartmentExcelJsonArray = new JSONArray();
        for (int i = 0; i < departmentExcels.size(); i++) {
            DepartmentExcel departmentExcel = departmentExcels.get(i);
            if (departmentParentId.equals(departmentExcel.getDepartmentParentId())) {
                if (departmentExcel.getDepartmentId() == null || departmentExcel.getDepartmentId() == null || StringUtils.isBlank(departmentExcel.getDepartmentName()) || departmentExcel.getDepartmentType() == null) {
                    DepartmentExcelError departmentExcelError = new DepartmentExcelError();
                    departmentExcelError.setDepartmentId(departmentExcel.getDepartmentId());
                    departmentExcelError.setDepartmentName(departmentExcel.getDepartmentName());
                    departmentExcelError.setDepartmentParentId(departmentExcel.getDepartmentParentId());
                    departmentExcelError.setDepartmentSummary(departmentExcel.getDepartmentSummary());
                    departmentExcelError.setDepartmentTel(departmentExcel.getDepartmentTel());
                    departmentExcelError.setDepartmentType(departmentExcel.getDepartmentType());
                    departmentExcelError.setReason("必填项存在空值，请检查");
                    departmentExcelErrors.add(departmentExcelError);
                    continue;
                }
                JSONObject departmentJsonObject = new JSONObject();
                departmentJsonObject.put("departmentId", departmentExcel.getDepartmentId());
                departmentJsonObject.put("departmentParentId", departmentExcel.getDepartmentParentId());
                departmentJsonObject.put("departmentName", departmentExcel.getDepartmentName());
                departmentJsonObject.put("departmentSummary", departmentExcel.getDepartmentSummary());
                departmentJsonObject.put("departmentTel", departmentExcel.getDepartmentTel());
                departmentJsonObject.put("departmentType", departmentExcel.getDepartmentType());
                departmentJsonObject.put("subDepartments", listTreeExcelJsonArray(departmentExcel.getDepartmentId(), departmentExcels, departmentExcelErrors));
                treeDepartmentExcelJsonArray.add(departmentJsonObject);
                departmentExcels.remove(i);
                i--;
            }
        }
        return treeDepartmentExcelJsonArray;
    }
}
