『低程式碼平臺』規則引擎、表示式解析
阿新 • • 發佈:2022-05-15
背景
低程式碼平臺中的一個核心內容是規則引擎,規則引擎致力於解決靈活繁複的硬編碼問題,以全新的思想和更高的靈活性解決複雜規則/表單流程規則等問題。
示例一、員工加班申請表單,當加班小時總數>36小時時,需要提示"加班小時總數已超過36H,不允許申請";
功能介紹
靈活的表示式規則解析
1、不帶資料的表示式解析
介面:/api/Rule/GetRuleResult/{ruleText}
用swagger模擬請求
普通計算:
比較值:
帶自定義函式的計算(如Days為獲取兩個日期相隔的天數):
2、帶資料的表示式解析
表示式中使用"{欄位}"來獲取資料欄位值
對於比較常用的三元表示式,程式中內建了IIF函式(IIF(a,b,c) :如果表示式a為真,則取b,否則取c)。
老婆給我打電話:下班順路買四個包子帶回來,如果看到賣西瓜的,買一個。
當晚,我手捧一個包子進了家門……
老婆怒道:你怎麼就買了一個包子?!
我答曰:因為看到了賣西瓜的。
完整的表示式規則校驗
介面:/api/Rule/ValidateRule/{ruleText}
提供表示式校驗的介面,校驗表示式正確性。
表示式錯誤:
方法不存在錯誤:
方法格式錯誤:
方法引數錯誤:
程式碼
目錄說明
└──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),
};
}
git地址:LsqParserEngine