318 lines
7.9 KiB
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
|
|
}
|
|
|