从 PHP 到 AI + Golang,程序员自救转型手记(五十):增加管理员角色组管理
这是一个系列 Blog,作者将以一个 PHP 全栈工程师的身份,利用 AI 工具(claude code、codex、deepseek、豆包等):从零开始学习 golang 语言,并最终完成 ai-go-admin(github | gitee)开源项目的制作(感谢 star ~),全程记录分享。
在上一期,我们进行了 “多条件组筛选、字段白名单”,本期将完成:增加管理员角色组管理
增加管理员角色组管理
我们之前已经建立好了菜单规则管理和管理员账号管理,下一步再制作好管理员角色组管理,RBAC权限模型就基本完善了,这个权限模型非常简单,最核心的是:用户、角色、权限的三层关联,其中用户并不直接跟权限挂钩,而是先建立角色组,将具体的权限节点分配给角色组,然后将角色组分配给用户,它还有以下细节:
- 一个角色组可以有多个权限节点
- 一个用户可以属于多个角色组
- 一个权限也可以被分配给多个角色
受益于之前已经准备好的服务端基类和前端的table组件及表格管家hook,我们这次基本上只需要考虑核心业务,其他的基本功能都是唾手可得的,首先直接让 AI 参考之前已有的 CRUD 实现角色组管理的基本功能,就像建立菜单规则管理实现时一样,然后从基础的 CRUD 开始角色组功能特有的特性开发,为了让读者脑海里有概念,这里提供两张成品图:
重写服务层 Create 方法
在基础的CRUD上,额外对一个角色组设定的权限规则进行处理和检查:
- 首先是对分组基础信息中的
上级分组不能是自身、上级分组不存在的检查。 - 如果输入的权限节点包含了系统所有的权限节点,则将权限节点直接折叠为
*通配符号,而不再单独的存储所有权限节点的 id 号(即超管,好处是新增的权限节点,不再需要额外向超管级分组追加)。 - 这一点是重中之重:对输入权限节点进行检查,首先是非超管不能建立超管级分组,其次是普通管理员能分配给新组的权限,必需是自己拥有的。
重写服务层 Update 方法
和以上 Create 方法类似,额外检查上级分组,不能是自己的子级分组(含直接子级、间接子级)避免形成环。
重写服务层 Delete 方法
主要是防止出现游离分组,能被删除的分组,必需没有子级,或使用批量删除连同所有子级一起删。
重写服务层 List 方法
这下基本上是全部重写了😂。
List 方法这边主要也是权限相关,即:只读取当前管理员拥有全部权限的角色组,但凡某个角色组有一个权限节点不属于他,就不能让他查看。
这里有个小坑,不能是:读取当前管理员拥有的分组(因为管理员可以拥有多个分组,只读取自己拥有的分组看起来是合理的,但是会出现比如:当前管理员有角色组管理的权限,自己操作添加了一个分组,那么这个分组就必须分配给当前管理员,否则他查都查不到 等情况)
重写控制器 List 方法
从图中可以看出来,这次的列表也是支持树状表格的,如果单这一点的话,我们还可以使用List 数据适配函数对输出的数据进行格式化;
不过这次我们还得查出权限规则字段的摘要文本,比如控制台等 29 项这种字样,目前在List 数据适配函数中不能访问到服务层实例,所以选择直接重写控制器 List 方法,核心的代码如下:
// List 覆写通用列表方法: 追加 rules_title 摘要字段,并渲染为树状结构func(h*AuthAdminGroupHandler)List(c*gin.Context){// ......// 批量构建每个分组的 rules 摘要文本(例如: 控制台等 60 项)titles,err:=h.svc.BuildRulesTitles(c,groups)iferr!=nil{httpx.Fail(c,httpx.WithMessage("规则标题查询失败: "+err.Error()))return}// 转 map 以备后续树状数据组装,并注入 rules_titlegroupData:=make([]map[string]any,len(groups))fori:=rangegroups{m:=groups[i].ToMap()m["rules_title"]=titles[groups[i].ID]groupData[i]=m}varlist[]map[string]anyifopts.Selector{list=tree.Render(groupData,"id","pid","name")}else{list=tree.Build(groupData,"id","pid","children")}httpx.Success(c,httpx.WithData(gin.H{"list":list,"total":total,}))// ......}如上,主要是调用了服务层的BuildRulesTitles方法,以及最后输出时根据opts.Selector(请求是否来自选择器)输出为不同的树状数据,其中BuildRulesTitles方法用于提取权限规则的中文摘要,规则入库的是ids,前台展示1,2,3肯定没有控制器等 20 项好看。
重写控制器 Get 方法
这一点是为了适配前端el-tree组件的数据格式,在编辑一个角色组时,特意将规则中的:所有父级节点去除,这些父级节点的选中状态由子级决定
