package system import ( "context" "time" ) // SuperAdminAuthorityID is the seeded super-administrator role created by the // bootstrap data in internal/data/system/seed.go. It is the fallback authority // for new users and the recipient group for system-wide alerts. const SuperAdminAuthorityID uint = 888 type AuthorityAccessRepo interface { CreateAuthority(context.Context, *Authority) error CopyAuthority(context.Context, uint, *Authority) error UpdateAuthority(context.Context, *Authority) error DeleteAuthority(context.Context, uint) error ListAuthorities(context.Context) ([]*Authority, error) SetAuthorityUsers(context.Context, uint, []uint) error AuthorityUserIDs(context.Context, uint) ([]uint, error) SetDataScope(context.Context, uint, int, []uint) error DataScopeDepartmentIDs(context.Context, uint) ([]uint, error) ResolveDataScope(context.Context, uint, uint) (DataScope, error) } // Authority is the domain representation of an administration role. Keep the // audit timestamps because exports and API clients use them even though the // administration screens do not display them. type Authority struct { CreatedAt time.Time UpdatedAt time.Time DeletedAt *time.Time AuthorityID uint AuthorityName string ParentID *uint DataScope int DefaultRouter string Children []*Authority Menus []*Menu } type AuthorityUsecase struct{ AuthorityAccessRepo } func NewAuthorityUsecase(repo AuthorityAccessRepo) *AuthorityUsecase { return &AuthorityUsecase{AuthorityAccessRepo: repo} } func (uc *AuthorityUsecase) AuthorityTree(ctx context.Context) ([]*Authority, error) { items, err := uc.ListAuthorities(ctx) if err != nil { return nil, err } byID := make(map[uint]*Authority, len(items)) for _, item := range items { item.Children = nil byID[item.AuthorityID] = item } roots := make([]*Authority, 0) for _, item := range items { if item.ParentID != nil && *item.ParentID != 0 && byID[*item.ParentID] != nil { parent := byID[*item.ParentID] parent.Children = append(parent.Children, item) } else if item.ParentID != nil && *item.ParentID == 0 { roots = append(roots, item) } } // In strict-authority mode a non-root actor receives its direct // children as the top-level result even though their parent is omitted. if len(roots) == 0 { if actor, ok := ActorFromContext(ctx); ok { for _, item := range items { if item.ParentID != nil && *item.ParentID == actor.AuthorityID { roots = append(roots, item) } } } } return roots, nil }