1. 程式人生 > 其它 >『低程式碼平臺』規則引擎、表示式解析

『低程式碼平臺』規則引擎、表示式解析

背景

低程式碼平臺中的一個核心內容是規則引擎,規則引擎致力於解決靈活繁複的硬編碼問題,以全新的思想和更高的靈活性解決複雜規則/表單流程規則等問題。
示例一、員工加班申請表單,當加班小時總數>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