yunzer_go/server/models/permission.go
2025-11-03 18:00:12 +08:00

306 lines
8.4 KiB
Go
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

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 status = 1 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 status = 1 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
}