根据两组相关联数据(部门 + 用户),生成列表树

news2024/11/20 12:30:58

文章目录

  • 背景
  • 想要得到的数据格式
  • 业务层获取数据,部门及用户,构建树结构
  • TreeUtil生成的格式
  • 部门实体
  • 用户实体

背景

目前拥有用户和部门两组数据,根据部门和用户的关系,生成部门树,且每个部门下拥有哪些与子部门同级的用户列表
在这里插入图片描述

想要得到的数据格式

[
    {
        "id":100,
        "name":"东方金信",
        "parentId":0,
        "depts":[
            {
                "id":101,
                "name":"SDP组",
                "parentId":100,
                "depts":null,
                "users":null
            }
        ],
        "users":[
            {
                "id":1,
                "nickname":"芋道源码",
                "username":"admin"
            }
        ]
    },
    {
        "id":102,
        "name":"AAA",
        "parentId":0,
        "depts":[
            {
                "id":103,
                "name":"SDP组",
                "parentId":102,
                "depts":null,
                "users":null
            }
        ],
        "users":null
    }
]

业务层获取数据,部门及用户,构建树结构

public List<DeptUserSimpleRespVO> getDeptUserListByNickname(String nickname, CommonStatusEnum status) {

    List<DeptUserSimpleRespVO> deptUserSimpleRespVOs = deptService.getDeptList();
    List<UserSimpleRespVO> adminUserDOS = userMapper.selectList();

    AtomicBoolean hasUndistributedDeptUser = new AtomicBoolean(false);
    // 设置未分配部门的用户部门为 -1L
    Long undistributedDeptId = -1L;
    Map<Long, List<AdminUserDO>> deptIdAndUsers = adminUserDOS.stream()
            .filter(adminUserDO -> {
                if (adminUserDO.getDeptId() == null) {
                    adminUserDO.setDeptId(undistributedDeptId);
                    hasUndistributedDeptUser.set(true);
                }
                return true;
            })
            .collect(Collectors.groupingBy(AdminUserDO::getDeptId));

    if (hasUndistributedDeptUser.get()) {
        DeptUserSimpleRespVO undistributedDept = new DeptUserSimpleRespVO();
        undistributedDept.setId(undistributedDeptId);
        undistributedDept.setParentId(0L);
        undistributedDept.setName("未分配部门");
        deptUserSimpleRespVOs.add(undistributedDept);
    }

	// 将用户塞进部门
    deptUserSimpleRespVOs.forEach(deptUserSimpleRespVO -> {
        List<UserSimpleRespVO> userSimpleRespVOS = deptIdAndUsers.get(deptUserSimpleRespVO.getId()));
        deptUserSimpleRespVO.setUsers(userSimpleRespVOS);
    });

	// 构建部门树并返回
    return TreeUtil.buildTree(deptUserSimpleRespVOs,
            DeptUserSimpleRespVO::getId, DeptUserSimpleRespVO::getParentId,
            DeptUserSimpleRespVO::getId, DeptUserSimpleRespVO::setDepts);
}

TreeUtil生成的格式

import cn.hutool.core.collection.CollUtil;
import cn.hutool.json.JSONUtil;
import org.springframework.util.Assert;

import java.util.*;
import java.util.function.Function;
import java.util.stream.Collectors;
import java.util.stream.Stream;

/**
 * href: https://blog.csdn.net/qq_41902662/article/details/127984296
 * @author jiaohongtao
 * @version 1.0.0
 * @since 2023/08/15
 */
public class TreeUtil {

    /**
     * 构造数据树 O(N)
     *
     * @param list           原集合
     * @param mKey           父级被子集关联的字段,比如id
     * @param treeConnectKey 子集关联父级的字段,比如parentId
     * @param treeSortKey    同级菜单的排序字段,比如sort
     * @param consumer       添加子集的函数,如dto.setChild(list)
     * @param <T>
     * @return
     */
    public static <T> List<T> buildTree(List<T> list, Function<T, ?> mKey, Function<T, ?> treeConnectKey,
                                        Function<T, ? extends Comparable> treeSortKey, Consumers<T, T> consumer) {
        if (CollUtil.isEmpty(list)) {
            return Collections.emptyList();
        }
        /*Assert.notNull(mKey, "父级被子级关联的字段不能为空");
        Assert.notNull(treeConnectKey, "子级关联父级的字段不能为空");
        Assert.notNull(consumer, "消费函数不能为空");*/
        //线程安全类集合
        List<T> tree = Collections.synchronizedList(new ArrayList<>());
        //按上级id分组
        /*final Map<?, List<T>> collect = list.parallelStream()
                .collect(Collectors.groupingBy(treeConnectKey));*/
        final Map<?, List<T>> collect = list.stream().collect(Collectors.groupingBy(treeConnectKey));
        // list.parallelStream().filter(m -> {
        list.stream().filter(m -> {
            final boolean b = (Long) treeConnectKey.apply(m) != 0L;
            if (!b) {
                tree.add(m);
            }
            return b;
        }).forEach(
                //通过对象地址引用实现父子级关联,即使父级先添加了子级,子级在添加孙子级,父子孙三级也全部都会关联
                m -> {
                    if (treeSortKey != null) {
                        consumer.accept(m, CollUtil.sort(collect.get(mKey.apply(m)), Comparator.comparing(treeSortKey)));
                    } else {
                        consumer.accept(m, collect.get(mKey.apply(m)));
                    }
                }
        );
        if (treeSortKey != null) {
            //排序
            tree.sort(Comparator.comparing(treeSortKey));
            Stream<T> peek = tree.parallelStream().peek(b -> consumer.accept(b, CollUtil.sort(collect.get(mKey.apply(b)), Comparator.comparing(treeSortKey))));
            return peek.collect(Collectors.toList());
        } else {
            return tree.parallelStream().peek(b -> consumer.accept(b, collect.get(mKey.apply(b)))).collect(Collectors.toList());
        }
    }

    @FunctionalInterface
    public interface Consumers<T, N> {

        /**
         * 消费函数
         *
         * @param m
         * @param n
         */
        void accept(T m, List<N> n);
    }

    public static void main(String[] args) {
        //模拟原始数据
        final List<DeptDTO> dtos = new ArrayList<>();
        DeptDTO deptDTO1 = new DeptDTO(1L, 0L, "A");
        DeptDTO deptDTO2 = new DeptDTO(2L, 1L, "B");
        DeptDTO deptDTO3 = new DeptDTO(3L, 1L, "C");
        DeptDTO deptDTO4 = new DeptDTO(4L, 2L, "D");
        DeptDTO deptDTO5 = new DeptDTO(5L, 3L, "E");
        DeptDTO deptDTO6 = new DeptDTO(6L, 3L, "F");
        DeptDTO deptDTO7 = new DeptDTO(7L, null, "G");
        DeptDTO deptDTO8 = new DeptDTO(8L, null, "H");
        dtos.add(deptDTO1);
        dtos.add(deptDTO2);
        dtos.add(deptDTO3);
        dtos.add(deptDTO4);
        dtos.add(deptDTO5);
        dtos.add(deptDTO6);
        dtos.add(deptDTO7);
        dtos.add(deptDTO8);

        DeptDTO noDeptDTO = new DeptDTO(-1L, 0L, "未分配部门");
        dtos.add(noDeptDTO);
        List<DeptDTO> collect = dtos.stream().filter(dto -> {
            if (dto.getParentId() == null) {
                dto.setParentId(-1L);
            }
            return true;
        }).collect(Collectors.toList());

        //获取树结构
        List<DeptDTO> tree = TreeUtil.buildTree(collect, DeptDTO::getId, DeptDTO::getParentId, DeptDTO::getParentId, DeptDTO::setChild);

        System.out.println(JSONUtil.toJsonStr(tree));
    }
}

部门实体


import com.jiao.vo.user.UserSimpleRespVO;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

import java.util.List;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class DeptUserSimpleRespVO {
    private Long id;
    private String name;
    private Long parentId;
    
    private List<DeptUserSimpleRespVO> depts;
    private List<UserSimpleRespVO> users;
}

用户实体

package com.jiao.vo.user;

import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;

@Data
@NoArgsConstructor
@AllArgsConstructor
public class UserSimpleRespVO {
    private Long id;
    private String nickname;
    private String username;
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/885812.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

接口自动化测试框架搭建

为什么要做&#xff08;自动化&#xff09;接口测试&#xff1f; 1、由于现在各个系统的复杂度不断上升&#xff0c;导致传统的测试方法成本上升且测试效率大幅下降&#xff0c;而接口测试相对于UI测试更加稳定&#xff0c;且相对容易实现自动化持续集成&#xff0c;可以减少人…

Spring BeanDefinition 也分父子关系?

在 Spring 框架中&#xff0c;BeanDefinition 是一个核心概念&#xff0c;用于定义和配置 bean 的元数据&#xff0c;虽然在实际应用中&#xff0c;我们一般并不会或者很少直接定义 BeanDefinition&#xff0c;但是&#xff0c;我们在 XML 文件中所作的配置&#xff0c;以及利用…

数字化智慧工地云平台,劳务实名制系统、视频监控系统、环境监测系统、人员定位系统、工资代发系统、AI识别系统、视频监控系统

智慧工地概念 智慧工地是一种崭新的工程全生命周期管理理念&#xff0c;是指运用信息化手段&#xff0c;通过对工程项目进行精确设计和施工模拟&#xff0c;围绕施工过程管理&#xff0c;建立互联协同、智能生产、科学管理的施工项目信息化生态圈&#xff0c;并将此数据在虚拟…

RabbitMQ的5种消息队列

RabbitMQ的5种消息队列 1、七种模式介绍与应用场景 1.1 简单模式(Hello World) 一个生产者对应一个消费者&#xff0c;RabbitMQ 相当于一个消息代理&#xff0c;负责将 A 的消息转发给 B。 应用场景&#xff1a;将发送的电子邮件放到消息队列&#xff0c;然后邮件服务在队列…

LeetCode150道面试经典题-- 环形链表(简单)

1.题目 给你一个链表的头节点 head &#xff0c;判断链表中是否有环。 如果链表中有某个节点&#xff0c;可以通过连续跟踪 next 指针再次到达&#xff0c;则链表中存在环。 为了表示给定链表中的环&#xff0c;评测系统内部使用整数 pos 来表示链表尾连接到链表中的位置&…

通过pycharm使用git和github的步骤(图文详解)

一、在Pycharm工具中配置集成Git和GitHub。 1.集成Git。 打开Pycharm, 点击File-->Settins-->Version Control-->Git 然后在 Path to Git executable中选择本地的git.exe路径。如下图&#xff1a; 2.集成GitHub 打开Pycharm, 点击File-->Settins-->Version…

基于Googlenet深度学习网络的信号调制类型识别matlab仿真

目录 1.算法运行效果图预览 2.算法运行软件版本 3.部分核心程序 4.算法理论概述 4.1 深度学习与卷积神经网络 4.2 数据预处理 4.3 GoogLeNet结构 4.4 分类器 5.算法完整程序工程 1.算法运行效果图预览 2.算法运行软件版本 MATLAB2022a 3.部分核心程序 ............…

Qt 父子对象的关系

文章目录 前言一. Qt 对象可以存在父子关系&#xff1a;二. 父子关系的建立&#xff1a;三. Qt 对象的销毁&#xff1a; 总结 前言 Qt是一个流行的C框架&#xff0c;用于开发跨平台的图形用户界面&#xff08;GUI&#xff09;应用程序。Qt提供了一种强大的对象模型&#xff0c…

leetcode做题笔记83删除排序链表中的重复元素

给定一个已排序的链表的头 head &#xff0c; 删除所有重复的元素&#xff0c;使每个元素只出现一次 。返回 已排序的链表 。 输入&#xff1a;head [1,1,2] 输出&#xff1a;[1,2] 思路一&#xff1a;模拟题意 struct ListNode* deleteDuplicates(struct ListNode* head){i…

韦东山-电子量产工具项目:显示单元

所有代码都已通过测试跑通&#xff0c;其中代码结构如下&#xff1a; 一、include文件夹 1.1 disp_manager.h #ifndef _DISP_MANAGER_H //防止头文件重复包含,只要右边的出现过&#xff0c;就不会再往下编译 #define _DISP_MANAGER_H //区域结构体 typedef struct DispBuff …

机器学习样本数据划分的典型Python方法

机器学习样本数据划分的典型Python方法 DateAuthorVersionNote2023.08.16Dog TaoV1.0完成文档撰写。 文章目录 机器学习样本数据划分的典型Python方法样本数据的分类Training DataValidation DataTest Data numpy.ndarray类型数据直接划分交叉验证基于KFold基于RepeatedKFold基…

【C++】面向对象编程引入 ( 面向过程编程 | 查看 iostream 依赖 | 面向对象编程 )

文章目录 一、面向过程编程二、查看 iostream 依赖三、面向对象编程 一、面向过程编程 给定 圆 的 半径 , 求该圆 的 周长 和 面积 ; 半径为 r r r , 周长就是 2 π r 2 \pi r 2πr , 面积是 π r 2 \pi r^2 πr2 ; 使用 面向过程 的方法解决上述问题 , 只能是令程序顺序执…

DDCX——运维开发准备

DD——运维开发准备 一4 linux用的什么版本&#xff0c;常见命令&#xff08;awk sed grep telnet netstate tcpdump top ps perf&#xff09;5 数据库有哪些类型&#xff0c;关系型数据库有哪些&#xff0c;非关系型数据库有哪些6 mysql事务7 mysql集群了解多少8 redis数据类型…

Spring 框架入门介绍及IoC的三种注入方式

目录 一、Spring 简介 1. 简介 2. spring 的核心模块 ⭐ 二、IoC 的概念 2.1 IoC 详解 2.2 IoC的好处 2.3 谈谈你对IoC的理解 三、IoC的三种注入方式 3.1 构造方法注入 3.2 setter方法注入 3.3 接口注入&#xff08;自动分配&#xff09; 3.4 spring上下文与tomcat整…

ChatGPT聊天微信小程序源码/适配H5和WEB端

ChatGPT-MP(基于ChatGPT实现的微信小程序&#xff0c;适配H5和WEB端) 可二开包含前后台&#xff0c;支持打字效果输出流式输出&#xff0c;支持AI聊天次数限制&#xff0c;支持分享增加次数等功能。开源版禁止商用&#xff0c;仅供学习交流&#xff0c;禁止倒卖。 技术栈&…

本地linux 搭建云服务器

本人穷逼&#xff0c;三年268 腾讯云可以接受&#xff0c;续费千百块 承担不起 研究了一会&#xff0c;发现搭建云服务器有两种较好的方式 一种是有公网IP的&#xff0c;另外是没有公网IP的&#xff0c;这里实验成功的是没有公网ip的方法 这种方法有缺点&#xff0c;因为fre…

前端组件高级封装技巧--纯干货

对于前端的小伙伴来说&#xff0c;最常见的工作就是写后台管理系统的页面&#xff0c;而后台管理系统最多的操作就是CRUD了&#xff0c;类似下面的&#xff0c;一个搜索框&#xff0c;一个表格&#xff0c;一个分页&#xff0c;然后点击新增编辑有个弹框 当你写过一段时间的CRU…

OpenDDS安装教程 Java开发

一、环境搭建 1、版本介绍 笔者使用以下版本&#xff08;不同版本的openDDS对应ACETAO版本不同&#xff09; openDDS&#xff1a;3.14 ACETAO&#xff1a;6.5.12 perl&#xff1a;5.32.0.1-64bit Visual Studio&#xff1a;Community 2019 jdk&#xff1a;jdk-8u111-windows-…

spark的standalone 分布式搭建

一、环境准备 集群环境hadoop11&#xff0c;hadoop12 &#xff0c;hadoop13 安装 zookeeper 和 HDFS 1、启动zookeeper -- 启动zookeeper(11,12,13都需要启动) xcall.sh zkServer.sh start -- 或者 zk.sh start -- xcall.sh 和zk.sh都是自己写的脚本-- 查看进程 jps -- 有…

【CTF-web】备份是个好习惯(查找备份文件、双写绕过、md5加密绕过)

题目链接&#xff1a;https://ctf.bugku.com/challenges/detail/id/83.html 经过扫描可以找到index.php.bak备份文件&#xff0c;下载下来后打开发现是index.php的原代码&#xff0c;如下图所示。 由代码可知我们要绕过md5加密&#xff0c;两数如果满足科学计数法的形式的话&a…