template-monorepo/internal/model/permission/usecase/permission_tree.go

122 lines
3.5 KiB
Go
Raw Normal View History

package usecase
import (
"sort"
"gateway/internal/model/permission/domain/entity"
"gateway/internal/model/permission/domain/enum"
dom "gateway/internal/model/permission/domain/usecase"
)
// buildPermissionTree converts the flat permission list into a parent →
// children tree. Roots (parent == "") are returned in alphabetical order
// by Name for stable client rendering.
func buildPermissionTree(perms []*entity.Permission) []*dom.PermissionTreeNode {
byParent := make(map[string][]*dom.PermissionTreeNode)
indexByID := make(map[string]*dom.PermissionTreeNode, len(perms))
for _, perm := range perms {
node := permissionToNode(perm)
indexByID[node.ID] = node
byParent[perm.Parent] = append(byParent[perm.Parent], node)
}
for _, children := range byParent {
sort.SliceStable(children, func(i, j int) bool {
return children[i].Name < children[j].Name
})
}
for parentID, children := range byParent {
if parent, ok := indexByID[parentID]; ok {
parent.Children = children
}
}
return byParent[""]
}
// filterOpenNodes prunes status=close subtrees (and any parent that has
// no remaining children). Mirrors permission-server's filterOpenNodes.
func filterOpenNodes(nodes []*dom.PermissionTreeNode) []*dom.PermissionTreeNode {
out := make([]*dom.PermissionTreeNode, 0, len(nodes))
for _, node := range nodes {
if node.Status != enum.StatusOpen {
continue
}
if len(node.Children) > 0 {
node.Children = filterOpenNodes(node.Children)
}
out = append(out, node)
}
return out
}
// filterByType drops subtrees whose type does not match. Useful for the
// "frontend menu only" client query.
func filterByType(nodes []*dom.PermissionTreeNode, t enum.PermissionType) []*dom.PermissionTreeNode {
out := make([]*dom.PermissionTreeNode, 0, len(nodes))
for _, node := range nodes {
// Category nodes inherit the type of their children when they
// have no leaf type of their own; filter recursively first.
var kids []*dom.PermissionTreeNode
if len(node.Children) > 0 {
kids = filterByType(node.Children, t)
}
if node.Type == t || len(kids) > 0 {
node.Children = kids
out = append(out, node)
}
}
return out
}
// getFullParentPermissionIDs walks up the parent chain for each id in
// requestedIDs and returns the deduplicated closure (requested + every
// ancestor). Mirrors permission-server's helper of the same name; used
// when persisting RolePermissions so a tenant prefix-clicking a leaf also
// gets the parent UI sections.
func getFullParentPermissionIDs(
requestedIDs []string,
allPermissions []*entity.Permission,
) []string {
parentByID := make(map[string]string, len(allPermissions))
for _, perm := range allPermissions {
parentByID[perm.ID.Hex()] = perm.Parent
}
seen := make(map[string]struct{}, len(requestedIDs)*2)
out := make([]string, 0, len(requestedIDs)*2)
for _, id := range requestedIDs {
walkParents(id, parentByID, seen, &out)
}
return out
}
func walkParents(
id string,
parentByID map[string]string,
seen map[string]struct{},
out *[]string,
) {
for id != "" {
if _, ok := seen[id]; ok {
return
}
seen[id] = struct{}{}
*out = append(*out, id)
parent, ok := parentByID[id]
if !ok || parent == "" {
return
}
id = parent
}
}
func permissionToNode(perm *entity.Permission) *dom.PermissionTreeNode {
return &dom.PermissionTreeNode{
ID: perm.ID.Hex(),
Parent: perm.Parent,
Name: perm.Name,
HTTPMethods: perm.HTTPMethods,
HTTPPath: perm.HTTPPath,
Status: perm.Status,
Type: perm.Type,
}
}