『低代码平台』规则引擎、表达式解析

背景

低代码平台中的一个核心内容是规则引擎,规则引擎致力于解决灵活繁复的硬编码问题,以全新的思想和更高的灵活性解决复杂规则/表单流程规则等问题。
示例一、员工加班申请表单,当加班小时总数>36小时时,需要提示"加班小时总数已超过36H,不允许申请";


示例二、一套标准的生活费领取流程如下



...

功能介绍

  • 普通规则解析
  • 自定义规则解析(支持扩展函数)
  • 带数据的规则解析
  • 规则校验

灵活的表达式规则解析

1、不带数据的表达式解析

接口:/api/Rule/GetRuleResult/{ruleText}
用swagger模拟请求
普通计算:



比较值:



带自定义函数的计算(如Days为获取两个日期相隔的天数):

2、带数据的表达式解析

表达式中使用"{字段}"来获取数据字段值



对于比较常用的三元表达式,程序中内置了IIF函数(IIF(a,b,c) :如果表达式a为真,则取b,否则取c)。
老婆给我打电话:下班顺路买四个包子带回来,如果看到卖西瓜的,买一个。
当晚,我手捧一个包子进了家门……
老婆怒道:你怎么就买了一个包子?!
我答曰:因为看到了卖西瓜的。

完整的表达式规则校验

接口:/api/Rule/ValidateRule/{ruleText}
提供表达式校验的接口,校验表达式正确性。
表达式错误:




方法不存在错误:



方法格式错误:



方法参数错误:

代码


示例程序使用DDD架构,包含规则解析领域和组织机构领域

目录说明

└──LsqParserEngine.Application 应用层:调用领域对象
├── Organization
├── RuleParser
└──LsqParserEngine.Common 公共类
└──LsqParserEngine.Domain 领域层:包含所有的业务规则
├── Organization
├── Repositories 组织机构仓储
├── RuleParser
└──LsqParserEngine.Entity 实体层:数据实体、接口等
├── Organization 组织机构实体
├── RuleParser 规则实体
├── Math
├── ExecutionItem 各种类型的解析器
├── Function 自定义函数
├── VariableTable 数据集类型(暂时只加了字典类型)
└──LsqParserEngine.WebApi 接口层

规则解析类图

添加自定义函数

如何添加自定义函数,以Days(d2,d1)为例
1.添加函数类,继承Function (LsqParserEngine.Entity=>RuleParser=>Math=>Function)

/// <summary>
/// Days获取两个日期相隔的天数
/// </summary>
internal class DaysFunction : Function
{
public const string Name = "Days";
public DaysFunction(IOrganization organization) : base(organization)
{
}
/// <summary>
/// 描述的内容
/// </summary>
/// <param name="Parameters"></param>
/// <returns></returns>
public override string Describe(List<string> Parameters)
{
}
/// <summary>
/// 描述的html
/// </summary>
/// <param name="Parameters"></param>
/// <returns></returns>
public override string DescribeAsHtml(List<string> Parameters)
{
}
/// <summary>
/// 方法帮助:控制参数个数及参数类型
/// </summary>
/// <param name="Parameters"></param>
/// <returns></returns>
public override FunctionHelper GetHelper()
{
}
/// <summary>
/// 解析的实现
/// </summary>
/// <param name="Parameters"></param>
/// <returns></returns>
public override Variant Parse(FunctionExpression desc, IVariableTable variables)
{
}
public override string FunctionName
{
get
{
return "Days";
}
}
}

方法说明:

  • Describe:函数描述,前台函数列表可取此内容
  • DescribeAsHtml:函数描述的html,前台函数列表可取此内容
  • GetHelper:函数帮助,包含函数名、描述、示例、入参约束(支持非必填属性)、出参约束
  • Parse:函数解析的实现

完整代码:
1.添加函数类,继承Function (LsqParserEngine.Entity=>RuleParser=>Math=>Function)

/// <summary>
/// Days获取两个日期相隔的天数
/// </summary>
internal class DaysFunction : Function
{
public const string Name = "Days";
public DaysFunction(IOrganization organization) : base(organization)
{
return string.Format("获取日期{1}与{0}相隔的天数", Parameters[0], Parameters[1]);
}
/// <summary>
/// 描述的内容
/// </summary>
/// <param name="Parameters"></param>
/// <returns></returns>
public override string Describe(List<string> Parameters)
{
if (Parameters.Count > 1)
{
return string.Format("<a>{0}</a>(<a>{1}</a>,<a>{2}</a>,<a>{3}</a>)", this.FunctionName, Parameters[0], Parameters[1]);
}
return base.DescribeAsHtml(Parameters);
}
/// <summary>
/// 描述的html
/// </summary>
/// <param name="Parameters"></param>
/// <returns></returns>
public override string DescribeAsHtml(List<string> Parameters)
{
if (Parameters.Count > 1)
{
return string.Format("<a>{0}</a>(<a>{1}</a>,<a>{2}</a>,<a>{3}</a>)", this.FunctionName, Parameters[0], Parameters[1]);
}
return base.DescribeAsHtml(Parameters);
}
/// <summary>
/// 方法帮助:控制参数个数及参数类型
/// </summary>
/// <param name="Parameters"></param>
/// <returns></returns>
public override FunctionHelper GetHelper()
{
return new FunctionHelper(this.FunctionName,
"获取两个日期相隔的天数",
this.FunctionName + "({EndData},{StartData})",
new Parameter[] {
//Parameter重载中提供参数是否必填设置
new Parameter("EndData", "结束日期", new DataLogicType[] { DataLogicType.DateTime }),
new Parameter("StartData", "开始日期", new DataLogicType[] { DataLogicType.DateTime })
},
new Parameter("Return", "天数", new DataLogicType[] { DataLogicType.Int }));
}
/// <summary>
/// 解析的实现
/// </summary>
/// <param name="Parameters"></param>
/// <returns></returns>
public override Variant Parse(FunctionExpression desc, IVariableTable variables)
{
if (desc == null || desc.Count < 1 || desc.Count > 2)
{
throw new CalcException("The function \"" + this.FunctionName + "\" must have two parameter.");
}
Variant variant = desc[0];
Variant variant2 = desc[1];
DateTime def1 = DateTime.Now;
if (variant.Value == null || string.IsNullOrEmpty(variant.Value.ToString()) || !DateTime.TryParse(variant.Value.ToString(), out def1))
{
return new Variant(-1);
}
DateTime def2 = DateTime.Now;
if (variant2.Value == null || string.IsNullOrEmpty(variant2.Value.ToString()) || !DateTime.TryParse(variant2.Value.ToString(), out def2))
{
return new Variant(0);
}
DateTime t1 = Convert.ToDateTime(def1.ToShortDateString());
DateTime t2 = Convert.ToDateTime(def2.ToShortDateString());
TimeSpan ts = t1.Subtract(t2);
double diffInDays = ts.TotalDays;
return new Variant(diffInDays);
}
public override string FunctionName
{
get
{
return "Days";
}
}
}

2.方法工厂中注入该方法

public static Function[] Create(IOrganization organization)
{
return new Function[] {
//...
new DaysFunction(organization),
};
}

规则表达式中便可以使用Days()函数了。

git地址LsqParserEngine

posted @   齐大齐  阅读(1950)  评论(0编辑  收藏  举报
相关博文:
阅读排行:
· winform 绘制太阳,地球,月球 运作规律
· 超详细:普通电脑也行Windows部署deepseek R1训练数据并当服务器共享给他人
· TypeScript + Deepseek 打造卜卦网站:技术与玄学的结合
· AI 智能体引爆开源社区「GitHub 热点速览」
· 写一个简单的SQL生成工具
点击右上角即可分享
微信分享提示