本篇是一个补充知识点, 目的是为了下篇的后台管理系统中, 菜单权限的接口进行铺垫一下.

同时也是做个笔记, 因为在很多地方都会用这种 "树结构" 来实现很多权限, 层级, 菜单的处理哈.

在表设计层面通常是通过 idpid 来体现层级关系.

  • id 表示表的每行菜单的唯一标识
  • pid 标识这一行的 上级菜单id 是谁, 这个 id 一定是在 所有 id 中的
  • 假设我们约定, pid = 0 是顶级菜单

表结构设计

于是表设计就可以这样:

-- 菜单树的表结构
drop table if exists test_tree;
create table test_tree (
id int auto_increment primary key comment '自增id'
, pid int not null default 0 comment '父级id'
, name varchar(100) not null comment '名称'
, orders int not null default 0 comment '排序号'
); -- 插入数据
INSERT INTO test_tree (id, pid, name, orders) VALUES
(1, 0, 'A1', 10),
(2, 1, 'A1-1', 20),
(3, 1, 'A1-2', 20),
(4, 3, 'A1-2-1', 30),
(5, 3, 'A1-2-2', 30), (6, 0, 'B1', 10),
(7, 6, 'B1-1', 20),
(8, 7, 'B1-1-1', 30),
(9, 8, 'B1-1-1-1', 40); -- 递归查询某个 id 及其子节点
WITH RECURSIVE subordinates AS (
SELECT id, pid, name, orders
FROM test_tree
WHERE ID = 1
UNION ALL
SELECT t.ID, t.PID, t.Name, t.`Orders`
FROM test_tree t
INNER JOIN subordinates s ON t.PID = s.ID
)
SELECT * FROM subordinates;
id, pid, orders
1 0 A1 10
2 1 A1-1 20
3 1 A1-2 20
4 3 A1-2-1 30
5 3 A1-2-2 30

拼接为 json 树

目的是为了方便前端渲染层级菜单, 通过 children 来进行拓展.

Python版

from typing import List, Dict

def build_tree(menu_items: List[Dict], id='id', pid='pid') -> List[Dict]:
"""将菜单层级数据的 id, pid 平铺为 json 方式的""" menu_dict = { menu.get(id): menu for menu in menu_items }
tree = [] for menu in menu_items:
if not menu.get(pid):
tree.append(menu) # 根节点
else:
# 非根节点, 将其添加到父节点的 child 中
parent_menu = menu_dict.get(menu[pid])
print(parent_menu)
if parent_menu:
if 'children' not in parent_menu:
parent_menu['children'] = []
parent_menu['children'].append(menu) return tree

Go版

package main

import (
"encoding/json"
"fmt"
) type Menu struct {
ID int `json:"id"`
PID int `json:"parent_id"`
Name string `json:"name"`
Order int `json:"order"`
Children []*Menu `json:"children"`
} func BuildMenuTree(items []*Menu) []*Menu {
nodeMap := make(map[int]*Menu)
for _, node := range items {
nodeMap[node.ID] = node
} var tree []*Menu
for _, node := range items {
// 已约定 pid = 0 则为顶层节点
if node.PID == 0 {
tree = append(tree, node)
} else {
// 找到父节点,将其挂载到其 children 中
if parent, exist := nodeMap[node.PID]; exist {
parent.Children = append(parent.Children, node)
}
}
}
return tree
}

Go 也是一样的逻辑, 只是代码编写上要复杂一点, 原因在于,

  • 它是静态编译型语言, 要确定类型, 同时结构体和 json 之间需要用到反射 reflect
  • Go 中数组是 值类型, 切片是对它的引用, 在处理中需要用到 指针, 不然会进行节点重复创建
// 继续上面的测试
func main() {
items := []*Menu{ {ID: 1, PID: 0, Name: "A1", Order: 10},
{ID: 2, PID: 1, Name: "A1-1", Order: 20},
{ID: 3, PID: 1, Name: "A1-2", Order: 20},
{ID: 4, PID: 3, Name: "A1-2-1", Order: 30},
{ID: 5, PID: 3, Name: "A1-2-2", Order: 30}, {ID: 6, PID: 0, Name: "B1", Order: 10},
{ID: 7, PID: 6, Name: "B1-1", Order: 20},
{ID: 8, PID: 7, Name: "B1-1-1", Order: 30},
{ID: 9, PID: 8, Name: "B1-1-1-1", Order: 40},
} tree := BuildMenuTree(items) // 将树结构体 (指针, 切片, 数组, map 等) 转为 json
// prefix = "" 表示不用加前缀; indent = " " 表示每层缩进2空格
jsonData, err := json.MarshalIndent(tree, "", " ")
if err != nil {
fmt.Println("转换j son 失败: ", err)
return
}
fmt.Println(string(jsonData))
}

输出:

[
{
"id": 1,
"parent_id": 0,
"name": "A1",
"order": 10,
"children": [
{
"id": 2,
"parent_id": 1,
"name": "A1-1",
"order": 20,
"children": null
},
{
"id": 3,
"parent_id": 1,
"name": "A1-2",
"order": 20,
"children": [
{
"id": 4,
"parent_id": 3,
"name": "A1-2-1",
"order": 30,
"children": null
},
{
"id": 5,
"parent_id": 3,
"name": "A1-2-2",
"order": 30,
"children": null
}
]
}
]
},
{
"id": 6,
"parent_id": 0,
"name": "B1",
"order": 10,
"children": [
{
"id": 7,
"parent_id": 6,
"name": "B1-1",
"order": 20,
"children": [
{
"id": 8,
"parent_id": 7,
"name": "B1-1-1",
"order": 30,
"children": [
{
"id": 9,
"parent_id": 8,
"name": "B1-1-1-1",
"order": 40,
"children": null
}
]
}
]
}
]
}
]

用的频率还是蛮高的, 但凡涉及这种树的结构, 基本都会用到这种 id + parent_id 的方式, 同时也是 SQL 的一个必备知识点, 即 自关联 + 子查询, 这个技能必须要拿下. 真的是自从有了 AI , 似乎理解知识点都是轻而易举呢.

Go 层级菜单树转 json 处理的更多相关文章

  1. bootstrap treeview实现菜单树

    本博客,介绍通过Bootstrap的treeview插件实现菜单树的功能. treeview链接:http://www.htmleaf.com/Demo/201502141380.html ORM框架 ...

  2. Mybatis通过colliection属性递归获取菜单树

    1.现有商品分类数据表category结构如下,三个字段都为varchar类型 2.创建商品分类对应的数据Bean /** * */ package com.xdw.dao; import java. ...

  3. vue+element-ui实现无限级动态菜单树

    使用vue+element-ui实现无限级动态菜单 该案例实现主要使用递归的思想,递归对新人来容易迷惑的是自己调用自己,直到满足条件为止,接下来我们就一步一步实现一个动态多级菜单vue组件 搭建项目并 ...

  4. 蓝桥杯Web:【功能实现】菜单树检索

    [功能实现]菜单树检索 背景介绍 实际工作中很多前端攻城狮都会遇到这样一个需求:在多级菜单树中模糊搜索匹配的菜单项,并显示出来. 本题需要在已提供的基础项目中使用 Vue.js 知识,实现对已提供的二 ...

  5. React + Antd Menu组件实现菜单树

    准备好两个变量,一个用来保存平级菜单列表,一个用来保存遍历后的菜单树. 推荐后端返回平级菜单树,假如菜单比较多,可以直接结合find方法找到菜单,做搜索功能很省事. const [menuList, ...

  6. 原创的基于HTML/CSS/JavaScript的层级目录树

    之前参加过一些基于HTML/CSS/JavaScript的项目,当在页面中需要生成一颗目录树时,总是首先想着网上有没有现成的生成树的源代码,比如dtree.zthee,或者使用一些javascript ...

  7. java实现的可以无限级别添加子节点的菜单树

    网上大部分菜单树,都是单独用js代码来实现的,这样做的缺点是:用户无法动态的设置菜单项,比如,超级管理员可能需要根据每个用户的权限,赋予他们不同的系统功能,不同的功能对应着不同数量的菜单项. 对于此问 ...

  8. 以正确的姿势实现一棵JavaScript菜单树

    菜单树是常见的前端特效, 一般长下面这样 还有各种形态的变种, 有长这样的 也有长这样的 尽管这些菜单的相貌都不尽相同, 在功能实现的本质上却都是相同的.实现程序的大致流程如下 读取服务器端的菜单数据 ...

  9. vue中组件之间的相互调用,及通用后台管理系统左侧菜单树的迭代生成

    由于本人近期开始学习使用vue搭建一个后端管理系统的前端项目,在左侧生成菜单树的时候遇到了一些问题.在这里记录下 分析:由于本人设定的菜单可以使多级结构,直接使用vue的v-for 遍历并不是很方便. ...

  10. DWZ SSH2 菜单树--使用Struts2 标签(iterator/set/if 组合使用)

    最近在研究DWZ框架,然后要写一个菜单树,后台我使用了SSH2,然后想把菜单通过后台传过来的对象展示出来. 但是,发现应用样式的时候,如果子菜单在子循环中为空的话,会多出一对空标签“<ul> ...

随机推荐

  1. day:2 软件测试流程——H模型

    软件测试流程_H 模型 一.详细流程 1.产品召开需求澄清会议,产品.开发.测试都参加 2.测试和开发拿到需求 3.测试经理拿到需求,根据需求编写测试计划 测试计划(内容:测试目的,背景,范围,测试准 ...

  2. 5. 想在代码中验证sql的正确性?

    1. 简介 我们在平时的开发中可能会遇到需要验证一下sql是否正确,也就是需要check一下sql. 判断sql是否正确一般包含一下几点: 1. sql中使用的列是否存在 2. sql语法是否正确 3 ...

  3. python二级 计算生态

    生态地址: https://pypi.python.org/pypi 常用函数:

  4. Linux 下载安装CUDA Toolkit 12.8,配置Nvidia Driver

    cuda下载地址 https://developer.nvidia.com/cuda-downloads nvidia-smi Mon Mar 17 02:08:35 2025 +---------- ...

  5. 变速精灵+百D网盘

    首先找一下相对低一点版本的客户端,比如7.26.10 https://issuepcdn.baidupcs.com/issue/netdisk/yunguanjia/BaiduNetdisk_7.26 ...

  6. MOS管的引脚,G、S、D分别代表什么?

    引脚解析: G:gate 栅极,N沟道的电源一般接在D. S:source 源极,输出S,P沟道的电源一般接在S. D:drain 漏极,输出D.增强耗尽接法基本一样. mos管是金属(metal)- ...

  7. BundleFusion+WIN11+VS2019 + CUDA11.7环境配置

    BundleFusion+WIN11+VS2019环境配置 Step1 一开始会提示你重定解决方案,点是即可,如果点错了,也可以在这里再点一次: 简要记录一下环境的配置过程,刚下载下来BundleFu ...

  8. SpringSecurity5(10-动态权限管理)

    授权流程 SpringSecurity 的授权流程如下: 拦截请求,已认证用户访问受保护的 web 资源将被 SecurityFilterChain 中的 FilterSecurityIntercep ...

  9. JDK7-时间类、时间格式化类--java进阶day07

    1.Date类:表示时间的类 1.Date常用的构造方法 . 2.Date常用的成员方法 1.getTime:返回从时间原点到对象设定的时间之间的时间 2.setTime:将对象的时间设置为setTi ...

  10. 一个检查左右括号是否配对的语法检查器(c语言)

    目录 一.题目如下 二.解题思路 三.代码实现 四.测试结果 一.题目如下 通过键盘输入一个包括 '(' 和 ')' 的字符串string ,判断字符串是否有效.要求设计算法实现检查字符串是否有效,有 ...