在企业管理系统中,常常有这样的要求:
1. 用户一般只能查看自己部门的数据
2. 可以设置用户可以查看哪些部门的数据
这种权限的控制,一般称为数据权限,与之对应的功能权限,则是系统中哪些功能可以使用——①菜单、按钮等元素能正常显示;②如果用户访问了本身不可见的功能,系统也能阻止(访问控制)。
开发时间长了,就发现编程一般就是两个问题:
1. 在哪里设置(数据从哪里来)
2. 在哪里使用(数据到哪里去)
比如工作流引擎,设置,即在界面上拖画流程图,并保存为特定的xml数据;使用,即流程引擎解读xml数据,使其正确的按照用户的意图执行程序。
数据权限的问题,同样可以分成这两个问题来处理。
设置的问题:
在用户设置界面,设置用户的管辖权限
做到这样的设置界面,并不困难。
在这里,主要要解决的一个问题是,如何把这种关系存储下来
常规方案:使用关联表[用户ID,组织ID]
可是发现,一旦组织机构数量多了以后(如省市县乡,一般一个省就有1000个乡/街道以上),再加上用户数量多了以后——往往是组织机构节点越多,则用户也越多。理论上,这个关联表的数据量将到达千万级(仅理论上,因为对于绝大多数用户来说,仅能访问本部门数据)。
创新方案:组织机构使用code做主键,并且在用户表中新建字段为“已授权的组织(authorized_orgs)”,直接使用字符串逗号分隔的方式将该关系存起来。
可是马上会发现,这个字段会很长很长,一个4级的组织,一般需要8位长的代码,如果选中的全部数据,按1000个单位来算,就会有9000长度(含逗号)的字符串。
这里有一个取巧的办法,对于全选了数据,只保存上级单位的code,下级单位就不保存了——因为选中了上级就必然下级全部选上了。
该办法要求上下级代码存在一种明显的关系,如上级代码为 0100,下级代码0101。
这个取巧的办法,理论上依然不能规避很长的组织机构代码(如选的全部是叶子节点数据),但在实践中,却是很好的办法——应该很少出现全部只选叶子节点,而不选父级节点的。
使用zTree(js的树)的代码,可以这样写
if(zTree==null){ return; } var nodes = zTree.getNodes(); var arr = []; for(var i=0; i<nodes.length; i++){ pushToArray(arr, nodes[i]); } console.log(arr.join(",")); obj.authorizedOrgs = arr.join(","); function pushToArray(arr, node){ if(!node.checked){ return; } if(node.check_Child_State==2||node.check_Child_State==-1){//是节点完全选上(即不是半选状态)或叶子节点,直接添加 arr.push(node.code); }else if(node.check_Child_State==1){//节点半选状态,递归往下找完全选中状态的 for(var i=0; i<node.children.length; i++){ pushToArray(arr, node.children[i]); } } }
-----分割线:以上解决怎么设置的问题,以下讨论怎么解决使用的问题--------
数据权限控制的基本思路,一般是会执行的SQL语句中添加where条件,以便限定查出的数据,
如 where 所属机构 in (用户可访问的机构)
而不是在数据查询出来之后,再到代码中进行过滤——因为一般都会对数据进行高效分页,如果已经查询出来数据,再在代码中进行过滤的话,就可能出现一页数据不足一页的情况,数据总数也会与实际页面上查出的数据行不一致。
第一步,在需要进行过滤的表中,要添加一个字段,如org_code,标明每条记录所属的组织机构。
第二步,在查询语句中加入过滤条件。
我们要加入的SQL语句,大概如下:
where 所属机构 in ('ZZ0101', 'ZZ0102',...)
假如我们前面存的代码是'ZZ0100,ZZ0201',其中存的是其父级节点'ZZ0100',代表了 'ZZ0101', 'ZZ0102', ...
所以要写成
where ((所属机构 like 'ZZ01%') or (所属机构='ZZ0201'))
拼凑这样的SQL语句估计也是比较麻烦的一件事,有没有简便一点的方法呢?答案是有。
经查,oracle,sqlserver,mysql都是支持正则式查询的。
这样,我们可以把'ZZ0100,ZZ0201'变成一个正则式,放入条件中进行查询即可!
变成正则式就是 '(^ZZ01.*)|(^ZZ0201)',在mysql使用正则式查询,就是 where 所属机构 RLIKE '(^ZZ01.*)|(^ZZ0201)'
变成正则式之后,也便于放入session中进行存储。
什么,你的数据库支持正则式,orm不支持正则式,那还用orm做什么?