EOS 菜单树 NodeID 与 ObjectID 说明

本文档说明 eosrole 页面「配置 EOS 菜单权限」树节点中 NodeID(前端 zTree 的 id)与 ObjectID(前端 objectId)的含义、数据库来源、后端组装与前端使用方式。

关联文档:EOS 菜单与角色关联关系说明

2026-07-31 更新:权限树接口额外返回 menuType / sysMenuId;保存 body 含 MES 按钮与 ERP 操作扩展字段。

结论摘要

前端字段 含义 数据库来源 主要用途
zTree id 菜单节点 ID messys.Tree.NodeID 树结构、父子关系(pId);前端勾选态缓存键
zTree objectId EOS 权限对象 ID messys.Tree.ObjectID = messys.ObjectRegister.ObjectID 角色菜单授权(STPermission.ObjectID,「执行」)
zTree menuType 菜单类型 EosMenuTypeHelper.resolveMenuType mes / erp / folder,决定右侧详情面板
zTree sysMenuId MES 菜单 ID 优先 sys_menu_eos_node,否则 NodeDesc=MES_MENU: MES 菜单/按钮写入 sys_role_menu

NodeID 与 ObjectID 是两套独立编号体系,不可混用。角色菜单「执行」授权必须使用 ObjectID;NodeID 用于树层级与 detail 查询参数。


1. NodeID 是什么

NodeID 是 EOS 菜单树 messys.dbo.Tree 的主键,标识一个菜单/目录节点在树中的位置。

在 MES 前端 zTree 中,NodeID 映射为节点的 **id**(idKey: "id"),ParentID 映射为 **pId**,用于渲染树形结构与展开/折叠。前端勾选态 permissionCheckedNodeIds 也按 NodeID 缓存。

NodeID 不写入 STPermission。权限表引用的是 ObjectID。detail 接口用 nodeId 定位节点后再解析业务 ObjectID。


2. NodeID 从哪来

2.1 数据库表

字段 说明
Tree messys NodeID 菜单节点主键
Tree messys ParentID 父节点 NodeID(根为 0)
UniqueNO messys No 下一个可用 NodeID(NName='tree'

2.2 MES 新建节点时的分配逻辑

MES 通过 TreeServiceImpl.getNextNodeIdForTree()UniqueNO 表取号:

  1. 悲观锁查询 UniqueNONName='tree' 的记录(PESSIMISTIC_WRITE
  2. 取当前 No 作为新 NodeID
  3. No + 1 写回,占用该编号
1
2
3
4
5
6
// TreeServiceImpl.getNextNodeIdForTree()
UniqueNOEntity row = uniqueNODao.findByNnameForUpdate(UNIQUE_NO_NAME_TREE);
int currentNo = row.getNo();
row.setNo(currentNo + 1);
uniqueNODao.save(row);
return Long.valueOf(currentNo);

2.3 触发 NodeID 分配的操作

操作 方法 说明
新增菜单节点 TreeServiceImpl.addNode getNextNodeIdForTree(),再写 Tree
sys_menu 同步 TreeServiceImpl.syncFromSysMenu 每个同步节点分配新 NodeID
复制菜单 TreeServiceImpl.copyMenu 源节点及子树每个节点各分配新 NodeID
交接班菜单批量创建 createShiftLogDispatchMenusaddNode 同上

2.4 EOS 原生 / 历史数据

在 MES 介入之前,NodeID 可能由 EOS 客户端或其他工具写入 Tree,同样依赖 UniqueNO 序列或历史遗留编号。MES 只保证新增/同步/复制时走 getNextNodeIdForTree() 逻辑。

2.5 NodeID 在 Functions 中的用途

Tree.Functions 字段保存菜单跳转 URL,其中 menuId 参数值为 NodeID(不是 ObjectID)。例如同步 sys_menu 时:

1
{baseUrl}/{url}?token=%Token%&menuId={NodeID}

复制菜单时,TreeServiceImpl 会将 Functions 里的 menuId=旧NodeID 替换为 menuId=新NodeID

2.6 NodeID 生命周期

  • 查询treeDao.findAll(...)queryObject(nodeid)
  • 删除delete / deleteBatch 按 NodeID 删除 Tree,并先 ObjectRegisterDao.deleteByOwnerId(NodeID) 清理关联对象
1
2
3
4
flowchart LR
A[UniqueNO<br/>NName=tree] -->|取 No 作为 NodeID| B[Tree.NodeID]
B --> C[listTreeForPermission<br/>返回 id=NodeID]
C --> D[zTree id / pId<br/>树结构展示]

3. ObjectID 是什么

ObjectID 是 EOS 对象注册表 messys.dbo.ObjectRegister 的主键。每个可授权对象在 ObjectRegister 中注册一条记录;菜单/目录节点对应 TypeID = 7,并通过 OwnerID = Tree.NodeID 与菜单树关联。

MES 维护菜单时会将 ObjectID 冗余写入 Tree.ObjectID,便于权限树接口一次查询带出。

在角色菜单授权场景下,前端勾选菜单后提交的是 objectId 列表,写入 mesdata.dbo.STPermission.ObjectID(配合 UserID=角色 OID、Oprate=「执行」、Permission=1)。


4. ObjectID 从哪来

4.1 数据库表

层级 库/表 字段 含义
权威主键 messys ObjectRegister ObjectID 全局对象 ID(自增:MAX+1)
关联 messys ObjectRegister OwnerID = Tree.NodeID
类型 messys ObjectRegister TypeID MES 新建菜单固定为 7(菜单/目录)
冗余 messys Tree ObjectID ObjectRegister.ObjectID 一致
授权 mesdata STPermission ObjectID 引用 ObjectRegister.ObjectID

关系:Tree.NodeIDObjectRegister.OwnerIDTree.ObjectID = ObjectRegister.ObjectID

4.2 MES 新建节点时的分配逻辑

TreeServiceImpl.saveObjectRegisterForTree()

  1. ObjectRegisterDao.findMaxObjectId() 取 MAX(ObjectID) + 1
  2. 插入 ObjectRegisterTypeID=7OwnerID=NodeIDObject=菜单名)
  3. 将返回的 ObjectID 写入 Tree.ObjectID

与 NodeID 分配在同一事务内完成(如 addNode 先取 NodeID,再创建 ObjectRegister)。

4.3 触发 ObjectID 分配的操作

与 NodeID 分配的操作相同(新增、同步、复制)。复制菜单时 NodeID 与 ObjectID 均重新分配,不会沿用源节点的 ObjectID。

场景 ObjectID 来源
MES 新增 EOS 菜单节点 saveObjectRegisterForTree 新分配
从 sys_menu 同步 每个同步节点一条新 ObjectRegister
复制菜单 新 NodeID + 新 ObjectRegister + 新 ObjectID
EOS 原生/历史数据 可能由 EOS 客户端写入;Tree.ObjectID 可能已存在
失败兜底 object == nullTree.ObjectID = 0(无有效授权对象)
1
2
3
4
5
6
flowchart LR
A[分配 NodeID] --> B[插入 ObjectRegister<br/>ObjectID=MAX+1]
B --> C[Tree.ObjectID = ObjectID]
C --> D[listTreeForPermission<br/>返回 objectId]
D --> E[前端勾选提交 objectIds]
E --> F[STPermission 写入]

5. NodeID 与 ObjectID 对照

1
2
3
4
5
6
Tree.NodeID       ──►  zTree.id         (树结构、ParentID 父子关系)
Tree.ObjectID ──► zTree.objectId (STPermission「执行」授权键)

ObjectRegister.OwnerID = Tree.NodeID
ObjectRegister.ObjectID = Tree.ObjectID
ObjectRegister.TypeID = 7(菜单/目录)
对比项 NodeID ObjectID
所属表 messys.Tree 主键 messys.ObjectRegister 主键,冗余在 Tree.ObjectID
分配方式 UniqueNO(NName=’tree’)递增 MAX(ObjectID)+1
前端 zTree 字段 id objectId
是否用于 STPermission「执行」
复制菜单 重新分配 重新分配

5.1 业务 ObjectID 与 NodeID 的非 OwnerID 关联

菜单授权与 ERP 按钮授权使用不同的 ObjectID,且业务对象的 OwnerID 不一定等于 Tree.NodeID

场景 ObjectID 示例 TypeID OwnerID STPermission 用途
菜单对象 468 7 430 Oprate=执行, Permission=1
业务对象 965 1 null Oprate=EX61:..., Permission=0

解析路径EosMenuObjectResolver.resolveBusinessObjects):

  1. ObjectRegister.OwnerID = NodeIDTypeID != 7
  2. Tree.Functions 括号内单据名 + 菜单对象名 → DjDy.DjCNameObjectRegister.Object = DjCNameTypeID != 7
  3. 回退:Tree.ObjectID 对应 ObjectRegister(可能含 TypeID=7

禁止仅用 OwnerID=NodeIDTree.ObjectID 推断按钮授权 ObjectID。复合页可能返回多个业务 ObjectID;同一 ObjectID 上可有多个 EXxx: 按钮 Oprate。

listAvailableOperations:标准操作 + ExFunction EXxx: + 库中已有非「执行」Oprate。

示例(NodeID=430):菜单树 objectId=468 用于勾选「执行」;按钮 EX61:组织架构分发 写在 ObjectID=965。


6. 后端组装与 API

6.1 权限菜单树

  • 接口GET /eosrole/menu/tree(或 @RequestMapping
  • 实现EosRoleController.menuTree()TreeService.listTreeForPermission()
  • 返回字段
字段 来源 说明
id Tree.NodeID zTree 节点 ID
pId Tree.ParentID(0 → -1) 父节点
name Tree.NodeCaption 显示名
objectId Tree.ObjectID STPermission「执行」键
menuType EosMenuTypeHelper.resolveMenuType mes / erp / folder
sysMenuId sys_menu_eos_node,缺失时 NodeDesc fallback 仅 MES 节点有值
1
2
3
4
5
6
7
// TreeServiceImpl.listTreeForPermission()
m.put("id", e.getNodeid());
m.put("pId", e.getParentid() == null || e.getParentid() == 0 ? -1 : e.getParentid());
m.put("name", e.getNodecaption() != null ? e.getNodecaption().trim() : "");
m.put("objectId", e.getObjectid());
m.put("menuType", EosMenuTypeHelper.resolveMenuType(e));
m.put("sysMenuId", sysMenuIdLookup.get(e.getNodeid()));

listTreeForSelect()(eostree 选目录)的区别:后者不含 objectId / menuType / sysMenuId,仅 id / pId / name

6.2 权限查询与保存

接口 作用
GET /eosrole/permission/{roleId} 返回已授权 objectId 列表(「执行」+ Permission=1)
GET /eosrole/permission/detail/{roleId}?nodeId= 按 NodeID 加载对象/按钮详情(MES 或 ERP)
GET /eosrole/permission/mes-menu-ids/{roleId} 返回 MES sys_role_menu 全部 menuId
POST /eosrole/permission/save 菜单执行 + ERP 操作 + MES 按钮

EosRolePermissionServiceImpl.queryMenuObjectIds 过滤条件:Oprate=执行Permission=1当前未按 TypeID=7 过滤)。

save body(EosRolePermissionSaveRequest):

字段 说明
roleIds EOS 角色 OID
objectIds 勾选菜单的 Tree.ObjectID → STPermission「执行」
mode append / replace
mesMenuIds MES 目录/菜单 menu_id → sys_role_menu
mesButtonMenuIds MES 按钮 menu_id → sys_role_menu
operationPermissions ERP {objectId, oprate, granted}
operationReplaceObjectIds replace 时清理非「执行」行的业务 ObjectID

保存委托:先 EosRolePermissionServiceImpl 写「执行」,再 EosRoleObjectPermissionServiceImpl.saveMesAndObjectPermissions 写 ERP/MES。


7. 前端使用(eosrole.js)

7.1 zTree 配置

1
2
3
4
5
6
7
8
9
10
11
var permissionTreeSetting = {
data: {
simpleData: {
enable: true,
idKey: "id", // = NodeID
pIdKey: "pId", // = ParentID
rootPId: -1
}
},
check: { enable: true, nocheckInherit: true }
};

7.2 加载与回显

  1. loadPermissionMenuTree → 请求 /eosrole/menu/tree,用 treeList 初始化 zTree(含 menuType / sysMenuId
  2. 单角色 + replace 模式:
    • /eosrole/permission/{roleId} → 按 objectId 勾选菜单树(不是 id
    • /eosrole/permission/mes-menu-ids/{roleId} → 拆分 MES 菜单 ID / 按钮 ID 到 permissionMesMenuIds
  3. 点击节点 → /eosrole/permission/detail/{roleId}?nodeId=
    • menuType=mes:展示 MES 按钮勾选
    • menuType=erp:展示 ERP 操作「禁止」勾选
    • menuType=folder:目录摘要

勾选 MES 节点时把 sysMenuId 加入 permissionMesMenuIds;取消勾选时清理对应按钮态。

7.3 保存

savePermission 收集并提交:

收集方法 字段
collectPermissionObjectIds() objectIds(勾选节点的 objectId,不是 id
collectMesMenuIdsForSave() mesMenuIds
collectMesButtonMenuIdsForSave() mesButtonMenuIds
collectOperationPermissionsForSave() operationPermissions
collectOperationReplaceObjectIdsForSave() operationReplaceObjectIds

连同 roleIdsmode 一并 POST 到 /eosrole/permission/save


8. 验证 SQL

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
-- NodeID 与 ObjectID 对照
SELECT
t.NodeID,
RTRIM(t.NodeCaption) AS MenuCaption,
t.ParentID,
t.ObjectID AS TreeObjectID,
o.ObjectID AS RegObjectID,
o.TypeID,
o.OwnerID
FROM messys.dbo.Tree t
LEFT JOIN messys.dbo.ObjectRegister o ON o.OwnerID = t.NodeID
ORDER BY t.NodeID;

-- UniqueNO 当前序列
SELECT NName, No FROM messys.dbo.UniqueNO WHERE NName = 'tree';

9. 相关代码路径

环节 路径
NodeID 分配 TreeServiceImpl.getNextNodeIdForTree()
ObjectID 分配 TreeServiceImpl.saveObjectRegisterForTree()
新增/同步/复制 TreeServiceImpl.addNode / syncFromSysMenu / copyMenu
权限树 API TreeServiceImpl.listTreeForPermission()EosRoleController
菜单类型 / sysMenuId EosMenuTypeHelperSysMenuEosNodeService
菜单「执行」读写 EosRolePermissionServiceImpl
ERP/MES 扩展保存 EosRoleObjectPermissionServiceImpl
业务 ObjectID 解析 EosMenuObjectResolver
实体 TreeEntityObjectRegisterEntityStPermissionEntity
前端 static/js/modules/security/eosrole.js

10. 修订记录

日期 说明
2026-06-16 初版:梳理 Tree NodeID 与 ObjectID 从数据库到前端权限树的完整链路
2026-06-16 补充 NodeID 在 Tree.Functions URL 中作为 menuId 参数的说明
2026-07-01 补充业务 ObjectID 与 NodeID 非 OwnerID 关联及 eosrole 解析路径
2026-07-31 按最新实现修订:权限树补 menuType/sysMenuId;save/前端补 MES 与 ERP 扩展字段;代码路径补 ObjectPermission / Resolver