java递归菜单加redis,递归查询单表菜单树形结构(多级树形结构)

/**

* 递归查询树形结构==================================================

*/

@PostMapping("/recursive")

@ResponseBody

public List> findWorld() {

List> allList = new ArrayList<>(); //接收所有的信息

List> parentMapList = new ArrayList<>(); //接收获取的父节点

List> retList = new ArrayList<>(); //接收获取的父节点

try {

allList = myTestService.findWorld(); //获取所有的信息

for (int i = 0; i < allList.size(); i++) { //循环所有信息找出所有的根节点(p_id=-1)

if ("-1".equals(allList.get(i).get("p_id").toString())) {

parentMapList.add(allList.get(i)); //将找出来的根节点重新存到一个List集合中

allList.remove(i); //从所有的数据中移除根节点

i--; //每移除一个根节点就将所有数据的个数减一

}

}

retList = recursive(allList,parentMapList); //调用此方法寻找子节点

} catch (Exception e) {

e.printStackTrace();

}

return retList;

}

/**

* @param parentMapList 所有父节点

* @param allList 所有数据

* @return

*/

public List> recursive(List> allList,List> parentMapList) {

//循环根节点

for(int j = 0;j

List> tempList = new ArrayList<>(); //用来存取子节点

//循环全部节点,判断节点中P_id是否与根节点中的id相同

for(int k = 0;k

if(allList.get(k).get("p_id").toString().equals(parentMapList.get(j).get("id").toString())){

tempList.add(allList.get(k));

}

}

if(tempList.size()>0){

parentMapList.get(j).put("children",tempList);

recursive(allList,tempList); //此次循环又将tempList作为parentMapList(父节点去找其他的子节点),直到没有子节点在执行j+1

}

}

return parentMapList;

}

### Spring Boot 中菜单管理表的设计 在设计菜单管理表时,通常需要考虑以下几个方面:菜单的层次结构、权限控制以及与用户的关联关系。以下是常见的表设计方案: #### 1. **菜单(sys_menu)** 该表用于存储系统的菜单项及其属性。 | 字段名 | 类型 | 描述 | |----------------|------------|--------------------------------------------------------------| | id | BIGINT | 主键 ID | | parent_id | BIGINT | 父级菜单 ID,顶级菜单为 `0` 或 `-1` | | name | VARCHAR | 菜单名称 | | path | VARCHAR | 前端路径,例如 `/dashboard` | | component | VARCHAR | 对应前端组件地址,例如 `views/dashboard/index.vue` | | redirect | VARCHAR | 重定向地址 | | icon | VARCHAR | 图标类名 | | order_num | INT | 排序号 | | perms | VARCHAR | 权限标识符,例如 `system:menu:list` | | is_frame | TINYINT(1) | 是否外链 (`0`: 否, `1`: 是) | | visible | TINYINT(1) | 是否可见 (`0`: 不可见, `1`: 可见) | | status | TINYINT(1) | 菜单状态 (`0`: 正常, `1`: 停用) | 此表支持多级菜单结构,并通过 `parent_id` 和 `order_num` 定义父子关系和显示顺序[^1]。 --- ### 动态路由功能实现方案 动态路由的核心在于根据当前登录用户的角色或权限动态载对应的菜单数据。以下是具体实现流程: #### 1. **后端逻辑** 后端提供接口返回当前用户可访问的菜单列表。以下是一个典型的实现方式: ```java @RestController @RequestMapping("/menu") public class MenuController { @Autowired private SysMenuService menuService; /** * 获取用户对应路由信息 */ @GetMapping("getRouters") public AjaxResult getRouters() { Long userId = SecurityUtils.getUserId(); List<SysMenu> menus = menuService.selectMenuTreeByUserId(userId); return AjaxResult.success(menuService.buildMenus(menus)); } } ``` 上述代码中,`selectMenuTreeByUserId` 方法负责查询指定用户的所有可用菜单;而 `buildMenus` 则将这些菜单转换成适合前端使用的树形结构[^2]。 #### 2. **服务层逻辑** 服务层主要完成两件事: - 查询数据库中的菜单记录; - 将其转化为树状结构以便于前端渲染。 ```java @Service public class SysMenuServiceImpl implements ISysMenuService { @Override public List<SysMenu> selectMenuTreeByUserId(Long userId) { // 根据用户ID获取菜单列表 return baseMapper.selectMenuListByUserId(userId); } @Override public List<RouterVO> buildMenus(List<SysMenu> menus) { List<RouterVO> routers = new ArrayList<>(); RouterHelper.buildMenus(routers, menus); return routers; } } ``` 其中,`RouterHelper` 工具类可以用来构建最终传递给前端的数据格式。 #### 3. **工具方法** 为了简化开发过程,可以通过工具类封装一些通用操作,比如过滤隐藏菜单或者设置默认参数。 ```java public class RouterHelper { public static void buildMenus(List<RouterVO> routers, List<SysMenu> menus) { menus.stream() .filter(item -> !StringUtils.isEmpty(item.getPath())) .forEach(item -> { RouterVO router = new RouterVO(); router.setName(getRouteName(item.getName())); router.setPath(getRouterPath(item)); router.setComponent(StringUtils.nvl(item.getComponent(), "Layout")); router.setMeta(new MetaVo(item.getVisible().equals("0") ? false : true, item.getName(), StringUtils.isNotEmpty(item.getIcon()) ? item.getIcon() : null)); List<SysMenu> children = getChildPerms(menus, item.getId()); if (!children.isEmpty() && hasChild(children)) { router.setAlwaysShow(true); router.setRedirect("noRedirect"); router.setChildren(buildMenus(new ArrayList<>(), children)); } else { router.setMeta(router.getMeta().setTitle(item.getName()) .setHideBreadcrumb(false).setIcon(null)); } routers.add(router); }); } private static String getRouteName(String menuName) { return StringUtils.toCamelCase(menuName); } private static boolean hasChild(List<SysMenu> children) { return children.parallelStream().anyMatch(child -> child.getParentId() != 0L); } } ``` 以上代码片段展示了如何递归处理子节点并生成完整的路由配置。 --- ### 最佳实践总结 1. 使用统一的服务框架(如 MyBatis Plus),减少重复编码工作量。 2. 配合 Redis 缓存机制优化性能,尤其是针对频繁调用的菜单数据。 3. 在实际应用中建议增日志监控模块,便于排查问题。 4. 如果涉及国际化需求,则需额外扩展字段支持多种语言版本的内容展示。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值