NewLife.XCode是一个有10多年历史的开源数据中间件,支持nfx/netcore,由新生命团队(2002~2019)开发完成并维护至今,以下简称XCode。
整个系列教程会大量结合示例代码和运行日志来进行深入分析,蕴含多年开发经验于其中,代表作有百亿级大数据实时计算项目。
开源地址:https://github.com/NewLifeX/X (求star, 864+)
前面讲解了XCode的各种用法,这一章我们来讲讲内置的Membership,同时也是XCode的第一标准示例!
设计背景
现代管理信息系统绝大部分采用BS架构,无一例外需要用户角色权限的支持!
结合团队诸多兄弟姐妹的经验,设计了一个大小适中的用户权限系统Membership,目标是满足80%的使用场景,并具备一定的扩展性。
Membership刚开始就采用了角色授权体系,每个用户只有一种角色,角色拥有菜单资源权限集。
随着Membership实用性日益增加,2015年初正式合并进入XCode,作为一个模块存在。
2016年第二代魔方NewLife.Cube采用ASP.Net MVC5重构,让Membership的荣誉达到了鼎峰!
在MVC中,每个Controller就是一个菜单资源,其下的Search/Detail/Insert/Update/Delete等Action作为角色在该菜单资源下的权限子项,保存在角色属性数据中。
2018年为了增强魔方功能,在某些场景下支持单用户多角色,且兼容已有系统,用户表增加RoleIDs字段,保存扩展角色,原来的RoleID作为主角色。
管理提供者
管理提供者接口 IManageProvider ,提供了Membership基本操作实现。
- 当前登录用户 GetCurrent、SetCurrent,静态访问 ManageProvider.User
- 查找用户 FindByID、FindByName
- 注册登录注销 Register、Login、Logout
- 当前用户主机(访问者IP)ManageProvider.UserHost
- IManageProvider 默认由XCode.Membership中的UserX/Role/Menu支持,如若用户使用自己的用户权限表,可重新实现该接口
用户权限
用户 UserX
用户数据模型:
<Table Name="User" Description="用户" RenderGenEntity="true"> <Columns> <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" /> <Column Name="Name" DataType="String" Master="True" Nullable="False" Description="名称。登录用户名" /> <Column Name="Password" DataType="String" Description="密码" /> <Column Name="DisplayName" DataType="String" Description="昵称" /> <Column Name="Sex" DataType="Int32" Description="性别。未知、男、女" Type="SexKinds" /> <Column Name="Mail" DataType="String" Description="邮件" /> <Column Name="Mobile" DataType="String" Description="手机" /> <Column Name="Code" DataType="String" Description="代码。身份证、员工编号等" /> <Column Name="Avatar" DataType="String" Length="200" Description="头像" /> <Column Name="RoleID" DataType="Int32" Description="角色。主要角色" /> <Column Name="RoleIDs" DataType="String" Length="200" Description="角色组。次要角色集合" /> <Column Name="DepartmentID" DataType="Int32" Description="部门。组织机构" /> <Column Name="Online" DataType="Boolean" Description="在线" /> <Column Name="Enable" DataType="Boolean" Description="启用" /> <Column Name="Logins" DataType="Int32" Description="登录次数" /> <Column Name="LastLogin" DataType="DateTime" Description="最后登录" /> <Column Name="LastLoginIP" DataType="String" Description="最后登录IP" /> <Column Name="RegisterTime" DataType="DateTime" Description="注册时间" /> <Column Name="RegisterIP" DataType="String" Description="注册IP" /> <Column Name="Ex1" DataType="Int32" Description="扩展1" /> <Column Name="Ex2" DataType="Int32" Description="扩展2" /> <Column Name="Ex3" DataType="Double" Description="扩展3" /> <Column Name="Ex4" DataType="String" Description="扩展4" /> <Column Name="Ex5" DataType="String" Description="扩展5" /> <Column Name="Ex6" DataType="String" Description="扩展6" /> <Column Name="UpdateUser" DataType="String" Description="更新用户" /> <Column Name="UpdateUserID" DataType="Int32" Description="更新用户" /> <Column Name="UpdateIP" DataType="String" Description="更新地址" /> <Column Name="UpdateTime" DataType="DateTime" Nullable="False" Description="更新时间" /> <Column Name="Remark" DataType="String" Length="200" Description="备注" /> </Columns> <Indexes> <Index Columns="Name" Unique="True" /> <Index Columns="RoleID" /> <Index Columns="UpdateTime" /> </Indexes> </Table>
常用字段有ID、用户名和密码,登录注册相关信息;
角色RoleID、RoleIDs用于实现权限集控制;
部分场景需要邮箱Mail、手机Mobile或者工号Code登录;
如果仍然不能满足要求,可以考虑使用Ex1~Ex6等扩展字段。
常用功能点:
- 初始化时,如果数据表为空,自动插入admin/admin用户账号,角色是“管理员”
- 支持注册登录,使用MD5保存密码
- 支持编号查询FindByID和名称查询FindByName,分别采用了对象缓存和对象从键,轻松实现百万级账号快速查询
- 支持IIdentity接口
角色 Role
角色数据模型:
<Table Name="Role" Description="角色" RenderGenEntity="true"> <Columns> <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" /> <Column Name="Name" DataType="String" Master="True" Nullable="False" Description="名称" /> <Column Name="Enable" DataType="Boolean" Description="启用" /> <Column Name="IsSystem" DataType="Boolean" Description="系统。用于业务系统开发使用,不受数据权限约束,禁止修改名称或删除" /> <Column Name="Permission" DataType="String" Length="500" Description="权限。对不同资源的权限,逗号分隔,每个资源的权限子项竖线分隔" /> <Column Name="Ex1" DataType="Int32" Description="扩展1" /> <Column Name="Ex2" DataType="Int32" Description="扩展2" /> <Column Name="Ex3" DataType="Double" Description="扩展3" /> <Column Name="Ex4" DataType="String" Description="扩展4" /> <Column Name="Ex5" DataType="String" Description="扩展5" /> <Column Name="Ex6" DataType="String" Description="扩展6" /> <Column Name="CreateUser" DataType="String" Description="创建用户" /> <Column Name="CreateUserID" DataType="Int32" Description="创建用户" /> <Column Name="CreateIP" DataType="String" Description="创建地址" /> <Column Name="CreateTime" DataType="DateTime" Nullable="False" Description="创建时间" /> <Column Name="UpdateUser" DataType="String" Description="更新用户" /> <Column Name="UpdateUserID" DataType="Int32" Description="更新用户" /> <Column Name="UpdateIP" DataType="String" Description="更新地址" /> <Column Name="UpdateTime" DataType="DateTime" Nullable="False" Description="更新时间" /> <Column Name="Remark" DataType="String" Length="200" Description="备注" /> </Columns> <Indexes> <Index Columns="Name" Unique="True" /> </Indexes> </Table>
角色表比较简单主要是名称和启用,以及保存菜单权限数据的Permission
角色支持的操作权限:
/// <summary>操作权限</summary> [Flags] [Description("操作权限")] public enum PermissionFlags { /// <summary>无权限</summary> [Description("无权限")] None = 0, /// <summary>查看权限</summary> [Description("查看")] Detail = 1, /// <summary>添加权限</summary> [Description("添加")] Insert = 2, /// <summary>修改权限</summary> [Description("修改")] Update = 4, /// <summary>删除权限</summary> [Description("删除")] Delete = 8, /// <summary>所有权限</summary> [Description("所有")] All = 0xFF, }
主要功能点:
- 数据表为空时初始化4个基本角色:管理员、高级用户、普通用户、游客
- 启动时角色权限校验,清理角色中无效的权限项(可能菜单已删除),以及授权管理员访问所有角色都无权访问的新菜单
- 支持编号查询FindByID和名称查询FindByID,采用实体缓存,目标系统不会超过1000个角色
- 支持权限判断与设置 Has/Get/Set/Reset 等
- 重载实体类 Delete/Save/Update/OnLoad/OnPropertyChanged,加载实体对象时展开权限,保存时合并
菜单 Menu
菜单数据模型:
<Table Name="Menu" Description="菜单" BaseType="EntityTree" RenderGenEntity="true"> <Columns> <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" /> <Column Name="Name" DataType="String" Master="True" Nullable="False" Description="名称" /> <Column Name="DisplayName" DataType="String" Description="显示名" /> <Column Name="FullName" DataType="String" Length="200" Description="全名" /> <Column Name="ParentID" DataType="Int32" Description="父编号" /> <Column Name="Url" DataType="String" Length="200" Description="链接" /> <Column Name="Sort" DataType="Int32" Description="排序" /> <Column Name="Icon" DataType="String" Description="图标" /> <Column Name="Visible" DataType="Boolean" Description="可见" /> <Column Name="Necessary" DataType="Boolean" Description="必要。必要的菜单,必须至少有角色拥有这些权限,如果没有则自动授权给系统角色" /> <Column Name="Permission" DataType="String" Length="200" Description="权限子项。逗号分隔,每个权限子项名值竖线分隔" /> <Column Name="Ex1" DataType="Int32" Description="扩展1" /> <Column Name="Ex2" DataType="Int32" Description="扩展2" /> <Column Name="Ex3" DataType="Double" Description="扩展3" /> <Column Name="Ex4" DataType="String" Description="扩展4" /> <Column Name="Ex5" DataType="String" Description="扩展5" /> <Column Name="Ex6" DataType="String" Description="扩展6" /> <Column Name="CreateUser" DataType="String" Description="创建用户" /> <Column Name="CreateUserID" DataType="Int32" Description="创建用户" /> <Column Name="CreateIP" DataType="String" Description="创建地址" /> <Column Name="CreateTime" DataType="DateTime" Nullable="False" Description="创建时间" /> <Column Name="UpdateUser" DataType="String" Description="更新用户" /> <Column Name="UpdateUserID" DataType="Int32" Description="更新用户" /> <Column Name="UpdateIP" DataType="String" Description="更新地址" /> <Column Name="UpdateTime" DataType="DateTime" Nullable="False" Description="更新时间" /> <Column Name="Remark" DataType="String" Length="200" Description="备注" /> </Columns> <Indexes> <Index Columns="Name" /> <Index Columns="ParentID,Name" Unique="True" /> </Indexes> </Table>
菜单实体类采用树形实体基类 EntityTree ,通过 ParentID 实现上下级关联,同级 ParentID+Name 唯一
主要功能点:
- 支持自动扫描Controller作为菜单,因此魔方只需要增加Controller,即可在菜单表看到新页面
- 实体树适用于1000行以内树形数据表,一次性加载数据到内存,在内存中根据ParentID构造实体对象树,最常用树形是Parent/Childs
日志统计
日志 Log
数据模型:
<Table Name="Log" Description="日志" ConnName="Log" RenderGenEntity="true"> <Columns> <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" /> <Column Name="Category" DataType="String" Description="类别" /> <Column Name="Action" DataType="String" Description="操作" /> <Column Name="LinkID" DataType="Int32" Description="链接" /> <Column Name="UserName" DataType="String" Description="用户名" /> <Column Name="Ex1" DataType="Int32" Description="扩展1" /> <Column Name="Ex2" DataType="Int32" Description="扩展2" /> <Column Name="Ex3" DataType="Double" Description="扩展3" /> <Column Name="Ex4" DataType="String" Description="扩展4" /> <Column Name="Ex5" DataType="String" Description="扩展5" /> <Column Name="Ex6" DataType="String" Description="扩展6" /> <Column Name="CreateUser" DataType="String" Description="创建用户" /> <Column Name="CreateUserID" DataType="Int32" Description="用户编号" /> <Column Name="CreateIP" DataType="String" Description="IP地址" /> <Column Name="CreateTime" DataType="DateTime" Nullable="False" Description="时间" /> <Column Name="Remark" DataType="String" Length="500" Description="详细信息" /> </Columns> <Indexes> <Index Columns="Category" /> <Index Columns="CreateUserID" /> <Index Columns="CreateTime" /> </Indexes> </Table>
日志表记录分类、操作和日志内容。
主要功能点:
- 日志提供者LogProvider,提供了唯一核心方法 WriteLog,默认实现就是写该日志表。可从对象容器取得日志提供者 ObjectContainer.Resolve<LogProvider>()
- 从IManageProvider接口获取当前登录用户以及远程访问IP写入日志相应字段
在线 UserOnline
数据模型:
<Table Name="UserOnline" Description="用户在线" ConnName="Log"> <Columns> <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" /> <Column Name="UserID" DataType="Int32" Description="用户" /> <Column Name="Name" DataType="String" Master="True" Description="名称" /> <Column Name="SessionID" DataType="String" Description="会话。Web的SessionID或Server的会话编号" /> <Column Name="Times" DataType="Int32" Description="次数" /> <Column Name="Page" DataType="String" Description="页面" /> <Column Name="Status" DataType="String" Length="200" Description="状态" /> <Column Name="OnlineTime" DataType="Int32" Description="在线时间。本次在线总时间,秒" /> <Column Name="CreateIP" DataType="String" Description="创建地址" /> <Column Name="CreateTime" DataType="DateTime" Nullable="False" Description="创建时间" /> <Column Name="UpdateTime" DataType="DateTime" Nullable="False" Description="修改时间" /> </Columns> <Indexes> <Index Columns="UserID" /> <Index Columns="SessionID" /> <Index Columns="CreateTime" /> </Indexes> </Table>
借助用户行为模块 UserBehaviorModule , 维护用户在线记录,持久化在 UserOnline 表
访问统计 VisitStat
<Table Name="VisitStat" Description="访问统计" ConnName="Log"> <Columns> <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" /> <Column Name="Level" DataType="Int32" Description="层级" Type="XCode.Statistics.StatLevels" /> <Column Name="Time" DataType="DateTime" Description="时间" /> <Column Name="Page" DataType="String" Nullable="False" Description="页面" /> <Column Name="Title" DataType="String" Master="True" Description="标题" /> <Column Name="Times" DataType="Int32" Description="次数" /> <Column Name="Users" DataType="Int32" Description="用户" /> <Column Name="IPs" DataType="Int32" Description="IP" /> <Column Name="Error" DataType="Int32" Description="错误" /> <Column Name="Cost" DataType="Int32" Description="耗时。毫秒" /> <Column Name="MaxCost" DataType="Int32" Description="最大耗时。毫秒" /> <Column Name="CreateTime" DataType="DateTime" Nullable="False" Description="创建时间" /> <Column Name="UpdateTime" DataType="DateTime" Nullable="False" Description="更新时间" /> <Column Name="Remark" DataType="String" Length="5000" Description="详细信息" /> </Columns> <Indexes> <Index Columns="Page,Level,Time" Unique="True" /> <Index Columns="Level,Time" /> </Indexes> </Table>
借助用户行为模块 UserBehaviorModule , 维护用户访问记录,写入日志表,并写入访问统计表。
主要功能要点:
- 记录页面访问统计,简单支持IP数和用户数
- 支持年月日三级统计,作为XCode日期统计表的标准示例
其它
部门 Department
数据模型:
<Table Name="Department" Description="部门。组织机构,多级树状结构" BaseType="EntityTree" RenderGenEntity="true"> <Columns> <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" /> <Column Name="Code" DataType="String" Description="代码" /> <Column Name="Name" DataType="String" Master="True" Nullable="False" Description="名称" /> <Column Name="FullName" DataType="String" Length="200" Description="全名" /> <Column Name="ParentID" DataType="Int32" Description="父级" /> <Column Name="Level" DataType="Int32" Description="层级。树状结构的层级" /> <Column Name="Sort" DataType="Int32" Description="排序。同级内排序" /> <Column Name="Enable" DataType="Boolean" Description="启用" /> <Column Name="Visible" DataType="Boolean" Description="可见" /> <Column Name="Ex1" DataType="Int32" Description="扩展1" /> <Column Name="Ex2" DataType="Int32" Description="扩展2" /> <Column Name="Ex3" DataType="Double" Description="扩展3" /> <Column Name="Ex4" DataType="String" Description="扩展4" /> <Column Name="Ex5" DataType="String" Description="扩展5" /> <Column Name="Ex6" DataType="String" Description="扩展6" /> <Column Name="CreateUser" DataType="String" Description="创建用户" /> <Column Name="CreateUserID" DataType="Int32" Description="创建用户" /> <Column Name="CreateIP" DataType="String" Description="创建地址" /> <Column Name="CreateTime" DataType="DateTime" Nullable="False" Description="创建时间" /> <Column Name="UpdateUser" DataType="String" Description="更新用户" /> <Column Name="UpdateUserID" DataType="Int32" Description="更新用户" /> <Column Name="UpdateIP" DataType="String" Description="更新地址" /> <Column Name="UpdateTime" DataType="DateTime" Nullable="False" Description="更新时间" /> <Column Name="Remark" DataType="String" Length="200" Description="备注" /> </Columns> <Indexes> <Index Columns="Name" /> <Index Columns="ParentID,Name" Unique="True" /> <Index Columns="Code" /> <Index Columns="UpdateTime" /> </Indexes> </Table>
字典参数 Parameter
数据模型:
<Table Name="Parameter" Description="字典参数"> <Columns> <Column Name="ID" DataType="Int32" Identity="True" PrimaryKey="True" Description="编号" /> <Column Name="Category" DataType="String" Description="类别" /> <Column Name="Name" DataType="String" Master="True" Description="名称" /> <Column Name="Value" DataType="String" Length="200" Description="数值" /> <Column Name="LongValue" DataType="String" Length="2000" Description="长数值" /> <Column Name="Kind" DataType="Int32" Description="种类。0普通,21列表,22名值" Type="XCode.Membership.ParameterKinds" /> <Column Name="Enable" DataType="Boolean" Description="启用" /> <Column Name="Ex1" DataType="Int32" Description="扩展1" /> <Column Name="Ex2" DataType="Int32" Description="扩展2" /> <Column Name="Ex3" DataType="Double" Description="扩展3" /> <Column Name="Ex4" DataType="String" Description="扩展4" /> <Column Name="Ex5" DataType="String" Description="扩展5" /> <Column Name="Ex6" DataType="String" Description="扩展6" /> <Column Name="CreateUser" DataType="String" Description="创建用户" /> <Column Name="CreateUserID" DataType="Int32" Description="创建用户" /> <Column Name="CreateIP" DataType="String" Description="创建地址" /> <Column Name="CreateTime" DataType="DateTime" Nullable="False" Description="创建时间" /> <Column Name="UpdateUser" DataType="String" Description="更新用户" /> <Column Name="UpdateUserID" DataType="Int32" Description="更新用户" /> <Column Name="UpdateIP" DataType="String" Description="更新地址" /> <Column Name="UpdateTime" DataType="DateTime" Nullable="False" Description="更新时间" /> <Column Name="Remark" DataType="String" Length="200" Description="备注" /> </Columns> <Indexes> <Index Columns="Category,Name" Unique="True" /> <Index Columns="Name" /> <Index Columns="UpdateTime" /> </Indexes> </Table>
系列教程
NewLife.XCode教程系列[2019版]
- 增删改查入门。快速展现用法,代码配置连接字符串
- 数据模型文件。建立表格字段和索引,名字以及数据类型规范,推荐字段(时间,用户,IP)
- 实体类详解。数据类业务类,泛型基类,接口
- 功能设置。连接字符串,调试开关,SQL日志,慢日志,参数化,执行超时。代码与配置文件设置,连接字符串局部设置
- 反向工程。自动建立数据库数据表
- 数据初始化。InitData写入初始化数据
- 高级增删改。重载拦截,自增字段,Valid验证,实体模型(时间,用户,IP)
- 脏数据。如何产生,怎么利用
- 增量累加。高并发统计
- 事务处理。单表和多表,不同连接,多种写法
- 扩展属性。多表关联,Map映射
- 高级查询。复杂条件,分页,自定义扩展FieldItem,查总记录数,查汇总统计
- 数据层缓存。Sql缓存,更新机制
- 实体缓存。全表整理缓存,更新机制
- 对象缓存。字典缓存,适用用户等数据较多场景。
- 百亿级性能。字段精炼,索引完备,合理查询,充分利用缓存
- 实体工厂。元数据,通用处理程序
- 角色权限。Membership
- 导入导出。Xml,Json,二进制,网络或文件
- 分表分库。常见拆分逻辑
- 高级统计。聚合统计,分组统计
- 批量写入。批量插入,批量Upsert,异步保存
- 实体队列。写入级缓存,提升性能。
- 备份同步。备份数据,恢复数据,同步数据
- 数据服务。提供RPC接口服务,远程执行查询,例如SQLite网络版
- 大数据分析。ETL抽取,调度计算处理,结果持久化