503 lines
14 KiB
Go
503 lines
14 KiB
Go
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
|
||
}
|