package models import ( "fmt" "strings" "time" "github.com/beego/beego/v2/client/orm" ) // RoleMenu 角色-菜单关联表模型 type RoleMenu struct { Id int `orm:"auto" json:"id"` RoleId int `orm:"column(role_id)" json:"role_id"` MenuId int `orm:"column(menu_id)" json:"menu_id"` CreateTime time.Time `orm:"column(create_time);type(datetime);auto_now_add" json:"create_time"` CreateBy string `orm:"column(create_by);size(50);null" json:"create_by"` } // TableName 指定表名 func (r *RoleMenu) TableName() string { return "yz_role_menus" } // RolePermission 角色权限响应结构(包含菜单信息) type RolePermission struct { RoleId int `json:"role_id"` RoleName string `json:"role_name"` MenuIds []int `json:"menu_ids"` Permissions []string `json:"permissions"` // 权限标识列表 } // MenuPermission 菜单权限信息 type MenuPermission struct { MenuId int `json:"menu_id"` MenuName string `json:"menu_name"` Path string `json:"path"` MenuType int `json:"menu_type"` // 1: 页面菜单, 2: API接口 Permission string `json:"permission"` // 权限标识 ParentId int `json:"parent_id"` } func init() { orm.RegisterModel(new(RoleMenu)) } // GetRoleMenus 获取指定角色的所有菜单权限 func GetRoleMenus(roleId int) ([]int, error) { o := orm.NewOrm() var menuIds []int _, err := o.Raw("SELECT menu_id FROM yz_role_menus WHERE role_id = ?", roleId).QueryRows(&menuIds) if err != nil { return nil, fmt.Errorf("获取角色菜单失败: %v", err) } return menuIds, nil } // GetRolePermissions 获取角色的详细权限信息(包括菜单和API权限) func GetRolePermissions(roleId int) (*RolePermission, error) { o := orm.NewOrm() // 获取角色信息 var role Role err := o.Raw("SELECT * FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow(&role) if err != nil { return nil, fmt.Errorf("角色不存在: %v", err) } // 获取角色关联的所有菜单ID menuIds, err := GetRoleMenus(roleId) if err != nil { return nil, err } // 获取权限标识列表 var permissions []string if len(menuIds) > 0 { // 构建IN查询的占位符和参数 placeholders := make([]string, len(menuIds)) args := make([]interface{}, len(menuIds)) for i, id := range menuIds { placeholders[i] = "?" args[i] = id } query := fmt.Sprintf("SELECT DISTINCT permission FROM yz_menus WHERE id IN (%s) AND permission IS NOT NULL AND permission != ''", strings.Join(placeholders, ",")) _, err = o.Raw(query, args...).QueryRows(&permissions) if err != nil { return nil, fmt.Errorf("获取权限标识失败: %v", err) } } return &RolePermission{ RoleId: role.RoleId, RoleName: role.RoleName, MenuIds: menuIds, Permissions: permissions, }, nil } // GetAllMenuPermissions 获取所有菜单权限列表(用于分配权限时展示,未删除的) func GetAllMenuPermissions() ([]*MenuPermission, error) { o := orm.NewOrm() var menus []*MenuPermission _, err := o.Raw("SELECT id as menu_id, name as menu_name, path, menu_type, permission, parent_id FROM yz_menus WHERE delete_time IS NULL ORDER BY parent_id, `order`").QueryRows(&menus) if err != nil { return nil, fmt.Errorf("获取菜单列表失败: %v", err) } return menus, nil } // AssignRolePermissions 为角色分配权限(菜单) func AssignRolePermissions(roleId int, menuIds []int, createBy string) error { o := orm.NewOrm() // 先删除该角色的所有权限(使用更快的方式) _, err := o.Raw("DELETE FROM yz_role_menus WHERE role_id = ?", roleId).Exec() if err != nil { return fmt.Errorf("删除旧权限失败: %v", err) } // 如果没有新权限,直接返回 if len(menuIds) == 0 { return nil } // 使用更高效的批量插入方式 // 如果数据量太大,分批插入以避免超时 batchSize := 500 // 每批500条,MySQL可以高效处理 total := len(menuIds) for i := 0; i < total; i += batchSize { end := i + batchSize if end > total { end = total } batch := menuIds[i:end] // 构建批量INSERT语句 query := "INSERT INTO yz_role_menus (role_id, menu_id, create_by) VALUES " values := make([]interface{}, 0, len(batch)*3) placeholders := make([]string, 0, len(batch)) for _, menuId := range batch { placeholders = append(placeholders, "(?, ?, ?)") values = append(values, roleId, menuId, createBy) } query += strings.Join(placeholders, ", ") // 执行批量插入 _, err = o.Raw(query, values...).Exec() if err != nil { return fmt.Errorf("插入新权限失败(批次 %d/%d): %v", i/batchSize+1, (total+batchSize-1)/batchSize, err) } } return nil } // GetUserPermissions 获取用户的所有权限(通过用户角色) func GetUserPermissions(userId int) (*RolePermission, error) { o := orm.NewOrm() // 获取用户信息 var user User err := o.Raw("SELECT * FROM yz_users WHERE id = ? AND delete_time IS NULL", userId).QueryRow(&user) if err != nil { return nil, fmt.Errorf("用户不存在: %v", err) } // 如果用户没有角色,返回空权限 if user.Role == 0 { return &RolePermission{ RoleId: 0, RoleName: "无角色", MenuIds: []int{}, Permissions: []string{}, }, nil } // 获取角色权限 return GetRolePermissions(user.Role) } // CheckUserPermission 检查用户是否拥有指定权限 func CheckUserPermission(userId int, permission string) (bool, error) { if permission == "" { return true, nil // 空权限标识表示不需要权限控制 } userPerms, err := GetUserPermissions(userId) if err != nil { return false, err } // 检查权限列表中是否包含指定权限 for _, perm := range userPerms.Permissions { if perm == permission { return true, nil } } return false, nil } // MenuTreeNode 菜单树节点(包含子节点) type MenuTreeNode struct { Id int `json:"id"` Name string `json:"name"` Path string `json:"path"` ParentId int `json:"parent_id"` Icon string `json:"icon"` Order int `json:"order"` Status int8 `json:"status"` ComponentPath string `json:"component_path"` IsExternal int8 `json:"is_external"` ExternalUrl string `json:"external_url"` MenuType int8 `json:"menu_type"` Permission string `json:"permission"` Children []*MenuTreeNode `json:"children"` } // GetUserMenuTree 获取用户有权限访问的菜单树(仅页面菜单) func GetUserMenuTree(userId int) ([]*MenuTreeNode, error) { o := orm.NewOrm() // 获取用户角色 var user User err := o.Raw("SELECT * FROM yz_users WHERE id = ? AND delete_time IS NULL", userId).QueryRow(&user) if err != nil { return nil, fmt.Errorf("用户不存在: %v", err) } if user.Role == 0 { return []*MenuTreeNode{}, nil } // 获取角色的菜单ID列表 menuIds, err := GetRoleMenus(user.Role) if err != nil { return nil, err } if len(menuIds) == 0 { return []*MenuTreeNode{}, nil } // 获取菜单信息(仅页面菜单) var menus []*Menu // 构建IN查询的占位符和参数 placeholders := make([]string, len(menuIds)) args := make([]interface{}, len(menuIds)) for i, id := range menuIds { placeholders[i] = "?" args[i] = id } query := fmt.Sprintf("SELECT * FROM yz_menus WHERE id IN (%s) AND menu_type = 1 AND delete_time IS NULL ORDER BY parent_id, `order`", strings.Join(placeholders, ",")) _, err = o.Raw(query, args...).QueryRows(&menus) if err != nil { return nil, fmt.Errorf("获取菜单列表失败: %v", err) } // 转换为MenuTreeNode var nodes []*MenuTreeNode for _, m := range menus { nodes = append(nodes, &MenuTreeNode{ Id: m.Id, Name: m.Name, Path: m.Path, ParentId: m.ParentId, Icon: m.Icon, Order: m.Order, Status: m.Status, ComponentPath: m.ComponentPath, IsExternal: m.IsExternal, ExternalUrl: m.ExternalUrl, MenuType: m.MenuType, Permission: m.Permission, Children: []*MenuTreeNode{}, }) } // 构建菜单树 return buildMenuTree(nodes, 0), nil } // buildMenuTree 构建菜单树 func buildMenuTree(menus []*MenuTreeNode, parentId int) []*MenuTreeNode { var tree []*MenuTreeNode for _, menu := range menus { if menu.ParentId == parentId { menu.Children = buildMenuTree(menus, menu.Id) tree = append(tree, menu) } } return tree }