package services import ( "encoding/json" "strconv" "strings" "server/models" ) // ParseMenuRights 将角色 rights 解析为菜单 ID 集合(兼容 JSON 数组 / 逗号分隔)。 // 数字数组是角色保存接口 json.Marshal([]uint64) 写回的规范格式,必须支持; // 若只按 []string 反序列化会整体失败并退化成逗号切分, // 首尾元素会带上 [ / ] 而解析不出数字,导致第一个和最后一个菜单被静默丢弃。 func ParseMenuRights(raw string) map[uint64]bool { set := map[uint64]bool{} var arr []interface{} if err := json.Unmarshal([]byte(raw), &arr); err == nil { for _, v := range arr { if n := menuIDOf(v); n > 0 { set[n] = true } } return set } for _, s := range strings.Split(raw, ",") { if n, e := strconv.ParseUint(strings.TrimSpace(s), 10, 64); e == nil { set[n] = true } } return set } // menuIDOf 把 JSON 元素(数字或字符串)转成菜单 ID,无效时返回 0 func menuIDOf(v interface{}) uint64 { switch t := v.(type) { case float64: if t > 0 { return uint64(t) } case string: if n, e := strconv.ParseUint(strings.TrimSpace(t), 10, 64); e == nil { return n } } return 0 } // CheckUserPermission 校验用户是否拥有指定权限标识。 // rights 里存的是菜单 ID 而不是权限串,必须经 yz_system_menu.permission 换算后再比对; // 直接拿 rights 元素和权限串比永远不相等,会把本来有权限的角色全部判成无权访问。 // rights 为空(nil / 空串)视为全权限,与菜单过滤 filterMenusByRights 口径一致; // rights 有内容却解析不出任何菜单 ID(历史脏数据)按无权限处理,不放行。 func CheckUserPermission(userID int, permission string) (bool, error) { if permission == "" || userID <= 0 { return true, nil } var user models.AdminUser if err := models.Orm.QueryTable(new(models.AdminUser)).Filter("id", userID).One(&user); err != nil { return false, err } var role models.AdminRole if err := models.Orm.QueryTable(new(models.AdminRole)).Filter("id", user.RoleID).One(&role); err != nil { return false, err } if role.Rights == nil || strings.TrimSpace(*role.Rights) == "" { return true, nil } menuIDs := ParseMenuRights(*role.Rights) if len(menuIDs) == 0 { return false, nil } idList := make([]uint64, 0, len(menuIDs)) for id := range menuIDs { idList = append(idList, id) } // 只有启用且未删除的菜单才能授予权限,禁用的菜单不应继续放行其权限标识 count, err := models.Orm.QueryTable(new(models.SystemMenu)). Filter("id__in", idList). Filter("permission", permission). Filter("status", 1). Filter("delete_time__isnull", true). Count() if err != nil { return false, err } return count > 0, nil }