291 lines
7.2 KiB
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
|
||
|
|
}
|