使用非递归的方式遍历排序组织机构

247 阅读3分钟

公司需要对接第三方数据,同步对方的部门组织数据,众所周知递归遍历数据容易出现栈溢出,所以我自己写了个工具,根据 id、parentId 拆解数据结构,实现数据遍历排序。

代码如下:

  • 部门机构的VO类
@Getter
@Setter
@Builder
public class PhxDeptSyncReqVO {

    private String thirdId;
    private String parentThirdId;
    private String name;
    
}
  • 排序方法.
  • 思路是这样的,首先将 deptList 集合拆分成 deptIdMap、deptParentIdMap, 然后根据规则找到 root 节点,按照 root 节点来遍历拼装完整的 dept Tree。
@Slf4j
public class OuSortV2Util {

    public static List<PhxDeptSyncReqVO> sort(List<PhxDeptSyncReqVO> ouList) {

        List<String> thirdIdList = ouList.stream().map(PhxDeptSyncReqVO::getThirdId).distinct().collect(Collectors.toList());
        log.info("获取元素的id: thirdIdListSize:{}", thirdIdList);

        List<String> parentIdList = ouList.stream().map(PhxDeptSyncReqVO::getParentThirdId).distinct().collect(Collectors.toList());
        log.info("获取元素的parentId: parentIdListSize:{}", parentIdList.size());

        List<String> rootParentIdList = new ArrayList<>();
        parentIdList.stream()
                .distinct()
                .forEach(item -> {
            if (StringUtils.isBlank(item)) {
                rootParentIdList.add(item);
                log.info("找到parentId为空的root节点");
            } else if (item.equals("0")) {
                rootParentIdList.add(item);
                log.info("找到parentId为0空的root节点");
            } else if (!thirdIdList.contains(item)) {
                rootParentIdList.add(item);
                log.info("找到parentId为{}空的root节点", item);
            }
        });
        log.info("按规则提取出rootId:rootParentIdListSize:{}", rootParentIdList.size());

        return sort(ouList, rootParentIdList, null, null, null);
    }

    public static List<PhxDeptSyncReqVO> sort(List<PhxDeptSyncReqVO> ouList, List<String> rootParentIdList) {
        return sort(ouList, rootParentIdList, null, null, null);
    }

    public static List<PhxDeptSyncReqVO> sort(List<PhxDeptSyncReqVO> ouList, List<String> rootParentIdList, List<String> excludeParentIdList, Boolean containsSub) {
        return sort(ouList, rootParentIdList, excludeParentIdList, containsSub, null);
    }

    /**
     * 部门排序
     *
     * @param ouList              部门列表
     * @param rootParentIdList    部门根节点id列表
     * @param excludeParentIdList 需要剔除的节点的 parentId 列表
     * @param containsSub         剔除的节点是否包含子节点
     * @param excludeIdList       需要剔除的节点的 id 列表
     * @return 排序后的数据
     */
    public static List<PhxDeptSyncReqVO> sort(List<PhxDeptSyncReqVO> ouList, List<String> rootParentIdList, List<String> excludeParentIdList, Boolean containsSub, List<String> excludeIdList) {
        if (CollectionUtil.isEmpty(ouList) || CollectionUtil.isEmpty(rootParentIdList)) {
            log.error("数据不能为空:ouListSize:{}, rootParentIdListSize:{}", ouList.size(), rootParentIdList.size());
            return ouList;
        }

        //根据 parentId 将部门数据分组
        Map<String, List<PhxDeptSyncReqVO>> ouMapList = convert2Map(ouList, "parentThirdId");

        //过滤 excludeParentId
        ouFilter(ouMapList, excludeParentIdList, containsSub, excludeIdList);

        //获取 root 部门的列表
        List<PhxDeptSyncReqVO> rootOuList = getRootOuList(ouMapList, rootParentIdList);

        //部门最终排序后的结果集
        List<PhxDeptSyncReqVO> sortedList = new ArrayList<>();

        //遍历 root 节点
        for (PhxDeptSyncReqVO rootOu : rootOuList) {
            //写入 root 节点
            sortedList.add(rootOu);

            //找出 root 的子节点,然后使用子节点的 orgNum 向 MapList 中获取数据
            List<String> childIdList = new ArrayList<>();
            childIdList.add(rootOu.getThirdId());
            while (CollectionUtil.isNotEmpty(childIdList)) {
                //获取map中的数据,添加到 sortedList 队列中
                List<PhxDeptSyncReqVO> childOrgList = getChildList(ouMapList, childIdList);
                sortedList.addAll(childOrgList);

                //更新子节点查询列表,查询下一级的子节点
                childIdList = new ArrayList<>();
                for (PhxDeptSyncReqVO childOrg : childOrgList) {
                    childIdList.add(childOrg.getThirdId());
                }
            }
        }

        //打印找不到的部门
        printNotExistsData(ouMapList);
        return sortedList;
    }

    /**
     * 打印找不到父节点的,无法进行排序的数据
     *
     * @param ouMapList 数据集合
     */
    private static void printNotExistsData(Map<String, List<PhxDeptSyncReqVO>> ouMapList) {
        for (Iterator<Map.Entry<String, List<PhxDeptSyncReqVO>>> it = ouMapList.entrySet().iterator(); it.hasNext(); ) {
            for (PhxDeptSyncReqVO ou : it.next().getValue()) {
                log.error("部门数据同步异常,id为:{} 的部门:{}, 父节点:id={} 不存在",
                        ou.getThirdId(), ou.getName(), ou.getParentThirdId());
            }
        }
    }

    /**
     * 获取子节点列表
     *
     * @param ouMapList       部门分组集合
     * @param childOrgNumList 子节点id列表
     * @return 子节点列表
     */
    private static List<PhxDeptSyncReqVO> getChildList(Map<String, List<PhxDeptSyncReqVO>> ouMapList, List<String> childOrgNumList) {
        List<PhxDeptSyncReqVO> childOrgList = new ArrayList<>();
        for (String childOrgNum : childOrgNumList) {
            if (!ouMapList.containsKey(childOrgNum)) {
                continue;
            }
            childOrgList.addAll(ouMapList.get(childOrgNum));
            ouMapList.remove(childOrgNum);
        }
        return childOrgList;
    }

    /**
     * 获取根节点列表
     *
     * @param ouMapList  部门分组集合
     * @param rootIdList 根节点id列表
     * @return 根节点列表
     */
    private static List<PhxDeptSyncReqVO> getRootOuList(Map<String, List<PhxDeptSyncReqVO>> ouMapList, List<String> rootIdList) {
        List<PhxDeptSyncReqVO> rootOuList = new ArrayList<>();
        for (Iterator<Map.Entry<String, List<PhxDeptSyncReqVO>>> it = ouMapList.entrySet().iterator(); it.hasNext(); ) {
            Map.Entry<String, List<PhxDeptSyncReqVO>> item = it.next();
            String parentId = item.getKey();
            if (rootIdList.contains(parentId)) {
                //找出 root 节点,添加到集合中
                rootOuList.addAll(ouMapList.get(parentId));
                it.remove();
            }
        }
        return rootOuList;
    }

    /**
     * 根据 parentId 将 ouList 分组,转换成 MapList
     *
     * @param ouList 部门数据
     * @return MapList, Key=parentId
     */
    private static Map<String, List<PhxDeptSyncReqVO>> convert2Map(List<PhxDeptSyncReqVO> ouList, String groupKeyName) {
        //根据parent_id 分组
        List<List<PhxDeptSyncReqVO>> ouListGroup = CollectionUtil.groupByField(ouList, groupKeyName);

        //以 parent_id 为 Key,将 ouList 转为 MapList
        Map<String, List<PhxDeptSyncReqVO>> ouMapList = new HashMap<>();
        for (List<PhxDeptSyncReqVO> itemList : ouListGroup) {
            PhxDeptSyncReqVO item = itemList.get(0);
            ouMapList.put(item.getParentThirdId(), itemList);
        }
        return ouMapList;
    }

    /**
     * 数据过滤,过滤掉 excludeParentIdList 指定的节点及其子节点。
     *
     * @param ouMapList           组织机构map
     * @param excludeParentIdList 需要过滤的parentId
     * @param containsSub         是否过滤子节点
     * @param excludeIdList       需要过滤的 id
     */
    private static void ouFilter(Map<String, List<PhxDeptSyncReqVO>> ouMapList, List<String> excludeParentIdList, Boolean containsSub, List<String> excludeIdList) {
        if (MapUtil.isEmpty(ouMapList)) {
            return;
        }

        if (CollectionUtil.isNotEmpty(excludeIdList)) {
            ouFilterById(ouMapList, excludeIdList);
        }

        if (CollectionUtil.isNotEmpty(excludeParentIdList)) {
            ouFilterByParentId(ouMapList, excludeParentIdList, containsSub);
        }
    }

    /**
     * 根据指定的 parentId 过滤数据
     */
    private static void ouFilterByParentId(Map<String, List<PhxDeptSyncReqVO>> ouMapList, List<String> excludeParentIdList, Boolean containsSub) {
        //从参数中获取要移除的部门id,遍历移除
        for (String excludeItem : excludeParentIdList) {
            //获取子节点
            List<PhxDeptSyncReqVO> excludeItemList = ouMapList.get(excludeItem);
            if (CollectionUtil.isEmpty(excludeItemList)) {
                continue;
            }

            //移除当前节点
            ouMapList.remove(excludeItem);

            if (!containsSub) {
                //不移除子节点
                continue;
            }

            //遍历当前节点的子节点,并依次移除
            Iterator<PhxDeptSyncReqVO> iterator = excludeItemList.iterator();
            while (iterator.hasNext()) {
                PhxDeptSyncReqVO next = iterator.next();
                //先从迭代器中移除,防止循环遍历
                iterator.remove();

                //查询是否有子节点
                List<PhxDeptSyncReqVO> ouList = ouMapList.get(next.getThirdId());
                if (CollectionUtil.isEmpty(ouList)) {
                    continue;
                }
                //删除数据
                ouMapList.remove(next.getThirdId());

                //将需要删除的节点写入list
                excludeItemList.addAll(ouList);

                //重置迭代器
                iterator = excludeItemList.iterator();
            }
        }
    }

    /**
     * 根据指定的 id 过滤数据
     */
    private static void ouFilterById(Map<String, List<PhxDeptSyncReqVO>> ouMapList, List<String> excludeIdList) {
        //根据指定的id过滤部门
        for (Map.Entry<String, List<PhxDeptSyncReqVO>> item : ouMapList.entrySet()) {
            Iterator<PhxDeptSyncReqVO> iterator = item.getValue().iterator();
            while (iterator.hasNext()) {
                PhxDeptSyncReqVO next = iterator.next();
                if (excludeIdList.contains(next.getThirdId())) {
                    iterator.remove();
                }
            }
        }
    }
}