backend/tmp/reborn/usecase/permission_tree.go

291 lines
7.2 KiB
Go

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
}