关于常见的组织树结构实现方法

因关于组织树在项目中常见,且功能基本一致,故整理一下之前遇到的相关实现.

1 组织树形结构 (递归实现)

1 部门组织类

@Data
public class Org {
    
    
    
    @ApiModelProperty(value = "部门ID")
    private String orgId;

    @ApiModelProperty(value = "部门名称")
    private String orgName;

    @ApiModelProperty(value = "父部门ID")
    private String parentOrgId;
    
    // ... 还有其他字段如 org_path  org_level
}

2 组织树形类

@Data
public class OrgTreeNode {
    
    
    private String id;
    private String label;
    private String parentId;
    private Boolean disabled;
    private String isLeaf;
    private String isHidden;
    private String orgPath;
    private String ownerUserIds;
    private String ownerUserNames;
    private int sortNo;
    private Object data;

    private List<OrgTreeNode> children;

    public void addChildren(OrgTreeNode orgTree) {
    
    
        if (children == null) {
    
    
            children = new ArrayList<>();
        }
        children.add(orgTree);
    }

    public void addChildrens(List<OrgTreeNode> orgTreeList) {
    
    
        if (children == null) {
    
    
            children = new ArrayList<>();
        }
        children.addAll(orgTreeList);
    }

}

3 树形工具类

@Slf4j
public class OrgTreeMenuUtil {
    
    

    /*
     * 排序,根据order排序
     */
    public Comparator<OrgTreeNode> order() {
    
    
        return (o1, o2) -> {
    
    
            if (o1.getSortNo() != o2.getSortNo()) {
    
    
                return o1.getSortNo() - o2.getSortNo();
            }
            return 0;
        };
    }

    public List<OrgTreeNode> findTree(List<OrgTreeNode> allMenu, String rootNode) {
    
    
        List<OrgTreeNode> rootMenu = new ArrayList<>();
        try {
    
    
            // 根节点
            for (OrgTreeNode nav : allMenu) {
    
    
                // rootNode为根节点
                if (rootNode.equals(nav.getParentId())) {
    
    
                    rootMenu.add(nav);
                }
            }
            // 根据Menu类的order排序
            rootMenu.sort(order());
            // 为根菜单设置子菜单,getClild是递归调用的
            for (OrgTreeNode nav : rootMenu) {
    
    
                // 获取根节点下的所有子节点 使用getChild方法
                List<OrgTreeNode> childList = getChild(nav.getId(), allMenu);
                // 给根节点设置子节点
                nav.setChildren(childList);
            }
            // 输出构建好的菜单数据
            return rootMenu;
        } catch (Exception e) {
    
    
            log.error(e.getMessage());
            return Collections.emptyList();
        }
    }

    /**
     * 获取子节点
     * @param id 父节点id
     * @param allMenu 所有菜单列表
     * @return 每个根节点下,所有子菜单列表
     */
    public List<OrgTreeNode> getChild(String id,List<OrgTreeNode> allMenu){
    
    
        // 子菜单
        List<OrgTreeNode> childList = new ArrayList<OrgTreeNode>();
        for (OrgTreeNode nav : allMenu) {
    
    
            // 遍历所有节点,将所有菜单的父id与传过来的根节点的id比较
            // 相等说明:为该根节点的子节点。
            if (id.equals(nav.getParentId())){
    
    
                childList.add(nav);
            }
        }
        // 递归
        for (OrgTreeNode nav : childList) {
    
    
            nav.setChildren(getChild(nav.getId(), allMenu));
        }
        // 排序
        childList.sort(order()); 
        // 如果节点下没有子节点,返回一个空List(递归退出)
        if (CollectionUtils.isEmpty(childList)){
    
    
            return new ArrayList<OrgTreeNode>();
        }
        return childList;
    }
}

4 使用说明

首先,将所有部门信息查询出来,转换为OrgTreeNode对象, 再调用OrgTreeMenuUtil工具类的findTree方法,并传入OrgTreeNode对象和组织根节点. 工具类会使用递归,将组织结构封装.

另关于org_path和org_level的编辑时,对于子部门的修改问题, 对于org_path修改:(分隔符建议使用,尽量少用/, 如: 阿里巴巴,采购部,手机业务部)

1 先使用原路径+上一级路径, 去进行模糊查询,把所有子部门查询出来.

2 使用字符串的切割,将路径前一半去掉,拼上最新的前缀.

3 更新所有子部门的org_path字段.

对于org_level修改:(如: 阿里巴巴,采购部,手机业务部 就是第三等级部门)

1 本字段依赖于org_path字段,看org_path字段中的分隔符能拆分为几部分,即为那个等级.

2 组织树形结构 (非递归实现)

1 部门组织类

@Data
public class Org {
    
    
    
    @ApiModelProperty(value = "部门ID")
    private String orgId;

    @ApiModelProperty(value = "部门名称")
    private String orgName;

    @ApiModelProperty(value = "父部门ID")
    private String orgParentId;
    
    @ApiModelProperty(value = "子部门集合")
    private List<Org> children;
    
    // ... 还有其他字段如 org_path  org_level
}

2 树形工具类

@Slf4j
public class OrgTreeMenuUtil {
    
    
    
    /**
     * 生成组织树
     *
     * @param list 
     * @param root 根节点id
     * @return
     */
    public List<Org> getTree(List<Org> list, String root) {
    
    
        List<Org> orgList = new ArrayList<>();
        Map<String, Org> parentObjs = new HashMap<>();
        // 找出所有的一级菜单
        for (Org org : list) {
    
     
            if (org.getOrgParentId().equals(root)) {
    
    
                orgList.add(org);
            }
            // 记录所有菜单的id及对应的实体
            parentObjs.put(org.getOrgId(), org);
        }
 
        // 把每个组织加到父组织的子集中
        for (Org org : list) {
    
    
            // 如果是一级菜单,不需要找它的父级
            if (org.getOrgParentId().equals(root)) {
    
    
                continue;
            }
            // 每个组织找到自己的直接父级,并加入到父级的子集中
            Org parentObj = parentObjs.get(org.getOrgParentId());

// 如果还没有子组织集合,新建一个集合并添加子组织
            if (Objects.isNull(parentObj.getChildren())) {
    
    
                parentObj.setChildren(new ArrayList<>());
            }
            parentObj.getChildren().add(org);
        }
        return orgList;
        
    }
    
}

用空间换时间的做法,不用循环递归去封装,直接通过两次遍历,完成组织树的封装.

3 部门和人员的树形结构

1 组织用户对象

@Data
public class OrgDropdownListRes {
    
    
    private String id;
    private String pId;
    private String isLeaf;
    private String value;
    private String title;
    private List<Person> persons;
}

2 用户对象

@Data
public class Person {
    
    
    private String id;
    private String username;
    private String password;
    private String orgId;
    private Integer gender;
}

3 部门集合与用户集合合并代码

	    private List<OrgDropdownListRes> convertOrg(List<Org> orgList, List<Person> personList) {
    
    
        List<OrgDropdownListRes> resList = Lists.newArrayList();
        orgList.forEach(o -> {
    
    
            OrgDropdownListRes res = new OrgDropdownListRes();
            res.setId(o.getOrgId());
            res.setPId(o.getParentOrgId());
            res.setValue(o.getOrgId());
            res.setTitle(o.getOrgName());
            res.setIsLeaf(SysConstants.IS_LEAF_1.equals(o.getIsLeaf()));
            if (!CollectionUtils.isEmpty(personList)) {
    
    
                Map<String, List<Person>> stringListMap = personList.stream().collect(Collectors.groupingBy(Person::getOrgId));
                res.setPersons(stringListMap.getOrDefault(o.getOrgId(), Collections.emptyList()));
            }
            resList.add(res);
        });
        return resList;
    }

查询所有部门 和 所有人员(根据部门id 和 企业id),通过Stream流将人员集合变成map结构,遍历部门,set用户数据.

Guess you like

Origin blog.csdn.net/ABestRookie/article/details/120498700