因关于组织树在项目中常见,且功能基本一致,故整理一下之前遇到的相关实现.
@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
}
@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);
}
}
@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;
}
}
首先,将所有部门信息查询出来,转换为OrgTreeNode对象, 再调用OrgTreeMenuUtil工具类的findTree方法,并传入OrgTreeNode对象和组织根节点. 工具类会使用递归,将组织结构封装.
另关于org_path和org_level的编辑时,对于子部门的修改问题, 对于org_path修改:(分隔符建议使用,尽量少用/, 如: 阿里巴巴,采购部,手机业务部)
1 先使用原路径+上一级路径, 去进行模糊查询,把所有子部门查询出来.
2 使用字符串的切割,将路径前一半去掉,拼上最新的前缀.
3 更新所有子部门的org_path字段.
对于org_level修改:(如: 阿里巴巴,采购部,手机业务部 就是第三等级部门)
1 本字段依赖于org_path字段,看org_path字段中的分隔符能拆分为几部分,即为那个等级.
@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
}
@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;
}
}
用空间换时间的做法,不用循环递归去封装,直接通过两次遍历,完成组织树的封装.
@Data
public class OrgDropdownListRes {
private String id;
private String pId;
private String isLeaf;
private String value;
private String title;
private List<Person> persons;
}
@Data
public class Person {
private String id;
private String username;
private String password;
private String orgId;
private Integer gender;
}
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用户数据.