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. 悲观锁查询 UniqueNO 中 NName='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
交接班菜单批量创建 createShiftLogDispatchMenus → addNode 同上

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) 清理关联对象
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.NodeID ← ObjectRegister.OwnerID;Tree.ObjectID = ObjectRegister.ObjectID。

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

TreeServiceImpl.saveObjectRegisterForTree():

  1. ObjectRegisterDao.findMaxObjectId() 取 MAX(ObjectID) + 1
  2. 插入 ObjectRegister(TypeID=7,OwnerID=NodeID,Object=菜单名)
  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 == null 时 Tree.ObjectID = 0(无有效授权对象)
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 = NodeID 且 TypeID != 7
  2. Tree.Functions 括号内单据名 + 菜单对象名 → DjDy.DjCName → ObjectRegister.Object = DjCName 且 TypeID != 7
  3. 回退:Tree.ObjectID 对应 ObjectRegister(可能含 TypeID=7)

禁止仅用 OwnerID=NodeID 或 Tree.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

连同 roleIds、mode 一并 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 EosMenuTypeHelper、SysMenuEosNodeService
菜单「执行」读写 EosRolePermissionServiceImpl
ERP/MES 扩展保存 EosRoleObjectPermissionServiceImpl
业务 ObjectID 解析 EosMenuObjectResolver
实体 TreeEntity、ObjectRegisterEntity、StPermissionEntity
前端 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