集团化企业上固定资产管理系统,90% 的返工都不在扫码、不在盘点,而在**“谁能看到哪些资产、谁能操作哪些资产”**这一层。本文完整拆解多组织架构下的资产权限模型:组织树建模、数据权限范围、四层鉴权链路、跨组织调拨、性能优化与避坑清单,并附上易点易动固定资产管理系统的落地实现思路。可直接抄作业。
目录
- 多组织架构到底“多”在哪:四类真实场景
- 权限模型选型:ACL、RBAC、ABAC 与“RBAC + 数据域”
- 数据模型设计:组织树、资产归属与权限表
- 数据权限范围的五种粒度与查询下推
- 四层鉴权链路:功能、数据、字段、流程
- 跨组织资产调拨:权限最容易崩的地方
- 性能优化:路径索引、权限缓存与批量鉴权
- 10 条避坑清单(血泪版)
- 易点易动固定资产管理系统是怎么落地的
- 总结与常见问题答疑
一、多组织架构到底“多”在哪:四类真实场景
很多团队一开始把“多组织”简单理解成“部门多了一层”,于是在资产表上加个 dept_id 就上线了。真正落地时会发现,集团客户的组织复杂度至少来自四个方向:
1. 法人层级:集团 → 子公司 → 分公司
资产的所有权属于法人主体,折旧要分账套、报表要分主体,子公司之间默认互相不可见。
2. 管理层级:事业部 → 中心 → 部门 → 小组
与法人层级不是同一棵树。一个事业部可能横跨三个法人主体,这是双树建模的根源。
3. 物理层级:区域 → 城市 → 园区 → 楼栋 → 房间
盘点、IT 运维、行政都按位置作业。位置树与组织树正交,“华东区 IT 管理员”需要的是跨部门、按位置的权限。
4. 责任层级:使用人 / 保管人 / 管理员 / 财务归口
同一台笔记本,使用人只能看到自己名下的、保管人能看到整个部门的、IT 管理员能看到全公司的 IT 类资产。权限的差异不在“资产”,而在“人与资产的关系”。
结论很清晰:资产权限不是一维的组织归属,而是“主体维度 × 组织维度 × 位置维度 × 关系维度”的交集。任何只做单树父子继承的方案,最终都会被“事业部横跨主体”“区域管理员”“外派人员携带资产”这三类需求击穿。
二、权限模型选型:ACL、RBAC、ABAC 与“RBAC + 数据域”
| 模型 | 核心思路 | 在资产场景的问题 |
|---|---|---|
| ACL | 资源上直接挂用户与操作 | 10 万条资产 × 500 人,授权记录爆炸,组织调整无法批量迁移 |
| RBAC | 用户 → 角色 → 功能点 | 只解决“能不能点按钮”,解决不了“能看哪些行”,会出现北京管理员改上海资产 |
| ABAC | 按属性动态求值 | 表达力最强,但规则难以下推到 SQL,列表分页与导出性能崩塌,业务管理员看不懂 |
| RBAC + 数据域(推荐) | 角色管功能点,数据域(Scope)管可见行范围,少量 ABAC 规则做兜底 | 配置可解释、可下推为 SQL 条件、组织调整自动生效,是主流资产系统的落地选择 |
一句话选型建议:功能权限用 RBAC,数据权限用“数据域 + 组织树闭包”,特殊场景(如密级资产、租赁资产)用少量 ABAC 规则收口。 不要一上来就上通用策略引擎,规则可解释性对甲方管理员是硬需求。
三、数据模型设计:组织树、资产归属与权限表
3.1 组织树:物化路径 + 闭包表双写
纯邻接表(parent_id)查子孙要递归,纯闭包表写入成本高。生产环境推荐 邻接表存结构 + 物化路径做范围查询 + 闭包表做复杂授权判定。
-- 组织节点:同一张表承载法人树、管理树、位置树,用 tree_type 区分
CREATE TABLE org_node (
id BIGINT PRIMARY KEY,
tenant_id BIGINT NOT NULL,
tree_type TINYINT NOT NULL COMMENT '1法人 2管理 3位置',
parent_id BIGINT NULL,
code VARCHAR(64) NOT NULL,
name VARCHAR(128) NOT NULL,
-- 物化路径:'/1/17/233/' 形式,前缀 LIKE 即可取整棵子树
path VARCHAR(512) NOT NULL,
depth SMALLINT NOT NULL,
status TINYINT NOT NULL DEFAULT 1,
KEY idx_tenant_tree_path (tenant_id, tree_type, path),
UNIQUE KEY uk_tenant_code (tenant_id, tree_type, code)
);
-- 闭包表:祖先-后代全展开,用于权限求交与“是否在管辖范围内”O(1) 判定
CREATE TABLE org_closure (
ancestor_id BIGINT NOT NULL,
descendant_id BIGINT NOT NULL,
distance SMALLINT NOT NULL,
PRIMARY KEY (ancestor_id, descendant_id),
KEY idx_desc (descendant_id)
);
3.2 资产表:一条资产要挂四个“锚点”
资产的可见性由多个冗余字段共同决定,这些冗余是刻意设计的——为了让权限条件能直接下推到索引,而不是靠 JOIN 三张表。
CREATE TABLE asset (
id BIGINT PRIMARY KEY,
tenant_id BIGINT NOT NULL,
asset_no VARCHAR(64) NOT NULL COMMENT '资产编码',
category_id BIGINT NOT NULL,
-- ① 权属锚点:法人主体,决定账套与折旧归属
legal_org_id BIGINT NOT NULL,
-- ② 管理锚点:使用部门,决定“本部门及以下”可见性
manage_org_id BIGINT NOT NULL,
manage_path VARCHAR(512) NOT NULL COMMENT '冗余部门物化路径,供前缀匹配',
-- ③ 位置锚点:决定区域/园区管理员可见性
location_id BIGINT NULL,
location_path VARCHAR(512) NULL,
-- ④ 责任锚点:使用人与保管人
user_id BIGINT NULL,
keeper_id BIGINT NULL,
security_level TINYINT NOT NULL DEFAULT 0 COMMENT '密级:0普通 1受控 2保密',
status TINYINT NOT NULL,
KEY idx_scope (tenant_id, manage_path, status),
KEY idx_loc (tenant_id, location_path),
KEY idx_user (tenant_id, user_id)
);
经验提示: 资产调拨、部门合并、组织改名都会导致
manage_path失效。务必把“组织树变更 → 资产路径重刷”做成幂等的异步任务,并保留变更前后的快照,否则历史盘点单据的权限归属会漂移。
3.3 权限侧三张表
CREATE TABLE role (
id BIGINT PRIMARY KEY, tenant_id BIGINT, name VARCHAR(64),
-- 数据域类型:1本人 2本部门 3本部门及下级 4自定义组织 5全部
scope_type TINYINT NOT NULL DEFAULT 3
);
CREATE TABLE role_permission ( -- 功能点:asset:transfer / asset:scrap ...
role_id BIGINT, perm_code VARCHAR(64), PRIMARY KEY (role_id, perm_code)
);
CREATE TABLE user_role_grant ( -- 同一用户可在不同组织上持有不同角色
user_id BIGINT, role_id BIGINT, org_id BIGINT,
tree_type TINYINT, PRIMARY KEY (user_id, role_id, org_id)
);
user_role_grant 上的 org_id 是整个模型的关键:它让“张三是华东大区的资产管理员,同时是研发中心的普通使用人”这种真实情况可以被表达,而不需要新建一个“华东资产管理员”角色。角色是能力模板,授权点才是范围。
四、数据权限范围的五种粒度与查询下推
| 数据域 | 典型角色 | 下推的 SQL 片段 |
|---|---|---|
| SELF 本人 | 普通员工 | user_id = :uid |
| DEPT 本部门 | 部门资产员 | manage_org_id = :orgId |
| DEPT_TREE 本部门及下级 | 事业部管理员 | manage_path LIKE '/1/17/%' |
| CUSTOM 自定义 | 区域 IT 管理员 | manage_path LIKE ANY(...) OR location_path LIKE ANY(...) |
| ALL 全部 | 集团资产总账 | 1 = 1(仅限 tenant_id 内) |
实现上最忌讳“先全量查出来再在内存里过滤”。正确做法是把数据域编译成 SQL 条件,在拦截器层统一注入:
/** 把用户在某功能点上的全部授权,编译成一段可下推的 WHERE 条件 */
public ScopeSql compile(Long uid, String permCode) {
List<Grant> grants = grantRepo.findEffective(uid, permCode); // 缓存命中
if (grants.isEmpty()) return ScopeSql.DENY_ALL; // 显式拒绝,不是放行
if (grants.stream().anyMatch(g -> g.scope() == ALL))
return ScopeSql.of("a.tenant_id = ?", tenantOf(uid));
// 多个授权之间是 OR(并集),同一授权内部维度是 AND(交集)
List<String> ors = new ArrayList<>();
List<Object> args = new ArrayList<>();
for (Grant g : grants) {
switch (g.scope()) {
case SELF -> { ors.add("a.user_id = ?"); args.add(uid); }
case DEPT -> { ors.add("a.manage_org_id = ?"); args.add(g.orgId()); }
case DEPT_TREE -> { ors.add("a.manage_path LIKE ?"); args.add(pathOf(g.orgId()) + "%"); }
case CUSTOM -> { ors.add(customCond(g, args)); }
}
}
// 密级作为 ABAC 兜底规则,永远 AND 在最外层
return ScopeSql.of("a.tenant_id = ? AND (" + String.join(" OR ", ors)
+ ") AND a.security_level <= ?", args, maxLevelOf(uid));
}
三个必须写进规范的细节:① 无授权时返回 DENY_ALL 而非不加条件(这是最常见的越权漏洞来源);② 多授权取并集、单授权内多维度取交集;③ 密级、租赁状态等敏感属性永远 AND 在最外层,不参与 OR 抵消。
五、四层鉴权链路:功能、数据、字段、流程
1. 功能权限(能不能点)
接口级注解 + 前端按钮级控制。注意前端隐藏按钮不等于鉴权,服务端必须重校验。
2. 数据权限(能看哪些行)
列表、详情、导出、统计报表四个入口必须走同一套条件编译器。绕过率最高的是导出和 BI 取数接口。
3. 字段权限(能看哪些列)
采购价、残值、供应商合同价通常只对财务开放。用序列化层的字段脱敏切面实现,避免每个 DTO 手写判断。
4. 流程权限(能不能审)
审批节点的候选人要由“资产所属组织”动态解析,而不是配死人名。人一离职流程就断,是集团客户的高频投诉点。
六、跨组织资产调拨:权限最容易崩的地方
调拨是唯一一个“操作发起时资产不在你范围内、操作完成后资产不在对方范围内”的双向场景,必须单独建模:
- 调出侧校验: 发起人对源资产需同时满足
asset:transfer功能权限 + 源资产落在其数据域内。 - 目标侧校验: 目标组织必须在发起人“可调入范围”内,或走跨主体审批。跨法人主体的调拨等于资产处置,必须触发财务审批与账务凭证,不能只改个 org_id。
- 在途状态: 单据审批中资产处于 IN_TRANSIT,此时双方均可只读可见,双方均不可再次调拨——用状态机而不是布尔锁。
- 路径重写: 生效瞬间同步更新 manage_org_id / manage_path / location_path,并写入资产变动流水,保证审计可回溯。
public void submitTransfer(Long uid, TransferCmd cmd) {
authz.requirePerm(uid, "asset:transfer");
// 逐条校验源资产是否落在发起人数据域内(批量鉴权,一次 SQL)
Set<Long> allowed = assetRepo.filterInScope(cmd.assetIds(), authz.compile(uid, "asset:transfer"));
if (allowed.size() != cmd.assetIds().size()) throw new ForbiddenException("存在越权资产");
boolean crossLegal = orgSvc.legalOf(cmd.targetOrgId()) != orgSvc.legalOf(cmd.sourceOrgId());
Flow flow = crossLegal ? Flow.CROSS_LEGAL_TRANSFER : Flow.INNER_TRANSFER; // 不同审批链
assetSvc.markInTransit(cmd.assetIds());
flowSvc.start(flow, cmd, resolveApprovers(cmd.targetOrgId())); // 按组织动态解析审批人
}
七、性能优化:路径索引、权限缓存与批量鉴权
- 路径前缀匹配而非递归查询。
LIKE '/1/17/%'可命中 B+ 树索引;递归 CTE 在 10 万级组织下会成为慢查询首位。 - 权限结果二级缓存。 按
uid:permCode缓存编译后的条件,本地 Caffeine + Redis 双层,组织/授权变更时按 tenant 维度精准失效。 - 批量鉴权代替 N 次单条鉴权。 盘点、批量调拨、批量报废场景下,把 ID 列表一次性带入 scope 条件做
IN + WHERE scope求交,避免 N+1。 - 自定义域的 OR 条件设上限。 管理员勾选 300 个部门会生成 300 个 LIKE。超过阈值时改为“临时权限组织表 JOIN”,把 OR 变成半连接。
- 报表走预聚合。 按
(法人, 部门, 类别, 月份)预聚合宽表,权限仍按路径前缀过滤,集团级看板才能秒开。
八、10 条避坑清单(血泪版)
- 无授权时默认放行 —— 最严重的越权,必须默认拒绝。
- 导出接口、BI 取数接口忘记注入数据权限。
- 用“部门名称”而非“组织 ID + 路径”做权限判断,改名即失效。
- 组织合并/撤销后,资产
manage_path未重刷,出现“幽灵资产”谁都看不到。 - 把管理树和法人树混为一棵树,事业部横跨主体时无法建模。
- 审批人写死人名,员工离职后流程永久挂起。
- 字段权限在每个接口手写 if,漏一个就泄露采购价。
- 调拨只改归属不留流水,审计与资产盘盈盘亏无法追溯。
- 用一个“超级管理员”绕过所有校验做数据修复,且无操作日志。
- 多租户
tenant_id依赖业务代码自觉传,未在框架层强制隔离。
九、易点易动固定资产管理系统是怎么落地的
上面这套模型不是纸上推演。易点易动固定资产管理系统在集团型客户(多子公司、多园区、跨区域)的实施中,已经把这些设计沉淀为开箱可用的配置能力——业务管理员在后台点几下就能完成,不需要开发介入。
多组织多主体架构
法人树、管理树、位置树独立维护并可交叉授权,支持集团—子公司—分公司—部门多级,账套与折旧按主体分账。
可视化数据权限配置
本人 / 本部门 / 本部门及下级 / 自定义组织 / 全部五档数据域,勾选即生效,组织调整后权限自动跟随。
字段级脱敏
采购价、净值、合同信息可按角色隐藏,财务与行政看到同一张列表、不同的列。
跨组织调拨与审批流
内部调拨与跨主体调拨走不同审批链,审批人按资产所属组织动态解析,全程留痕可追溯。
扫码盘点按权限下发
盘点任务按组织/位置范围自动拆分到人,员工手机端只看到自己该盘的资产,避免越权与重复盘。
全链路审计日志
领用、归还、调拨、维修、报废、折旧全部生成资产变动流水,支持按资产 / 按人 / 按组织三种视角回溯。
同时打通OA组织架构,员工与部门变动自动同步——组织同步做不好,权限模型再漂亮也是空转,这也是自研团队最容易低估的工作量。
与其从零造权限引擎,不如先看看成熟方案
多组织权限模型从设计到稳定,通常要经历 3—6 个月的踩坑期,还不含组织同步、审批流、盘点端的联调。易点易动固定资产管理系统已在制造、连锁、医疗、互联网等行业的集团客户中落地,支持标准化上线与按需定制。
开箱即用的多组织权限配置 扫码盘点 · RFID · 移动端 折旧与财务对账 支持私有化部署
十、总结与常见问题答疑
把整篇文章压缩成一句可执行的结论:
用 RBAC 管功能点,用 数据域 + 组织物化路径管可见范围,用 “用户—角色—组织”三元授权表达真实岗位,用统一条件编译器覆盖列表/详情/导出/报表四个出口,默认拒绝、敏感属性最外层 AND。
Q1:固定资产管理系统的多组织权限,应该自研还是采购?
如果组织结构稳定、资产量小于 2000 条,自研可控;一旦涉及多法人主体、跨区域、多套折旧政策,采购成熟系统的总成本更低——权限只是冰山一角,折旧、对账、盘点、移动端的工作量更大。
Q2:组织架构频繁调整,权限会不会每次都要重配?
不会。授权绑定的是组织节点而非部门名称,数据域用“本部门及下级”表达,新增下级部门自动纳入范围;只有部门被撤销合并时需要触发资产路径重刷任务。
Q3:一个人管多个区域、多个部门,要建多少角色?
一个即可。角色是能力模板,把区域差异放在“授权组织”上,用同一个“资产管理员”角色在多个组织上授权,避免角色数量随组织数爆炸。
Q4:跨子公司调拨资产,财务上要注意什么?
跨法人主体的资产转移在会计上属于处置与购入,需生成对应凭证并调整折旧起始,不能仅在系统内改归属部门。系统层面必须把“跨主体”识别为独立单据类型。
Q5:怎么验证权限模型没有漏洞?
建一套“权限矩阵回归用例”:N 个典型角色 × M 个入口(列表/详情/导出/报表/移动端/审批),每次发版自动跑一遍断言可见条数,比人工点测可靠得多。

1015

被折叠的 条评论
为什么被折叠?



