package models import ( "database/sql" "encoding/json" "fmt" "strings" "time" "github.com/beego/beego/v2/client/orm" ) // 角色-菜单关联表 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"` } func (r *RoleMenu) TableName() string { return "yz_role_menus" } // 角色权限响应结构 type RolePermission struct { RoleId int `json:"role_id"` RoleName string `json:"role_name"` MenuIds []int `json:"menu_ids"` Permissions []string `json:"permissions"` } // 菜单权限信息结构 type MenuPermission struct { MenuId int `json:"menu_id"` MenuName string `json:"menu_name"` Path string `json:"path"` MenuType int `json:"menu_type"` // 1页面菜单, 2API Permission string `json:"permission"` // 权限标识 ParentId int `json:"parent_id"` Default int8 `json:"default"` // 0全局, 1平台用户, 2租户用户 } func init() { orm.RegisterModel(new(RoleMenu)) } // 获取指定角色的菜单ID列表 func GetRoleMenus(roleId int) ([]int, error) { o := orm.NewOrm() var menuIdsJson sql.NullString var menuIdsStr string // 优先尝试解析JSON字段 err := o.Raw("SELECT IFNULL(JSON_UNQUOTE(JSON_EXTRACT(menu_ids, '$')), '[]') FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow(&menuIdsStr) if err == nil && menuIdsStr != "" && menuIdsStr != "[]" { menuIdsJson = sql.NullString{String: menuIdsStr, Valid: true} } else { // 若失败用CAST再次尝试,依然失败直接返回空 menuIdsStr = "" err2 := o.Raw("SELECT CAST(IFNULL(menu_ids, '[]') AS CHAR) FROM yz_roles WHERE role_id = ? AND delete_time IS NULL", roleId).QueryRow(&menuIdsStr) if err2 != nil { if err2 == orm.ErrNoRows { return []int{}, nil } return []int{}, nil } if menuIdsStr != "" && menuIdsStr != "[]" && menuIdsStr != "null" { menuIdsJson = sql.NullString{String: menuIdsStr, Valid: true} } else { return []int{}, nil } } if !menuIdsJson.Valid || menuIdsJson.String == "" { return []int{}, nil } jsonStr := strings.TrimSpace(menuIdsJson.String) jsonStr = strings.ReplaceAll(jsonStr, "\n", "") jsonStr = strings.ReplaceAll(jsonStr, "\r", "") jsonStr = strings.ReplaceAll(jsonStr, " ", "") if jsonStr == "" || jsonStr == "[]" || jsonStr == "null" || jsonStr == "NULL" { return []int{}, nil } var menuIds []int err = json.Unmarshal([]byte(jsonStr), &menuIds) if err != nil { return []int{}, nil } return menuIds, nil } // min 辅助函数 func min(a, b int) int { if a < b { return a } return b } // 获取角色的详细权限(菜单和API权限) func GetRolePermissions(roleId int) (*RolePermission, error) { o := orm.NewOrm() role, err := GetRoleById(roleId) if err != nil { return nil, fmt.Errorf("角色不存在: %v", err) } menuIds := role.MenuIds if menuIds == nil { menuIds = []int{} } permissions := []string{} if len(menuIds) > 0 { 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 delete_time IS NULL 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) } if permissions == nil { permissions = []string{} } } return &RolePermission{ RoleId: role.RoleId, RoleName: role.RoleName, MenuIds: menuIds, Permissions: permissions, }, nil } // 获取全部菜单权限信息(未删除) func GetAllMenuPermissions() ([]*MenuPermission, error) { o := orm.NewOrm() var results []struct { MenuId int MenuName string Path string MenuType int Permission sql.NullString ParentId int } _, 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(&results) if err != nil { return nil, fmt.Errorf("获取菜单列表失败: %v", err) } menus := make([]*MenuPermission, 0, len(results)) for _, r := range results { menu := &MenuPermission{ MenuId: r.MenuId, MenuName: r.MenuName, Path: r.Path, MenuType: r.MenuType, ParentId: r.ParentId, Default: 0, } if r.Permission.Valid { menu.Permission = r.Permission.String } else { menu.Permission = "" } menus = append(menus, menu) } return menus, nil } // 获取可分配菜单列表,支持平台/租户员工 func GetAllMenuPermissionsForUser(userId int, userType string, roleId int) ([]*MenuPermission, error) { o := orm.NewOrm() var roleDefault int8 = 0 if roleId > 0 { role, err := GetRoleById(roleId) if err == nil && role != nil { roleDefault = role.Default } } if userType == "user" { allMenus, err := GetAllMenuPermissions() if err != nil { return nil, err } if roleDefault > 0 { filteredMenus := make([]*MenuPermission, 0) for _, menu := range allMenus { if menu.Default == 0 || menu.Default == roleDefault { filteredMenus = append(filteredMenus, menu) } } return filteredMenus, nil } return allMenus, nil } if userType == "employee" { var employee Employee err := o.Raw("SELECT * FROM yz_tenant_employees WHERE id = ? AND delete_time IS NULL", userId).QueryRow(&employee) if err != nil { return nil, fmt.Errorf("员工不存在: %v", err) } if employee.Role == 0 { return []*MenuPermission{}, nil } menuIds, err := GetRoleMenus(employee.Role) if err != nil { return nil, fmt.Errorf("获取角色菜单失败: %v", err) } if len(menuIds) == 0 { return []*MenuPermission{}, nil } // 获取全部菜单父子关系,内存递归收集所有父菜单ID type menuParent struct { Id int ParentId int } var allMenuParents []menuParent _, err = o.Raw("SELECT id, parent_id FROM yz_menus WHERE delete_time IS NULL").QueryRows(&allMenuParents) if err != nil { return nil, fmt.Errorf("获取菜单父子关系失败: %v", err) } menuParentMap := make(map[int]int) for _, mp := range allMenuParents { menuParentMap[mp.Id] = mp.ParentId } parentIds := make(map[int]bool) var findParents func(pid int) findParents = func(pid int) { if pid == 0 || parentIds[pid] { return } parentIds[pid] = true if parentId, exists := menuParentMap[pid]; exists && parentId > 0 { findParents(parentId) } } for _, menuId := range menuIds { if parentId, exists := menuParentMap[menuId]; exists && parentId > 0 { findParents(parentId) } } allMenuIds := make(map[int]bool) for _, id := range menuIds { allMenuIds[id] = true } for pid := range parentIds { allMenuIds[pid] = true } finalMenuIds := make([]int, 0, len(allMenuIds)) for id := range allMenuIds { finalMenuIds = append(finalMenuIds, id) } placeholders := make([]string, len(finalMenuIds)) args := make([]interface{}, len(finalMenuIds)) for i, id := range finalMenuIds { placeholders[i] = "?" args[i] = id } type menuResult struct { MenuId int MenuName string Path string MenuType int Permission sql.NullString ParentId int } var results []menuResult query := fmt.Sprintf("SELECT id as menu_id, name as menu_name, path, menu_type, permission, parent_id FROM yz_menus WHERE id IN (%s) AND delete_time IS NULL ORDER BY parent_id, `order`", strings.Join(placeholders, ",")) _, err = o.Raw(query, args...).QueryRows(&results) if err != nil { return nil, fmt.Errorf("获取菜单列表失败: %v", err) } menus := make([]*MenuPermission, 0, len(results)) for _, r := range results { menu := &MenuPermission{ MenuId: r.MenuId, MenuName: r.MenuName, Path: r.Path, MenuType: r.MenuType, ParentId: r.ParentId, Default: 0, } if r.Permission.Valid { menu.Permission = r.Permission.String } else { menu.Permission = "" } menus = append(menus, menu) } // 菜单表没有default字段,下面逻辑实际上不会筛选掉任何菜单,仅保留 if roleDefault > 0 { filteredMenus := make([]*MenuPermission, 0) for _, menu := range menus { if menu.Default == 0 || menu.Default == roleDefault { filteredMenus = append(filteredMenus, menu) } } return filteredMenus, nil } return menus, nil } return []*MenuPermission{}, nil } // 分配角色权限,更新role菜单ID func AssignRolePermissions(roleId int, menuIds []int, createBy string) error { var jsonData []byte var err error if len(menuIds) == 0 { jsonData = []byte("[]") } else { jsonData, err = json.Marshal(menuIds) if err != nil { return fmt.Errorf("序列化菜单ID失败: %v", err) } } // 使用重试机制处理连接失效问题 maxRetries := 3 var lastErr error for i := 0; i < maxRetries; i++ { o := orm.NewOrm() // 在执行前检查连接有效性 db, dbErr := orm.GetDB("default") if dbErr == nil { // 尝试 Ping 检查连接 if pingErr := db.Ping(); pingErr != nil { // 连接失效,等待后重试 if i < maxRetries-1 { time.Sleep(200 * time.Millisecond) lastErr = fmt.Errorf("数据库连接失效: %v", pingErr) continue } } } // 尝试执行更新 _, err = o.Raw("UPDATE yz_roles SET menu_ids = ?, update_by = ?, update_time = NOW() WHERE role_id = ?", string(jsonData), createBy, roleId).Exec() if err == nil { // 成功,返回 return nil } // 检查是否是连接错误 errStr := err.Error() if strings.Contains(errStr, "invalid connection") || strings.Contains(errStr, "driver: bad connection") || strings.Contains(errStr, "connection reset") { lastErr = err // 如果是连接错误且不是最后一次重试,等待后继续 if i < maxRetries-1 { time.Sleep(200 * time.Millisecond) continue } } else { // 非连接错误,直接返回 return fmt.Errorf("更新角色权限失败: %v", err) } } // 所有重试都失败了 return fmt.Errorf("更新角色权限失败(已重试%d次): %v", maxRetries, lastErr) } // 获取用户所有权限(从所属角色) 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) } // 检查用户是否拥有指定权限 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 } // 菜单树节点 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"` } // 获取用户菜单树(仅页面菜单) 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 } menuIds, err := GetRoleMenus(user.Role) if err != nil { return nil, err } if len(menuIds) == 0 { return []*MenuTreeNode{}, nil } var menus []*Menu 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) } 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 } // 构建菜单树 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 }