package usecase import ( "fmt" "permission/reborn/domain/entity" "permission/reborn/domain/errors" "permission/reborn/domain/usecase" ) // PermissionTree 權限樹 (優化版本) type PermissionTree struct { // 所有節點 (ID -> Node) nodes map[int64]*PermissionNode // 根節點列表 roots []*PermissionNode // 名稱索引 (Name -> IDs) nameIndex map[string][]int64 // 子節點索引 (ParentID -> Children IDs) childrenIndex map[int64][]int64 } // PermissionNode 權限節點 type PermissionNode struct { Permission *entity.Permission Parent *PermissionNode Children []*PermissionNode PathIDs []int64 // 從根到此節點的完整路徑 ID } // NewPermissionTree 建立權限樹 func NewPermissionTree(permissions []*entity.Permission) *PermissionTree { tree := &PermissionTree{ nodes: make(map[int64]*PermissionNode), roots: make([]*PermissionNode, 0), nameIndex: make(map[string][]int64), childrenIndex: make(map[int64][]int64), } // 第一遍:建立所有節點 for _, perm := range permissions { node := &PermissionNode{ Permission: perm, Children: make([]*PermissionNode, 0), PathIDs: make([]int64, 0), } tree.nodes[perm.ID] = node // 建立名稱索引 tree.nameIndex[perm.Name] = append(tree.nameIndex[perm.Name], perm.ID) // 建立子節點索引 tree.childrenIndex[perm.ParentID] = append(tree.childrenIndex[perm.ParentID], perm.ID) } // 第二遍:建立父子關係 for _, node := range tree.nodes { if node.Permission.ParentID == 0 { // 根節點 tree.roots = append(tree.roots, node) } else { // 找到父節點並建立關係 if parent, ok := tree.nodes[node.Permission.ParentID]; ok { node.Parent = parent parent.Children = append(parent.Children, node) // 複製父節點的路徑並加上父節點 ID node.PathIDs = append(node.PathIDs, parent.PathIDs...) node.PathIDs = append(node.PathIDs, parent.Permission.ID) } } } return tree } // GetNode 取得節點 func (t *PermissionTree) GetNode(id int64) *PermissionNode { return t.nodes[id] } // 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 entity.Permissions) (entity.Permissions, error) { expanded := make(entity.Permissions) visited := make(map[int64]bool) for name, status := range permissions { if status != entity.PermissionOpen { continue } nodes := t.GetNodesByName(name) if len(nodes) == 0 { return nil, errors.Wrap(errors.ErrCodePermissionNotFound, fmt.Sprintf("permission not found: %s", name), nil) } 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 } } // 加入此節點 if !visited[node.Permission.ID] { expanded.AddPermission(node.Permission.Name) visited[node.Permission.ID] = 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 entity.Permissions) ([]int64, error) { ids := make([]int64, 0) visited := make(map[int64]bool) for name, status := range permissions { if status != entity.PermissionOpen { continue } nodes := t.GetNodesByName(name) if len(nodes) == 0 { return nil, errors.Wrap(errors.ErrCodePermissionNotFound, fmt.Sprintf("permission not found: %s", name), nil) } 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 } } // 加入此節點和所有父節點 pathIDs := append(node.PathIDs, node.Permission.ID) for _, id := range pathIDs { if !visited[id] { ids = append(ids, id) visited[id] = true } } } } return ids, nil } // BuildPermissionsFromIDs 從權限 ID 列表建立權限集合 (包含父權限) func (t *PermissionTree) BuildPermissionsFromIDs(permissionIDs []int64) entity.Permissions { permissions := make(entity.Permissions) visited := make(map[int64]bool) for _, id := range permissionIDs { node := t.GetNode(id) if node == nil { continue } // 加入此節點 if !visited[node.Permission.ID] { permissions.AddPermission(node.Permission.Name) visited[node.Permission.ID] = 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 := entity.PermissionOpen if !node.Permission.IsActive() { status = entity.PermissionClose } treeNode := &usecase.PermissionTreeNode{ PermissionResponse: &usecase.PermissionResponse{ ID: node.Permission.ID, ParentID: node.Permission.ParentID, 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[int64]bool) if err := t.detectCircular(node, visited); err != nil { return err } } return nil } func (t *PermissionTree) detectCircular(node *PermissionNode, visited map[int64]bool) error { if visited[node.Permission.ID] { return errors.Wrap(errors.ErrCodeCircularDependency, fmt.Sprintf("circular dependency detected at permission: %s", node.Permission.Name), nil) } visited[node.Permission.ID] = true if node.Parent != nil { return t.detectCircular(node.Parent, visited) } return nil }