小编之前写过如何实现一二级菜单的文章,回顾了下之前所实现的逻辑方式,简直惨不忍睹~~
由于近期小编接触新的项目需要实现展示菜单功能,但这次的菜单是需要多级,并且级数不固定。
像这种需求,一般很简单的来说就是用递归实现了,可以从第一级一直往下查,一直查询到为空为止。
这里小编之列出来几个基本的字段,但是够用了
CREATE TABLE `mall_category` (
`id` int(11) NOT NULL AUTO_INCREMENT COMMENT '类别Id',
`parent_id` int(11) DEFAULT NULL COMMENT '父类别id当id=0时说明是根节点,一级类别',
`name` varchar(50) DEFAULT NULL COMMENT '类别名称',
`status` tinyint(1) DEFAULT '1' COMMENT '类别状态1-正常,2-已废弃',
`sort_order` int(4) DEFAULT NULL COMMENT '排序编号,同类展示顺序,数值相等则自然排序',
`create_time` datetime DEFAULT NULL COMMENT '创建时间',
`update_time` datetime DEFAULT NULL COMMENT '更新时间',
PRIMARY KEY (`id`)
) ENGINE=InnoDB AUTO_INCREMENT=100034 DEFAULT CHARSET=utf8;
步骤一:一共有两个实体类,一个是对应数据库的实体类,还有一个是返回给前端的实体类
/**
* @Auther: IT贱男
* @Date: 2019/12/16 16:45
* @Description: 商品类目
*/
@Data
public class Category {
private Integer id;
private Integer parentId;
private String name;
private Integer status;
private Integer sortOrder;
private Date createTime;
private Date updateTime;
}
/**
* @Auther: IT贱男
* @Date: 2020/2/26 13:58
* @Description: 返回前端对应实体类
*/
@Data
public class CategoryVo {
private Integer id;
private Integer parentId;
private String name;
private Integer sortOrder;
/**
* 存放类目子级目录
*/
private List subCategories;
}
步骤二:具体实现逻辑,小编这是从项目里面直接截取出来的代码,但是能够实现多级菜单功能~~
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.Comparator;
import java.util.List;
import java.util.stream.Collectors;
/**
* @Auther: IT贱男
* @Date: 2020/2/26 13:47
* @Description: 类目查询
*/
@Service
public class ICategoryServiceImpl implements ICategoryService {
@Autowired
private CategoryMapper categoryMapper;
/**
* 对象转换 将数据库实体类对象转还给前端实体
*
* @param category
* @return
*/
private CategoryVo categoryTOCategoryVo(Category category) {
CategoryVo categoryVo = new CategoryVo();
BeanUtils.copyProperties(category, categoryVo);
return categoryVo;
}
@Override
public ResponseVo selectCategoryAll() {
// 先查询出全部类目
List categories = categoryMapper.selectCategoryAll();
// 获取一级菜单 , 0 代表一级目录
List rootCategory = categories.stream()
.filter(e -> e.getParentId().equals("0"))
.map(this::categoryTOCategoryVo)
.sorted(Comparator.comparing(CategoryVo::getSortOrder).reversed())
.collect(Collectors.toList());
// 查找子节点
findSubCategory(categories, rootCategory);
// ResponseVo 这个是封装类了,实际看小伙伴自己的需求
return ResponseVo.success(rootCategory);
}
/**
* 根据一级递归调用子级
*
* @param categoryList
* @param rootCategory
* @return
*/
private void findSubCategory(List categoryList, List rootCategory) {
// 遍历一级
for (CategoryVo categoryVo : rootCategory) {
List subCategoryVoList = new ArrayList<>();
// 查找子级
for (Category category : categoryList) {
// 判断当前目录是否是子父级关系
if (categoryVo.getId().equals(category.getParentId())) {
subCategoryVoList.add(categoryTOCategoryVo(category));
}
// 递归调用,不管有几级菜单,都能够适用
findSubCategory(categoryList, subCategoryVoList);
// 类目显示排序,reversed 表示数字越大靠前
subCategoryVoList.sort(Comparator.comparing(CategoryVo::getSortOrder).reversed());
}
// 最后把查到的子级保存到一级目录中
categoryVo.setSubCategories(subCategoryVoList);
}
}
}
一、小伙伴可能会有疑问,这样循环嵌套会不会影响效率
其实像这些循环操作来说,都是基于内存操作,内存操作是很快的,这点可以不用考虑。
二、需要避免的问题
其实对于这种数据的操作,最好一次性能够查询出来最好,商品目录最多也不过几千条。千万不要循环去查询数据库,这种是极度危险的行为。小编就在工作当中遇到有在循环一直去查询数据库的情况,如果不考虑效率可以这么操作。
好了,最后提醒大家,疫情期间,好好保护自己
不出门,就是对祖国做出最大的贡献哈哈哈 (老板要求去公司,俺也没办法,赚钱要紧,赚钱要紧) ~~~~
——————————————————————————————————————————————————
评论有小伙伴提到了ResponseVo这个对象,其功能就是包装我们的返回数据的格式,如下:
因为这些code,msg 这些字段内容都是规定好的,这样做一是为了规范,二也减少了代码的冗余。
// 没包装前的返回数据格式
{
"data":{}
}
// 包装后的返回数据格式
{
"code": 1,
"msg":"成功",
"data":{}
}
代码也贴出来给大家参考一下吧,代码有简化,但基本上也都差不多。
import com.fasterxml.jackson.annotation.JsonInclude;
import com.kane.mall.enums.ResponseEnum;
import lombok.Data;
import org.springframework.validation.BindingResult;
/**
* @Auther: IT贱男
* @Date: 2020/2/17 21:45
* @Description: 返回结果集
*/
@Data
@JsonInclude(JsonInclude.Include.NON_NULL)
public class ResponseVo {
private Integer status;
private String msg;
private T data;
public ResponseVo(Integer status, String msg) {
this.status = status;
this.msg = msg;
}
public ResponseVo(Integer status, String msg, T data) {
this.status = status;
this.msg = msg;
this.data = data;
}
public static ResponseVo success() {
return new ResponseVo(ResponseEnum.SUCCESS.getCode(), ResponseEnum.SUCCESS.getMsg());
}
public static ResponseVo success(T data) {
return new ResponseVo(ResponseEnum.SUCCESS.getCode(), ResponseEnum.SUCCESS.getMsg(), data);
}
public static ResponseVo error(ResponseEnum responseEnum) {
return new ResponseVo(responseEnum.getCode(), responseEnum.getMsg());
}
}
import lombok.Getter;
/**
* @Auther: IT贱男
* @Date: 2020/2/17 21:52
* @Description: 返回参数枚举
*/
// 这个注解使用Lombok
@Getter
public enum ResponseEnum {
ERROR(-1, "服务端错误"),
SUCCESS(0, "成功"),;
private Integer code;
private String msg;
ResponseEnum(Integer code, String msg) {
this.code = code;
this.msg = msg;
}
}