窝来辣😁
下面是前几篇的内容:
第一篇:【Go】基于GoFiber从零开始搭建一个GoWeb后台管理系统(一)搭建项目
第二篇:【Go】基于GoFiber从零开始搭建一个GoWeb后台管理系统(二)日志输出中间件、校验token中间件、配置路由、基础工具函数。
第三篇:【Go】基于GoFiber从零开始搭建一个GoWeb后台管理系统(三)日志管理(登录日志、操作日志)、用户登录模块
第四篇:【Go】基于GoFiber从零开始搭建一个GoWeb后台管理系统(四)用户管理、部门管理模块
碎碎念:这几天变得好冷,厚衣服穿得好累😂多穿几天可能就习惯了🤣
本篇内容是角色管理、菜单管理模块,有角色菜单关联权限。废话不多说,直接看代码
package sys
import (
"github.com/gofiber/fiber/v2"
"github.com/mozillazg/go-pinyin"
"go-web2/app/common/config"
"go-web2/app/common/util"
"go-web2/app/model/sys"
"strings"
)
type RoleController struct{}
// 角色列表分页
func (RoleController) GetPage(c *fiber.Ctx) error {
role := sys.SysRole{}
role.RoleKey = c.Query("code")
role.RoleName = c.Query("name")
pageSize := c.QueryInt("pageSize", 10)
pageNum := c.QueryInt("pageNum", 1)
return c.Status(200).JSON(config.Success(role.GetPage(pageSize, pageNum)))
}
// 根据id获取角色
func (RoleController) GetById(c *fiber.Ctx) error {
role := sys.SysRole{}
role.Id = c.Params("id")
role.GetById()
return c.Status(200).JSON(config.Success(role))
}
// 生成角色代码
func (RoleController) CreateCode(c *fiber.Ctx) error {
roleName := c.Query("roleName")
p := pinyin.NewArgs()
p.Style = pinyin.FirstLetter
roleKey := util.ConvertToPinyin(roleName, p)
return c.Status(200).JSON(config.Success(roleKey))
}
// 新增角色
func (RoleController) Insert(c *fiber.Ctx) error {
role := sys.SysRole{}
if err := c.BodyParser(&role); err != nil {
return c.Status(200).JSON(config.Error(err.Error()))
}
role.Token = c.Get(config.TokenHeader)
err := role.Insert()
if err != nil {
return c.Status(200).JSON(config.Error(err.Error()))
}
return c.Status(200).JSON(config.Success(nil))
}
// 修改角色
func (RoleController) Update(c *fiber.Ctx) error {
role := sys.SysRole{}
if err := c.BodyParser(&role); err != nil {
//log.Error(err.Error())
return c.Status(200).JSON(config.Error(err.Error()))
}
err := role.Update()
if err != nil {
return c.Status(200).JSON(config.Error(err.Error()))
}
return c.Status(200).JSON(config.Success(nil))
}
// 修改角色状态
func (RoleController) UpdateState(c *fiber.Ctx) error {
role := sys.SysRole{}
if err := c.BodyParser(&role); err != nil {
//log.Error(err.Error())
return c.Status(200).JSON(config.Error(err.Error()))
}
err := role.UpdateState()
if err != nil {
return c.Status(200).JSON(config.Error(err.Error()))
}
return c.Status(200).JSON(config.Success(nil))
}
// 删除角色
func (RoleController) Delete(c *fiber.Ctx) error {
role := sys.SysRole{}
if err := c.BodyParser(&role); err != nil {
//log.Error(err.Error())
return c.Status(200).JSON(config.Error(err.Error()))
}
ids := strings.Split(role.Id, ",")
if err := role.Delete(ids); err != nil {
return c.Status(200).JSON(config.Error(err.Error()))
}
return c.Status(200).JSON(config.Success(nil))
}
// 角色下拉列表
func (RoleController) GetSelectList(c *fiber.Ctx) error {
role := sys.SysRole{}
return c.Status(200).JSON(config.Success(role.GetSelectList()))
}
package sys
import (
"fmt"
"github.com/google/uuid"
"github.com/pkg/errors"
"go-web2/app/common/config"
"strings"
"time"
)
// 角色管理
type SysRole struct {
config.BaseModel
RoleKey string `json:"roleKey" form:"roleKey"` // 角色代码
RoleName string `json:"roleName" form:"roleName"` // 角色名称
IsOpen bool `json:"isOpen" form:"isOpen"` // 菜单树是否展开(0折叠 1展开 )
State int `json:"state" form:"state"` // 角色状态(1正常 2停用 3删除)
Remark string `json:"remark" form:"remark"` // 备注
MenuIds []string `gorm:"-" json:"menuIds" form:"menuIds"` // 菜单组
}
// 获取表名
func (SysRole) TableName() string {
return "sys_role"
}
// 列表
func (e *SysRole) GetPage(pageSize int, pageNum int) config.PageInfo {
var list []SysRole // 查询结果
var total int64 // 总数
query := config.DB.Table(e.TableName())
if e.RoleName != "" {
query.Where("role_name like ?", fmt.Sprintf("%%%s%%", e.RoleName))
}
if e.RoleKey != "" {
query.Where("role_key like ?", fmt.Sprintf("%%%s%%", e.RoleKey))
}
offset := (pageNum - 1) * pageSize // 计算跳过的记录数
query.Debug().Order("create_time desc").Offset(offset).Limit(pageSize).Find(&list) // 分页查询,根据offset和limit来查询
query.Count(&total)
return config.PageInfo{list, total}
}
// 详情
func (e *SysRole) GetById() {
config.DB.Table(e.TableName()).Where("id = ?", e.Id).Find(e)
}
// 新增
func (e *SysRole) Insert() (err error) {
// 校验角色名称和角色代码
if checkRoleNameAndKey(e.RoleName, "", "") {
err = errors.New("角色名称已存在!")
return
}
if checkRoleNameAndKey("", e.RoleKey, "") {
err = errors.New("角色代码已存在!")
return
}
e.Id = strings.ReplaceAll(uuid.NewString(), "-", "")
e.CreatorId = GetLoginId(e.Token)
e.CreateTime = time.Now()
config.DB.Table(e.TableName()).Create(e)
// 保存菜单树
roleMenu := SysRoleMenu{RoleId: e.Id}
roleMenu.Insert(e.MenuIds)
return
}
// 修改
func (e *SysRole) Update() (err error) {
// 校验角色名称和角色代码
if checkRoleNameAndKey(e.RoleName, "", e.Id) {
err = errors.New("角色名称已存在!")
return
}
if checkRoleNameAndKey("", e.RoleKey, e.Id) {
err = errors.New("角色代码已存在!")
return
}
config.DB.Model(&SysRole{}).Select("role_key", "role_name", "is_open", "state", "remark").Where("id = ?", e.Id).Save(e)
// 保存菜单树
roleMenu := SysRoleMenu{RoleId: e.Id}
roleMenu.Insert(e.MenuIds)
return
}
// 修改状态
func (e *SysRole) UpdateState() (err error) {
config.DB.Model(&SysRole{}).Select("state").Where("id = ?", e.Id).Save(e)
return
}
// 删除
func (e *SysRole) Delete(ids []string) (err error) {
for _, id := range ids {
e.Id = id
e.GetById()
// 首先查询角色是否已分配用户
if CheckRoleExistUser(id) {
err = errors.New(fmt.Sprintf("%s角色已分配,不允许删除", e.RoleName))
return
}
}
if err = config.DB.Table(e.TableName()).Delete(&SysRole{}, ids).Error; err != nil {
return
}
// 删除角色同时删除角色菜单关联
roleMenu := &SysRoleMenu{}
roleMenu.Delete(ids)
return
}
// 角色下拉列表
func (e *SysRole) GetSelectList() []SysRole {
var list []SysRole // 查询结果
config.DB.Table(e.TableName()).Find(&list)
return list
}
// 校验角色名称和代码是否存在
func checkRoleNameAndKey(roleName, roleKey, id string) bool {
var count int64
query := config.DB.Table(SysRole{}.TableName())
if roleName != "" {
query.Where("role_name = ?", roleName)
}
if roleKey != "" {
query.Where("role_key = ?", roleKey)
}
if id != "" {
query.Where("id <> ?", id)
}
query.Count(&count)
return count > 0
}
package sys
import (
"github.com/gofiber/fiber/v2"
"go-web2/app/common/config"
"go-web2/app/model/sys"
)
type MenuController struct{}
// 菜单树列表
func (MenuController) GetList(c *fiber.Ctx) error {
menu := sys.SysMenu{}
menu.Name = c.Query("name")
menu.State = c.QueryInt("state")
return c.Status(200).JSON(config.Success(menu.GetList()))
}
// 路由列表
func (MenuController) GetRouters(c *fiber.Ctx) error {
menu := sys.SysMenu{}
token := c.Get(config.TokenHeader)
menu.Id = sys.GetLoginUser(token).RoleId
return c.Status(200).JSON(config.Success(menu.GetRouters()))
}
// 根据id获取菜单
func (MenuController) GetById(c *fiber.Ctx) error {
menu := sys.SysMenu{}
menu.Id = c.Params("id")
menu.GetById()
return c.Status(200).JSON(config.Success(menu))
}
// 获取对应角色菜单列表树
func (MenuController) RoleMenuTree(c *fiber.Ctx) error {
menu := sys.SysMenu{}
roleMenu := sys.SysRoleMenu{}
roleMenu.RoleId = c.Params("roleId")
checkedIds := roleMenu.GetMenuIdByRoleId() // 根据角色id获取当前角色选中的菜单id
result := map[string]any{}
result["menus"] = menu.GetList() // 获取所有菜单
result["checkedKeys"] = checkedIds // 当前角色选中的菜单id
return c.Status(200).JSON(config.Success(result))
}
// 新增菜单
func (MenuController) Insert(c *fiber.Ctx) error {
menu := sys.SysMenu{}
if err := c.BodyParser(&menu); err != nil {
return c.Status(200).JSON(config.Error(err.Error()))
}
menu.Token = c.Get(config.TokenHeader)
err := menu.Insert()
if err != nil {
return c.Status(200).JSON(config.Error(err.Error()))
}
return c.Status(200).JSON(config.Success(nil))
}
// 修改菜单
func (MenuController) Update(c *fiber.Ctx) error {
menu := sys.SysMenu{}
if err := c.BodyParser(&menu); err != nil {
//log.Error(err.Error())
return c.Status(200).JSON(config.Error(err.Error()))
}
err := menu.Update()
if err != nil {
return c.Status(200).JSON(config.Error(err.Error()))
}
return c.Status(200).JSON(config.Success(nil))
}
// 删除菜单
func (MenuController) Delete(c *fiber.Ctx) error {
menu := sys.SysMenu{}
menu.Id = c.Params("id")
if err := menu.Delete(); err != nil {
return c.Status(200).JSON(config.Error(err.Error()))
}
return c.Status(200).JSON(config.Success(nil))
}
package sys
import (
"fmt"
"github.com/google/uuid"
"github.com/pkg/errors"
"go-web2/app/common/config"
"strings"
"time"
)
// 菜单管理
type SysMenu struct {
config.BaseModel
ParentId string `json:"parentId" form:"parentId"` // 上级部门id
Name string `json:"name" form:"name"` // 菜单名称
Sort int `json:"sort" form:"sort"` // 排序
Url string `json:"url" form:"url"` // 访问路径
Path string `son:"path" form:"path"` // 组件名称
Type string `json:"type" form:"type"` // 菜单类型(M目录 C菜单 F按钮)
State int `json:"state" form:"state"` // 菜单状态(1正常 2停用 3删除)
Perms string `json:"perms" form:"perms"` // 权限标识
Visible bool `json:"visible" form:"visible"` // 显示状态(0隐藏 1显示)
Icon string `json:"icon" form:"icon"` // 菜单图标
ActiveMenu string `json:"activeMenu" form:"activeMenu"` // 菜单高亮
IsFrame bool `json:"isFrame" form:"isFrame"` // 是否外链(0 否 1 是)
Remark string `json:"remark" form:"remark"` // 备注
Children []SysMenu `gorm:"-" json:"children"` // 子级数据
}
type Meta struct {
Title string `json:"title"` // 设置该路由在侧边栏和面包屑中展示的名字
Icon string `json:"icon"` // 菜单图标
ActiveMenu string `json:"activeMenu"` // 菜单高亮
RedDot bool `json:"redDot" `
}
// 前端路由
type Router struct {
Name string `json:"name"` // 菜单名称
Path string `json:"path"` // 组件名称
Hidden bool `json:"hidden"` // 是否隐藏路由,当设置 true 的时候该路由不会再侧边栏出现
Redirect string `json:"redirect"` // 重定向地址,当设置 noRedirect 的时候该路由在面包屑导航中不可被点击
Component string `json:"component"` // 组件地址
AlwaysShow bool `json:"alwaysShow"` // 当你一个路由下面的 children 声明的路由大于1个时,自动会变成嵌套的模式--如组件页面
ActiveMenu string `json:"activeMenu"` // 菜单高亮
Meta Meta `json:"meta"` // 其他元素
Children []Router `json:"children"` // 子级数据
}
// 获取表名
func (SysMenu) TableName() string {
return "sys_menu"
}
// 查询菜单列表的sql
var sql = `
select a.id,parent_id,name,type,url,path,state,ifnull(perms,'') as perms,icon, sort,visible,active_menu,is_frame
from sys_menu a
left join sys_role_menu b on a.id = b.menu_id
`
// 树形菜单列表
func (e *SysMenu) GetList() interface{} {
var list []SysMenu // 查询结果
query := config.DB.Table(e.TableName())
if e.Id != "" { // 角色id不为空,根据角色获取菜单
where := sql + " where b.role_id = ?"
args := []interface{}{e.Id}
if e.Name != "" {
where += " and name like ?"
args = append(args, fmt.Sprintf("%%%s%%", e.Name))
}
if e.State != 0 {
where += " and state = ?"
args = append(args, e.State)
}
query.Debug().Order("parent_id,sort asc").Raw(where, args...).Find(&list)
} else {
if e.Name != "" {
query.Where("name like ?", fmt.Sprintf("%%%s%%", e.Name))
}
if e.State != 0 {
query.Where("state = ?", e.State)
}
query.Debug().Order("parent_id,sort asc").Find(&list)
}
return e.BuildTree(list, "ROOT")
}
// 获取路由(根据当前用户的角色id)
func (e *SysMenu) GetRouters() interface{} {
var list []SysMenu // 查询结果
where := ` where b.role_id = ? and type in ('M', 'C') and a.state = 1`
where = sql + where
config.DB.Table(e.TableName()).Debug().Order("parent_id,sort asc").Raw(where, e.Id).Find(&list)
return buildMenus(e.BuildTree(list, "ROOT"))
}
// 详情
func (e *SysMenu) GetById() {
config.DB.Table(e.TableName()).Where("id = ?", e.Id).Find(e)
}
// 根据角色id获取菜单权限标识
func GetPermsMenuByRoleId(roleId string) []string {
var list []SysMenu
sql := `
select a.id,perms
from sys_menu a
left join sys_role_menu b on a.id = b.menu_id
where b.role_id = ?
order by parent_id,sort
`
config.DB.Raw(sql, roleId).Find(&list)
var result []string
for _, menu := range list {
result = append(result, menu.Perms)
}
return result
}
// 新增
func (e *SysMenu) Insert() (err error) {
var count int64
// 校验角色名称和角色代码
query := config.DB.Table(e.TableName())
if e.ParentId == "0" {
e.ParentId = "ROOT"
}
query.Where("name = ? and parent_id = ?", e.Name, e.ParentId).Count(&count)
if count > 0 {
err = errors.New("菜单名称已存在!")
return
}
e.Id = strings.ReplaceAll(uuid.NewString(), "-", "")
e.CreatorId = GetLoginId(e.Token)
e.CreateTime = time.Now()
config.DB.Table(e.TableName()).Create(e)
return
}
// 修改
func (e *SysMenu) Update() (err error) {
var count int64
// 校验角色名称和角色代码
query := config.DB.Table(e.TableName())
if e.ParentId == "0" {
e.ParentId = "ROOT"
}
if e.Id == e.ParentId {
err = errors.New("上级菜单不能是自己!")
return
}
query.Where("name = ? and parent_id = ? and id <> ?", e.Name, e.ParentId, e.Id).Count(&count)
if count > 0 {
err = errors.New("菜单名称已存在!")
return
}
//config.DB.Model(&SysMenu{}).Select("parent_id", "name", "sort", "url", "path", "type", "state", "perms", "visible", "icon", "active_menu", "is_frame", "remark").Where("id = ?", e.Id).Save(e)
config.DB.Model(&SysMenu{}).Omit("id", "create_time").Where("id = ?", e.Id).Save(e)
return
}
// 删除
func (e *SysMenu) Delete() (err error) {
// 1、校验是否存在下级
var count int64
query := config.DB.Table(e.TableName())
query.Where("parent_id = ?", e.Id).Count(&count)
if count > 0 {
err = errors.New("存在子级菜单,不允许删除")
return
}
// 2、校验是否存在用户
if CheckMenuExistRole(e.Id) {
err = errors.New("菜单已分配,不允许删除")
return
}
if err = config.DB.Table(e.TableName()).Where("id = ?", e.Id).Delete(SysMenu{}).Error; err != nil {
return
}
return
}
// 构建树结构
func (e *SysMenu) BuildTree(list []SysMenu, parentId string) []SysMenu {
var tree []SysMenu
for _, item := range list {
if item.ParentId == parentId {
children := e.BuildTree(list, item.Id)
if len(children) > 0 {
item.Children = children
}
tree = append(tree, item)
}
}
return tree
}
// 构建前端所需要的路由菜单
func buildMenus(menus []SysMenu) []Router {
routerList := []Router{}
for _, menu := range menus {
router := Router{Hidden: menu.Visible, Name: strings.Title(menu.Path), Path: getRouterPath(menu), Component: menu.Url}
meta := Meta{Title: menu.Name, Icon: menu.Icon, ActiveMenu: menu.ActiveMenu}
router.Meta = meta
cMenus := menu.Children
if len(cMenus) > 0 && menu.Type == "M" {
router.AlwaysShow = true
router.Redirect = "noRedirect"
router.Children = buildMenus(cMenus)
} else if menu.Type == "C" && menu.ParentId == "ROOT" {
childrenList := []Router{}
children := Router{Name: strings.Title(menu.Path), Path: menu.Path, Component: menu.Url}
childMeta := Meta{Title: menu.Name, Icon: menu.Icon, ActiveMenu: menu.ActiveMenu}
children.Meta = childMeta
childrenList = append(childrenList, children)
router.Children = childrenList
}
routerList = append(routerList, router)
}
return routerList
}
// 获取路由地址
func getRouterPath(menu SysMenu) string {
routerPath := menu.Path
if menu.Type == "M" {
if menu.Url != config.PARENT_VIEW {
routerPath = "/" + menu.Path
}
} else if menu.Type == "C" && menu.ParentId == "ROOT" {
routerPath = "/"
}
return routerPath
}
package sys
import "go-web2/app/common/config"
// 角色菜单关联
type SysRoleMenu struct {
RoleId string `json:"roleId" form:"roleId"` // 角色ID
MenuId string `json:"menuId" form:"menuId"` // 菜单ID
}
// 获取表名
func (SysRoleMenu) TableName() string {
return "sys_role_menu"
}
// 新增角色和菜单关联
func (e *SysRoleMenu) Insert(menuIds []string) {
// 先删除当前角色关联的菜单id
config.DB.Table(e.TableName()).Where("role_id = ?", e.RoleId).Delete(SysRoleMenu{})
// 再添加当前角色关联的菜单id
var list []SysRoleMenu // 存放要添加的数据
for _, menuId := range menuIds {
item := SysRoleMenu{RoleId: e.RoleId, MenuId: menuId}
list = append(list, item)
}
config.DB.Table(e.TableName()).Create(&list)
}
// 删除角色和菜单关联
func (e *SysRoleMenu) Delete(roleIds []string) {
// DELETE FROM `sys_role_menu` WHERE role_id in ('1','2','3')
config.DB.Table(e.TableName()).Where("role_id in (?)", roleIds).Delete(SysRoleMenu{})
}
// 根据角色id获取菜单列表id
func (e *SysRoleMenu) GetMenuIdByRoleId() []string {
var list []SysRoleMenu
var result []string
query := config.DB.Table(e.TableName())
query.Where("role_id = ?", e.RoleId)
query.Find(&list)
for _, menu := range list {
result = append(result, menu.MenuId)
}
return result
}
// 根据菜单id校验该菜单是否已分配给角色
func CheckMenuExistRole(menuId string) bool {
var count int64
config.DB.Table(SysRoleMenu{}.TableName()).Where("menu_id = ?", menuId).Count(&count)
return count > 0
}
好像没啥需要特别注意的地方。
好啦,以上就是本篇文章的全部内容辣,等我更完这个项目的全部文章,我会放出完整代码的地址,到现在的话,大概还剩下一篇文章的内容了,很快这个专栏就能更完了,欢迎大家多多点赞支持下,最后可以关注我不迷路~