\App\Services\AdminMaintainMenuService

菜单维护服务

职责:菜单的创建、更新、删除、树构建等核心逻辑集中在此, Controller 只负责参数接收和响应返回;用户菜单按直授角色授权过滤。

┌────────────────── 分区导航(按此顺序阅读)──────────────────┐ 分区 1 · 构造与依赖 __construct 分区 2 · 查询(读) tree / userTree / collectDescendantIds 分区 3 · 菜单写操作 create / update / destroy 分区 4 · 内部辅助 normalizeEmptyStrings / clearAllUserMenusCache / buildTree buildFromGroup / userTreeQuery / buildAuthorizedMenuTree buildMenuNode / collectAncestorIds / validateTypeLevel validateChildType / validateTypeChange / validateParentChange └────────────────────────────────────────────────────────────┘

Summary

Methods
Properties
Constants
__construct()
tree()
userTree()
collectDescendantIds()
create()
update()
destroy()
No public properties found
TYPE_NAMES
STRING_FIELDS
PATHLESS_TYPES
normalizeStringColumns()
No protected properties found
N/A
normalizeEmptyStrings()
clearAllUserMenusCache()
buildTree()
buildFromGroup()
userTreeQuery()
buildAuthorizedMenuTree()
buildMenuNode()
collectAncestorIds()
validateTypeLevel()
validateChildType()
validateTypeChange()
validateParentChange()
$cacheService
N/A

Constants

TYPE_NAMES

TYPE_NAMES = [1 => '目录', 2 => '页面', 3 => '外链', 4 => '容器']

STRING_FIELDS

STRING_FIELDS = ['path', 'query', 'component', 'component_name', 'icon', 'tag']

NOT NULL DEFAULT '' 的字符串列:中间件会把空串转 null,写库前兜底为空串

PATHLESS_TYPES

PATHLESS_TYPES = [\App\Models\SysMenu::TYPE_DIR, \App\Models\SysMenu::TYPE_CONTAINER]

不可导航的类型(目录/容器):path 无意义,写库强制清空(仅页面/外链使用 path)

Properties

Methods

__construct()

__construct(\App\Services\Admin\PermissionCacheService  $cacheService) : mixed

注入权限缓存服务

Parameters

\App\Services\Admin\PermissionCacheService $cacheService

Returns

mixed —

tree()

tree(array  $filters = []) : array

构建完整菜单树(可选带权限)。

无筛选(管理页初始打开)走 sys:menu:tree 全局缓存;带筛选(search/type/status/ with_permissions)实时查——筛选是管理页低频操作,不缓存避免参数组合爆炸。

Parameters

array $filters

Returns

array —

userTree()

userTree(\App\Models\SysUser  $user) : array

我的菜单:按当前用户角色过滤的菜单树。 超管返回全部(含隐藏,隐藏菜单由前端侧边栏过滤、路由保留)。

Parameters

\App\Models\SysUser $user

Returns

array —

collectDescendantIds()

collectDescendantIds(\App\Models\SysMenu  $menu) : array

收集菜单的所有后代 ID。

内存 BFS 单源(ArrHelper::descendantIds——seen 集保证环数据也能终止), 替代逐节点查库递归(原实现查询数 = 节点数)。

Parameters

\App\Models\SysMenu $menu

Returns

array —

create()

create(array  $data) : \App\Models\SysMenu

创建菜单,含全部业务校验。

Parameters

array $data

Throws

\InvalidArgumentException

Returns

\App\Models\SysMenu —

update()

update(\App\Models\SysMenu  $menu, array  $data) : \App\Models\SysMenu

更新菜单,含全部业务校验。

Parameters

\App\Models\SysMenu $menu
array $data

Throws

\InvalidArgumentException

Returns

\App\Models\SysMenu —

destroy()

destroy(\App\Models\SysMenu  $menu) : void

删除菜单、全部后代、及关联权限。

mass delete 不触发模型事件:role_menu/role_permission 绑定行需显式级联清理, 否则留孤儿行;删后全量清用户权限+菜单缓存(权限结构变更本来就是全用户语义, 不清的话已删权限码在用户权限 Set 里残留至 TTL 过期,期间越权判定仍通过)。

Parameters

\App\Models\SysMenu $menu

normalizeStringColumns()

normalizeStringColumns(array<string,mixed>  $data, string[]  $columns, array<string,string>  $defaults = [], bool  $fillMissing = false) : array

字符串列归一化:值为 null 的键写入 $defaults 对应默认值(缺省 '')。

Parameters

array $data

待写库数据(按引用字段处理,返回归一化副本)

string[] $columns

参与归一化的列名

array $defaults

列 => 默认值(缺省 '',如 link_target => '_self')

bool $fillMissing

true 时缺失键也补默认值(创建路径全字段落库用);false 只处理已存在的键

Returns

array —

normalizeEmptyStrings()

normalizeEmptyStrings(array  $data) : array

字符串字段 null → '',link_target null → '_self'(DB 均为 NOT NULL 带默认值)。

Parameters

array $data

Returns

array —

clearAllUserMenusCache()

clearAllUserMenusCache() : void

菜单变更后清理所有用户的菜单树缓存(CacheManager tag flush 一次清整组)+ 全局菜单树。

buildTree()

buildTree(array  $filters) : array

菜单树真实构建(查询 + 内存组树);tree() 无筛选分支包缓存

Parameters

array $filters

Returns

array —

buildFromGroup()

buildFromGroup(\Illuminate\Support\Collection  $grouped, int  $parentId) : \Illuminate\Support\Collection

内存递归组树:全量按 parent_id 分组,逐层拼 children,零额外查询。 返回 Collection——setRelation 的 children 必须是 Arrayable(数组会在模型 toArray 时被丢弃)。

Parameters

\Illuminate\Support\Collection $grouped
int $parentId

Returns

\Illuminate\Support\Collection —

userTreeQuery()

userTreeQuery(\App\Models\SysUser  $user) : array

非超管用户的菜单树(缓存未命中时查库)

Parameters

\App\Models\SysUser $user

Returns

array —

buildAuthorizedMenuTree()

buildAuthorizedMenuTree(array  $authorizedMenuIds) : array

根据授权的菜单 ID 列表构建菜单树。

Parameters

array $authorizedMenuIds

Returns

array —

buildMenuNode()

buildMenuNode(\App\Models\SysMenu  $menu, mixed  $groupedChildren, array  $authorizedIds) : array

递归构建菜单节点(children 按 parent_id 分组索引,O(1) 取子)。

Parameters

\App\Models\SysMenu $menu
mixed $groupedChildren
array $authorizedIds

Returns

array —

collectAncestorIds()

collectAncestorIds(array  $menuIds) : array

收集菜单 ID 的所有祖先 ID(用于确保树结构完整)。

Parameters

array $menuIds

Returns

array —

validateTypeLevel()

validateTypeLevel(int  $type, \App\Models\SysMenu  $parent) : void

校验类型层级限制。

Parameters

int $type
\App\Models\SysMenu $parent

validateChildType()

validateChildType(\App\Models\SysMenu  $parent, int  $childType) : void

校验父菜单是否允许该类型的子级。

Parameters

\App\Models\SysMenu $parent
int $childType

validateTypeChange()

validateTypeChange(\App\Models\SysMenu  $menu, int  $newType) : void

校验类型变更(不能与已有子级冲突)。

Parameters

\App\Models\SysMenu $menu
int $newType

validateParentChange()

validateParentChange(\App\Models\SysMenu  $menu, int  $newParentId, int  $type) : void

校验父节点变更(循环引用、类型兼容、深度限制)。

Parameters

\App\Models\SysMenu $menu
int $newParentId
int $type