业务场景: 系统的界面,前端设计的时候,一般会给一个菜单栏,顶部横向以及左侧纵向的导航栏菜单,这里后端返回菜单栏的时候,就涉及层级父子项的问题,所以返回数据的时候,我们需要按照树化形式返回菜单栏数据,方便用户进行解析读取
菜单栏的数据表设计
CREATE TABLE `dwr_quality_management_menu_bar` (
`uid` int NOT NULL AUTO_INCREMENT,
`id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '树节点id',
`parent_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '父id,树化时使用',
`resource_id` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '唯一资源id',
`name_cn` varchar(50) CHARACTER SET utf8 COLLATE utf8_general_ci NOT NULL COMMENT '中文名',
`url` varchar(255) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '路径',
`level` tinyint DEFAULT NULL COMMENT '菜单层级',
`show_order` int DEFAULT NULL COMMENT '显示顺序',
`description` varchar(500) CHARACTER SET utf8 COLLATE utf8_general_ci DEFAULT NULL COMMENT '描述',
`is_home_page` tinyint(1) DEFAULT '2' COMMENT '首页标识:1:是,2:否',
`update_date` datetime DEFAULT NULL COMMENT '更改时间',
`picture_id` varchar(255) DEFAULT NULL,
`picture_url` varchar(255) DEFAULT NULL,
PRIMARY KEY (`uid`) USING BTREE
) ENGINE=InnoDB AUTO_INCREMENT=3845 DEFAULT CHARSET=utf8 COMMENT='菜单栏配置';
控制层
@RestController
@RequestMapping(value = "/management/menu", produces = {"application/json;charset=UTF-8"})
@Validated
public class ManagementMenuController {
@Autowired(required=false)
private ManagementMenuDelegate delegate;
@RequestMapping(
value = "/getAllMenu",
produces = { "application/json" },
method = RequestMethod.GET)
public ResponseVo getAllMenu( @RequestParam(value = "p", required = false) String p)
{
return delegate.getAllMenu(p);
}
}
服务层 接口
public interface ManagementMenuDelegate {
ResponseVo getAllMenu(String pdu) ;
}
服务层 接口实现
@Service
public class ManagementMenuDelegateImpl implements ManagementMenuDelegate {
@Resource
private MenuBarService menuBarService;
/**
* getAllMenu
*
* @param p
* @return ResponseVo
*/
@Override
public ResponseVo getAllMenu(String p) {
return ResponseUtils.successResponse(menuBarService.getCompleteMenu(p), "");
}
}
服务层 具体接口
public interface MenuBarService {
/**
* 获取所有菜单
*
* @param p
* @return List<ManagementMenuBarVo>
*/
List<ManagementMenuBarVo> getCompleteMenu(String p);
}
服务层 具体接口实现
@Service
@Slf4j
public class MenuBarServiceImpl implements MenuBarService {
@Resource
private ManagementMenuBarMapper menuBarMapper;
/**
* getCompleteMenu
*
* @param p
* @return List<ManagementMenuBarVo>
*/
@Override
public List<ManagementMenuBarVo> getCompleteMenu(String p) {
List<ManagementMenuBarVo> result = TreeUtils.treeing(getAllFlattingMenu());
if (ObjectUtils.isNotEmpty(p)) {
for (ManagementMenuBarVo barVo : result) {
if (barVo.getNameCn().equals(p)) {
return barVo.getChildren();
}
}
return new ArrayList<>();
}
return result;
}
/**
* getAllFlattingMenu
*
* @return List<ManagementMenuBarVo>
*/
private List<ManagementMenuBarVo> getAllFlattingMenu() {
QueryWrapper<ManagementMenuBar> queryWrapper = new QueryWrapper<>();
// 排序规则与getBarRootNode()一致
queryWrapper.lambda()
.orderBy(true, true, ManagementMenuBar::getLevel, ManagementMenuBar::getParentId,
ManagementMenuBar::getShowOrder);
List<ManagementMenuBar> menuBarList = menuBarMapper.selectList(queryWrapper);
return convertToVo(menuBarList);
}
/**
* convertToVo
*
* @param list list
* @return List<ManagementMenuBarVo>
*/
private List<ManagementMenuBarVo> convertToVo(List<ManagementMenuBar> list) {
List<ManagementMenuBarVo> voList = new LinkedList<>();
for (ManagementMenuBar menuBar : list) {
ManagementMenuBarVo vo = new ManagementMenuBarVo();
MyBeanUtils.shallowCopy(menuBar, vo);
vo.initSuperNodeId();
vo.initSuperParentNodeId();
voList.add(vo);
}
return voList;
}
}
dao层 mapper
@Mapper
public interface ManagementMenuBarMapper extends BaseMapper<ManagementMenuBar> {
}
实体类DO表
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
@NoArgsConstructor
@TableName("dwr_quality_management_menu_bar")
public class ManagementMenuBar implements Serializable {
private static final long serialVersionUID = 1L;
@TableId(value = "uid" ,type = IdType.AUTO)
private String uid;
/**
* 树节点id
*/
@TableField("id")
private String id;
/**
* 父id,树化时使用
*/
@TableField("parent_id")
private String parentId;
/**
* 唯一资源id
*/
@TableField("resource_id")
private String resourceId;
/**
* 图片ID
*/
@TableField("picture_id")
private String pictureId;
/**
* 中文名
*/
@TableField("name_cn")
private String nameCn;
/**
* 路径
*/
@TableField("url")
private String url;
/**
* 图片访问路径url
*/
@TableField("picture_url")
private String pictureUrl;
/**
* 菜单层级
*/
@TableField("level")
private Integer level;
/**
* 显示顺序
*/
@TableField("show_order")
private Integer showOrder;
/**
* 描述
*/
@TableField("description")
private String description;
/**
* 首页标识:1:是,2:否
*/
@TableField("is_home_page")
private Integer isHomePage;
/**
* 更改时间
*/
@TableField("update_date")
private Date updateDate;
}
传输前端类VO
-
作为传输转换类,其中就需要继承我们自定义的一个树化类结构
@Data
@EqualsAndHashCode(callSuper = false)
@Accessors(chain = true)
public class ManagementMenuBarVo extends TreeNode<ManagementMenuBarVo> implements Serializable {
private static final long serialVersionUID = 1L;
private String uid;
private String id;
private String parentId;
private String resourceId;
private String pictureId;
private String pictureUrl;
private String nameCn;
private String url;
private Integer level;
private String description;
private Integer isHomePage;
private Integer isAccessible;
private String functionName;
private Integer showOrder;
/**
*
*/
/**
* initSuperNodeId
*
*/
@Override
public void initSuperNodeId() {
super.setNodeId(this.id);
}
/**
* 初始化TreeNode的parentNodeId属性
*/
@Override
public void initSuperParentNodeId() {
super.setParentNodeId(this.parentId);
}
}
树化工具节点类
/**
* 树化工具实体
*
*/
@Data
public abstract class TreeNode<T> {
// 仅用于树化,序列化时忽略
@JsonIgnore
private String nodeId;
@JsonIgnore
private String parentNodeId;
@JsonIgnore
private T parent;
private List<T> children = new ArrayList<>();
/**
* 初始化TreeNode的NodeId属性
*/
public abstract void initSuperNodeId();
/**
* 初始化TreeNode的parentNodeId属性
*/
public abstract void initSuperParentNodeId();
}
树化操作工具类
/*
* Copyright (c) Huawei Technologies Co., Ltd. 2022-2022. All rights reserved.
*/
package com.xxx.utils;
/**
* 树化工具
*
*/
public class TreeUtils {
/**
* 树化
* @param treeNodes 扁平节点数据
* @return 树化的节点列表(列表中每一节点都是一棵树)
*/
public static <T extends TreeNode<T>> List<T> treeing(List<T> treeNodes) {
Map<String, T> nodeMap = new HashMap<>(treeNodes.size());
treeNodes.forEach(t -> nodeMap.put(t.getNodeId(), t));
List<T> result = new ArrayList<>();
for (T treeNode : treeNodes) {
T parent = nodeMap.get(treeNode.getParentNodeId());
if (parent != null) {
parent.getChildren().add(treeNode);
treeNode.setParent(parent);
continue;
}
// 仅添加顶层节点
result.add(treeNode);
}
return result;
}
/**
* 扁平化
* @param treeNodes 树化数据
* @return 扁平数据
*/
public static <T extends TreeNode<T>> List<T> flattening(List<T> treeNodes) {
if (!CollectionUtils.isEmpty(treeNodes)) {
return new ArrayList<>();
}
List<T> newList = new LinkedList<>();
Queue<T> queue = new LinkedList<>(treeNodes);
while (!queue.isEmpty()) {
T pollMenuBar = queue.poll();
List<T> children = pollMenuBar.getChildren();
if (!CollectionUtils.isEmpty(children)) {
queue.addAll(children);
}
newList.add(pollMenuBar);
}
return newList;
}
}
- 返回前端的JSON数据格式如下
- 每个菜单栏层级就比较清晰,通过children集合嵌套当前菜单栏的子菜单栏