潍坊市论坛

首页 » 分类 » 问答 » 基于DevExpress从零开始搭建
TUhjnbcbe - 2021/9/21 16:08:00
基于DevExpress从零开始搭建多租户自洽的权限数据配置模块(一)

从零开始设计一个最简单的winform练习程序,在这个过程中会涉及到devexpress、简单数据库设计、简单分层设计、基本操作方法等一些简单知识点的演示。

、思路设计

基础数据的维护管理,以简单基本操作的形式展开。主要是演示devexpress做基本的增删改查、加载表单、建立多表关联、用户操作动态加载数据等。

、程序基本代码轮廓

我们先把这个简单工具的代码划分一个模糊的层次。总体上是一个这样的思路:

基本代码结构UI结构登录界面+主界面+Mdi子界面管理+响应交互的逻辑视图winform窗体,主要是基本操作界面后台控制数据请求层+Json转化层(为后续前后端分离做准备)模型数据库模型+DTO模型数据库数据库结构设计以及建库脚本保留

对UI结构做一个基本要求:基础数据的维护操作,必须是基于单个租户作为基本范围进行配置。系统进入的时候,必须强制管理员选择要操作的租户。

这么做的原因是,我们有很多非常小巧的孤岛业务应用,如果每部署一个应用都需要跟着部署一套权限模块的话,有点浪费。多租户可以在同一个程序中进行托管运维,逻辑可以在租户约束的范围内自洽适应。

在这里,我们定义了一个XtraFormBaseTenancy。

usingDevExpress.XtraEditors;usingSystem;usingSystem.Collections.Generic;usingSystem.ComponentModel;usingSystem.Data;usingSystem.Drawing;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows.Forms;namespaceHiAuth0{publicpartialclassXtraFormBaseTenancy:XtraForm{privateboolisNeedAutoClose=false;publicXtraFormBaseTenancy(){InitializeComponent();this.Activated+=XtraFormBase_Activated;this.Shown+=XtraFormBase_Shown;}///summary///因为Activated事件订阅的优先级顺序要早于Shown事件的订阅契机,因此私有属性会在此生效////summary///paramname="sender"/param///paramname="e"/paramprivatevoidXtraFormBase_Shown(objectsender,EventArgse){if(isNeedAutoClose)this.Close();}///summary///在基类窗体被激活时,判断是否已经进入选定的租户范围,如果没有,提醒用户进行选择,并强制退出继承该基类窗体的子窗体////summary///paramname="sender"/param///paramname="e"/paramprivatevoidXtraFormBase_Activated(objectsender,EventArgse){if(StaticGlobal.Tenancy==null

string.IsNullOrEmpty(StaticGlobal.Tenancy.CurrentTenancy)){isNeedAutoClose=true;HiAuthStaticSetting.AlertErrorMsg(this,"请先选择您要配置的租户!");}HiAuthStaticSetting.SetCurrentLocationText(this.Text);}}}

其他的业务操作界面只需要继承上面的基类窗体,就可以实现不选择租户窗体就打不开的效果。变相等于实现了菜单权限的控制逻辑。

publicpartialclassHiAuthUsersManage:XtraFormBaseTenancy{privateboolisNeedPass=false;privateListDbModels.t_auth_userssource;publicHiAuthUsersManage(){InitializeComponent();this.Shown+=HiAuthUsersManage_Shown;gridView.RowCellClick+=GridView_RowCellClick;gridView.FocusedRowChanged+=GridView_FocusedRowChanged;gridView.SelectionChanged+=GridView_SelectionChanged;...

我们使用一个全局状态管理器,来静态地托管用户登录以后的状态数据。当然,这种方式仅仅是适用于winform形式,web的时候他其实是个会话(Session)

usingHiAuth0.DtoModels;usingSystem;usingSystem.Collections.Generic;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;namespaceHiAuth0{///summary///全局静态数据////summarypublicstaticclassStaticGlobal{publicstaticDtoModels.TenancyCuttingTenancy{get;set;}=newDtoModels.TenancyCutting();publicstaticListDtoModels.TenancyCuttingTenancySource{get;set;}=RestHelperDtoModels.TenancyCutting.GetDataSourceByRestRequest(()={returnnewHiAuthBasicsRestBussiness.HiAuthTenancyRestBusiness().GetTenancyCuttingSource();});}///summary///系统登录个人Session加密后记录到缓存服务器////summarypublicclassLoginInfo{///summary///主界面窗体////summarypublicstaticHiAuthManageSystemMainForm{get;set;}///summary///登录窗体////summarypublicstaticHiAuthLoginLoginForm{get;set;}///summary///登录状态////summarypublicstaticLoginStateLoginState{get;set;}///summary///登录参数////summarypublicstaticLoginEntityLoginConfig{get;set;}///summary///用户账号////summarypublicstaticstringUserCode{get;set;}///summary///用户姓名////summarypublicstaticstringUserName{get;set;}///summary///性别////summarypublicstaticstringUserSex{get;set;}///summary///部门代码////summarypublicstaticstringUserDeptCode{get;set;}///summary///部门名称////summarypublicstaticstringUserDeptName{get;set;}///summary///岗位////summarypublicstaticstringUserDuty{get;set;}///summary///IP地址////summarypublicstaticstringUserLoginIp{get;set;}///summary///当前设备名称////summarypublicstaticstringDevice{get;set;}///summary///当前登录域账户////summarypublicstaticstringWindowsUser{get;set;}///summary///认证角色id////summarypublicstaticListintRoleIds{get;set;}///summary///可访问菜单集合////summarypublicstaticListPowerMenusAccessablePowerMenus{get;set;}///summary///共享文件夹根路径////summarypublicstaticstringShareDiskRootPath{get=

"***";}///summary///连接共享文件夹的账号////summarypublicstaticstringShareDiskUser{get="***";}///summary///连接共享文件夹的密码////summarypublicstaticstringShareDiskPassword{get="***";}///summary///记住皮肤////summarypublicstaticSkinConfigSkinRemember{get{SkinConfigskinConfig=newSkinConfig(){SkinName="Office06Colorful"};//判断是否存在记住密码文件if(FileOps.IsExistFile("SkinPath")){SkinConfigconfig=FileOps.LoadObjectFromXml("SkinPath",typeof(SkinConfig))asSkinConfig;if(config!=null){returnconfig;}else{returnskinConfig;}}else{returnskinConfig;}}}}///summary///认证成功返回权限菜单集合////summarypublicclassPowerMenus{///summary///父项号////summarypublicint?ParentId{get;set;}///summary///子项号////summarypublicintMenuId{get;set;}///summary///菜单名称////summarypublicstringMenuName{get;set;}///summary///角色Id////summarypublicintRoleId{get;set;}///summary///角色名称////summarypublicstringRoleName{get;set;}///summary///菜单路径(命名空间加类名)////summarypublicstringMenuPath{get;set;}}///summary///登录状态////summarypublicenumLoginState{LoginFail=-,//登录失败Login=0,//登录成功Logout=,//注销Close=,//关闭Exit=3,//退出LostConnection=4,//失去连接}///summary///皮肤配置////summarypublicclassSkinConfig{publicstringSkinName{get;set;}}}

同时,我还需要把一些需要能够被全局控制的控件对象,也放到全局的状态管理器里面,以便于我在程序的任何一个位置,都能够调用到他,使他配合我们显示合适的场景或内容。比如,我当前正在访问哪个页面。

usingDevExpress.XtraBars;usingDevExpress.XtraBars.Alerter;usingDevExpress.XtraSplashScreen;usingDevExpress.XtraTabbedMdi;usingDevExpress.XtraTreeList;usingDevExpress.XtraTreeList.Nodes;usingSystem;usingSystem.Collections.Generic;usingSystem.Diagnostics;usingSystem.IO;usingSystem.Linq;usingSystem.Text;usingSystem.Threading.Tasks;usingSystem.Windows.Forms;namespaceHiAuth0{publicstaticclassHiAuthStaticSetting{publicstaticHiAuthManageSystemMainForm{get;set;}publicstaticXtraTabbedMdiManagerMdi{get;set;}publicstaticSplashScreenManagerSplashManager{get;set;}publicstaticAlertControlAlert{get;set;}publicstaticBarStaticItemCurrentTenancy{get;set;}publicstaticBarStaticItemCurrentLogin{get;set;}publicstaticBarStaticItemCurrentLocation{get;set;}publicstaticvoidSetCurrentLocationText(stringtext){HiAuthStaticSetting.CurrentLocation.Caption=$"您正在访问:{text}";}publicstaticvoidCloseMdiPagesOpenning(){intcount=HiAuthStaticSetting.Mdi.Pages.Count;for(inti=0;icount;i++)HiAuthStaticSetting.Mdi.Pages[0].MdiChild.Close();}publicstaticvoidSetWaittingFormOpen(Formfrm){SplashManager.ShowWaitForm();}publicstaticvoidSetWaittingFormClose(Formfrm){SplashManager.CloseWaitForm();}publicstaticvoidAlertMsg(Formfrm,stringmsg){Alert.Show(frm,newAlertInfo("温馨提示",msg));}publicstaticvoidAlertErrorMsg(Formfrm,stringmsg){Alert.Show(frm,newAlertInfo("错误提示",msg,global::HiAuth0.Properties.Resources.bug));}///summary///系统菜单点击事件处理////summary///paramname="sender"/param///paramname="e"/paramprivatestaticvoidMenuTree_MouseClick(objectsender,MouseEventArgse){TreeListHitInfohitinfo=(senderasTreeList).CalcHitInfo(e.Location);if(hitinfo.HitInfoType!=HitInfoType.Cell

e.Button!=MouseButtons.Left){return;}ShowChildForm(hitinfo.Node);}///summary///系统菜单树的图标分层显示////summary///paramname="sender"/param///paramname="e"/paramprivatestaticvoidMenuTree_CustomDrawNodeImages(objectsender,CustomDrawNodeImagesEventArgse){if(e.Node.Nodes.Count0!e.Node.Expanded){e.SelectImageIndex=0;}elseif(e.Node.Nodes.Count0e.Node.Expanded){e.SelectImageIndex=;}else{e.SelectImageIndex=;}}///summary///根据窗体Text属性获取打开的窗体实例////summary///paramname="formName"/param///returns/returnspublicstaticdynamicGetOpenedForm(stringformName){foreach(dynamicopenedForminApplication.OpenForms){if(openedForm.AccessibilityObject.Name==formName){returnopenedForm;}}returnnull;}///summary///根据窗体Text属性判断是否已经打开窗体////summary///paramname="formName"/param///returns/returnspublicstaticdynamicIsHasOpened(stringformName){foreach(dynamicopenedForminApplication.OpenForms){if(!openedForm.IsDisposedopenedForm.AccessibilityObject.Name==formName){returntrue;}}returnfalse;}///summary///连接共享文件夹,连接上后可以像操作本地磁盘的方式操作文件夹和文件////summary///paramname="path"共享文件夹路径/param///paramname="userName"用户名/param///paramname="passWord"密码/param///returns连接成功返回true,否则返回false/returnspublicstaticboolShareDiskConnect(){stringpath=LoginInfo.ShareDiskRootPath;//共享地址stringuserName=LoginInfo.ShareDiskUser;//共享用户stringpassWord=LoginInfo.ShareDiskPassword;//共享密钥boolFlag=false;//标识Processproc=newProcess();try{proc.StartInfo.FileName="cmd.exe";proc.StartInfo.UseShellExecute=false;proc.StartInfo.RedirectStandardInput=true;proc.StartInfo.RedirectStandardOutput=true;proc.StartInfo.RedirectStandardError=true;proc.StartInfo.CreateNoWindow=true;proc.Start();stringdosLine=

"netuse"+path+"/User:"+userName+""+passWord+"/PERSISTENT:YES";proc.StandardInput.WriteLine(dosLine);proc.StandardInput.WriteLine("exit");while(!proc.HasExited){proc.WaitForExit();}stringerrormsg=proc.StandardError.ReadToEnd();proc.StandardError.Close();if(string.IsNullOrEmpty(errormsg)){Flag=true;}else{//判断是否已经连上if(errormsg.Contains("发生系统错误9")){Flag=true;}else{thrownewException(errormsg);}}}catch(Exceptionex){throwex;}finally{proc.Close();proc.Dispose();}returnFlag;}///summary///获取样式保存位置////summary///returns/returnspublicstaticstringGetShareStylePath(){stringpath="\\DeepSeaAps\\ShareStyle\\"+LoginInfo.UserCode+LoginInfo.UserName;returnPath.Combine(LoginInfo.ShareDiskRootPath,path.StartsWith("\\")?path.Substring():path)+"\\";}///summary///获取固定列保存位置////summary///returns/returnspublicstaticstringGetFixedColumnConfigPath(){stringpath="\\DeepSeaAps\\FixedColumnConfig\\"+LoginInfo.UserCode+LoginInfo.UserName;returnPath.Combine(LoginInfo.ShareDiskRootPath,path.StartsWith("\\")?path.Substring():path)+"\\";}///summary///把打开窗体作为Mdi子窗体合并到主界面中////summary///paramname="frm"/parampublicstaticvoidShowAsChildForm(Formfrm){if(!IsHasOpened(frm.Text)){frm.MdiParent=MainForm;frm.Show();}else{dynamicform=GetOpenedForm(frm.Text);form.Dispose();frm.MdiParent=MainForm;frm.Show();}}///summary///打开窗体////summary///paramname="node"/parampublicstaticvoidShowChildForm(TreeListNodenode){if(!node.HasChildrennode.Visible==true){varformtype=node.GetValue("MenuPath")==null?null:node.GetValue("MenuPath").ToString();varformName=node.GetValue("MenuName")==null?null:node.GetValue("MenuName").ToString();if(formtype==null

formName==null){AlertErrorMsg(MainForm,"界面加载失败!请确认选择界面是否已经开放进入!如有疑问,请联系系统工程师!");}else{ShowChildForm(formtype,formName);}}}///summary///打开窗体////summary///paramname="formtype"窗体对象的全路径类型名称/param///paramname="formName"窗体的Text需要唯一识别/parampublicstaticvoidShowChildForm(stringformtype,stringformName){if(!IsHasOpened(formName)){try{dynamicfrm=Type.GetType(formtype).Assembly.CreateInstance(formtype);frm.MdiParent=MainForm;frm.Show();}catch(Exception){AlertErrorMsg(MainForm,"界面加载失败!请确认选择界面是否已经开放进入!如有疑问,请联系系统工程师!");}}else{dynamicform=GetOpenedForm(formName);form.Activate();}}///summary///设置等待框开启////summarypublicstaticvoidSetWaitFormOpen(Formfrm){if(SplashManager==null

SplashManager.IsSplashFormVisible!=true){SplashManager=newSplashScreenManager(frm,typeof(HiAuthWaitting),true,true);SplashManager.ShowWaitForm();}}///summary///设置等待框关闭////summarypublicstaticvoidSetWaitFormClose(Formfrm){if(SplashManager!=null

SplashManager.IsSplashFormVisible==true){SplashManager.CloseWaitForm();SplashManager.Dispose();}}}}

3、数据库结构

我们在这里仅仅展示一下表和结构的设计,随着程序的慢慢细化,我们可能会有一些调整。

下一篇开始,我们开始具体业务界面的操作介绍。

预览时标签不可点收录于话题#个上一篇下一篇
1
查看完整版本: 基于DevExpress从零开始搭建