2023年6月21日发(作者:)
【分享】⼀个通⽤强⼤的主数据管理系统(架构设计讲解及源码下载) 这篇⽂章要讲主数据管理系统(Master Data Management System),是本⼈创业的其中⼀个项⽬,⽤于管理公司的内部所有系统、系统⽤户、系统⾓⾊、系统权限、服务器管理、系统⽇志等数据。项⽬开发只⽤了1个多⽉时间,全部由⼀个⼈完成的,⽬前只是个初版本,只满⾜了初期的需求,如果没时间看下⾯的架构设计,可以直接下载源码。
⽤户/密码:test1/test1 test2/test2 (注:同⼀⽤户在另⼀浏览器登录,另⼀⽤户在session失效后会被逼下线)
不少朋友下载源码后不怎么知道运⾏,请看这⾥补充的说明:项⽬使⽤vs2010打开,数据使⽤sql server2005/2008⼀、数据库创建与初始化数据新建名为 Db_MDMS 的数据库然后按顺序执⾏⽬录 ntsDB 1.0Scripts ⾥的sql1_2_3_4_init_⼆、修改数据库连接信息在⽬录 buildMSH ⾥修改 的配置节点 _ConnectionString 的值三、在vs2010中运⾏Web项⽬ 或右键选择 页⾯点击在浏览器中浏览,然后打开⽬录 buildMSH ,双击运⾏之这时,你可以⽤默认⽤户/密码 admin/admin888 登录系统了
主数据管理问题存在的根源 对于⼤多数的企业都存在主数据管理的问题,个⼈以为这是由于业务发展的渐进性以及IT技术发展的渐进性造成的,正是由于这种渐进性,各⼤企业的业务系统从经历了从⽆到有,从简单到复杂,从⽽形成了⼀个⼜⼀个的业务竖井。从根本上来说,不可能只使⽤⼀个业务系统就能覆盖企业的所有业务,即便对⼀些国际⼤型的公司提供的套件来说也是⼀个不可能完成的任务(即便对套件来说,经常也存在⼀个跨国企业在不同的国家或地区部署多个实例的现象,也就是没有集中部署该套件,⽽是在很多地⽅分散部署了该套件)。对企业来说,业务系统的构建更多是以项⽬为中⼼,从下⽽上的构建系统,⽽不是⾄上⽽下的构建系统——这必然缺乏整个企业范围内的统⼀规划,从⽽使得⼀些需要在各个业务中共享的数据(主数据)被分散到了各个业务系统进⾏分别管理。由于分散管理的主数据不具备⼀致性、准确性、完整性,使得各个企业普遍存在着产品管理不⼒、供应商管理不⼒、订单管理不⼒等现象。解决这⼀问题的根本⽅法就是引⼊主数据管理(MDM),主数据不光指需要共享的数据,更包含需要共享的业务规则和策略。⼀、主要功能需求 1.公司内部系统的管理,包括管理系统信息、系统权限定义、系统⾓⾊、系统⾓⾊拥有的权限等等。 2.统⼀的⽤户管理,包括管理⽤户信息、登录系统的限制、个⼈在各个系统的⾓⾊、个⼈在各个系统的永久与临时权限等等。 3.各系统的⽇志记录与查看。 4.服务器的管理。(说明⼀下,这⾥只是管理服务器的信息,数据是提供给⼀个专门管理服务器远程登录的⼦系统的) ...其它扩展需求(多语⾔⽬前不需要,但MTV框架已实现了)
⼆、主要界⾯展⽰1.主界⾯ 2.⽤户列表3.个⼈登录限制4.个⼈⾓⾊管理5.个⼈永久权限6.个⼈临时权限7.系统列表 8.系统权限定义列表 9.添加权限项 10.系统⾓⾊管理 11.系统⾓⾊的权限12.服务器管理13.系统⽇志
三、数据表设计LoginLimit : ⽤户在各个系统的登录限制LoginState : ⽤户在各个系统的登录情况OperationLog : ⽤户在各个系统的操作⽇志PermissionGroup : 系统的权限定义分组PermissionItem : 系统权限分组⾥的项Role : 系统⾓⾊信息Server : 服务器(组)信息ServerParameter : 服务器参数信息System : 系统信息User :⽤户信息UserPermission : ⽤户在各个系统的永久权限UserRoleMapping : ⽤户在各个系统的⾓⾊关系UserTempPermission : ⽤户在各个系统的临时权限详细设计内容太多,请下载源码看⾥⾯的数据库设计⽂档。
三、系统架构说明
整个解决⽅案共20个项⽬,其中ProviderModules⽬录下的13个项⽬是应⽤WCF服务向业务层提供数据接⼝,这么做了为了以后把数据接⼝和⽹站开发分开来给团队独⽴管理维护。系统应⽤了N-tier架构的基本设计模式+WCF服务+MTV框架。其中WCF服务采⽤了KudySharp⾥的ModuleFramework,MTV框架也是集成在KudySharp⾥⾯的。下⾯对各个项⽬作简单的解说。
ModuleFramework相关⽂章(MTV框架还没有时间相关的⽂章):
项⽬是核⼼库,IProvider下是数据操作接⼝的定义,Models下是数据表实体模型类和其它扩展模型类,Modules下是应⽤ModuleFramework的相关类。DataProviderFactory⽤于根据配置产⽣实现数据接⼝的实例,⽽DataProviderManager则是管理这些实例的,下⾯请看代码:(原先设计是把Data、IProvider、Models分别放在独⽴项⽬的,经典多层项⽬都这么⼲,但为了减少项⽬数,本⼈都放在项⽬中了)DataProviderFactory /// ///
/// internal static class DataProviderFactory { /// ///
/// /// /// public static TProvider Create() { TProvider provider; Type interfaceType = typeof(TProvider); string interfaceName = me; string providerTypeName = (_Db_Provider, ".", ing(1)); try { object instance = (_Db_Provider).CreateInstance(providerTypeName);
provider = (TProvider)instance; } catch (Exception ex) { throw new Exception("Fail to create provider instance of " + providerTypeName, ex); } return provider; } }
DataProviderManager /// ///
/// public static class DataProviderManager { private static readonly ReadFreeCache providerCache = ReadFreeCache.Create(lIgnoreCase); /// ///
/// /// /// public static TProvider Get() { string interfaceName = typeof(TProvider).FullName; object provider = (interfaceName, key => ()); return (TProvider)provider; } } verProvider项⽬是数据接⼝sql server实现类库。⾥⾯⽤了KudySharp⾥的SqlServerHelper助⼿类,使数据操作更加简洁,请看下⾯的基类和⽤户表的数据接⼝实现类:ProviderBase /// /// 数据提供者基类 /// public abstract class ProviderBase { protected virtual string ConnectionString { get { return _Db_ConnectionString; } } protected virtual object ExecuteNonQueryWithReturnValueParameter(string spName, params object[] parameterValues) { // ⾃动获取存储过程参数(第⼆次后会取被缓存的⼀个拷贝),并设置有返回值参数 SqlParameter[] parameters = arameterSet(ConnectionString, spName, true, parameterValues); // 执⾏存储过程 eNonQuery(ConnectionString, Procedure, spName, parameters); // 存储过程执⾏后的返回值 return parameters[0].Value; } protected virtual TModel GetModel(Func getModel, string spName, params object[] parameterValues) where TModel : new() { TModel entity = new TModel(); // ⾃动获取存储过程参数(第⼆次后会取被缓存的⼀个拷贝),并设置没有返回值参数 SqlParameter[] parameters = arameterSet(ConnectionString, spName, false, parameterValues);
// 执⾏存储过程 using (var reader = eReader(ConnectionString, Procedure, spName, parameters)) { if (()) { // 读取数据 entity = getModel(reader); } } return entity; } protected virtual List GetModelList(Func getModel, string spName, params object[] parameterValues) where TModel : new() { List list = new List(); // ⾃动获取存储过程参数(第⼆次后会取被缓存的⼀个拷贝),并设置没有返回值参数 SqlParameter[] parameters = arameterSet(ConnectionString, spName, false, parameterValues); // 执⾏存储过程 using (var reader = eReader(ConnectionString, Procedure, spName, parameters)) { while (()) { // 读取数据 TModel entity = getModel(reader); (entity); } } return list; } public virtual PagedList GetPagedList(Func getModel, PagedSqlParameters pagedSqlParameters) where TModel : new() { if (ze < 1 || ze > 50) { ze = 10; } // 执⾏存储过程(SqlServerHelper内置的分页存储过程) using (var reader = ePagedReader(ConnectionString, pagedSqlParameters)) { List list = new List(); while (()) { // 读取数据 TModel model = getModel(reader); (model); } return dList(ze, dex, Count); } } }
UserProvider /// /// ⽤户接⼝实现 /// public sealed class UserProvider : ProviderBase, IUserProvider { public bool Add(string userName, string password, string passwordSalt, UserGender gender, string realName, string email, string mobile, UserStatus status) { object value = ExecuteNonQueryWithReturnValueParameter("SP_MDMS_User_Add", userName, password, passwordSalt, gender, realName, email, mobile, status); return (32(value) > 0); } public bool EditById(Guid id, string password, string passwordSalt, UserGender gender, string realName, string email, string mobile, UserStatus status) { object value = ExecuteNonQueryWithReturnValueParameter("SP_MDMS_User_EditById", id, password, passwordSalt, gender, realName, email, mobile, status); return (32(value) > 0); } public bool EditPasswordByUserName(string userName, string password, string passwordSalt) { object value = ExecuteNonQueryWithReturnValueParameter("SP_MDMS_User_EditPasswordByUserName", userName, password, passwordSalt); return (32(value) > 0); } public bool DeleteByIds(Guid[] ids) { object value = ExecuteNonQueryWithReturnValueParameter("SP_MDMS_User_DeleteByIds", aString(ids)); return (32(value) > 0); } public bool SetStatusByIds(Guid[] ids, UserStatus status) { object value = ExecuteNonQueryWithReturnValueParameter("SP_MDMS_User_SetStatusByIds", aString(ids), (byte)status); return (32(value) > 0); } public UserModel GetById(Guid id) { return GetModel(GetUserModel, "SP_MDMS_User_GetById", id); } public UserModel GetByUserName(string userName) { return GetModel(GetUserModel, "SP_MDMS_User_GetByUserName", userName); } public PagedList GetPagedList(string userName, UserGender? gender, UserStatus? status, int pageSize, int pageIndex) { PagedSqlParameters sqlParameters = new PagedSqlParameters() { SelectExpression = "*", FromExpression = "[dbo].[User]", MasterTable = "[dbo].[User]", MasterPrimaryKey = "[Id]", AutoIncrementKeyMode = false, WhereExpression = , OrderExpression = "[CreateDate]", Descending = true, MaxPageIndex = 0, PageSize = pageSize, PageIndex = pageIndex }; if (!OrEmpty(userName)) { xpression += " AND [UserName] LIKE '%" + Like(userName) + "%'"; } if (ue) { xpression += " AND [Gender]=" + ((byte)).ToString(); } if (ue) { xpression += " AND [Status]=" + ((byte)).ToString(); } return GetPagedList(GetUserModel, sqlParameters); } private UserModel GetUserModel(DataReader reader) { return new UserModel { Id = d("Id"), UserName = ing("UserName").Trim(), Password = ing("Password"), PasswordSalt = ing("PasswordSalt"), Gender = (UserGender)e("Gender"), RealName = ing("RealName").Trim(), Email = ing("Email"), Mobile = ing("Mobile").Trim(), Status = (UserStatus)e("Status"), CreateDate = eTime("CreateDate") }; } }.....其它省略....
四、⼦系统怎么与主数据管理系统整合? 只要继承 iModule 项⽬⾥的ServiceContextBase类,就可以很轻松的实现⽤户的登录,退出,添加⽇志,读取权限等操作,其中主数据管理系统也作为了其中的⼀个⼦系统来管理了,可以参考此系统是怎么整合的,时间问题,不想再多写解说了。请谅解。
另⼀⽂中讲解了此系统的权限设计⽅案:
2023年6月21日发(作者:)
【分享】⼀个通⽤强⼤的主数据管理系统(架构设计讲解及源码下载) 这篇⽂章要讲主数据管理系统(Master Data Management System),是本⼈创业的其中⼀个项⽬,⽤于管理公司的内部所有系统、系统⽤户、系统⾓⾊、系统权限、服务器管理、系统⽇志等数据。项⽬开发只⽤了1个多⽉时间,全部由⼀个⼈完成的,⽬前只是个初版本,只满⾜了初期的需求,如果没时间看下⾯的架构设计,可以直接下载源码。
⽤户/密码:test1/test1 test2/test2 (注:同⼀⽤户在另⼀浏览器登录,另⼀⽤户在session失效后会被逼下线)
不少朋友下载源码后不怎么知道运⾏,请看这⾥补充的说明:项⽬使⽤vs2010打开,数据使⽤sql server2005/2008⼀、数据库创建与初始化数据新建名为 Db_MDMS 的数据库然后按顺序执⾏⽬录 ntsDB 1.0Scripts ⾥的sql1_2_3_4_init_⼆、修改数据库连接信息在⽬录 buildMSH ⾥修改 的配置节点 _ConnectionString 的值三、在vs2010中运⾏Web项⽬ 或右键选择 页⾯点击在浏览器中浏览,然后打开⽬录 buildMSH ,双击运⾏之这时,你可以⽤默认⽤户/密码 admin/admin888 登录系统了
主数据管理问题存在的根源 对于⼤多数的企业都存在主数据管理的问题,个⼈以为这是由于业务发展的渐进性以及IT技术发展的渐进性造成的,正是由于这种渐进性,各⼤企业的业务系统从经历了从⽆到有,从简单到复杂,从⽽形成了⼀个⼜⼀个的业务竖井。从根本上来说,不可能只使⽤⼀个业务系统就能覆盖企业的所有业务,即便对⼀些国际⼤型的公司提供的套件来说也是⼀个不可能完成的任务(即便对套件来说,经常也存在⼀个跨国企业在不同的国家或地区部署多个实例的现象,也就是没有集中部署该套件,⽽是在很多地⽅分散部署了该套件)。对企业来说,业务系统的构建更多是以项⽬为中⼼,从下⽽上的构建系统,⽽不是⾄上⽽下的构建系统——这必然缺乏整个企业范围内的统⼀规划,从⽽使得⼀些需要在各个业务中共享的数据(主数据)被分散到了各个业务系统进⾏分别管理。由于分散管理的主数据不具备⼀致性、准确性、完整性,使得各个企业普遍存在着产品管理不⼒、供应商管理不⼒、订单管理不⼒等现象。解决这⼀问题的根本⽅法就是引⼊主数据管理(MDM),主数据不光指需要共享的数据,更包含需要共享的业务规则和策略。⼀、主要功能需求 1.公司内部系统的管理,包括管理系统信息、系统权限定义、系统⾓⾊、系统⾓⾊拥有的权限等等。 2.统⼀的⽤户管理,包括管理⽤户信息、登录系统的限制、个⼈在各个系统的⾓⾊、个⼈在各个系统的永久与临时权限等等。 3.各系统的⽇志记录与查看。 4.服务器的管理。(说明⼀下,这⾥只是管理服务器的信息,数据是提供给⼀个专门管理服务器远程登录的⼦系统的) ...其它扩展需求(多语⾔⽬前不需要,但MTV框架已实现了)
⼆、主要界⾯展⽰1.主界⾯ 2.⽤户列表3.个⼈登录限制4.个⼈⾓⾊管理5.个⼈永久权限6.个⼈临时权限7.系统列表 8.系统权限定义列表 9.添加权限项 10.系统⾓⾊管理 11.系统⾓⾊的权限12.服务器管理13.系统⽇志
三、数据表设计LoginLimit : ⽤户在各个系统的登录限制LoginState : ⽤户在各个系统的登录情况OperationLog : ⽤户在各个系统的操作⽇志PermissionGroup : 系统的权限定义分组PermissionItem : 系统权限分组⾥的项Role : 系统⾓⾊信息Server : 服务器(组)信息ServerParameter : 服务器参数信息System : 系统信息User :⽤户信息UserPermission : ⽤户在各个系统的永久权限UserRoleMapping : ⽤户在各个系统的⾓⾊关系UserTempPermission : ⽤户在各个系统的临时权限详细设计内容太多,请下载源码看⾥⾯的数据库设计⽂档。
三、系统架构说明
整个解决⽅案共20个项⽬,其中ProviderModules⽬录下的13个项⽬是应⽤WCF服务向业务层提供数据接⼝,这么做了为了以后把数据接⼝和⽹站开发分开来给团队独⽴管理维护。系统应⽤了N-tier架构的基本设计模式+WCF服务+MTV框架。其中WCF服务采⽤了KudySharp⾥的ModuleFramework,MTV框架也是集成在KudySharp⾥⾯的。下⾯对各个项⽬作简单的解说。
ModuleFramework相关⽂章(MTV框架还没有时间相关的⽂章):
项⽬是核⼼库,IProvider下是数据操作接⼝的定义,Models下是数据表实体模型类和其它扩展模型类,Modules下是应⽤ModuleFramework的相关类。DataProviderFactory⽤于根据配置产⽣实现数据接⼝的实例,⽽DataProviderManager则是管理这些实例的,下⾯请看代码:(原先设计是把Data、IProvider、Models分别放在独⽴项⽬的,经典多层项⽬都这么⼲,但为了减少项⽬数,本⼈都放在项⽬中了)DataProviderFactory /// ///
/// internal static class DataProviderFactory { /// ///
/// /// /// public static TProvider Create() { TProvider provider; Type interfaceType = typeof(TProvider); string interfaceName = me; string providerTypeName = (_Db_Provider, ".", ing(1)); try { object instance = (_Db_Provider).CreateInstance(providerTypeName);
provider = (TProvider)instance; } catch (Exception ex) { throw new Exception("Fail to create provider instance of " + providerTypeName, ex); } return provider; } }
DataProviderManager /// ///
/// public static class DataProviderManager { private static readonly ReadFreeCache providerCache = ReadFreeCache.Create(lIgnoreCase); /// ///
/// /// /// public static TProvider Get() { string interfaceName = typeof(TProvider).FullName; object provider = (interfaceName, key => ()); return (TProvider)provider; } } verProvider项⽬是数据接⼝sql server实现类库。⾥⾯⽤了KudySharp⾥的SqlServerHelper助⼿类,使数据操作更加简洁,请看下⾯的基类和⽤户表的数据接⼝实现类:ProviderBase /// /// 数据提供者基类 /// public abstract class ProviderBase { protected virtual string ConnectionString { get { return _Db_ConnectionString; } } protected virtual object ExecuteNonQueryWithReturnValueParameter(string spName, params object[] parameterValues) { // ⾃动获取存储过程参数(第⼆次后会取被缓存的⼀个拷贝),并设置有返回值参数 SqlParameter[] parameters = arameterSet(ConnectionString, spName, true, parameterValues); // 执⾏存储过程 eNonQuery(ConnectionString, Procedure, spName, parameters); // 存储过程执⾏后的返回值 return parameters[0].Value; } protected virtual TModel GetModel(Func getModel, string spName, params object[] parameterValues) where TModel : new() { TModel entity = new TModel(); // ⾃动获取存储过程参数(第⼆次后会取被缓存的⼀个拷贝),并设置没有返回值参数 SqlParameter[] parameters = arameterSet(ConnectionString, spName, false, parameterValues);
// 执⾏存储过程 using (var reader = eReader(ConnectionString, Procedure, spName, parameters)) { if (()) { // 读取数据 entity = getModel(reader); } } return entity; } protected virtual List GetModelList(Func getModel, string spName, params object[] parameterValues) where TModel : new() { List list = new List(); // ⾃动获取存储过程参数(第⼆次后会取被缓存的⼀个拷贝),并设置没有返回值参数 SqlParameter[] parameters = arameterSet(ConnectionString, spName, false, parameterValues); // 执⾏存储过程 using (var reader = eReader(ConnectionString, Procedure, spName, parameters)) { while (()) { // 读取数据 TModel entity = getModel(reader); (entity); } } return list; } public virtual PagedList GetPagedList(Func getModel, PagedSqlParameters pagedSqlParameters) where TModel : new() { if (ze < 1 || ze > 50) { ze = 10; } // 执⾏存储过程(SqlServerHelper内置的分页存储过程) using (var reader = ePagedReader(ConnectionString, pagedSqlParameters)) { List list = new List(); while (()) { // 读取数据 TModel model = getModel(reader); (model); } return dList(ze, dex, Count); } } }
UserProvider /// /// ⽤户接⼝实现 /// public sealed class UserProvider : ProviderBase, IUserProvider { public bool Add(string userName, string password, string passwordSalt, UserGender gender, string realName, string email, string mobile, UserStatus status) { object value = ExecuteNonQueryWithReturnValueParameter("SP_MDMS_User_Add", userName, password, passwordSalt, gender, realName, email, mobile, status); return (32(value) > 0); } public bool EditById(Guid id, string password, string passwordSalt, UserGender gender, string realName, string email, string mobile, UserStatus status) { object value = ExecuteNonQueryWithReturnValueParameter("SP_MDMS_User_EditById", id, password, passwordSalt, gender, realName, email, mobile, status); return (32(value) > 0); } public bool EditPasswordByUserName(string userName, string password, string passwordSalt) { object value = ExecuteNonQueryWithReturnValueParameter("SP_MDMS_User_EditPasswordByUserName", userName, password, passwordSalt); return (32(value) > 0); } public bool DeleteByIds(Guid[] ids) { object value = ExecuteNonQueryWithReturnValueParameter("SP_MDMS_User_DeleteByIds", aString(ids)); return (32(value) > 0); } public bool SetStatusByIds(Guid[] ids, UserStatus status) { object value = ExecuteNonQueryWithReturnValueParameter("SP_MDMS_User_SetStatusByIds", aString(ids), (byte)status); return (32(value) > 0); } public UserModel GetById(Guid id) { return GetModel(GetUserModel, "SP_MDMS_User_GetById", id); } public UserModel GetByUserName(string userName) { return GetModel(GetUserModel, "SP_MDMS_User_GetByUserName", userName); } public PagedList GetPagedList(string userName, UserGender? gender, UserStatus? status, int pageSize, int pageIndex) { PagedSqlParameters sqlParameters = new PagedSqlParameters() { SelectExpression = "*", FromExpression = "[dbo].[User]", MasterTable = "[dbo].[User]", MasterPrimaryKey = "[Id]", AutoIncrementKeyMode = false, WhereExpression = , OrderExpression = "[CreateDate]", Descending = true, MaxPageIndex = 0, PageSize = pageSize, PageIndex = pageIndex }; if (!OrEmpty(userName)) { xpression += " AND [UserName] LIKE '%" + Like(userName) + "%'"; } if (ue) { xpression += " AND [Gender]=" + ((byte)).ToString(); } if (ue) { xpression += " AND [Status]=" + ((byte)).ToString(); } return GetPagedList(GetUserModel, sqlParameters); } private UserModel GetUserModel(DataReader reader) { return new UserModel { Id = d("Id"), UserName = ing("UserName").Trim(), Password = ing("Password"), PasswordSalt = ing("PasswordSalt"), Gender = (UserGender)e("Gender"), RealName = ing("RealName").Trim(), Email = ing("Email"), Mobile = ing("Mobile").Trim(), Status = (UserStatus)e("Status"), CreateDate = eTime("CreateDate") }; } }.....其它省略....
四、⼦系统怎么与主数据管理系统整合? 只要继承 iModule 项⽬⾥的ServiceContextBase类,就可以很轻松的实现⽤户的登录,退出,添加⽇志,读取权限等操作,其中主数据管理系统也作为了其中的⼀个⼦系统来管理了,可以参考此系统是怎么整合的,时间问题,不想再多写解说了。请谅解。
另⼀⽂中讲解了此系统的权限设计⽅案:
发布评论