一、设计目标
a)规则引擎语法能够满足分单,计费,WMS策略的配置要求。语法是一致和统一的
b)能够在不修改规则引擎模块的情况下,加入任意一个新的规则;实现上述需求之外的规则配置需求
c)运算速度快
d)有良好的展现效果,能够在售前阶段帮助销售
e)提供良好的调试和诊断手段,便于配置规则
二、基本语法及使用
在讲解以下章节的内容时,我们来模拟OMS中一个真实的分单业务场景:根据订单不同的出发地城市和目的地城市指派不一样的承运商并创建运单,如果目的地城市为北京则在天津进行中转。
首先:在相关类中增加如下代码:
1.把页面表单控件值取出来放到集合中
Dictionary<string, object>parames = newDictionary<string, object>(); Dictionary<string, object> returns = newDictionary<string, object>();
publicvoidGetFormToParames() { parames.Clear(); parames.Add("出发地", this.txtChuFaDi.Text); parames.Add("到达地", this.txtDaoDa.Text); parames.Add("业务类型", this.icbeCategory.Text); parames.Add("总金额", this.txtTotalMoney.Text); parames.Add("重量", this.txtZhongLiang.Text); parames.Add("运费", this.txtMoney.Text);
}
|
1.把组装好的参数调整规则“类型不同折扣不同主规则”进行执行。
//类型不同折扣不同 privatevoidbtnCategory_Click(object sender, EventArgs e) { GetFormToParames(); RuleServiceruleService = (RuleService)CacheManager.Instance.Retrieve("RuleService"); returns = ruleService.Execute(this.UserInfo, "类型不同折扣不同主规则", parames, this.DbHelper, this.UserCenterDbHelper); if (returns != null) { if (returns.ContainsKey("费用") && returns["费用"] != null) { this.txtMoney.Text = returns["费用"].ToString(); } } } |
ruleService.Execute方法传递参数调用“类型不同折扣不同主规则”,并返回费用进行处理。
2.1、调试输出
1. 语法:打印(Object)
2. 说明:该函数用于调试输出,内容会输出到规则日志上。
类型不同折扣主规则:
打印(“执行类型不同折扣主规则”); |
调用执行后,规则日志控制台便会输出"执行类型不同折扣主规则"
2.2、局部变量
1. 语法:int运费=100
2. 说明:
a)文字前增加int符号就表明该变量是一个整型局部变量;
b)局部变量只能在单个规则文件内部使用。不能被外部赋值和读取
c) 类型可以是C#语法中的任意类型。
规则内容如下:
|
调用方法后在日志控制台便会输出:"标识:0"
2.3、运算符
符号 | 样例 | 说明 |
= | 运费=100 | 向变量进行赋值 |
+,-,*,/,% | 运费=100*20+18 | 加,减,乘,除,取余 |
>,<,>=,<=,<>,!= | 如果(运费>100){} | 逻辑运算符 |
() | (运费>100)且(运费<1000) | 括号;可以无限嵌套 |
且 | (运费>100)且(运费<1000) | 与运算 |
|
|
|
|
|
|
例:
如果(字符串参数("业务类型") == "零售") { totalMoney = totalMoney * 0.9; }
如果( 字符串参数("业务类型").Equals( "大客户")) { totalMoney = totalMoney * 0.6; } |
2.4、条件语句
1. 语法:如果(条件表达式){语句}
2. 说明:
a)条件表达式为必须(包括小括号),并且肯定是逻辑表达式,而语句体中的语句可以为任意语句,也就是支持语句嵌套。
b)注意只有如果子句,没有或者子句。
主规则中内容如下:
int标识= 0 // 条件语句, 这里表示:如果订单集为空,则抛出一个异常 如果(订单集== 空) { 异常("订单集为空") } 如果(订单集!= 空) { 打印("正常,订单集不为空") } |
2.5、空值
1. 语法:空,可以用:a==空的方式对于空值进行判断
2. 说明:内置全局变量,用于进行空值判断
主规则中内容如下:
int标识= 0 // 空:内置全局变量,用于进行空值判断 如果(参数集== 空) { 异常("订单集为空") } 如果(参数集!= 空) { 打印("正常,订单集不为空") } |
2.6、循环
1. 语法:
每个(局部变量in列表){ 语句 } |
2. 说明:
a)条件表达式为必须(包括小括号),并且肯定是逻辑表达式,而语句体中的语句可以为任意语句,也就是支持语句嵌套。
b)注意:只有如果子句,没有或者子句。
主规则中内容如下:
int标识= 0 如果(订单集== 空) { 异常("订单集为空") } // 对集合进行循环遍历 每个(订单in订单集) { 打印(订单.序号) } |
调用方法后在控制台日志便会输出:订单序号
三、高级语法及使用
3.1、创建对象
1. 语法:new 实体名(),参数:无,返回值:实体类。
2. 说明:下例第8行创建了一个运单对象,对象可以包含很多属性,变量名.属性名,形如:运单.出发地="A"
int标识= 0 如果(订单集== 空) { 异常("订单集为空") } 每个(OrderEntity订单: 订单集) { OrderEntity运单= new OrderEntity() 运单.出发地= 订单.出发地 运单.目的地= 订单.目的地 打印(运单) } |
调用方法时,在控制台便会输出:运单相关信息
3.2、创建列表
1. 语法:创建列表(),参数:无,返回值:结果集
2. 说明:下例列表,用于存放键值对。
Dictionary<string, object>列表 = 创建列表() |
3.3、子规则
分单规则:
int标识= 0; 打印("分章主规则" + 标识); 如果(标识 == 0){ 子规则分单1(); } |
分单子规则:子规则分单1
/* * BaseUserInfo当前用户 * Dictionary<string, object>参数集 * IDbHelperdbHelper //业务数据库 * IDbHelperuserDbHelper//用户中心数据库 * * 取参数值方法:(强转类型)参数集["key"] * * Dictionary<string, object>返回列表 * 返回参数方法:返回列表.Add("key",value); */
打印("子规则分单1------------"); string s, m; BaseRuleLogEntity entity = new BaseRuleLogEntity(); entity.Type = "打印"; entity.Description = "哈哈哈"; SNFService.Instance.CreateService<BaseRuleLogService, IBaseRuleLogService>().Add(BaseSystemInfo.UserInfo, entity, out s, out m);
单值查表("费用表","大连","青岛");
返回列表("key",entity ); |
注:可以直接调用我们服务层代码.
同时我们的调用代码如下:
//01.规则调用代码 System.Collections.Generic.Dictionary<string, object>parames = new System.Collections.Generic.Dictionary<string, object>(); ruleService = (RuleService)CacheManager.Instance.Retrieve("RuleService"); ruleService.Execute(BaseSystemInfo.UserInfo, "分单主规则", parames, null, null); |
3.4、内置函数
函数 | 参数 | 返回值 | 说明 |
查表 | 参数1:规则表名称,参数2~N:表示查找参考项值,传值顺序跟规则表定义顺序一致 | 结果集 | 用于规则表的查询,$线路集=查表("上架规则表","海信",64) |
单值查表 | 参数1:数据表名;参数2:值参考项;其余:数据表查找参考项;返回:value | 值 | string feiyong = 单值查表("运费表","费用",参数集["出发地"].ToString(),参数集["到达地"].ToString()); |
多值查表 | 参数1:数据表名;其余:数据表查找参考项; | 结果列表集(key,value) | List< Dictionary<string, object>> |
最大 | 参数1~N:数值 | 最大数值 | 求最大值,int 最大值=最大(1,0,5,9,10) |
最小 | 参数1~N:数值 | 最大数值 | 求最小值,int 最小值=最小(1,0,5,9,10) |
|
|
|
|
求和 | 参数1:集合 | 数值 | 对集合中元素进行求和 |
四舍五入 | 参数1:值 | 数值 |
|
四舍五入 | 参数1:值,参数2:精度 | 数值 |
|
向上取整 | 数值 | 整型类型 | 向上取整(数量); |
向下取整 | 数值 | 整型 | 向下取整(数量); |
包含元素 | 参数1:集合类型,参数2:任意类型; | 布尔类型 |
|
格式化字符 | 参数1:值 ;参数2:占位符 ;参数3:位数 | 字符串值 |
|
创建列表 | 无参数 | 结果集 | 创建一个集合,Dictionary<string, object>返回列表=创建列表() |
相隔日 | 参数1:源日期,参数2:目标日期 | 天数 | 获得两个日期间相隔天数 |
相隔月 | 参数1:源日期,参数2:目标日期 | 月数 | 获得两个日期间相隔月数 |
现在 | 无 | 日期 | 获取当前日期 |
异常 | 参数1:消息 | 无 | 判断对象是否为空,如果为空则抛出异常,并且控制台输出消息 |
打印 | 参数1:对象 | 无 | 输出到控制台 |
字符串参数 | key 参数的key | string |
|
数值参数 | key 参数的key | Double |
|
整数参数 | key 参数的key | Int32 |
|
3.5、自定义函数-OK
项目中如果需要公用的函数,则可以在项目中的:RuleExtConfig.txt文件中进行扩展使用如下:
double 重量转换(double weight) { return ExtRule.重量转换(weight); } |
我们再修改一下(或新增)RuleExt.cs类,增加:ConvertWeight方法
using System; usingSystem.Collections.Generic; usingSystem.Linq; usingSystem.Text;
namespaceTony.DEMO.Business { publicclassRuleExt { publicdoubleConvertWeight(double weight) { return weight / 1000; } } }
|
同时要记录在Program或Global.asax入口类增加如下代码:
//3.第三步,初始扩展的业务规则引擎方法
SNF.Business.BaseRule.ExtRule = new Tony.DEMO.Business.RuleExt();
规则配置时使用方式如下:
四、规则表
用户可在系统中自定义数据存储表,二维结构,由行列组成,可将一些灵活多变的数据配置于规则表中供规则使用
1. 规则表的列由三个部分构成:
a)绑定参考项:可绑定平台、仓库,适用于多平台规则表独立维护数据;前台不可见,自动取Session中的值赋值;不是必须的。
b)查找参考项:是规则表的查询条件,使用时每个查找项必须全部匹配才能得到既定的结果;不是必须的。
c)值参考项:是规则表的返回值;必须的。
4.1、一般规则表
1. 新建一个规则表,规则表名称:承运商指派规则表,查找项:出发城市、目的城市,值项:承运商代码、承运商名称
出发地 | 目的地 | 费用 |
上海 | 苏州 | 200 |
我们根据这个表进行查找费用。
string feiyong = 单值查表("运费表","费用",字符串参数("出发地"),字符串参数("到达地"));
|
说明:通过查表函数调用:运费表,按查找项的顺序依次传入参数,即可查询得到对应的数据。
4.2、等级规则表
等级规则表就是:规则表的列中存在区间值,当调用查表函数时加入符号匹配。
根据上面的费用规则表扩展一下,我们得到:
- 规则表名称:费用表,查找项:出发地、目的地、重量下限、重量上限,值项:费用
出发地 | 目的地 | 重量下限 | 重量上限 | 费用 |
上海 | 苏州 | 20 | 200 | 200 |
string feiyong = 单值查表("运费表","费用",字符串参数("出发地"),字符串参数("到达地"),"<=" + 字符串参数("重量"),">=" +字符串参数("重量"));
返回列表("费用", feiyong ); |
4.3、多值规则表
多值规则表就是:根据同一组查找项能够查询得到一个结果集,一般用于某个结果集中存在优先级的时候
根据上面的费用表扩展一下,我们得到:
1. 规则表名称:费用表,查找项:出发地、目的地、重量下限、重量上限,值项:费用
出发地 | 目的地 | 重量下限 | 重量上限 | 费用 |
上海 | 苏州 | 20 | 200 | 200 |
上海 | 苏州 | 201 | 300 | 300 |
具体实例日后完善
4.4、规则表版本
当定义好规则表之后我们还需要为规则表新建一个版本才能够真正的使用规则表。规则表版本使得规则表在一段时间内是有效的,当过了有效期之后如果再调用就无法找到,类似于合同的期限。
比如:同一个规则表可能存在多个版本,在使用的过程中,框架会自动根据当前日期去匹配:在版本开始日期和结束日期之内的一个版本。
五、数据源
5.1、调用Sql脚本
在我们的规则里面除了一些自然方法外,我们还可以写C#方法。甚至可以直接执行sql脚本进行更新数据操作。
传入的参数当中有如下两个数据库连接,可以利用数据库连接对数据库进行操作。
IDbHelper dbHelper //业务数据库
IDbHelper userDbHelper//用户中心数据库
如:
DataTabledtData = this.DbHelper.Fill("select * from baseuser");
int r = this.DbHelper.ExecuteNonQuery("UPDATE BaseUser SET Code='' WHERE Id= 1"); |
5.2、调用服务层
在规则里还可以直接调用服务层代码。
如:
stringstateCode, message; DemoFeiYongEntity entity = new DemoFeiYongEntity(); entity.Type = 参数集["业务类型"].ToString(); entity.Money= 数值(参数集["运费"]); entity.Description = "测试哦"; SNFService.Instance.CreateService<DemoFeiYongService, IDemoFeiYongService>().Add(当前用户, entity, out stateCode, out message); |
六、规则相关程序使用手册
6.1、规则表设计
在某些业务场景下,经常会遇到通过查表来进行逻辑判断。为保证规则开发过程的灵活性,通过“规则表设计”可以很快的根据业务需求设计出一个规则表结构,并且可以设计规则表中的字段的各项属性。说明:“参考项”即为字段的列名,“类型”分为查找参考项和值参考项(使用方法参照3.4小结),“参考条件类型”为字段存储数据的类型,如果为枚举类型在“枚举值”中可以存储枚举数据(各项用逗号隔开)。
以WMS项目中“上架分配规则”为例,业务需求:上架时可以根据不同的物料分类,上架时要指定固定的仓库、区域和一定范围内的货位。根据以上要求可以创建规则表如下图所示:
6.2、规则表管理
当“规则表”设计完成后,在“规则表管理”画面中可以维护规则表中的业务逻辑数据,维护数据时以时间段为基准,每个时间段都可以创建一个数据表。调用规则时系统会根据当前时间,找到在时间段内的数据表,获取此时间范围内的业务逻辑数据。这样可以提前设计未来一段时间内的业务逻辑,系统可以根据时间自动切换业所对应的务逻辑规则。
明细,对规则表增加数据行。
6.3、规则配置
“规则配置”画面主要负责完成具体规则程序的编写,定义的名称即为业务程序所有调用的规则名。具体规则编写方法参照以上各小节使用说明,此外还需要注意以下几点:
- 只有在平台下线状态才能编辑规则。
- 每一规则至少要有一个主规则,可以有多个子规则。
- 规则编写完后要进行上线,并且要重新登录系统后才能加载到最新规则。
注意:规则名称要是连接的字母(a-zA-Z)或者汉字
在配置规则里,可以参照“4.规则表”进行调用。
6.4、规则日志
在规则日志画面里可以看到规则在执行过程中通过“打印”函数输出的变量的结果。由于在规则调试过程中无法使用断点,但在关键节点仍然需要对程序执行过程中的中间变量进行跟踪。此时可以把所需要查看的变量通过打印函数进行输出,在规则日志中就会看到所打印的结果,从而达到调试程序的目的。规则日志结果如下图所示:
七、测试程序
-------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
作者: 王金斗出处: Email: spring_best@yeah.netQQ:903639067 QQ群:322581894
这个系列教程文档,欢迎转载:
SNF快速开发平台
SNF快速开发平台
SNF快速开发平台
SNF快速开发平台
SNF快速开发平台
SNF快速开发平台
SNF快速开发平台
SNF快速开发平台MVC-自由排序组件
SNF快速开发平台
SNF快速开发平台
SNF快速开发平台
SNF快速开发平台WinForm
SNF快速开发平台
SNF快速开发平台
SNF快速开发平台
SNF快速开发平台WinForm-
SNF快速开发平台MVC-
SNF快速开发平台MVC-