HTML5技术

领域驱动设计实战—基于DDDLite的权限管理 - 李玉宝

字号+ 作者:H5之家 来源:博客园 2015-12-03 08:53 我要评论( )

在园子里面,搜索一下权限管理至少能得到上千条的有效记录。记得刚开始工作的时候,写个通用的权限系统一直是自己的一个梦想。中间因为工作忙(其实就是懒!)等原因,被无限期搁置了。最近想想,自己写东西时,很多都是偏理论方面的,常常找不到合适的例子

        在园子里面,搜索一下“权限管理”至少能得到上千条的有效记录。记得刚开始工作的时候,写个通用的权限系统一直是自己的一个梦想。中间因为工作忙(其实就是懒!)等原因,被无限期搁置了。最近想想,自己写东西时,很多都是偏理论方面的,常常找不到合适的例子来论证自己的观点。于是用业余时间来写点东西。

园子中的权限管理系统有以下几种:

什么也不说了,开干!文字太多了,来个动态图缓一缓:

screen

需求

        首先,做个东西必须要把需求搞清楚。园子里面的权限管理需求分析的比较合理的,应该是萧秦的我的权限系统设计实现MVC4 + WebAPI + EasyUI + Knockout(一) ,具体总结如下:

1、权限资源
    a.菜单权限  经理和业务员登陆系统拥有的功能菜单是不一样的
    b.按钮权限  经理能够审批,而业务员不可以
    c.数据权限  A业务员看不到B业务员的单据
    d.字段权限  某些人查询客户信息时看不到客户的手机号或其它字段

2、用户,应用系统的具体操作者,我这里设计用户是不能直接分配权限的,必须要分配一个角色,角色中再分配权限,如果某个用户权限比较特殊,可以为他专门建一个角色来应用解决,因为如果用户也可以分配权限系统就会复杂很多。【我采用的还是可以直接给用户分配菜单/按钮,毕竟我们的人员就喜欢搞些特殊待遇】

3、角色,为了对许多拥有相似权限的用户进行分类管理,定义了角色的概念,以上所有的权限资源都可以分配给角色,角色和用户N:N的关系。

4、机构,树形的公司部门结构,国内公司用的比较多,它实际上就是一个用户组,机构和用户设计成N:N的关系,也就是说有时候一个用户可以从属于两个部门,这种情况在我们客户需求中的确都出现过。

设计

        本来想用DDD(也就是把CQRS/AES等一堆的东西全用上,如果你想学习完整的DDD框架,可以参考我的另一个项目BestQ&A --开源中国推荐项目/集CQRS AES等DDD高级特性于一体的问答系统)实现这个项目,思考再三还是被自己否定了。毕竟自己也在学习真正的领域驱动设计,思想上不是很成熟。再者,我相信对于普通的经典DDD架构(好高大上的说,悄悄地告诉你其实就是分层分的格调不一样!),我是有绝对的信心可以把控的。

与其他权限管理相同的地方

        使用了万恶的EF+MVC结构,当然,我没恶俗到用EasyUI,为了体现个性,选择了酷炫的基于bootstrap的B-JUI前端(炫不炫,你说了算)。相同的东西总是无趣,你可以无视,请把注意力放在下面。

与其他权限管理不同的地方

1、项目采用经典DDD架构(用沃恩.弗农大神的话,其实这是DDD-Lite)思想进行开发,简洁而不简单,实用至上,并且所写每一行代码都经过深思熟虑,采用Autofac对项目进行解耦,符合S.O.L.I.D规则!来秀一下内在美:

using OpenAuth.Domain; using OpenAuth.Domain.Interface; using System; using System.Collections.Generic; using System.Linq; namespace OpenAuth.App { public class OrgManagerApp { private IOrgRepository _repository; public OrgManagerApp(IOrgRepository repository) { _repository = repository; } public IList<Org> GetAll() { return _repository.LoadOrgs().ToList(); } /// <summary> /// 部门的直接子部门 /// <para>TODO:可以根据用户的喜好决定选择LoadAllChildren或LoadDirectChildren</para> /// </summary> public IList<Org> LoadDirectChildren(int orgId) { return _repository.Find(u => u.ParentId == orgId).ToList(); } /// <summary> /// 得到部门的所有子部门 /// <para>如果orgId为0,表示取得所有部门</para> /// </summary> public IList<Org> LoadAllChildren(int orgId) { string cascadeId = "0."; if (orgId != 0) { var org = _repository.FindSingle(u => u.Id == orgId); if (org == null) throw new Exception("未能找到指定对象信息"); cascadeId = org.CascadeId; } return _repository.Find(u => u.CascadeId.Contains(cascadeId) && u.Id != orgId).ToList(); } /// <summary> /// 添加部门 /// </summary> public int AddOrUpdate(Org org) { if (org.Id == 0) { ChangeModuleCascade(org); _repository.Add(org); } else { _repository.Update(org); } return org.Id; } /// <summary> /// 删除指定ID的部门及其所有子部门 /// </summary> public void DelOrg(int id) { var delOrg = _repository.FindSingle(u => u.Id == id); if (delOrg == null) return; _repository.Delete(u => u.CascadeId.Contains(delOrg.CascadeId)); } #region 私有方法 //修改对象的级联ID,生成类似XXX.XXX.X.XX private void ChangeModuleCascade(Org org) { string cascadeId; int currentCascadeId = 1; //当前结点的级联节点最后一位 var sameLevels = _repository.Find(o => o.ParentId == org.ParentId && o.Id != org.Id); foreach (var obj in sameLevels) { int objCascadeId = int.Parse(obj.CascadeId.Split('.').Last()); if (currentCascadeId <= objCascadeId) currentCascadeId = objCascadeId + 1; } if (org.ParentId != 0) { var parentOrg = _repository.FindSingle(o => o.Id == org.ParentId); if (parentOrg != null) { cascadeId = parentOrg.CascadeId + "." + currentCascadeId; org.ParentName = parentOrg.Name; } else { throw new Exception("未能找到该组织的父节点信息"); } } else { cascadeId = "0." + currentCascadeId; org.ParentName = "根节点"; } org.CascadeId = cascadeId; } #endregion 私有方法 } }

 

1.本站遵循行业规范,任何转载的稿件都会明确标注作者和来源;2.本站的原创文章,请转载时务必注明文章作者和来源,不尊重原创的行为我们将追究责任;3.作者投稿可能会经我们编辑修改或补充。

相关文章
  • 设计模式(1)单例模式(Singleton) - Fonour

    设计模式(1)单例模式(Singleton) - Fonour

    2017-04-23 12:00

  • 架构设计师能力模型 - BloodyAngel

    架构设计师能力模型 - BloodyAngel

    2017-03-28 11:00

  • 设计模式(0)简单工厂模式 - Fonour

    设计模式(0)简单工厂模式 - Fonour

    2017-03-25 15:01

  • 没有功能需求设计文档?对不起,拒绝开发! - CharlieChu

    没有功能需求设计文档?对不起,拒绝开发! - CharlieChu

    2017-03-16 13:04

网友点评
l