本来打算对一些比较复杂的功能做些解析的,但发现还是挺有难度的,那还是两方面吧,一个难一点的,一个简单一点的搭配着写,其实一些写法上的优化也是有很多可以学习的地方,自己写代码很多都是直接一个for循环

java 递归组织部门 java 部门树_子节点

这次看一个在列表中排除某些元素的写法优化,用

java 递归组织部门 java 部门树_java 递归组织部门_02

removeIf 函数,传入一个 Predicate 用来过滤,跟我们平时用的 stream().filter() 挺像的,但确实之前我只知道用 stream() 但是又会显得很繁琐

这里再简单看下 sys_dept 的 存储

java 递归组织部门 java 部门树_字段_03

可以看到这个字段存储了这个节点的所有祖先,二叉树到根节点的所有父节点,不难推测,保存和修改节点的时候肯定会动态 set 这个属性

下面思考,加上这个字段以后,对部门树的操作service应该如何设计(crud)

首先看新增,新增只会增加子节点,查询到这个节点到根节点路径上的父节点就好了

/**
 * 新增保存部门信息
 * 
 * @param dept 部门信息
 * @return 结果
 */
@Override
public int insertDept(SysDept dept)
{
    SysDept info = deptMapper.selectDeptById(dept.getParentId());
    // 如果父节点不为正常状态,则不允许新增子节点
    if (!UserConstants.DEPT_NORMAL.equals(info.getStatus()))
    {
        throw new ServiceException("部门停用,不允许新增");
    }
    dept.setAncestors(info.getAncestors() + "," + dept.getParentId());
    return deptMapper.insertDept(dept);
}

这里其实有点递归的意思,父节点中已经存放了他的所有父节点,所以只用找出离他最近的一个父节点 parent.ancestor + parent.id,字符串拼接上即可

再看修改

/**
     * 修改部门
     */
    @PreAuthorize("@ss.hasPermi('system:dept:edit')")
    @Log(title = "部门管理", businessType = BusinessType.UPDATE)
    @PutMapping
    public AjaxResult edit(@Validated @RequestBody SysDept dept)
    {
        Long deptId = dept.getDeptId();
        deptService.checkDeptDataScope(deptId);
        if (UserConstants.NOT_UNIQUE.equals(deptService.checkDeptNameUnique(dept)))
        {
            return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,部门名称已存在");
        }
        else if (dept.getParentId().equals(deptId))
        {
            return AjaxResult.error("修改部门'" + dept.getDeptName() + "'失败,上级部门不能是自己");
        }
        else if (StringUtils.equals(UserConstants.DEPT_DISABLE, dept.getStatus()) && deptService.selectNormalChildrenDeptById(deptId) > 0)
        {
            return AjaxResult.error("该部门包含未停用的子部门!");
        }
        dept.setUpdateBy(getUsername());
        return toAjax(deptService.updateDept(dept));
    }

/**
 * 修改保存部门信息
 * 
 * @param dept 部门信息
 * @return 结果
 */
@Override
public int updateDept(SysDept dept)
{
    SysDept newParentDept = deptMapper.selectDeptById(dept.getParentId());
    SysDept oldDept = deptMapper.selectDeptById(dept.getDeptId());
    if (StringUtils.isNotNull(newParentDept) && StringUtils.isNotNull(oldDept))
    {
        String newAncestors = newParentDept.getAncestors() + "," + newParentDept.getDeptId();
        String oldAncestors = oldDept.getAncestors();
        dept.setAncestors(newAncestors);
        updateDeptChildren(dept.getDeptId(), newAncestors, oldAncestors);
    }
    int result = deptMapper.updateDept(dept);
    if (UserConstants.DEPT_NORMAL.equals(dept.getStatus()) && StringUtils.isNotEmpty(dept.getAncestors())
            && !StringUtils.equals("0", dept.getAncestors()))
    {
        // 如果该部门是启用状态,则启用该部门的所有上级部门
        updateParentDeptStatusNormal(dept);
    }
    return result;
}

controller 层确保

  • 部门名称唯一
  • 上级部门不能是自己
  • 不能包含未停用的子部门

service 层处理具体逻辑

  1. 首先处理自己的 ancestor 字段,找到修改后的父节点,拼接得到自己的 ancestor
  2. 递归处理所有子节点的 ancestor
/**
     * 修改子元素关系
     * 
     * @param deptId 被修改的部门ID
     * @param newAncestors 新的父ID集合
     * @param oldAncestors 旧的父ID集合
     */
    public void updateDeptChildren(Long deptId, String newAncestors, String oldAncestors)
    {
        List<SysDept> children = deptMapper.selectChildrenDeptById(deptId);
        for (SysDept child : children)
        {
            child.setAncestors(child.getAncestors().replaceFirst(oldAncestors, newAncestors));
        }
        if (children.size() > 0)
        {
            deptMapper.updateDeptChildren(children);
        }
    }
<select id="selectChildrenDeptById" parameterType="Long" resultMap="SysDeptResult">
		select * from sys_dept where find_in_set(#{deptId}, ancestors)
</select>

<update id="updateDeptChildren" parameterType="java.util.List">
    update sys_dept set ancestors =
    <foreach collection="depts" item="item" index="index"
        separator=" " open="case dept_id" close="end">
        when #{item.deptId} then #{item.ancestors}
    </foreach>
    where dept_id in
    <foreach collection="depts" item="item" index="index"
        separator="," open="(" close=")">
        #{item.deptId}
    </foreach>
</update>

这个 sql 会找出 ancestors 中包含这个 id 的,也就是这个节点所有的子节点,然后更新这个 set