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, } }