最新国产好看的视频,伊人天堂AV在线,国产Aaaaaa视频,蜜臀视频在线观看一区,人妻av色图,密臀久久久精品影片,青青视频免费观看毛片,久草在线观看视,国产三级精品色情在线

實現(xiàn)一個規(guī)則引擎的可視化具體方案

 更新時間:2021年04月19日 17:30:51   作者:Ronzy  
項目原因需要用到規(guī)則引擎,但是發(fā)現(xiàn)大部分不可以自由的進行規(guī)則定義,通過不斷嘗試變換關(guān)鍵字在搜索引擎搜索,最終在stackoverflow找到了一個探討這個問題的帖子,特此將帖子中提到的方案分享一下,如果你跟我一樣在研究同樣的問題,也許對你有用

在介紹這個方案之前,得先簡單了解一下什么是規(guī)則引擎

什么是規(guī)則引擎?

簡單的說,規(guī)則引擎所負責(zé)的事情就是:判定某個數(shù)據(jù)或者對象是否滿足某個條件,然后根據(jù)判定結(jié)果,執(zhí)行不同的動作。例如:

對于剛剛在網(wǎng)站上完成購物的一個用戶(對象),如果她是 "女性用戶 并且 (連續(xù)登錄天數(shù)大于10天 或者 訂單金額大于200元 )" (條件) , 那么系統(tǒng)就自動給該用戶發(fā)放一張優(yōu)惠券(動作)。

在上面的場景中,規(guī)則引擎最重要的一個優(yōu)勢就是實現(xiàn)“條件“表達式的配置化。如果條件表達式不能配置,那么就需要程序員在代碼里面寫死各種if...else... ,如果條件組合特別復(fù)雜的話,代碼就會很難維護;同時,如果不能配置化,那么每次條件的細微變更,就需要修改代碼,然后通過運維走發(fā)布流程,無法快速響應(yīng)業(yè)務(wù)的需求。

在groovy腳本的方案中,上面的場景可以這么實現(xiàn):

1)定義一個groovy腳本:[code]

def validateCondition(args){return args.用戶性別 == "女性" && (args.連續(xù)登錄天數(shù)>10 || args.訂單金額 > 200);}

2)通過Java提供的 ScriptEngineManager 對象去執(zhí)行

    <dependency>
      <groupId>org.codehaus.groovy</groupId>
      <artifactId>groovy</artifactId>
      <version>3.0.7</version>
    </dependency>
/*
 *
 * @params condition  從數(shù)據(jù)庫中讀出來的條件表達式
 */
private Boolean validateCondition(String condition){
    //實際使用上,ScriptEngineManager可以定義為單例
    ScriptEngineManager engineManager = new ScriptEngineManager();
    ScriptEngine engine = engineManager.getEngineByName(scriptLang);
    Map<String, Object> args = new HashMap<>();
    data.put("用戶性別", "女性");
    data.put("連續(xù)登錄天數(shù)", 11);
    data.put("訂單金額", 220);
    engine.eval(script);
    return ((Invocable) engine).invokeFunction("validateCondition", args);
}

在上面的groovy腳本中,經(jīng)常需要變動的部分就是 ”args.用戶性別 == "女性" && (args.連續(xù)登錄天數(shù)>10 || args.訂單金額 > 200)“ 這個表達式,一個最簡單的方案,就是在后臺界面提供一個文本框,在文本框中錄入整個groovy腳本,然后保存到數(shù)據(jù)庫。但是這種方案有個缺點:表達式的定義有一定門檻。對于程序員來說,這自然是很簡單的事,但是對于沒接觸過編程的業(yè)務(wù)人員,就有一定的門檻了,很容易錄入錯誤的表達式。這就引出了本文的另一個話題,如何實現(xiàn)bool表達式的可視化編輯?

如何實現(xiàn)bool表達式的可視化編輯?

一種方案就是對于一個指定的表達式,前端人員進行語法解析,然后渲染成界面,業(yè)務(wù)人員編輯之后,再將界面元素結(jié)構(gòu)轉(zhuǎn)換成表達式。然而,直接解析語法有兩個確定:

  • 1)需要考慮的邊界條件比較多,一不小心就解析出錯。
  • 2)而且也限定了后端可以選用的腳本語言。例如,在上面的方案中選用的是groovy,它使用的"與"運算符是 && , 假如某天有一種性能更好的腳本語言,它的"與"運算符定位為 and ,那么就會需要修改很多表達式解析的地方。

另一種方案,是定義一個數(shù)據(jù)結(jié)構(gòu)來描述表達式的結(jié)構(gòu)(說了這么多,終于來到重點了):

    { "all": [
        { "any": [
            { "gl": ["連續(xù)登錄天數(shù)", 10] },
            { "gl": ["訂單金額", 200] }
        ]},
        { "eq": ["用戶性別", "女性"] }
    ]}

然后,使用遞歸的方式解析該結(jié)構(gòu),對于前端開發(fā),可以在遞歸解析的過程中渲染成對應(yīng)的界面元素;對于后端人員,可以生成對應(yīng)的bool表達式,有了bool表達式,就可以使用預(yù)定的腳本模板,生成最終的規(guī)則。

// 模板的例子
def validateCondition(args){return $s;}
/**
 * 動態(tài)bool表達式解析器
 */
public class RuleParser {
    private static final Map<String, String> operatorMap = new HashMap<>();
    private static final ObjectMapper objectMapper = new ObjectMapper();
    static {
        operatorMap.put("all", "&&");
        operatorMap.put("any", "||");
        operatorMap.put("ge", ">=");
        operatorMap.put("gt", ">");
        operatorMap.put("eq", "==");
        operatorMap.put("ne", "!=");
        operatorMap.put("le", "<=");
        operatorMap.put("lt", "<");
    }
    /**
     * 解析規(guī)則字符串,轉(zhuǎn)換成表達式形式
     * 示例:
     * 輸入:
     *    { "any": [
     *        { "all": [
     *            { "ge": ["A", 10] },
     *            { "eq": ["B", 20] }
     *        ]},
     *        { "lt": ["C", 30] },
     *        { "ne": ["D", 50] }
     *    ]}
     *
     * 輸出:
     *    ( A >= 10 && B == 20 ) || ( C < 30 ) || ( D != 50 )
     * @param rule 規(guī)則的json字符串形式
     * @return 返回 bool 表達式
     * @throws IOException 解析json字符串異常
     */
    public static String parse(String rule) throws IOException {
        JsonNode jsonNode = objectMapper.readTree(rule);
        return parse(jsonNode);
    }
    /**
     * 解析規(guī)則節(jié)點,轉(zhuǎn)換成表達式形式
     * @param node Jackson Node
     * @return 返回bool表達式
     */
    private static String parse(JsonNode node) {
        // TODO: 支持變量的 ”arg.“ 前綴定義
        if (node.isObject()) {
            Iterator<Map.Entry<String, JsonNode>> it = node.fields();
            if(it.hasNext()){
                Map.Entry<String, JsonNode> entry = it.next();
                List<String> arrayList = new ArrayList<>();
                for (JsonNode jsonNode : entry.getValue()) {
                    arrayList.add(parse(jsonNode));
                }
                return "(" + String.join(" " + operatorMap.get(entry.getKey()) + " ", arrayList) + ")";
            } else {
                // 兼容空節(jié)點:例如 {"all": [{}, "eq":{"A","1"}]}
                return " 1==1";
            }
        } else if (node.isValueNode()) {
            return node.asText();
        }
        return "";
    }

以上就是本文要闡述的全部內(nèi)容,對于這個話題,如果你有這方面的經(jīng)驗或者更好的方案,也請多多指教,謝謝!

到此這篇關(guān)于如何實現(xiàn)一個規(guī)則引擎的可視化的文章就介紹到這了,希望對你有幫助,更多相關(guān)規(guī)則引擎可視化內(nèi)容請搜索腳本之家以前的文章或繼續(xù)瀏覽下面的相關(guān)文章,希望大家以后多多支持腳本之家!

相關(guān)文章

  • Java集合之LinkedHashSet類詳解

    Java集合之LinkedHashSet類詳解

    這篇文章主要介紹了Java集合之LinkedHashSet類詳解,LinkedHashSet 是 Java 中的一個集合類,它是 HashSet 的子類,并實現(xiàn)了 Set 接口,與 HashSet 不同的是,LinkedHashSet 保留了元素插入的順序,并且具有 HashSet 的快速查找特性,需要的朋友可以參考下
    2023-09-09
  • Eclipse配置使用web.xml的方法

    Eclipse配置使用web.xml的方法

    這篇文章主要為大家詳細介紹了Eclipse配置使用web.xml的方法,具有一定的參考價值,感興趣的小伙伴們可以參考一下
    2018-03-03
  • @Transactional遇到try catch失效的問題

    @Transactional遇到try catch失效的問題

    這篇文章主要介紹了@Transactional遇到try catch失效的問題及解決方案,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-01-01
  • SpringCloud Gateway實現(xiàn)限流功能詳解

    SpringCloud Gateway實現(xiàn)限流功能詳解

    SpringCloud Gateway 是 Spring Cloud 的一個全新項目,它旨在為微服務(wù)架構(gòu)提供一種簡單有效的統(tǒng)一的 API 路由管理方式。這篇文章主要介紹了SpringCloud Gateway實現(xiàn)限流,需要的朋友可以參考下
    2022-11-11
  • 使用IDEA創(chuàng)建Java Web項目并部署訪問的圖文教程

    使用IDEA創(chuàng)建Java Web項目并部署訪問的圖文教程

    本文通過圖文并茂的形式給大家介紹了使用IDEA創(chuàng)建Java Web項目并部署訪問的教程,非常不錯,具有一定的參考借鑒價值,需要的朋友可以參考下
    2018-08-08
  • java反射常用方法案例詳解

    java反射常用方法案例詳解

    Java反射指的是一個機制,這種機制可以在Java程序運行時(動態(tài))實例化對象、讀寫屬性、調(diào)用方法,這篇文章主要介紹了java反射常用方法,需要的朋友可以參考下
    2023-06-06
  • 深入理解java中的拷貝機制

    深入理解java中的拷貝機制

    這篇文章主要給大家深入介紹了java中的拷貝機制,網(wǎng)上關(guān)于java中拷貝的文章也很多,但覺得有必要再深的介紹下java的拷貝機制,有需要的朋友可以參考學(xué)習(xí),下面來一起看看吧。
    2017-02-02
  • 淺談Java如何實現(xiàn)一個基于LRU時間復(fù)雜度為O(1)的緩存

    淺談Java如何實現(xiàn)一個基于LRU時間復(fù)雜度為O(1)的緩存

    這篇文章主要介紹了淺談Java如何實現(xiàn)一個基于LRU時間復(fù)雜度為O(1)的緩存,文中通過示例代碼介紹的非常詳細,對大家的學(xué)習(xí)或者工作具有一定的參考學(xué)習(xí)價值,需要的朋友們下面隨著小編來一起學(xué)習(xí)學(xué)習(xí)吧
    2020-08-08
  • java在原字符中插入新字符或字符串實例

    java在原字符中插入新字符或字符串實例

    這篇文章主要介紹了java在原字符中插入新字符或字符串實例,具有很好的價值,希望對大家有所幫助。一起跟隨小編過來看看吧
    2020-08-08
  • springboot如何設(shè)置請求參數(shù)長度和文件大小限制

    springboot如何設(shè)置請求參數(shù)長度和文件大小限制

    這篇文章主要介紹了springboot如何設(shè)置請求參數(shù)長度和文件大小限制,具有很好的參考價值,希望對大家有所幫助。如有錯誤或未考慮完全的地方,望不吝賜教
    2022-09-09

最新評論

安宁市| 若尔盖县| 胶南市| 将乐县| 青铜峡市| 湘潭市| 彝良县| 青浦区| 海南省| 阆中市| 西青区| 沈阳市| 鸡西市| 建阳市| 百色市| 叙永县| 太白县| 青田县| 简阳市| 贵南县| 崇文区| 诸暨市| 昌江| 磴口县| 古浪县| 余干县| 凭祥市| 延川县| 柏乡县| 遵义县| 陆川县| 本溪市| 碌曲县| 鹰潭市| 奉贤区| 平凉市| 井冈山市| 静乐县| 东兰县| 昔阳县| 伊宁县|