客户关系管理系统中对客户及相关数据的导入导出分析处理

简介:

在很多系统,我们都知道,Excel数据的导入导出操作是必不可少的一个功能,这种功能能够给使用者和外部进行数据交换,也能批量迅速的录入数据到系统中;但在一些系统中,为了方便,可能把很多个基础表或者相关的数据综合到一个Excel表格文件里面,然后希望通过接口进行导入,这种需求处理就显得比较复杂一点了。本文探讨在我的客户关系管理系统中,对于单个Excel表格中,集合了客户基础数据及相关数据的导入和导出操作的处理。

1、导入导出的需求分析

本随笔主要介绍如何在系统中,导入单一文件中的数据到系统中,这个文件包含了基础数据和相关数据的导入和导出操作,一般来说这样的操作对于导入数据已经足够简便了,但是,有时候数据很多的情况下,我们可能需要每次选定文件也是一个麻烦的事情。因此指定目录进行批量数据的导入操作也是一个好的需求,可以进一步简化用户的数据导入操作。

下面我们就来介绍,导入、批量导入和导出的三个重要的操作,如图所示。

导入的数据,是一个Excel,它要求包含几个不同表的数据,导入操作一次性完成数据的导入,Excel文件的格式如下所示。

2、数据导入操作的界面设计及处理

我们知道,要一次性导入几个表的数据,需要先读取Excel获取各个Sheet(工作表)的数据,然后把它转换为DataTable的数据对象,这样我们就可以根据它的字段赋值给对应的实体类,然后调用业务逻辑处理将数据写入数据库即可。

为了直观的给使用者查看将要导入的数据,我们把需要导入到数据库的数据,展现在界面上,供客户确认,如果没有问题,就可以进行导入操作。由于我们需要操作多个数据表,因此有效读取Excel里面的Sheet就是第一步工作。

查看Excel数据的操作代码如下所示,主要的逻辑就是调用Apose.Cell的封装类进行处理

AsposeExcelTools.ExcelFileToDataSet(this.txtFilePath.Text, out myDs, out error);

把Excel文件里面多个Sheet的数据转换为DataSet,然后每个进行依次的处理,展示代码如下所示。

        private void ViewData()
        {
            if (this.txtFilePath.Text == "")
            {
                MessageDxUtil.ShowTips("请选择指定的Excel文件");
                return;
            }

            try
            {

                myDs.Tables.Clear();
                myDs.Clear();
                this.gridCustomer.DataSource = null;

                string error = "";
                AsposeExcelTools.ExcelFileToDataSet(this.txtFilePath.Text, out myDs, out error);
                this.gridCustomer.DataSource = myDs.Tables[0];
                this.gridView1.PopulateColumns();

                this.gridFollow.DataSource = myDs.Tables[1];
                this.gridView2.PopulateColumns();

                this.gridContact.DataSource = myDs.Tables[2];
                this.gridView3.PopulateColumns();

                this.gridSupplier.DataSource = myDs.Tables[3];
                this.gridView4.PopulateColumns();
            }
            catch (Exception ex)
            {
                LogTextHelper.Error(ex);
                MessageDxUtil.ShowError(ex.Message);
            }
        }

由于导入过程中需要耗费一定的时间,因此我们可以通过后台线程结合进度条的方式提示用户,界面设计效果如下效果所示。

刚才说到,保存数据,我们把它放到后台线程BackgroudWorker进行处理即可,处理代码如下所示。

        private void btnSaveData_Click(object sender, EventArgs e)
        {
            if (worker.IsBusy)
                return;

            if (this.txtFilePath.Text == "")
            {
                MessageDxUtil.ShowTips("请选择指定的Excel文件");
                return;
            }

            if (MessageDxUtil.ShowYesNoAndWarning("该操作将把数据导入到系统数据库中,您确定是否继续?") == DialogResult.Yes)
            {
                if (myDs != null && myDs.Tables[0].Rows.Count > 0)
                {
                    this.progressBar1.Visible = true;
                    worker.RunWorkerAsync();
                }
            }
        }

后台线程操作的主要业务逻辑代码如下所示,就是依次把不同的数据进行解析,并保存即可。

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            if (myDs != null && myDs.Tables.Count >= 4 && myDs.Tables[0].Rows.Count > 0)
            {
                try
                {
                    ImportCustomerDataHelper helper = new ImportCustomerDataHelper();
                    helper.LoginUserInfo = LoginUserInfo;

                    //写入或更新客户信息
                    string customerID = helper.UpdateCustomer(myDs.Tables[0]);
                    if (!string.IsNullOrEmpty(customerID))
                    {
                        helper.AddFollow(customerID, myDs.Tables[1], worker);
                        helper.AddContact(customerID, myDs.Tables[2], worker);
                        helper.AddSupplier(customerID, myDs.Tables[3], worker);

                        e.Result = "操作完成";
                    }
                    else
                    {
                        e.Result = "操作失败";
                    }
                }
                catch (Exception ex)
                {
                    e.Result = ex.Message;
                    LogTextHelper.Error(ex);
                    MessageDxUtil.ShowError(ex.ToString());
                }
            }
            else
            {
                e.Result = "请检查数据记录是否存在";
            }
        }

3、数据批量导入操作

虽然上面可以一次性导入客户和其相关数据,但是还是一次性导入一个Excel,如果对于客户数据比较多的情况下,一次次导入操作也是很繁琐的事情,因此客户提出,需要按照目录把所有相关的Excel数据一次性导入,这种导入有个问题就是我们不能再中途干预导入操作,因此为了数据的安全性,我提供一个界面让客户选择目录,然后把目录里面的Excel文件列出来,然后在让客户确认是否进一步导入。

上面操作的实现代码我逐一介绍,首先第一步是需要递归列出目录下面的Excel文件,然后显示出来供用户确认导入的清单。

        private void btnSelectPath_Click(object sender, EventArgs e)
        {
            string mydocDir = Environment.GetFolderPath(Environment.SpecialFolder.MyDocuments);
            string selectPath = FileDialogHelper.OpenDir(mydocDir);
            if (!string.IsNullOrEmpty(selectPath))
            {       
                //清空就记录
                this.lstPath.Items.Clear();

                string[] fileArray = Directory.GetFiles(selectPath, "*.xls", SearchOption.AllDirectories);
                if (fileArray != null && fileArray.Length > 0)
                {
                    foreach (string file in fileArray)
                    {
                        string fileName = Path.GetFileName(file);
                        this.lstPath.Items.Add(new CListItem(fileName, file));
                    }
                }
            }
        }

当用户确认操作的时候,提示客户确认是否进行,确认后将统一批量导入列表里面的文件,这个地方也是为了方便,使用后台线程进行数据的导出操作,并在过程中提供进度条的指示。

        private void btnConfirm_Click(object sender, EventArgs e)
        {
            if (worker.IsBusy)
                return;

            if (this.lstPath.Items.Count > 0)
            {
                if (MessageDxUtil.ShowYesNoAndTips("您确认导入列表的Excel文件吗?") == System.Windows.Forms.DialogResult.Yes)
                {
                    List<string> fileList = new List<string>();
                    foreach (object item in this.lstPath.Items)
                    {
                        CListItem fileItem = item as CListItem;
                        if (fileItem != null)
                        {
                            fileList.Add(fileItem.Value);
                        }
                    }

                    this.progressBar1.Visible = true;
                    worker.RunWorkerAsync(fileList);
                }
            }
        }

这个后台线程的处理逻辑和单个文件导入的操作差不多,只不过这里需要增加一个文件列表的遍历处理而已,具体代码如下所示。

        void worker_DoWork(object sender, DoWorkEventArgs e)
        {
            List<string> fileList = e.Argument as List<string>;
            if (fileList == null || fileList.Count == 0) return;

            bool hasError = false;
            ImportCustomerDataHelper helper = new ImportCustomerDataHelper();
            helper.LoginUserInfo = LoginUserInfo;

            foreach (string file in fileList)
            {
                DataSet myDs = new DataSet();
                string error = "";
                AsposeExcelTools.ExcelFileToDataSet(file, out myDs, out error);

                if (myDs != null && myDs.Tables.Count >= 4 && myDs.Tables[0].Rows.Count > 0)
                {
                    try
                    {
                        //写入或更新客户信息
                        string customerID = helper.UpdateCustomer(myDs.Tables[0]);
                        if (!string.IsNullOrEmpty(customerID))
                        {
                            helper.AddFollow(customerID, myDs.Tables[1], worker);
                            helper.AddContact(customerID, myDs.Tables[2], worker);
                            helper.AddSupplier(customerID, myDs.Tables[3], worker);
                        }
                    }
                    catch (Exception ex)
                    {
                        hasError = true;
                        LogTextHelper.Error(ex);
                    }
                }
            }

            string msg = "操作完成";
            if (hasError)
            {
                msg += ",导入出现错误。具体可以查看log.txt日志记录。";
            }
            e.Result = msg;
        }

和上面的单个文件导入一样,我们这里使用了一个封装类ImportCustomerDataHelper,用来对数据进行转换实体类,然后保存到数据库的操作过程,下面我们来简单看看里面的处理代码

    /// <summary>
    /// 客户数据的批量导入和普通导入的操作逻辑代码
    /// </summary>
    public class ImportCustomerDataHelper
    {
        /// <summary>
        /// 登陆用户信息
        /// </summary>
        public LoginUserInfo LoginUserInfo { get; set; }

        /// <summary>
        /// 写入或更新客户数据,如果成功更新返回ID值
        /// </summary>
        /// <param name="dataTable">客户数据表</param>
        /// <returns></returns>
        public string UpdateCustomer(DataTable dataTable)
        {
            bool success = false;
            bool converted = false;
            DateTime dtDefault = Convert.ToDateTime("1900-01-01");
            DateTime dt;
            string result = "";

            DataRow dr = dataTable.Rows[0];
            if (dr != null)
            {
                string customerName = dr["客户名称"].ToString();
                CustomerInfo info = CallerFactory<ICustomerService>.Instance.FindByName(customerName);
                bool isNew = false;
                if (info == null)
                {
                    info = new CustomerInfo();
                    isNew = true;
                }

                info.Name = customerName;
                info.HandNo = dr["客户编号"].ToString();
                info.SimpleName = dr["客户简称"].ToString();
                ..........................
                info.IsPublic = dr["公开与否"].ToString().ToBoolean();
                info.Satisfaction = dr["客户满意度"].ToString().ToInt32();
                info.TransactionCount = dr["交易次数"].ToString().ToInt32();
                info.TransactionTotal = dr["交易金额"].ToString().ToDecimal();
                info.Creator = dr["客户所属人员"].ToString();
                converted = DateTime.TryParse(dr["创建时间"].ToString(), out dt);
                if (converted && dt > dtDefault)
                {
                    info.CreateTime = dt;
                }
                info.Editor = LoginUserInfo.ID.ToString();
                info.EditTime = DateTime.Now;

                if (isNew)
                {
                    info.Dept_ID = LoginUserInfo.DeptId;
                    info.Company_ID = LoginUserInfo.CompanyId;
                    success = CallerFactory<ICustomerService>.Instance.Insert(info);
                }
                else
                {
                    success = CallerFactory<ICustomerService>.Instance.Update(info, info.ID);
                }

                if (success)
                {
                    result = info.ID;
                }
            }

            return result;
        }

...........................

4、数据的导出操作

导出操作,我们根据用户的选择,可以一次性导出多个Excel文件,每个Excel文件包含客户的基础信息,也包含相关数据,它们的格式和导入的格式保持一致即可,这样方便数据的交换处理。

导出操作,我们需要把客户的选择信息转换为需要导出的对象列表数据,然后绑定到Excel里面即可,因此我们的Excel里面,可以通过自定义模板,指定列的数据属性就可以绑定好数据了。

获取选择的客户信息的代码如下所示。

                List<CustomerInfo> list = new List<CustomerInfo>();
                foreach (int iRow in rowSelected)
                {
                    string ID = this.winGridViewPager1.GridView1.GetRowCellDisplayText(iRow, "ID");
                    CustomerInfo info = CallerFactory<ICustomerService>.Instance.FindByID(ID);
                    if (info != null)
                    {
                        list.Add(info);
                    }
                }

前面介绍了,我们将使用自定义模板,在模板文件里面的对应字段下面,绑定一个参数属性就可以了,通过Aspose.Cell的操作处理,我们就很方便把数据导出到Excel里面了,而里面的字段还可以很方便实现自由的裁剪操作。

自定义模板文件效果如下所示。

导出客户以及相关信息的主要核心代码如下所示。

                            #region 导出操作
                            //依次每个客户数据导出一个文件
                            string ownerUserName = CallerFactory<IUserService>.Instance.GetFullNameByID(customerInfo.Creator.ToInt32());
                            string filePath = Path.Combine(selectPath, ownerUserName);
                            DirectoryUtil.AssertDirExist(filePath);

                            Dictionary<string, object> dict = new Dictionary<string, object>();
                            dict.Add("Customer", new List<CustomerInfo>() { customerInfo });//需要构造一个列表绑定

                            List<FollowInfo> followList = CallerFactory<IFollowService>.Instance.Find(string.Format("Customer_ID ='{0}' ", customerInfo.ID));
                            dict.Add("Follow", followList);

                            List<ContactInfo> contactList = CallerFactory<IContactService>.Instance.FindByCustomer(customerInfo.ID);
                            dict.Add("Contact", contactList);

                            PagerInfo pagerInfo = null;
                            List<SupplierInfo> supplierList = CallerFactory<ISupplierService>.Instance.FindByCustomer(customerInfo.ID, "", ref pagerInfo);
                            dict.Add("Supplier", supplierList);

                            string templateFile = Path.Combine(System.AppDomain.CurrentDomain.BaseDirectory, "客户综合资料-导出模板.xls");
                            if (!File.Exists(templateFile))
                            {
                                throw new ArgumentException(templateFile, string.Format("{0} 文件不存在,", Path.GetFileName(templateFile)));
                            }

                            string saveFileName = string.Format("{0}.xls", customerInfo.Name);
                            string saveFilePath = Path.Combine(filePath, saveFileName);

                            WorkbookDesigner designer = new WorkbookDesigner();
                            designer.Workbook = new Workbook(templateFile);
                            foreach (string key in dict.Keys)
                            {
                                designer.SetDataSource(key, dict[key]);
                            }
                            designer.Process();
                            designer.Workbook.Save(saveFilePath, SaveFormat.Excel97To2003);   
                            #endregion

这样利用Aspose.Cell的处理操作,通过绑定相关的数据对象,我们就很容易实现数据导出到符合我们预期格式的Excel里面去了,这样操作高效、代码干净,Excel格式也非常符合我们的要求。

以上就是在客户关系管理系统里面碰到特殊的数据导入导出需求的介绍和实现,希望大家相互交流,共同把软件开发过程中,数据导入导出操作的使用体验做到最好,更符合我们客户使用的习惯和需求。

本文转自博客园伍华聪的博客,原文链接:客户关系管理系统中对客户及相关数据的导入导出分析处理,如需转载请自行联系原博主。



目录
相关文章
|
22天前
掌握销售之道:深入学习ERP系统的销售与客户关系管理模块
掌握销售之道:深入学习ERP系统的销售与客户关系管理模块
31 8
|
1月前
|
Oracle 关系型数据库 数据管理
企业进销存管理系统的设计与实现_kaic
企业进销存管理系统的设计与实现_kaic
|
20天前
|
数据采集 数据可视化 算法
深入解析ERP系统的业务智能与报表分析模块
深入解析ERP系统的业务智能与报表分析模块
21 3
|
23天前
|
监控 BI
财务智慧:全面解析ERP系统的财务管理模块
财务智慧:全面解析ERP系统的财务管理模块
21 0
|
1月前
|
人工智能 数据可视化 安全
提升工作效率,告别Excel,尝试Zoho CRM客户管理!
曾经有客户咨询我们:“EXCEL管理客户功能不够用,但是又觉得CRM管理系统太麻烦,应该如何选择?”这篇文章就告诉您:EXCEL在客户管理方面都有哪些局限性?CRM管理系统都有哪些优势?初创企业应该怎样选择适合的CRM系统?
34 0
|
4月前
|
数据可视化 BI
甲骨文ERP收款自动录入:提高财务效率,实现智能化管理
通过八爪鱼rpa自动化的方式,我们可以实现财务工作的智能化管理,提高工作效率和准确性。同时,八爪鱼rpa的使用也非常简单,用户可以通过可视化的界面进行操作,无需编写复杂的代码。因此,八爪鱼rpa不仅适用于大型企业,也适合中小型企业使用。
|
8月前
|
数据可视化 搜索推荐 数据管理
CRM客户关系管理系统的主要功能
一套完整的CRM系统应能实现营销、销售、服务等业务的自动化,实现客户数据共享,达到提高客户满意度、降低成本、增加收人、开拓市场、帮助企业高层进行生产、营销等决策的目的。当然,面向不同的行业,不同的CRM厂商开发的产品具备差异性的功能,例如比较注重生命周期管理面向B2B行业的CRM,注重营销管理面向电商行业的营销型CRM等等,一般CRM系统都会拥有如下主要功能。
|
10月前
|
存储 搜索推荐 数据可视化
基于电子商务平台客户管理系统的设计与实现_kaic
本论文旨在设计和实现一个基于电子商务平台的客户关系管理系统,以提高企业与客户之间的互动和关系维护效率。本文首先介绍了客户关系管理系统的相关理论和技术,并分析了其在电子商务平台中的应用价值。接着,进行了电子商务平台客户关系管理系统的需求分析,详细阐述了系统的功能需求和数据需求,并提出了相应的解决方案。在此基础上,本文设计了系统的结构和数据库,并实现了后台管理模块和前台客户模块。最后,本文进行了系统应用和效果评价,发现该系统能够有效提高企业与客户之间的沟通效率和关系维护效果。本文的研究成果为电子商务平台客户关系管理系统的设计和实现提供了有益的参考和借鉴,并为今后相关研究提供了一定的指导和启示。
|
存储 安全 数据管理
2.3连接企业存量资产管理系统|学习笔记(一)
快速学习2.3连接企业存量资产管理系统
143 0
2.3连接企业存量资产管理系统|学习笔记(一)
|
开发者
2.3连接企业存量资产管理系统|学习笔记(二)
快速学习2.3连接企业存量资产管理系统
103 0
2.3连接企业存量资产管理系统|学习笔记(二)