用户多部门切换部门,MySQL根据多个部门id递归获取所有上级(祖级)、获取部门的全路径(全结构名称)

背景

之前做过的项目,都是一个用户就一个部门的,现在碰到个一个用户在多个部门的需求,而且需要可以切换不同部门查看不同数据

就比如说一个大公司下面有多个子公司,每个子公司有好多部门、子部门等等,然后有部分用户就和多个子公司/部门都有关联,有些数据就需要根据当前用户选择的部门进行过滤。

实现

1、用户部门关联

首先就是用户和多部门关联,数据库要怎么保存。之前不是单部门的嘛,用户表和部门表关联就是用的一个dept_id字段;现在多部门我也不打算新建表存储关联关系,而是在原有的dept_id字段上,改个数据类型,比如由varchar改为longtext,然后多个部门之间用逗号隔开。

2、切换部门

其次就是切换部门,用户登录的时候查询用户信息,拿到了用户的部门id,根据部门id去递归获取所有上级。

因为我们切换部门的时候每次只能选中一个部门,而且这个部门需要显示全路径(比如:A公司/子公司1/开发部A公司/子公司1/办公室),所以为了后续好获取全路径,我们需要加一个checked属性,用户的部门id checked值为1,也就是true,其他的上级则为0,也就是false。

递归上级

根据多个部门id,递归所有上级,sql查询如下:

sql 复制代码
<select id="parentDeptList" resultType="com.entity.sys.vo.DeptVo">
    SELECT id,GROUP_CONCAT(DISTINCT `name`) `name`,GROUP_CONCAT(DISTINCT parent_id) parent_id,MAX(checked) checked
    FROM(
        WITH RECURSIVE temp_dept(id,name,parent_id,checked) AS (
            <!--查询指定部门-->
            SELECT id,name,parent_id,1 checked
            FROM sys_dept
            WHERE FIND_IN_SET(id,#{deptId})
            UNION ALL
            <!--递归查询父部门-->
            SELECT d.id,d.name,d.parent_id,0 checked
            FROM sys_dept d
            JOIN temp_dept c ON d.id = c.parent_id
        )
        SELECT id,name,parent_id,checked
        FROM temp_dept
        <!--不加条件则获取它的所有父节点,包括它本身-->
        <!--WHERE parent_id IN (SELECT id FROM sys_dept WHERE parent_id = 'ROOT')-->
    ) a
    GROUP BY id
</select>

查询结果用一个实体类接收:

java 复制代码
@Data
public class DeptVo {
    private String id;
    private String parentId;
    private String name;

    /**
     * 部门id全称:1/2/5/13
     */
    private String fullId;

    /**
     * 部门名称全称:A公司/子公司1/开发部/开发一组
     */
    private String fullName;

    /**
     * 是否选中
     */
    private Boolean checked;

    public DeptVo() {}

    public DeptVo(String id, String name, String fullId, String fullName,Boolean checked) {
        this.id = id;
        this.name = name;
        this.fullId = fullId;
        this.fullName = fullName;
        this.checked = checked;
    }
}

获取部门的全路径名称

拿到了查询结果,现在我们就可以来处理和获取部门的全路径名称了:

java 复制代码
/**
 * 递归查询,根据当前部门id(可能有多个)获取所有上级
 */
public List<DeptVo> parentDeptList(String id){
    List<DeptVo> deptList = baseMapper.parentDeptList(id);
    List<DeptVo> result = new ArrayList<>();
    int count = 0;
    for (DeptVo node : deptList) {
        if (node.getChecked()) {
            generateFullIdAndFullName(deptList, node);
            // 默认选中第一个部门
            if (count == 0) node.setChecked(true);
            else node.setChecked(false);
            result.add(node);
            count++;
        }
    }
    return result;
}

private static void generateFullIdAndFullName(List<DeptVo> nodeList, DeptVo currentNode) {
    StringBuilder fullIdBuilder = new StringBuilder();
    StringBuilder fullNameBuilder = new StringBuilder();
    generateFullIdAndFullNameRecursive(nodeList, currentNode, fullIdBuilder, fullNameBuilder);
    currentNode.setFullId(fullIdBuilder.toString());
    currentNode.setFullName(fullNameBuilder.toString());
}

private static void generateFullIdAndFullNameRecursive(List<DeptVo> nodeList, DeptVo currentNode, StringBuilder fullIdBuilder, StringBuilder fullNameBuilder) {
    fullIdBuilder.insert(0, currentNode.getId());
    fullNameBuilder.insert(0, currentNode.getName());
    if (!"ROOT".equals(currentNode.getParentId())) {
        fullIdBuilder.insert(0, "/");
        fullNameBuilder.insert(0, "/");
        for (DeptVo node : nodeList) {
            if (node.getId().equals(currentNode.getParentId())) {
                generateFullIdAndFullNameRecursive(nodeList, node, fullIdBuilder, fullNameBuilder);
                break;
            }
        }
    }
}

比如说用户A有id为4、5、6、7、8这几个部门,像这种的话就是新增/编辑用户时,用户选择部门时任意一级的部门都可以选,所以就会出现下面图片中的示例:单独选了营销部,但是又选了营销部下面的营销一部。这种情况就不做:切换部门时,查询所在部门及子部门数据。也就是只查询所在部门数据,子部门数据不查询,不然切换部门这个功能的意义不大。

然后上面的数据的输出结果如下:

将结果设置到用户信息中

能正确拿到数据后,我们在用户信息中,增加一个字段:

java 复制代码
@TableField(exist = false)
private List<DeptVo> deptArr;

parentDeptList 方法返回的数据设置到 deptArr 中,最后将用户信息存储到缓存中。用户登录成功,获取登录用户信息,就可以拿到 deptArr 了,切换时也是在这个列表进行切换选哪一个。

切换部门

切换部门时,前端传选中的部门的id,后端拿到当前登录用户的 deptArr 列表,匹配前端传过来的id,将匹配到的那一条数据的 checked 设置为true,其他的则设置为false:

java 复制代码
/**
 * 切换部门
 * @param id 部门id
 */
@GetMapping(value = "/qhbmsctk")
public ResultUtil qhbmsctk(String id,HttpServletRequest request){
    // 获取当前登录用户
    SysUser loginUser = LoginUtil.getLoginUser();
    List<DeptVo> deptArr = loginUser.getDeptArr();
    List<DeptVo> collect = deptArr.stream().peek(obj -> {
        if (obj.getId().equals(id)) {
            obj.setChecked(true); // 设置checked为true如果id等于前端传过来的deptId
        }else {
            obj.setChecked(false);
        }
    }).collect(Collectors.toList());
    loginUser.setDeptArr(collect);
    // 切换成功后,可以重新生成token重新登录(静默登录),也可以将更新后的用户信息重新更新到缓存中
    // 看自己实际需求选哪一种方式
    return ResultUtil.success();
}

切换成功后,可以重新生成token重新登录(静默登录),也可以将更新后的用户信息重新更新到缓存中,看自己实际需求选哪一种方式。

java 复制代码
/**
 * 获取当前用户选中的部门
 */
public DeptVo getCheckedDept(SysUser loginUser){
    loginUser = loginUser == null ? LoginUtil.getLoginUser() : loginUser;
    List<DeptVo> list = loginUser.getDeptArr();
    DeptVo vo = list.stream().filter(t -> t.getChecked()).findFirst().orElse(null);
    if (vo == null){
        throw new ExceptionVo(-1,"获取当前选中部门失败");
    }
    return vo;
}

3、数据过滤

数据过滤有两种过滤方式:1、根据部门id过滤(根据当前用户选中的部门id去过滤数据);2、根据当前用户id过滤(只查询当前用户创建的数据)。

第一种的话,没什么好说的,拿到当前用户选中的部门的id就行。

第二种呢就不能只根据用户id去过滤,因为用户是多部门的,所以需要用 用户id+选中的部门id 去过滤数据。这里的话要过滤数据的表在刚开始设计字段的时候,有两种方式:1、可以设计两个字段:create_id(创建人id)、create_dept(创建人部门id),通过这两个字段去过滤数据;2、也可以只设计一个字段,但是这个字段要包含创建人id创建人部门idcreate_id(由创建人id和创建人部门id组合而成),比如:userid#deptid,两个id之间用某个分隔符隔开,后续哪怕需要单独用的用户id也好、部门id也好,都可以很方便的截取出来。(我后面选择的是第二种方式。)

最后

大家如果有类似的需求要做,可以参考一下,我这里也是记录一下自己的实现方案,方便以后再碰到可以直接把代码拿过来用。

相关推荐
小灰灰__2 分钟前
IDEA加载通义灵码插件及使用指南
java·ide·intellij-idea
夜雨翦春韭6 分钟前
Java中的动态代理
java·开发语言·aop·动态代理
程序媛小果26 分钟前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
掘金-我是哪吒31 分钟前
微服务mysql,redis,elasticsearch, kibana,cassandra,mongodb, kafka
redis·mysql·mongodb·elasticsearch·微服务
追风林31 分钟前
mac m1 docker本地部署canal 监听mysql的binglog日志
java·docker·mac
芒果披萨1 小时前
El表达式和JSTL
java·el
duration~1 小时前
Maven随笔
java·maven
zmgst1 小时前
canal1.1.7使用canal-adapter进行mysql同步数据
java·数据库·mysql
令狐少侠20112 小时前
explain执行计划分析 ref_
mysql
随心............2 小时前
python操作MySQL以及SQL综合案例
数据库·mysql