在Winform应用中增加通用的业务编码规则生成

简介: 在Winform应用中增加通用的业务编码规则生成

在我们很多应用系统中,往往都需要根据实际情况生成一些编码规则,如订单号、入库单号、出库单号、退货单号等等,我们有时候根据规则自行增加一个函数来生成处理,不过我们仔细观察后,发现它们的编码规则有很大的共通性,因此可以考虑使用一些通用的业务编码规则生成,从而在系统中统一维护即可,本篇随笔介绍如何在WInform界面中实现通用的业务编码规则生成。

1、常见单号的业务编码规则

刚才我们提到一些编码规则,如订单号、入库单号、出库单号、退货单号等等,它们都是有大同小异的规则,有前缀、有日期的编码、有一些流水号,还有一些特殊的规则处理,往往就是这些,需要协调好流水号的增加处理即可。

例如,原来在我的CRM业务模块中,增加了一个函数,用来生成订单号的,如下所示。

/// <summary>
        /// 生成单据号码,编码为XS-{userId}-{yyyyMMdd}-流水号
        /// </summary>
        /// <returns></returns>
        public async Task<string> GetOrderNo()
        {
            string prefix = string.Format("XS-{0}-{1}", CurrentApiUser.Id, DateTime.Now.ToString("yyyyMMdd"));
            //获取当天的记录数量+1
            DateTime currentDate = DateTime.Now.ToString("yyyy-MM-dd").ToDateTime(); //当前日期
            //计算条件数量+1
            int count = this.EntityDb.Count(s => s.OrderDate >= currentDate && s.OrderDate <= currentDate.AddDays(1)) + 1;
            //循环检索,直到不重复的编号
            string number = string.Format("{0}-{1}", prefix, count);
            while (true)
            {
                var result = await CheckNumberExist(number);
                if (result)
                {
                    //存在增加1再判断
                    number = string.Format("{0}-{1}", prefix, count++);
                }
                else
                {
                    break;
                }
            }
            return number;
        }

这里为了增加对流水号的循环判断,直到没有重复的即可输出来作为订单号。

大多数的编码规则大同小异,因此我们可以考虑使用共同的规则进行处理,类似我们通用字典的模块处理。订单编码,可以在新建订单的时候生成,也可以提供用户手动生成【生成编号】的操作,如下界面所示。

 

2、设计通用的业务编码规则

我们归纳了一些编码规则,基本上也就是前缀,日期分隔,分隔符,后缀,流水号这些元素的组合,如果需要更加复杂的也可以自行调整接口,我们这里设计一个通用的编码规则,对这些元素进行组合配置,数据库设计如下所示。

根据这些内容,我们使用手工编码或者代码生成工具生成相关的基础代码 (可以基于EnterpriseLibrary的框架代码或者基于SqlSugar开发框架的代码),最终我们都用于WInform的界面调用。

这里以我们基于SqlSugar开发框架的代码生成为例。

生成后,会生成一个相关的业务类,实现相关的CRUD接口,如下代码定义所示,如果你有自己的基础框架实现,那么也可以忽略具体的代码生成,关注业务编码的生成的的规则即可。

/// <summary>
   /// 业务表编码规则 应用层服务接口实现
   /// </summary>
   public class TableNumberService : MyCrudService<TableNumberInfo, string, TableNumberPagedDto>, ITableNumberService

为了控制编码的规则生成,我们增加一个同步锁来实现冲突处理。

/// <summary>
        /// 同步锁
        /// </summary>
        private static SemaphoreSlim syncRoot = new SemaphoreSlim(1);

最终我们的实现代码如下所示。

/// <summary>
        /// 根据定义表名、单据头、分割符1、分割符2,生成业务编码。如果生成错误,返回空字符串
        /// </summary>
        /// <param name="tableNameOrCode">表名或代码</param>
        /// <returns></returns>
        public async Task<string> GenerateNumber(string tableNameOrCode)
        {
            string businessNumber = "";
            await syncRoot.WaitAsync(); //等待锁
            try
            {
                var info = await base.GetFirstAsync(s => s.TableName == tableNameOrCode || s.Code == tableNameOrCode);
                if (info != null)
                {
                    string currentDate = "";
                    string lastDate = "";
                    int currentNumber = 1; //流水号起始值
                    int serialLength = 3; //流水号长度
                    if(!info.LastGenerateTime.HasValue)
                    {
                        info.LastGenerateTime = DateTime.Now;
                    }
                    if (info.RuleFormat == "年月日")
                    {
                        currentDate = DateTime.Now.ToString("yyyyMMdd");
                        lastDate = info.LastGenerateTime.Value.ToString("yyyyMMdd");
                    }
                    else if (info.RuleFormat == "年月")
                    {
                        currentDate = DateTime.Now.ToString("yyyyMM");
                        lastDate = info.LastGenerateTime.Value.ToString("yyyyMM");
                    }
                    //如果当前日期和最后日期不一致,流水号重置为0
                    if(!currentDate.Equals(lastDate))
                    {
                        info.CurrentValue = 0;
                    }
                    //如果流水号非起始值,那么累计计算
                    if(info.CurrentValue.HasValue && info.CurrentValue >= 0)
                    {
                        currentNumber = (int)info.CurrentValue + 1;//流水号当前值
                    }
                    //流水号长度
                    if(info.ValueLength.HasValue && info.ValueLength > 3)
                    {
                        serialLength = (int)info.ValueLength;//流水号长度
                    }
                    var SplitString1 = string.IsNullOrEmpty(info.SplitString1) ? "-" : info.SplitString1;
                    var SplitString2 = string.IsNullOrEmpty(info.SplitString2) ? "-" : info.SplitString2;
                    //生成业务编码
                    businessNumber = $"{info.Prex}{SplitString1}{currentDate}{SplitString2}{currentNumber.ToString().PadLeft(serialLength, '0')}{info.Suffix}";
                    //更新记录
                    info.CurrentValue = currentNumber;
                    info.SplitString1 = SplitString1;
                    info.SplitString2 = SplitString2;
                    info.CurrentNumberString = businessNumber;
                    info.LastGenerateTime = DateTime.Now;//更新最后生成编码日期
                    await base.UpdateAsync(info);
                }
            }
            catch (Exception ex)
            {
                var errorText = $"生成单号时出现错误:{ex.Message}";
                LogTextHelper.Error(errorText, ex);
            }
            finally
            {
                syncRoot.Release();//释放锁
            }
            return businessNumber;
        }

上面主要注意的就是流水号的生成,这个稍微特殊处理一下,如果定义的规则是年月日,那么和最后的生成日期和当前日期不一致的话(转换为年月日对比),就认为流水号重新重置为1,否则是同一天的,流水号递增即可。如果是年月的,也是判断最后日期和当前日期的年月是否一致,不一致则重置为1,否则递增。注意流水号的编码长度,一般为4位,如果不满足的可以增加到6位等。

最终我们实际的业务编码的管理界面和查看的对应编码的界面如下所示,供参考设计界面处理。

编辑单个业务编码规则的界面如下所示。

为了方便,我们这里提供一个【测试生成】的按钮,用于测试具体的编码生成,我们具体的业务调用,就是类似这个调用即可。

var handNo = await BLLFactory<ITableNumberService>.Instance.GenerateNumber(tableNameOrCode);

同样,我们也可以把这个界面搬到WPF框架界面上去,可以重用具体的业务编码规则处理,如上类似的界面处理。

单个通用的业务编码规则的编辑界面如下所示。

因此,不管对于Winform还是WPF的界面,他们的展示方式都是类似的,我们可以重用业务层对通用编码规则的定义。

 

专注于代码生成工具、.Net/.NetCore 框架架构及软件开发,以及各种Vue.js的前端技术应用。著有Winform开发框架/混合式开发框架、微信开发框架、Bootstrap开发框架、ABP开发框架、SqlSugar开发框架等框架产品。
 转载请注明出处:撰写人:伍华聪  http://www.iqidi.com
   

相关文章
|
数据采集 监控 安全
数据标准应用(三):数据标准落标监控-下篇
数据标准创建完成后,需要指定其关联的资产对象才能发挥应用价值。数据标准和资产对象的映射关系通过落标映射规则来管理,对象是否遵循了映射到的标准定义则通过落标监控规则来判断。本文为您介绍落标监控评估的基本概念和监控逻辑。Dataphin 支持通过定义标准属性和资产对象元数据字段之间的匹配关系,自动生成数据标准和资产对象的映射关联;针对已确定的映射关系,可结合数据标准的定义对关联的资产对象进行落标监控,包括元数据监控和内容质量监控。上篇,我们为大家介绍了数据标准监控的分类和配置方式,本期我们将为您介绍配置好的落标监控如何生效以及如何查看监控结果。
539 0
|
6月前
【突破常规:让函数规范成为注目的亮点】(下)
【突破常规:让函数规范成为注目的亮点】
|
6月前
【突破常规:让函数规范成为注目的亮点】(上)
【突破常规:让函数规范成为注目的亮点】
|
存储 XML SQL
浅谈扩展字段设计
浅谈扩展字段设计
419 0
|
SQL 消息中间件 缓存
12种接口优化的通用方案
12种接口优化的通用方案
233 0
|
数据采集 存储 监控
数据标准应用(二):数据标准落标监控-上篇
数据标准创建完成后,需要指定其关联的资产对象才能发挥应用价值。数据标准和资产对象的映射关系通过落标映射规则来管理,对象是否遵循了映射到的标准定义则通过落标监控规则来判断。本文为您介绍落标监控评估的基本概念和监控逻辑。Dataphin 支持通过定义标准属性和资产对象元数据字段之间的匹配关系,自动生成数据标准和资产对象的映射关联了;针对已确定的映射关系,可结合数据标准的定义对关联的资产对象进行落标监控,包括元数据监控和内容质量监控。
1195 0
|
Java 调度 开发工具
QuickTask动态脚本支持框架整体介绍篇
一个简单的动态脚本调度框架,支持运行时,实时增加,删除和修改动态脚本,可用于后端的进行接口验证、数据订正,执行定时任务或校验脚本
263 0
QuickTask动态脚本支持框架整体介绍篇
|
数据安全/隐私保护
通用数据级别权限的框架设计与实现
个人花了不到2天时间,写了一个通用数据级别权限的框架设计与实现。 欢迎提意见及评论。有空请打赏! 通用数据级别权限的框架设计与实现(1)-相关业务场景的分析 通用数据级别权限的框架设计与实现(2)-数据权限的准备工作 通用数据级别权限的框架设计与实现(3)-数据列表的权限过滤 通用数据级别权限的框架设计与实现(4)-单条记录的权限控制 通用数据级别权限的框架设计与实现(5)-总结与延伸思考 个人代码已经完成,如需要请打赏后通知我。
2342 0
|
数据库
【自然框架】之通用权限:数据库设计的几种使用方式
      上次《【自然框架】之通用权限:用PowerDesigner重新设计了一下数据库,有ER图和表关系图 》里说了一大堆的表,好多人说太复杂了,做到权限到模块就可以了。       这个嘛,我也没有说所有的表都要一起使用呀。
1208 0
|
数据安全/隐私保护
通用数据级别权限的框架设计与实现(5)-总结与延伸思考
继上篇文章通用数据级别权限的框架设计与实现(4)-单条记录的权限控制后,通用数据级别权限的框架设计已经实现,但我们就这样满足了吗? 代码也只是花了我两个晚上完成的东西,难道他就百分百可用吗? 答案当然是NO,很多场景及问题我们没有考虑进去。
1189 0