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

小知识,大挑战!本文正在参与“程序员必备小知识”创作活动。

0 前言

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

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

1 部门组织类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
java复制代码@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 组织树形类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
java复制代码@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 树形工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
java复制代码@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 部门组织类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码@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 树形工具类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
java复制代码@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 组织用户对象

1
2
3
4
5
6
7
8
9
java复制代码@Data
public class OrgDropdownListRes {
private String id;
private String pId;
private String isLeaf;
private String value;
private String title;
private List<Person> persons;
}

2 用户对象

1
2
3
4
5
6
7
8
java复制代码@Data
public class Person {
private String id;
private String username;
private String password;
private String orgId;
private Integer gender;
}

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

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
java复制代码	    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用户数据.

本文转载自: 掘金

开发者博客 – 和开发相关的 这里全都有

0%