应用场景: 在RBAC权限控制场景下,需要权限的树状结构展示数据,laravel的实现比较简单,使用模型递归关联即可,但没有用gin+gorm实现过, 查了网上资料解决了问题,但gorm2.0有些坑,需要注意,文章结尾说明,做下笔记~
场景示例图:
菜单表结构
type Menu struct {
IdModel
CreateAdminId int64 `gorm:"column:create_admin_id;type:int unsigned;not null" json:"create_admin_id"` // 创建菜单用户
ParentId int64 `gorm:"column:parent_id;type:int unsigned;not null" json:"parent_id"` // 菜单父级
Level int64 `gorm:"column:level;type:int unsigned;not null" json:"level"`
Title string `gorm:"column:title;type:varchar(255);not null" json:"title"` // 菜单标题
Path string `gorm:"column:path;type:varchar(255);not null" json:"path"` // 菜单路径
Component string `gorm:"column:component;type:varchar(255);" json:"component"` // 菜单组件
Permission string `gorm:"column:permission;type:varchar(255)" json:"permission"` // 菜单权限标识
Icon string `gorm:"column:icon;type:varchar(255)" json:"icon"` // 菜单图标
Remark string `gorm:"column:remark;type:text;" json:"remark"` // 备注
Sort int64 `gorm:"column:sort;type:int unsigned;default:0;not null" json:"sort"` // 排序
Status int64 `gorm:"column:status;type:tinyint unsigned;default:1;not null" json:"status"` // 状态 1. 正常 0 禁用
Type int64 `gorm:"column:type;type:tinyint unsigned;default:1;not null" json:"type"` // 类型 1. 菜单 2. 按钮
TimeModel
}
输出结构体
type TreeMenu struct {
models.Menu
Children []*TreeMenu `json:"children" gorm:"-"`
}
生成树结构
func (m *menuService) GetChildrenList(menus []*modelsResponse.TreeMenu, parentId int64) []*modelsResponse.TreeMenu {
// 定义子节点目录
var nodes []*modelsResponse.TreeMenu
// 判断反射值的有效性
if reflect.ValueOf(menus).IsValid() {
// 循环所有菜单
for _, v := range menus {
// 操作指定级别菜单 parentId为0是表示一级
if v.ParentId == parentId {
// 将子级菜单 不定长压入 children数组
v.Children = append(v.Children, m.GetChildrenList(menus, v.ID)...)
// 放入结果数组
nodes = append(nodes, v)
}
}
}
return nodes
}
api接口输出
func (m *menuApi) GetTreeMenu(ctx *gin.Context) {
var treeMenu []*modelsResponse.TreeMenu
err := global.DB.Model(&models.Menu{}).Find(&treeMenu).Error
if err != nil {
response.Fail(err.Error(), ctx)
return
}
// 从第一层开始递归 parentId为0的为第一层
menu := service.MenuService.GetChildrenList(treeMenu, 0)
// 接口输出
response.Success(menu, ctx)
}
实现效果
避坑指南
在gorm2.0下, 在定义输出结构体TreeMenu时,只是为了输出,而不想要去创建数据表,如果没有在额外字段Children后方加了gorm:"-",在接口查询时,使用TreeMenu接收时,gorm会以为children是表中的关联字段而因此报错,报错信息如下:
define a valid foreign key for relations or implement the Valuer/Scanner interface