第四章 功能初始化

源代码GitHub:https://github.com/ZhaoRd/Zrd_0001_AuthorityManagement

1.介绍

         对于权限管理系统来说,系统模块是必须的一部分,那么如何处理和收集模块信息是一个管家步骤,在没有看郭民峰的osharp之前,我能想到的都是通过管理员通过后台管理进行管理,osharp里采用attirbute的方式采集模块信息。该demo借鉴osharp的方式,通过使用attribute定义模块信息,程序启动时通过判断attribute进行模块和具体功能权限的信息收集。

        对于新采集的信息,如何判定这些新信息哪些是需要更新的,哪些是需要从已有信息中删除的,哪些又是需要新增的呢?这里需要使用到集合运算,具体请看以下内容。

 

2.模块信息的定义

      在web中,操作一个功能的访问路径是 controller/action  ,在本demo中,一个controller就是一个功能,一个action就是一个具体的操作权限。在源代码中,定义SystemModelAttribute来定义功能信息,代码如下:

    /// <summary>
    /// 收集系统模块.
    /// </summary>
    public class SystemModelAttribute : Attribute
    {
        /// <summary>
        /// 模块名称.
        /// </summary>
        private string name;
        /// <summary>
        /// 分组名称.
        /// </summary>
        private string groupName;
        /// <summary>
        /// 只读模块名称.
        /// </summary>
        public string Name {
            get
            {
                return this.name;
            }
        }
        /// <summary>
        /// 分组名称.
        /// </summary>
        public string GroupName {
            get
            {
                return this.groupName;
            }
            set
            {
                // 如果是通过构造函数初始化的名称,则其他设置均无效
                if (string.IsNullOrEmpty(this.groupName))
                {
                    this.groupName = value;    
                }
            }
        }
        /// <summary>
        /// 模块的权限.
        /// </summary>
        private PermissionValue permissionValue;
        /// <summary>
        /// 模块的权限.
        /// </summary>
        public PermissionValue PermissionValue {
            get
            {
                return this.permissionValue;
            }
        }
        /// <summary>
        /// Initializes a new instance of the <see cref="SystemModelAttribute"/> class.
        /// </summary>
        /// <param name="name">
        /// 模块名称.
        /// </param>
        /// <param name="permissionValue">
        /// 模块权限值.
        /// </param>
        /// <param name="groupName">
        /// 分组名称.
        /// </param>
        public SystemModelAttribute(string name, PermissionValue permissionValue = PermissionValue.All, string groupName = null)
        {
            this.name = name;
            this.groupName = groupName;
            this.permissionValue = permissionValue;
        }
    }  

  

在controller上使用该attribute,即可定义一个模块,使用方式如下图

6afc5dd7-8d80-4e4d-ae25-38aaec33ac2a

     SystemModelAttribute可以定义需要采集的模块信息,同理,可以在定义一个attribute,来定义需要采集的权限信息,源代码如下

/// <summary>
    /// 权限信息设置,以便信息收集.
    /// </summary>
    public class PermissionSettingAttribute : Attribute
    {
        /// <summary>
        /// 具体权限.
        /// </summary>
        private readonly PermissionValue permissionValue;
        /// <summary>
        /// Initializes a new instance of the <see cref="PermissionSettingAttribute"/> class.
        /// </summary>
        /// <param name="value">
        /// The value.
        /// </param>
        public PermissionSettingAttribute(PermissionValue value)
        {
            this.permissionValue = value;
        }
        /// <summary>
        /// Gets the permission value.
        /// </summary>
        public PermissionValue PermissionValue
        {
            get
            {
                return this.permissionValue;
            }
        }
    }

  

使用方式如下:

5a5066d9-335d-497c-8240-b8566f522ae2

通过使用SystemModelAttribute和PermissionSettingAttribute来定义需要采集信息的内容.

 

3.信息采集

         我们在Controller和Action上分别使用了不同的Attribute来定义信息,如何在代码中收集这些信息呢?主要代码如下

1487c62d-78cd-4753-b05c-22371676db65

首先是获取到Controller所在的程序集,然后根据获得的类型处理Type,具体的Invoke方法如下

  /// <summary>
        /// 解析Controller类型来收集Attribute信息.
        /// </summary>
        /// <param name="target">
        /// The target.
        /// </param>
        /// <returns>
        /// The <see cref="IEnumerable"/>.
        /// </returns>
        public IEnumerable<FunctionDto> Invoke(Type target)
        {
            var result = new List<FunctionDto>();
            var targetType = target;
            // 是否使用SystemModelAttribute
            if (!targetType.IsDefined(typeof(SystemModelAttribute), false))
            {
                return result;
            }
            // 获取所有Controller里的方法
            var methods = targetType.GetMethods();
            // 获取功能模块的具体权限,必须是使用了PermissionSettingAttribute定义权限值,并取所有功能的并运算
            // 例如:一个Controller里只定义了Create和Edit,那么这个功能模块的权限就是 Create|Edit
            var functionPermissionValue = 
                (from methodInfo in methods 
                 where methodInfo.IsDefined(typeof(PermissionSettingAttribute), false)
                 select methodInfo.GetCustomAttribute<PermissionSettingAttribute>()).Aggregate(
                     PermissionValue.None,
                     (current, permissionSetting) => current | permissionSetting.PermissionValue);
            // 获取SystemModelAttribute具体的信息
            var description = targetType.GetCustomAttribute<SystemModelAttribute>();
            var areaName = this.GetArea(target);
            var function = new FunctionDto()
                               {
                                   FunctionName = description.Name,
                                   ModelName = areaName,
                                   PermissionValue = functionPermissionValue
                               };
            result.Add(function);
            return result;
        }

  

通过这样的一个函数,就可以采集到一个Controller里的权限信息,一个功能就是一个Controller,Controller里使用PermissionSettingAttribute定义的具体操作权限,所有定义的权限值取并运算,

11db1eb7-7385-4e6c-8153-030138182abef0067c64-76b8-4641-afd9-8cf4f07374ab

如图所示,一个Controller里包含Create和Edit,那么就说明这个功能模块的最大权限是Create|Edit,这样,在进行权限分配的时候,只能对该功能所具有的权限进行分配,未具有的权限不能分配.

 

4. 信息处理

           经过上面的处理,那么功能信息和权限信息都已经采集到了,将这些信息保存到数据库,即可快捷方便的初始化系统的功能信息。

       在信息处理方面,主要使用了集合运算,为了对集合运算进行说明,我把已存在数据库里的功能信息集合定义为 A集合,新采集的集合定义为 B集合,那么对于B集合而言,怎么判断哪些是需要新添加到数据库,哪些是需要更新到数据库的,已存在数据库里的功能,哪些又是需要删除的呢?

       a.对于需要添加的功能,可以理解为存在B集合但是不存在A集合

       b.对于需要更新的功能,可以理解为即存在B集合又存在A集合

       c.对于需要删除的功能,可以理解为存在A集合中但是不存在B集合中

  从集合的角度来处理a  b  c三种情况,那么

       a.   B集合 减 A集合

       b.   B集合 交 A集合

       c.    A集合 减 B集合

   通过集合运算,就可以得到需要进行添加、更新、删除的功能信息了

  代码如下:

    /// <summary>
        /// 初始化系统功能.
        /// </summary>
        /// <param name="functionDtos">
        /// The function dtos.
        /// </param>
        public void InitModel(IEnumerable<FunctionDto> functionDtos)
        {
            var functions = this.functionRepository.FindAll().ToList();
            var addFunctions = functionDtos.Select(Mapper.Map<FunctionDto, Function>)
                .AsEnumerable();
            // 创建如何判断两个function是否相等的条件
            var functionComparer = EqualityHelper<Function>.CreateComparer(m => m.ModelName + "#" + m.FunctionName);
            var enumerable = addFunctions as Function[] ?? addFunctions.ToArray();
            // 包含在将要处理的集合(addFunctions)
            // 但不包含在已经存在的集合(functions)
            // 表示需要添加到系统里的模块
            // 差集运算
            var toAddFunctions = enumerable.Except(functions, functionComparer);
            // 包含在已经存在的集合(functions)
            // 但不包含在将要处理的集合(addFunctions)
            // 表示需要从系统中删除的模块
            // 差集运算
            var toDeleteFunctions = functions.Except(enumerable, functionComparer);
            // 即包含在将要处理的集合(addFunctions)
            // 又包含在已经存在的集合(functions)
            // 表示需要更新内容
            // 交集运算
            var toUpdateFunctions = functions.Intersect(enumerable, functionComparer);
            LogHelper.Logger.Info(
                string.Format(
                    "新增功能:{0}条;更新功能:{1}条;删除功能:{2};",
                    toAddFunctions.Count(),
                    toUpdateFunctions.Count(),
                    toDeleteFunctions.Count()));
            foreach (var addFunction in toAddFunctions)
            {
                addFunction.ID = Guid.NewGuid();
                var role = this.roleRepository.Find(
                    Specification<Role>.Eval(u => u.RoleName == "系统管理员"));
                this.functionRepository.Add(addFunction);
                // 初始化系统管理员的权限
                this.functionInRoleRepository.Add(new FunctionInRole()
                {
                    ID = GuidHelper.GenerateGuid(),
                    Function = addFunction,
                    PermissionValue = addFunction.PermissionValue,
                    Role = role
                });
            }
            foreach (var deleteFunction in toDeleteFunctions)
            {
                this.functionRepository.Remove(deleteFunction);
            }
            foreach (var updateFunction in toUpdateFunctions)
            {
                var function = updateFunction;
                var query = enumerable.Where(m => m.FunctionName == function.FunctionName);
                var newValue = string.IsNullOrEmpty(updateFunction.ModelName) ? query.SingleOrDefault(u => u.ModelName == null) : query.SingleOrDefault(u => u.ModelName == function.ModelName);
                if (newValue == null)
                {
                    continue;
                }
                updateFunction.FunctionName = newValue.FunctionName;
                updateFunction.ActionName = newValue.ActionName;
                updateFunction.AreasName = newValue.AreasName;
                updateFunction.ControllerName = newValue.ControllerName;
                updateFunction.Description = newValue.Description;
                updateFunction.ModelName = newValue.ModelName;
                updateFunction.PermissionValue = newValue.PermissionValue;
                this.functionRepository.Update(updateFunction);
            }
            this.functionInRoleRepository.Context.Commit();
        }

  

5. 总结

          信息的定义和收集,主要是使用Attribute,该demo提供了一种使用Attribute的使用方式和方法。

          该篇还提供了一个使用集合进行处理数据的方式,扩展了我再处理集合时的一些思路,以前总是通过for 循环来处理两个集合,那么通过集合运算,可以很方便的得到集合信息。

 

推荐QQ群:

278252889(AngularJS中文社区)

5008599(MVC EF交流群)

134710707(ABP架构设计交流群 )

59557329(c#基地 )

230516560(.NET DDD基地 )

本人联系方式:QQ:351157970

posted @ 2015-10-09 22:01  诸葛小亮  阅读(845)  评论(2编辑  收藏  举报