backend/pkg/permission/usecase/permission_tree.go

318 lines
7.9 KiB
Go

package usecase
import (
"backend/pkg/permission/domain/entity"
"backend/pkg/permission/domain/permission"
"backend/pkg/permission/domain/usecase"
"fmt"
"go.mongodb.org/mongo-driver/v2/bson"
)
// PermissionTree 權限樹 (優化版本)
type PermissionTree struct {
// 所有節點 (ID -> Node)
nodes map[string]*PermissionNode
// 根節點列表
roots []*PermissionNode
// 名稱索引 (Name -> IDs)
nameIndex map[string][]string
// 子節點索引 (ParentID -> Children IDs)
childrenIndex map[string][]string
}
// PermissionNode 權限節點
type PermissionNode struct {
Permission *entity.Permission
Parent *PermissionNode
Children []*PermissionNode
PathIDs []string // 從根到此節點的完整路徑 ID
}
// NewPermissionTree 建立權限樹
func NewPermissionTree(permissions []*entity.Permission) *PermissionTree {
tree := &PermissionTree{
nodes: make(map[string]*PermissionNode),
roots: make([]*PermissionNode, 0),
nameIndex: make(map[string][]string),
childrenIndex: make(map[string][]string),
}
// 第一遍:建立所有節點
for _, perm := range permissions {
node := &PermissionNode{
Permission: perm,
Children: make([]*PermissionNode, 0),
PathIDs: make([]string, 0),
}
idHex := perm.ID.Hex()
tree.nodes[idHex] = node
// 建立名稱索引
tree.nameIndex[perm.Name] = append(tree.nameIndex[perm.Name], idHex)
// 建立子節點索引
parentIDHex := perm.ParentID.Hex()
tree.childrenIndex[parentIDHex] = append(tree.childrenIndex[parentIDHex], idHex)
}
// 第二遍:建立父子關係
for _, node := range tree.nodes {
if node.Permission.ParentID.IsZero() {
// 根節點
tree.roots = append(tree.roots, node)
} else {
// 找到父節點並建立關係
parentIDHex := node.Permission.ParentID.Hex()
if parent, ok := tree.nodes[parentIDHex]; ok {
node.Parent = parent
parent.Children = append(parent.Children, node)
}
}
}
// 第三遍:計算 PathIDs (從根節點向下遞迴)
var buildPathIDs func(*PermissionNode, []string)
buildPathIDs = func(node *PermissionNode, parentPath []string) {
node.PathIDs = make([]string, len(parentPath))
copy(node.PathIDs, parentPath)
// 為子節點建立新路徑 (加入當前節點 ID)
childPath := append(parentPath, node.Permission.ID.Hex())
for _, child := range node.Children {
buildPathIDs(child, childPath)
}
}
// 從所有根節點開始
for _, root := range tree.roots {
buildPathIDs(root, []string{})
}
return tree
}
// GetNode 取得節點
func (t *PermissionTree) GetNode(id string) *PermissionNode {
return t.nodes[id]
}
// GetNodeByObjectID 根據 ObjectID 取得節點
func (t *PermissionTree) GetNodeByObjectID(id bson.ObjectID) *PermissionNode {
return t.nodes[id.Hex()]
}
// GetNodesByName 根據名稱取得節點列表
func (t *PermissionTree) GetNodesByName(name string) []*PermissionNode {
ids, ok := t.nameIndex[name]
if !ok {
return nil
}
nodes := make([]*PermissionNode, 0, len(ids))
for _, id := range ids {
if node, ok := t.nodes[id]; ok {
nodes = append(nodes, node)
}
}
return nodes
}
// ExpandPermissions 展開權限 (包含所有父權限)
func (t *PermissionTree) ExpandPermissions(permissions permission.Permissions) (permission.Permissions, error) {
expanded := make(permission.Permissions)
visited := make(map[string]bool)
for name, status := range permissions {
if status != permission.Open {
continue
}
nodes := t.GetNodesByName(name)
if len(nodes) == 0 {
return nil, fmt.Errorf("permission not found: %s", name)
}
for _, node := range nodes {
// 如果是父節點,檢查是否有任何子節點被開啟
if len(node.Children) > 0 {
hasActiveChild := false
for _, child := range node.Children {
if permissions.HasPermission(child.Permission.Name) {
hasActiveChild = true
break
}
}
// 如果沒有任何子節點被開啟,跳過此父節點
if !hasActiveChild {
continue
}
}
// 加入此節點
idHex := node.Permission.ID.Hex()
if !visited[idHex] {
expanded.AddPermission(node.Permission.Name)
visited[idHex] = true
}
// 加入所有父節點
for _, parentID := range node.PathIDs {
if !visited[parentID] {
if parentNode := t.GetNode(parentID); parentNode != nil {
expanded.AddPermission(parentNode.Permission.Name)
visited[parentID] = true
}
}
}
}
}
return expanded, nil
}
// GetPermissionIDs 取得權限 ID 列表 (包含父權限)
func (t *PermissionTree) GetPermissionIDs(permissions permission.Permissions) ([]bson.ObjectID, error) {
ids := make([]bson.ObjectID, 0)
visited := make(map[string]bool)
for name, status := range permissions {
if status != permission.Open {
continue
}
nodes := t.GetNodesByName(name)
if len(nodes) == 0 {
return nil, fmt.Errorf("permission not found: %s", name)
}
for _, node := range nodes {
// 檢查父節點邏輯
if len(node.Children) > 0 {
hasActiveChild := false
for _, child := range node.Children {
if permissions.HasPermission(child.Permission.Name) {
hasActiveChild = true
break
}
}
if !hasActiveChild {
continue
}
}
// 加入此節點和所有父節點
idHex := node.Permission.ID.Hex()
pathIDs := append(node.PathIDs, idHex)
for _, id := range pathIDs {
if !visited[id] {
oid, _ := bson.ObjectIDFromHex(id)
ids = append(ids, oid)
visited[id] = true
}
}
}
}
return ids, nil
}
// BuildPermissionsFromIDs 從權限 ID 列表建立權限集合 (包含父權限)
func (t *PermissionTree) BuildPermissionsFromIDs(permissionIDs []bson.ObjectID) permission.Permissions {
permissions := make(permission.Permissions)
visited := make(map[string]bool)
for _, id := range permissionIDs {
node := t.GetNodeByObjectID(id)
if node == nil {
continue
}
// 加入此節點
idHex := node.Permission.ID.Hex()
if !visited[idHex] {
permissions.AddPermission(node.Permission.Name)
visited[idHex] = true
}
// 加入所有父節點
for _, parentID := range node.PathIDs {
if !visited[parentID] {
if parentNode := t.GetNode(parentID); parentNode != nil {
permissions.AddPermission(parentNode.Permission.Name)
visited[parentID] = true
}
}
}
}
return permissions
}
// ToTree 轉換為樹狀結構回應
func (t *PermissionTree) ToTree() []*usecase.PermissionTreeNode {
result := make([]*usecase.PermissionTreeNode, 0, len(t.roots))
for _, root := range t.roots {
result = append(result, t.buildTreeNode(root))
}
return result
}
func (t *PermissionTree) buildTreeNode(node *PermissionNode) *usecase.PermissionTreeNode {
status := permission.Open
if !node.Permission.State.IsActive() {
status = permission.Close
}
treeNode := &usecase.PermissionTreeNode{
PermissionResponse: &usecase.PermissionResponse{
ID: node.Permission.ID.Hex(),
ParentID: node.Permission.ParentID.Hex(),
Name: node.Permission.Name,
HTTPPath: node.Permission.HTTPPath,
HTTPMethod: node.Permission.HTTPMethod,
Status: status,
Type: node.Permission.Type,
},
Children: make([]*usecase.PermissionTreeNode, 0, len(node.Children)),
}
for _, child := range node.Children {
treeNode.Children = append(treeNode.Children, t.buildTreeNode(child))
}
return treeNode
}
// DetectCircularDependency 檢測循環依賴
func (t *PermissionTree) DetectCircularDependency() error {
for _, node := range t.nodes {
visited := make(map[string]bool)
if err := t.detectCircular(node, visited); err != nil {
return err
}
}
return nil
}
func (t *PermissionTree) detectCircular(node *PermissionNode, visited map[string]bool) error {
idHex := node.Permission.ID.Hex()
if visited[idHex] {
return fmt.Errorf("circular dependency detected at permission: %s", node.Permission.Name)
}
visited[idHex] = true
if node.Parent != nil {
return t.detectCircular(node.Parent, visited)
}
return nil
}