二级分类菜单及三级分类菜单的层级结构返回

news2024/11/28 18:50:12

前言

在开发投诉分类功能模块时,遇到过这样一个业务场景:后端需要按层级结构返回二级分类菜单所需数据,换言之,将具有父子关系的List结果集数据转为树状结构数据来返回

二级分类菜单

前期准备

这里简单复刻下真实场景中 出现的二级分类菜单层级结构返回

数据库设计

建表语句如下

DROP TABLE IF EXISTS `menu`;
CREATE TABLE `menu`  (
  `id` bigint NOT NULL AUTO_INCREMENT,
  `name` varchar(50) CHARACTER SET utf8mb3 COLLATE utf8mb3_general_ci NULL DEFAULT NULL COMMENT '名称',
  `parent_id` bigint NULL DEFAULT NULL COMMENT '父级id',
  PRIMARY KEY (`id`) USING BTREE
) ENGINE = InnoDB CHARACTER SET = utf8mb3 COLLATE = utf8mb3_general_ci ROW_FORMAT = DYNAMIC;

-- ----------------------------
-- Records of menu
-- ----------------------------
INSERT INTO `menu` VALUES (1, '质量问题', 0);
INSERT INTO `menu` VALUES (2, '变质', 1);
INSERT INTO `menu` VALUES (3, '过期', 1);
INSERT INTO `menu` VALUES (4, '包装破损', 1);
INSERT INTO `menu` VALUES (5, '服务问题', 0);
INSERT INTO `menu` VALUES (6, '态度恶劣', 5);
INSERT INTO `menu` VALUES (7, '东拉西扯', 5);

在这里插入图片描述

效果图预览

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

这里是在SpringBoot项目中演示实现的,持久层框架选用MyBatis-Plus,版本号:3.5.3.1

🌟之所以强调MP版本号,是因为之前在实现分页时遇过挫,真实项目选用的版本是3.3.2,而自己在学习MP时,选择版本是3.5.3.1。MP官方文档在不同版本中,有关分页插件的使用介绍可能存在不同,实际开发中对引入MP依赖的版本号还是要有所关注

在这里插入图片描述

开发测试

创建实体类

package com.atguigu.mybatisplus.pojo;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @author Songguo
 * @date 2023/11/15 9:56
 */
@Data
@TableName("menu")
public class Menu {
    private Long id;

    private String name;

    private Long parentId;
}

创建mapper层

package com.atguigu.mybatisplus.mapper;

import com.atguigu.mybatisplus.pojo.Menu;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface MenuMapper extends BaseMapper<Menu> {
}

创建service层接口及实现类

package com.atguigu.mybatisplus.service;

import com.atguigu.mybatisplus.pojo.Menu;
import com.baomidou.mybatisplus.extension.service.IService;

public interface MenuService extends IService<Menu> {
}
package com.atguigu.mybatisplus.service.impl;

import com.atguigu.mybatisplus.mapper.MenuMapper;
import com.atguigu.mybatisplus.pojo.Menu;
import com.atguigu.mybatisplus.service.MenuService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * @author Songguo
 * @date 2023/11/15 10:00
 */
@Service
public class MenuServiceImpl extends ServiceImpl<MenuMapper, Menu> implements MenuService {
}

创建测试类

package com.atguigu.mybatisplus;

import com.atguigu.mybatisplus.pojo.Menu;
import com.atguigu.mybatisplus.service.MenuService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Songguo
 * @date 2023/11/15 10:02
 */
@SpringBootTest
public class MenuServiceImplTest {
    @Autowired
    private MenuService menuService;

    @Test
    public void ListToTreeTest() {
        List<Menu> list = menuService.list();
        // 存放要返回的树状结构数据
        List<Map<String, Object>> MenuList = new ArrayList<>();

        for (Menu menu : list) {
            if (Long.valueOf(0) == menu.getParentId()) {
                Map<String, Object> map = new HashMap<>();
                map.put("id", menu.getId());
                map.put("name", menu.getName());
                // map.put("parentId", menu.getParentId());
                map.put("children", getChildren(list, menu.getId()));
                MenuList.add(map);
            }

        }

        MenuList.forEach(System.out::println);
    }

    public List<Map<String, Object>> getChildren(List<Menu> list, Long topId) {
        List<Map<String, Object>> data = new ArrayList<>();

        for (Menu menu : list) {
            if (topId == menu.getParentId()) {
                Map<String, Object> map = new HashMap<>();
                map.put("id", menu.getId());
                map.put("name", menu.getName());
                // map.put("parentId", menu.getParentId());
                data.add(map);
            }
        }

        return data;
    }

}

注意: 在真实场景中,并不需要返回每条记录的所有字段值,主要看前端需要接收那些数据

实现思路很简单:

1️⃣获取分类菜单所有记录

2️⃣遍历找出一级节点(父节点),然后在调用getChildren()(重金寻子方法)获取二级节点(子节点)

3️⃣通过List<Map<String,Object>>来存放结果集

三级分类菜单

前期准备

这里简单复刻下真实场景中 出现的三级分类菜单层级结构返回

数据库设计

建表语句如下

CREATE TABLE address (
  id INT AUTO_INCREMENT PRIMARY KEY,
  name VARCHAR(50) NOT NULL,
  parent_id INT NOT NULL,
  level INT NOT NULL
);

INSERT INTO address (name, parent_id, level) VALUES ('湖北', 0, 1);
INSERT INTO address (name, parent_id, level) VALUES ('辽宁', 0, 1);
INSERT INTO address (name, parent_id, level) VALUES ('武汉', 1, 2);
INSERT INTO address (name, parent_id, level) VALUES ('荆州', 1, 2);
INSERT INTO address (name, parent_id, level) VALUES ('沈阳', 2, 2);
INSERT INTO address (name, parent_id, level) VALUES ('蔡甸区', 3, 3);
INSERT INTO address (name, parent_id, level) VALUES ('江夏区', 3, 3);
INSERT INTO address (name, parent_id, level) VALUES ('沙市区', 4, 3);
INSERT INTO address (name, parent_id, level) VALUES ('大东区', 5, 3);
INSERT INTO address (name, parent_id, level) VALUES ('浑南区', 5, 3);

在这里插入图片描述

效果图预览

在这里插入图片描述
在这里插入图片描述
在这里插入图片描述

开发测试

创建实体类

package com.atguigu.mybatisplus.pojo;

import com.baomidou.mybatisplus.annotation.TableName;
import lombok.Data;

/**
 * @author Songguo
 * @date 2023/11/26 17:04
 */
@Data
@TableName("address")
public class Address {
    private int id;

    private String name;

    private int parentId;

    private int level;
}

创建mapper层

package com.atguigu.mybatisplus.mapper;

import com.atguigu.mybatisplus.pojo.Address;
import com.baomidou.mybatisplus.core.mapper.BaseMapper;

public interface AddressMapper extends BaseMapper<Address> {
}

创建service层接口及实现类

package com.atguigu.mybatisplus.service;


import com.atguigu.mybatisplus.pojo.Address;
import com.baomidou.mybatisplus.extension.service.IService;

public interface AddressService extends IService<Address> {
}
package com.atguigu.mybatisplus.service.impl;

import com.atguigu.mybatisplus.mapper.AddressMapper;
import com.atguigu.mybatisplus.pojo.Address;
import com.atguigu.mybatisplus.service.AddressService;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import org.springframework.stereotype.Service;

/**
 * @author Songguo
 * @date 2023/11/26 17:08
 */
@Service
public class AddressServiceImpl extends ServiceImpl<AddressMapper, Address> implements AddressService {
}

创建测试类

package com.atguigu.mybatisplus;

import com.atguigu.mybatisplus.pojo.Address;
import com.atguigu.mybatisplus.service.AddressService;
import org.junit.jupiter.api.Test;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.test.context.SpringBootTest;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

/**
 * @author Songguo
 * @date 2023/11/26 17:10
 */
@SpringBootTest
public class AddressServiceImplTest {
    @Autowired
    private AddressService addressService;

    @Test
    public void getAddress() {
        List<Address> list = addressService.list();
        // list.forEach(System.out::println);
        // 存放结果集
        List<Map<String, Object>> addressList = new ArrayList<>();

        for (Address address : list) {
            if (address.getLevel() == 1) {
                Map<String, Object> map = new HashMap<>();
                map.put("id", address.getId());
                map.put("name", address.getName());
                // map.put("parentId", address.getParentId());
                map.put("children", getChildren(list, address.getId()));
                addressList.add(map);
            }
        }

        addressList.forEach(System.out::println);
    }

    public List<Map<String, Object>> getChildren(List<Address> list, int parentId) {
        List<Map<String, Object>> data = new ArrayList<>();

        for (Address address : list) {
            HashMap<String, Object> map = new HashMap<>();
            if (parentId == address.getParentId()) {
                map.put("id", address.getId());
                map.put("name", address.getName());
                // map.put("parentId", address.getParentId());
                if (address.getLevel() < 3) {
                    map.put("children", getChildren(list, address.getId()));
                }
                data.add(map);
            }
        }

        return data;
    }

}

小结

二级分类菜单的层级结构返回是在真实项目中遇到的,而三级分类菜单的层级结构返回是模拟真实场景下的需求进行扩展实现的

弄懂了二级分类和三级分类,多级分类还会远嘛

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

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

相关文章

二十六、搜索结果处理(排序、分页、高亮)

目录 一、排序 二、分页 1、深度分页问题 2、三种方案的优缺点 &#xff08;1&#xff09;fromsize 优点&#xff1a; 缺点&#xff1a; 场景&#xff1a; &#xff08;2&#xff09;after search 优点&#xff1a; 缺点&#xff1a; 场景&#xff1a; &#xff0…

hive杂谈

数据仓库是一个面向主题的、集成的、非易失的、随时间变化的&#xff0c;用来支持管理人员决策的数据集合&#xff0c;数据仓库中包含了粒度化的企业数据。 数据仓库的主要特征是&#xff1a;主题性、集成性、非易失性、时变性。 数据仓库的体系结构通常包含4个层次&#xff…

03-《人月神话》巴赫、UML和领域驱动设计伪创新:中译本纠错及联想

DDD领域驱动设计批评文集 做强化自测题获得“软件方法建模师”称号 《软件方法》各章合集 2001年&#xff0c;我们翻译《人月神话》的时候&#xff0c;由于水平有限&#xff0c;译文中存在不少错误。 这些年&#xff0c;随着阅历的增长&#xff0c;在重读的时候偶尔也会有“…

推动卓越创新:了解 4 种研发团队架构如何优化您的组织

揭示敏捷实践中常犯的12大错误&#xff0c;了解如何避免这些敏捷失败 陷阱&#xff0c;找出问题根源并采取有效改进措施&#xff0c;提高项目成功率。立即连线 Runwise.co 社区敏捷专家获得专业建议&#xff0c;或 Runwise.co 在线学习敏捷方法实战课程&#xff0c;提升您和团队…

go当中的channel 无缓冲channel和缓冲channel的适用场景、结合select的使用

Channel Go channel就像Go并发模型中的“胶水”&#xff0c;它将诸多并发执行单元连接起来&#xff0c;或者正是因为有channel的存在&#xff0c;Go并发模型才能迸发出强大的表达能力。 无缓冲channel 无缓冲channel兼具通信和同步特性&#xff0c;在并发程序中应用颇为广泛。…

代码随想录算法训练营 ---第四十五天

前言&#xff1a; 昨天的题做过之后&#xff0c;今天的题基本上都很简单&#xff0c;但是要注重一下细节。 第一题&#xff1a; 简介&#xff1a; 动态规划五部曲&#xff1a; 1.确定dp数组的含义 dp[i]&#xff1a;爬到有i个台阶的楼顶&#xff0c;有dp[i]种方法 2.确定dp…

CSS之弹性盒子Flexible Box

我想大家在做布局的时候&#xff0c;没接触flex布局之前&#xff0c;大家都是用浮动来布局的&#xff0c;但现在我们接触了flex布局之后&#xff0c;我只能说&#xff1a;“真香”。让我为大家介绍一下弹性盒子模型吧&#xff01; Flexible Box 弹性盒子 在我们使用弹性盒子时&…

泛型你掌握多少?包装类你深入了解过吗?快进来看看吧~

目录 1、泛型是什么——引出泛型 2、泛型的使用 2.1、语法 2.2泛型类的使用 2.3、裸类型 3、泛型如何编译 3.1、擦除机制 3.2、为什么不能实例化泛型类型数组 4、泛型的上界 5、泛型方法 5.1、语法 5.2、举例 6、通配符 6.1、什么是通配符 6.2、统配符解决了什么…

2017年五一杯数学建模C题宜居城市问题值解题全过程文档及程序

2017年五一杯数学建模 C题 宜居城市问题 原题再现 城市宜居性是当前城市科学研究领域的热点议题之一&#xff0c;也是政府和城市居民密切关注的焦点。建设宜居城市已成为现阶段我国城市发展的重要目标,对提升城市居民生活质量、完善城市功能和提高城市运行效率具有重要意义。…

正则表达式回溯陷阱

一、匹配场景 判断一个句子是不是正规英文句子 text "I am a student" 一个正常的英文句子如上&#xff0c;英文单词 空格隔开 英文单词 多个英文字符 [a-zA-Z] 空格用 \s 表示 那么一个句子就是单词 空格&#xff08;一个或者多个&#xff0c;最后那个单词…

An example of a function uniformly continuous on R but not Lipschitz continuous

See https://math.stackexchange.com/questions/69457/an-example-of-a-function-uniformly-continuous-on-mathbbr-but-not-lipschitz?noredirect1

十七、事件组

1、事件组是什么 1.1、举例说明 (1)学校组织秋游&#xff0c;组长在等待&#xff1a; 张三&#xff1a;我到了李四&#xff1a;我到了王五&#xff1a;我到了组长说&#xff1a;好&#xff0c;大家都到齐了&#xff0c;出发&#xff01; (2)秋游回来第二天就要提交一篇心得…

leetcode刷题详解五

117. 填充每个节点的下一个右侧节点指针 II 关键点&#xff1a;先递归右子树 画一下就知道了&#xff0c;画一个四层的二叉树&#xff0c;然后右子树多画几个节点就知道为啥了 Node* connect(Node* root) {if(!root || (!root->left && !root->right)){return ro…

针对操作系统漏洞的反馈方法

一、针对操作系统漏洞的反馈方法 漏洞扫描指基于漏洞数据库&#xff0c;通过扫描等手段对指定的远程或者本地计算机系统的安全脆弱性进行检测&#xff0c;发现可利用漏洞的一种安全检测&#xff08;渗透攻击&#xff09;行为。在进行漏洞扫描后&#xff0c;需先确定哪些是业务…

二叉树:leetcode1457. 二叉树中的伪回文路径

给你一棵二叉树&#xff0c;每个节点的值为 1 到 9 。我们称二叉树中的一条路径是 「伪回文」的&#xff0c;当它满足&#xff1a;路径经过的所有节点值的排列中&#xff0c;存在一个回文序列。 请你返回从根到叶子节点的所有路径中 伪回文 路径的数目。 给定二叉树的节点数目…

SSD FTL 映射管理

映射的种类 根据映射粒度的不同分为如下几种 1.块映射 一个逻辑块&#xff08;logical block&#xff09;映射到一个闪存物理块(physical block) 优点是&#xff1a;映射表需要空间小&#xff0c;对大Block size顺序读写&#xff0c;但是对小尺寸数据的写入性能很差。因为即使…

基于改进莱维飞行和混沌映射粒子群优化算法(LPSO)-BP神经网络的时间序列预测模型

基于改进莱维飞行和混沌映射粒子群优化算法(LPSO)原理&#xff1a; 通过引入混沌映射机制&#xff0c;对其群体进行初始化&#xff0c;增加粒子群个体的多样性&#xff1b;然后在粒子群个体的位置更新公式上引入改进的莱维飞行机制&#xff0c;提高搜索精度&#xff0c;帮助粒…

重庆数字孪生技术推进制造业升级,工业物联网可视化应用加速

重庆数字孪生、5G、人工智能、物联网、大数据等新一代信息技术的出现及终端计算设备的发展&#xff0c;带来了研发模式、生产模式、消费模式、体制机制的系统性变革&#xff0c;企业应该建设适应工业4.0时代发展要求的新型生产体系。巨蟹数科数字孪生智能工厂通过部署多样化用例…

Java EE 进程线程

JavaEE 进程&线程 文章目录 JavaEE 进程&线程1. 进程1.1 概念1.2 进程管理1.3 PCB (Process Control Block) 2. 线程2.1 概念2.1 线程与进程的区别2.3 创建线程 1. 进程 1.1 概念 什么是进程&#xff1f; 进程是操作系统对一个正在执行的程序的一种抽象 我们可以打开…

Android 相机库CameraView源码解析 (二) : 拍照

1. 前言 这段时间&#xff0c;在使用 natario1/CameraView 来实现带滤镜的预览、拍照、录像功能。 由于CameraView封装的比较到位&#xff0c;在项目前期&#xff0c;的确为我们节省了不少时间。 但随着项目持续深入&#xff0c;对于CameraView的使用进入深水区&#xff0c;逐…