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

前言

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

二级分类菜单

前期准备

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

数据库设计

建表语句如下

sql 复制代码
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依赖的版本号还是要有所关注

开发测试

创建实体类

java 复制代码
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层

java 复制代码
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层接口及实现类

java 复制代码
package com.atguigu.mybatisplus.service;

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

public interface MenuService extends IService<Menu> {
}
java 复制代码
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 {
}

创建测试类

java 复制代码
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>>来存放结果集

三级分类菜单

前期准备

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

数据库设计

建表语句如下

sql 复制代码
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);

效果图预览



开发测试

创建实体类

java 复制代码
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层

java 复制代码
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层接口及实现类

java 复制代码
package com.atguigu.mybatisplus.service;


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

public interface AddressService extends IService<Address> {
}
java 复制代码
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 {
}

创建测试类

java 复制代码
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;
    }

}

小结

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

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

相关推荐
LuckyLay17 分钟前
Spring学习笔记_27——@EnableLoadTimeWeaving
java·spring boot·spring
向阳121830 分钟前
Dubbo负载均衡
java·运维·负载均衡·dubbo
Gu Gu Study40 分钟前
【用Java学习数据结构系列】泛型上界与通配符上界
java·开发语言
WaaTong1 小时前
《重学Java设计模式》之 原型模式
java·设计模式·原型模式
m0_743048441 小时前
初识Java EE和Spring Boot
java·java-ee
AskHarries1 小时前
Java字节码增强库ByteBuddy
java·后端
小灰灰__1 小时前
IDEA加载通义灵码插件及使用指南
java·ide·intellij-idea
夜雨翦春韭1 小时前
Java中的动态代理
java·开发语言·aop·动态代理
程序媛小果2 小时前
基于java+SpringBoot+Vue的宠物咖啡馆平台设计与实现
java·vue.js·spring boot
追风林2 小时前
mac m1 docker本地部署canal 监听mysql的binglog日志
java·docker·mac