app-cloudep-permission-server/pkg/usecase/permission_tree.go

230 lines
6.9 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 usecase
import (
"code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/entity"
"code.30cm.net/digimon/app-cloudep-permission-server/pkg/domain/permission"
"sync"
"go.mongodb.org/mongo-driver/bson/primitive"
)
// PermissionTree 用來管理權限節點,包含快速查詢 map 與輔助 map例如名稱對應的 ID 列表)
type PermissionTree struct {
// dummy root 節點
root *PermissionNode
// permission id => node
nodes map[string]*PermissionNode
// permission name => permission id
names map[string][]string
mu sync.RWMutex // 保護樹的並發存取
}
type PermissionNode struct {
Data entity.Permission
Parent *PermissionNode
Children []*PermissionNode
}
// GeneratePermissionTree 根據扁平權限資料建立樹,並掛在 dummy root 下
func GeneratePermissionTree(permissions []entity.Permission) *PermissionTree {
tree := &PermissionTree{
nodes: make(map[string]*PermissionNode),
names: make(map[string][]string),
}
// 1. 建立所有節點
for _, perm := range permissions {
node := &PermissionNode{
Data: perm,
Children: []*PermissionNode{},
}
tree.nodes[perm.ID.Hex()] = node
tree.names[perm.Name] = append(tree.names[perm.Name], perm.ID.Hex())
}
// 2. 建立 dummy root 節點
tree.root = &PermissionNode{
Data: entity.Permission{ID: primitive.NewObjectID(), Name: "root"},
Children: []*PermissionNode{},
}
// 3. 建立父子連結:若找不到父節點或 Parent 為 0則掛在 dummy root 下
for _, node := range tree.nodes {
if node.Data.Parent == "" {
node.Parent = tree.root
tree.root.Children = append(tree.root.Children, node)
} else if parent, ok := tree.nodes[node.Data.Parent]; ok {
node.Parent = parent
parent.Children = append(parent.Children, node)
} else {
// 若父節點不存在,預設掛在 dummy root 下
node.Parent = tree.root
tree.root.Children = append(tree.root.Children, node)
}
}
return tree
}
// getNode 輔助函數:根據 ID 從樹中查找節點
func (tree *PermissionTree) getNode(id string) *PermissionNode {
tree.mu.RLock()
defer tree.mu.RUnlock()
return tree.nodes[id]
}
func (tree *PermissionTree) put(node entity.Permission) {
parentNode := tree.getNode(node.Parent)
if parentNode == nil {
parentNode = tree.root
}
thisNode := &PermissionNode{
Data: node,
Parent: parentNode,
Children: make([]*PermissionNode, 0),
}
parentNode.Children = append(parentNode.Children, thisNode)
tree.names[node.Name] = append(tree.names[node.Name], node.ID.Hex())
tree.nodes[node.ID.Hex()] = thisNode
}
// filterOpenNodes 走訪整棵樹,列出有被打開的節點(父節點沒開,則底下的都不會開)
// 如果某個節點為非葉節點,則會檢查其子節點是否有啟用,否則該節點不會被展開。
// [permissionID] entity.Permission
func (tree *PermissionTree) filterOpenNodes() (map[string]entity.Permission, error) {
tree.mu.RLock()
defer tree.mu.RUnlock()
result := make(map[string]entity.Permission)
// dfs 為內部閉包,可存取 result
// 返回值 bool 表示目前節點或其子孫中是否存在有效 open 節點
var dfs func(node *PermissionNode) bool
dfs = func(node *PermissionNode) bool {
// 若本身狀態非 open則整個分支不展開
if node.Data.Status != permission.Open {
return false
}
// 節點本身是 open不論子節點狀態如何先將該節點加入結果
result[node.Data.ID.Hex()] = node.Data
// 遞迴處理子節點
for _, child := range node.Children {
dfs(child)
}
return true
}
// 從 dummy root 的 Children 開始走訪dummy root 本身不納入結果)
for _, child := range tree.root.Children {
dfs(child)
}
return result, nil
}
//// getFullParentPermissionIDs
//// 根據傳入的權限狀態 (Permissions) 回傳完整的權限 ID 列表,包含所有祖先。
//// 如果某個節點為非葉節點,則會檢查其子節點是否有啟用,否則該節點不會被展開。
//func (tree *PermissionTree) getFullParentPermissionIDs(permissions permission.Permissions) ([]string, error) {
// tree.mu.RLock()
// defer tree.mu.RUnlock()
//
// exist := make(map[string]bool)
// var ids []string
//
// for name, status := range permissions {
// if status != permission.OpenPermission {
// continue
// }
// idList, ok := tree.nameToIDs[name]
// if !ok {
// return nil, NotFoundError
// }
// for _, pid := range idList {
// node, exists := tree.nodes[pid]
// if !exists || node == nil {
// return nil, NotFoundError
// }
// // 如果為父節點,檢查其子節點是否有啟用,若都關閉則不展開
// if len(node.Children) > 0 {
// var childOpen bool
// for _, child := range node.Children {
// if childStatus, ok := permissions[child.Data.Name]; ok && childStatus == permission.OpenPermission {
// childOpen = true
// break
// }
// }
// if !childOpen {
// continue
// }
// }
// // 將該節點及所有祖先(直到 dummy root不包括 dummy root加入結果
// for cur := node; cur != nil && cur.Data.Name != "root"; cur = cur.Parent {
// if !exist[cur.Data.ID.Hex()] {
// ids = append(ids, cur.Data.ID.Hex())
// exist[cur.Data.ID.Hex()] = true
// }
// }
// }
// }
//
// return ids, nil
//}
//// getFullParentPermissionStatus
//// 根據傳入的權限狀態 (Permissions) 回傳完整的權限狀態,包含所有祖先的名稱設為啟用。
//func (tree *PermissionTree) getFullParentPermissionStatus(permissions permission.Permissions) (permission.Permissions, error) {
// tree.mu.RLock()
// defer tree.mu.RUnlock()
//
// result := make(permission.Permissions)
// exist := make(map[string]bool)
//
// for name, status := range permissions {
// if status != permission.OpenPermission {
// continue
// }
// idList, ok := tree.nameToIDs[name]
// if !ok {
// return nil, NotFoundError
// }
// for _, pid := range idList {
// node, exists := tree.nodes[pid]
// if !exists || node == nil {
// return nil, NotFoundError
// }
// // 將該節點及所有祖先標記為啟用
// for cur := node; cur != nil && cur.Data.Name != "root"; cur = cur.Parent {
// if !exist[cur.Data.ID.Hex()] {
// result[cur.Data.Name] = permission.OpenPermission
// exist[cur.Data.ID.Hex()] = true
// }
// }
// }
// }
//
// return result, nil
//}
//
//// getFullParentPermission
//// 根據角色權限 (RolePermission) 列表,回傳完整的權限狀態(名稱->狀態),包含所有祖先
//func (tree *PermissionTree) getFullParentPermission(rolePermissions []entity.RolePermission) permission.Permissions {
// tree.mu.RLock()
// defer tree.mu.RUnlock()
//
// result := make(permission.Permissions)
// for _, rp := range rolePermissions {
// node, ok := tree.nodes[rp.PermissionID]
// if !ok || node == nil {
// continue
// }
// // 將該節點及所有祖先設為啟用
// for cur := node; cur != nil && cur.Data.Name != "root"; cur = cur.Parent {
// result[cur.Data.Name] = permission.OpenPermission
// }
// }
// return result
//}