在上篇随笔《Winform开发框架之客户关系管理系统(CRM)的开发总结系列1-界面功能展示》中介绍了我的整个CRM系统的概貌,本篇继续本系列的文章,介绍如何基于我的《winform开发框架》的基础上进行CRM系统模块的开发工作,希望对大家在系统模块开发有所启示或者帮助。

在我整个开发框架的体系结构中,我都希望开发的业务模块尽可能重用,因此遵循这个要求,所有的模块除了一些基础模块外,尽可能和其他业务模块没有任何耦合关系,同时也可以动态对模块进行加载使用,和我在《Winform开发框架之插件化应用框架实现》的思想一样,各个模块之间可以动态组合起来,实现更多的业务整合。

1、CRM系统的工程项目介绍

本客户关系管理系统,也是基于这个目的和基础上进行模块开发,在整个项目模块开发过程中,将会利用到整个Winform开发框架的相关组件模块,包括基础界面模块、程序启动模块、权限管理模块、字典管理模块、分页控件、公用类库、附件管理等公用模块。

整个CRM系统的界面效果如下所示。

首先我们来看看CRM系统主要项目工程的布局和说明。

解决方案项目工程介绍
WHC.Framework.BaseUIDx 重用模块。各模块的界面基类,包括通用窗体界面基类BaseUI;通用高级查询模块AdvanceSearch;通用Excel导入模块;插件化接口相关类PlugInInterface等。
WHC.Framework.StarterDx 重用模块。插件化应用程序启动界面。通过用户登录后,获取用户的菜单,动态创建菜单并加载插件,该模块集成权限管理系统模块、集成字典数据管理模块、集成多文档界面控件和布局控件、集成美观实用的登陆界面、闪屏展示界面、托盘缩小提示功能、全局运行一次实例限制模块代码等内容。
WHC.CRM.Core 新开发模块。客户关系管理系统的核心模块,包括业务处理、数据接口层、数据访问层、实体层等相关类。
WHC.CRM.UIDx 新开发模块。客户关系管理系统的界面层,包括所系统模块的各种界面、控件等内容。

设计好CRM的相关数据库表后,利用C#代码生成工具Database2Sharp生成框架各层的代码,模块开始开发的时候,可以一次性把所有业务表的代码一次性生成,然后在整个新的解决方案(.sln)上进行递增式完善即可,如果是后续模块的开发,则需要增量把生成的代码,复制到相关的框架目录即可,整理后的业务逻辑层代码结构如下所示

这个时候,我们生成了界面层以下的所有分层代码,整个代码生成后,一次性即可编译通过,界面层我们另外建立一个Winform项目工程WHC.CRM.UIDx ,然后添加相关的界面引用程序集(如DevExpress的相关界面程序集)。处理完这些后,我们又可以利用C#代码生成工具Database2Sharp来实现界面的快速开发工作了,代码生成工具生成界面的操作界面如下所示,具体生成界面的操作可以参考随笔《利用代码生成工具Database2Sharp设计数据编辑界面》进行了解。

最后得到类似项目目录结构的CRM系统界面模块工程。

由于整个CRM系统包含很多界面元素,因此以上模块的界面部分只是其中一部分,如果内容较多,可以建立目录进行分类管理,这样会更加清晰。

2、CRM系统的界面层代码分析

利用C#代码生成工具Database2Sharp,可以快速生成所需要的框架界面代码,包括集成各种已有模块的界面基类、导入导出模块支持、高级查询能功能模块,各种实体类对应关系等内容,这些如果利用手工操作,效率非常低下,而且容易出错。即使利用一些代码生成工具,如果没有和现成的界面模块进行很好的整合,也需要花费大量的时间进行整理,下面通过几个界面代码的展示进行大致的了解。

1)列表显示界面的集成和分页整合

2)字典模块的整合处理(通过扩展类方法实现)

3)导入导出模块的整合

        private string moduleName = "客户合同信息";
/// <summary>
/// 导入Excel的操作
/// </summary>
private void btnImport_Click(object sender, EventArgs e)
{
string templateFile = string.Format("{0}-模板.xls", moduleName);
FrmImportExcelData dlg = new FrmImportExcelData();
dlg.SetTemplate(templateFile, System.IO.Path.Combine(Application.StartupPath, templateFile));
dlg.OnDataSave += new FrmImportExcelData.SaveDataHandler(ExcelData_OnDataSave);
dlg.OnRefreshData += new EventHandler(ExcelData_OnRefreshData);
dlg.ShowDialog();
} void ExcelData_OnRefreshData(object sender, EventArgs e)
{
BindData();
} bool ExcelData_OnDataSave(DataRow dr)
{
string customerName = dr["客户名称"].ToString();
if (string.IsNullOrEmpty(customerName))
return false; CustomerInfo customerInfo = BLLFactory<Customer>.Instance.FindByName(customerName);
if (customerInfo == null)
{
throw new ArgumentException(string.Format("客户名称【{0}】不存在,记录已跳过", customerName));
} bool success = false;
bool converted = false;
DateTime dtDefault = Convert.ToDateTime("1900-01-01");
DateTime dt;
ContractInfo info = new ContractInfo();
info.Customer_ID = customerInfo.ID;//客户ID
info.HandNo = dr["合同编号"].ToString();
info.ExpenditureType = dr["收支类型"].ToString();
info.ContractType = dr["合同类型"].ToString();
info.ContractName = dr["合同名称"].ToString();
info.ContractMoney = dr["合同金额"].ToString().ToDecimal();
converted = DateTime.TryParse(dr["签约日期"].ToString(), out dt);
if (converted && dt > dtDefault)
{
info.SignDate = dt;
}
........................................ success = BLLFactory<Contract>.Instance.Insert(info);
return success;
} /// <summary>
/// 导出Excel的操作
/// </summary>
private void btnExport_Click(object sender, EventArgs e)
{
string file = FileDialogHelper.SaveExcel(string.Format("{0}.xls", moduleName));
if (!string.IsNullOrEmpty(file))
{
string where = GetConditionSql();
List<ContractInfo> list = BLLFactory<Contract>.Instance.Find(where);
DataTable dtNew = DataTableHelper.CreateTable("序号|int,客户名称,合同编号,收支类型,合同类型,合同名称,合同金额,公司签约人,客户签约人,签约日期,签约地点,乙方名称,合同开始日期,合同结束日期,结算情况,合同状态,关联项目,联系人,联系人电话,联系人手机,合同内容,备注说明,经办人");
DataRow dr;
int j = ;
for (int i = ; i < list.Count; i++)
{
dr = dtNew.NewRow();
dr["序号"] = j++;
dr["客户名称"] = BLLFactory<Customer>.Instance.GetCustomerName(list[i].Customer_ID);//转义为客户名称
dr["合同编号"] = list[i].HandNo;
dr["收支类型"] = list[i].ExpenditureType;
dr["合同类型"] = list[i].ContractType;
......................................
dr["经办人"] = list[i].Operator;
dtNew.Rows.Add(dr);
} try
{
string error = "";
AsposeExcelTools.DataTableToExcel2(dtNew, file, out error);
if (!string.IsNullOrEmpty(error))
{
MessageDxUtil.ShowError(string.Format("导出Excel出现错误:{0}", error));
}
else
{
if (MessageDxUtil.ShowYesNoAndTips("导出成功,是否打开文件?") == System.Windows.Forms.DialogResult.Yes)
{
System.Diagnostics.Process.Start(file);
}
}
}
catch (Exception ex)
{
LogTextHelper.Error(ex);
MessageDxUtil.ShowError(ex.Message);
}
}
}

4)高级查询模块的整合

        private FrmAdvanceSearch dlg;
private void btnAdvanceSearch_Click(object sender, EventArgs e)
{
if (dlg == null)
{
dlg = new FrmAdvanceSearch();
dlg.FieldTypeTable = BLLFactory<Contract>.Instance.GetFieldTypeList();
dlg.ColumnNameAlias = BLLFactory<Contract>.Instance.GetColumnNameAlias();
dlg.DisplayColumns = "Customer_ID,HandNo,ExpenditureType,ContractType,ContractName,ContractMoney,CompanySigner,CustomerSigner,SignDate,SignLocation,PartyBName,StartDate,EndDate,Settlement,Status,RelatedItems,Contact,ContactPhone,ContactMobile,Content,Note,Operator"; #region 下拉列表数据 //dlg.AddColumnListItem("UserType", Portal.gc.GetDictData("人员类型"));//字典列表
//dlg.AddColumnListItem("Sex", "男,女");//固定列表
//dlg.AddColumnListItem("Credit", BLLFactory<Contract>.Instance.GetFieldList("Credit"));//动态列表 #endregion dlg.ConditionChanged += new FrmAdvanceSearch.ConditionChangedEventHandler(dlg_ConditionChanged);
}
dlg.ShowDialog();
} void dlg_ConditionChanged(SearchCondition condition)
{
advanceCondition = condition;
BindData();
}

5)编辑界面的基类继承

3、CRM系统的界面层的用户及权限信息传递

我们知道,每个业务模块都可能需要获取当前登录的一些用户信息和权限信息,以便达到更好的控制和数据的显示,如某些模块,可能只需要显示当前用户的数据,由于CRM系统的界面是独立开发,不整合在启动界面模块里面,那么如何获得用户和权限控制信息呢?例如下面的CRM模块里面的界面,需要根据当前用户获取到客户的分组列表的。

首先我们可以在界面基类中实现一个接口,以便传递相关的用户和权限信息。

namespace WHC.Framework.BaseUI
{
public partial class BaseForm : DevExpress.XtraEditors.XtraForm, IFunction
{

其中IFunction的定义如下所示。

namespace WHC.Framework.BaseUI
{
/// <summary>
/// 父窗体实现的权限控制接口
/// </summary>
public interface IFunction
{
/// <summary>
/// 初始化权限控制信息
/// </summary>
void InitFunction(LoginUserInfo userInfo, Dictionary<string, string> functionDict); /// <summary>
/// 是否具有访问指定控制ID的权限
/// </summary>
/// <param name="controlId">功能控制ID</param>
/// <returns></returns>
bool HasFunction(string controlId); /// <summary>
/// 登陆用户基础信息
/// </summary>
LoginUserInfo LoginUserInfo { get; set; } /// <summary>
/// 登录用户具有的功能字典集合
/// </summary>
Dictionary<string, string> FunctionDict { get; set; } /// <summary>
/// 应用程序基础信息
/// </summary>
AppInfo AppInfo { get; set; } }
}

其中就定义了接口进行用户和权限信息的赋值。在界面按钮构建相关模块的功能界面窗体的时候,我们可以为这些传递传递相关的对象信息。

        private void barCRMContact_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
ChildWinManagement.LoadMdiForm(this, typeof(FrmCustomerContact));
} private void barCRMCustomer_ItemClick(object sender, DevExpress.XtraBars.ItemClickEventArgs e)
{
ChildWinManagement.LoadMdiForm(this, typeof(FrmCustomerManage));
}
                tableForm = (Form) Activator.CreateInstance(formType);
tableForm.MdiParent = mainDialog; //如果窗体集成了IFunction接口(第一次创建需要设置)
IFunction function = tableForm as IFunction;
if (function != null)
{
//初始化权限控制信息
function.InitFunction(Portal.gc.LoginUserInfo, Portal.gc.FunctionDict); //记录程序的相关信息
function.AppInfo = new AppInfo(Portal.gc.AppUnit, Portal.gc.AppName, Portal.gc.AppWholeName, Portal.gc.SystemType);
}

通过在构造窗体的时候,传入相关的用户和权限对象属性即可实现这些信息的传递和使用。

另外,为了更加方便信息的传递,我们也可以在用户登陆的时候,把这些信息通过Cache对象把它缓存起来,类似Web开发里面的Session功能。

我的该CRM系统系列的几篇随笔链接如下,供阅读。

Winform开发框架之客户关系管理系统(CRM)的开发总结系列1-界面功能展示

Winform开发框架之客户关系管理系统(CRM)的开发总结系列2-基于框架的开发过程

Winform开发框架之客户关系管理系统(CRM)的开发总结系列3-客户分类和配置管理实现

Winform开发框架之客户关系管理系统(CRM)的开发总结系列4-Tab控件页面的动态加载

Winform开发框架之客户关系管理系统(CRM)的开发总结系列2-基于框架的开发过程的更多相关文章

  1. Winform开发框架之客户关系管理系统(CRM)的开发总结系列4-Tab控件页面的动态加载

    在前面介绍的几篇关于CRM系统的开发随笔中,里面都整合了多个页面的功能,包括多文档界面,以及客户相关信息的页面展示,这个模块就是利用DevExpress控件的XtraTabPage控件的动态加载实现的 ...

  2. Winform开发框架之客户关系管理系统(CRM)的开发总结系列3-客户分类和配置管理实现

    我在本系列随笔的开始,介绍了CRM系统一个重要的客户分类的展示界面,其中包含了从字典中加载分类.从已有数据中加载分类.以及分组列表中加载分类等方式的实现,以及可以动态对这些节点进行配置,实现客户分类的 ...

  3. Winform开发框架之客户关系管理系统(CRM)的开发总结系列1-界面功能展示

    一直以来,都希望整合一个以客户为中心的平台,有两个方面的考虑:一是实现客户数据.客户关系.客户管理等方面的整合,以便更好利用好客户的相关资源,发挥最大的营销效益:二是整合目前我的开发框架的所有模块和技 ...

  4. Winform开发框架之客户关系管理系统(CRM)的报价单和销售单的处理

    在前面介绍了很多CRM相关的界面和实现思路的随笔文章,本篇继续介绍一下系统中用到的一些经验和技巧片段.本篇随笔主要介绍客户关系管理系统(CRM)的报价单和销售单的处理界面效果,使用列表内置的选择代替弹 ...

  5. 客户关系管理系统(CRM)的开发过程中使用到的开发工具总结

    开发<客户关系管理系统(CRM)>软件过程,也就是一个标准的Winform程序的开发过程,我们可以通过这个典型的软件开发过程来了解目前的开发思路.开发理念,以及一些必要的高效率手段.本篇随 ...

  6. Java高级项目实战02:客户关系管理系统CRM系统模块分析与介绍

    本文承接上一篇:Java高级项目实战之CRM系统01:CRM系统概念和分类.企业项目开发流程 先来CRM系统结构图: 每个模块作用介绍如下: 1.营销管理 营销机会管理:针对企业中客户的质询需求所建立 ...

  7. 客户关系管理系统CRM

    http://www.cnblogs.com/Michael2397/tag/SSH%E9%A1%B9%E7%9B%AE-CRM/   客户关系管理系统

  8. 客户关系管理系统-CRM源码

    QQ:2112326142   邮箱:jxsupport@qq.com 本公司开发的CRM源代码系统一份,附源代码,本公司产品唯一销售客服QQ号:2112326142  请联系此QQ号,以免给您的工作 ...

  9. AEAI CRM V1.6.0 升级说明,开源客户关系管理系统

    1 升级说明 AEAI CRM v1.6.0版是AEAI CRM v1.5.2版客户关系管理系统的升级版本,本次版本是基于AEAI DP v3.8.0_20170228进行打包部署的,升级内容主要是针 ...

随机推荐

  1. 表单验证Jquery扩展方法类

    /** 表单数据验证 **/ $.fn.Validform = function () { var Validatemsg = ""; var Validateflag = tru ...

  2. 优先队列求解Huffman编码 c++

    优先队列小析      优先队列的模板: template <class T, class Container = vector<T>,class Compare = less< ...

  3. select into的不同数据库实现

    DB2中修改表的schema除了通常的创建别名,还可以删除表重建 删除表的顺序应该是先在另一个Schema下新建表,然后使用select ino将原表的数据导入新表,再删除原表. 实现如下: 在Sch ...

  4. 带着问题学 Spring MVC 源码: 一、概述

    摘要: 原创出处:www.bysocket.com 泥瓦匠BYSocket 希望转载,保留摘要,谢谢! 简单就好,生活可以很德国 Q:什么是 Spring MVC ? ※ Spring MVC 是 S ...

  5. SetHandleInformation设置内核对象标志

    当父进程创建子进程时,子进程将继承父进程的内核对象.这时如果要控制子进程使用父进程的内核对象.可以使用 SetHandleInformation设置. BOOL SetHandleInformatio ...

  6. mac 进程管理

    1.前言 启用goagent.firefly等服务的时候,如果非正常退出,再次启动经常会遇到address already in use,端口被先前启动的服务进程所占用,导致服务无法使用.这种情况,可 ...

  7. js类(继承)(二)

    1. 定义js类 js并不是一种面向对向的语言, 没有提供对类的支持, 因此我们不能像在传统的语言里那样 用class来定义类, 但我们可以利用js的闭包封装机制来实现js类, 我们来封装一个简的Sh ...

  8. 基于SWFUpload的angular上传组件

    回顾 由于工作内容比较多,特别是架构方面,需要耗费很多的时间调整.重构,因此很久没有写文章了. 话就不多说了,直接进入主题. 实现 首先分析一下SWFUpload初始化的时候,需要传入当前触发上传的元 ...

  9. JS文档和Demo自动化生成工具 - SmartDoc发布

    曾几何时,当你码神附体,一路披荆斩棘的完成代码后,带着“一码在手,天下我有”的傲然环顾之时,却发现单元测试.API文档.Demo实例陆续向你砸来,顿时有木有一种冰水挑战后的感觉.而这时你应该:哟哟,快 ...

  10. AppStore下载慢的真实原因

    今天有个朋友说他的app下载很慢,他说下载其他的不会,就他的会很慢很卡.我心想这东西苹果的我也没办法了呀,不过我心里也想我平时下载挺快的呀.于是就开始试试.搜索了几个app下载发现确实是有快有慢,并且 ...