突然发现一个非常棒的框架,迫不及待的想要和大家分享一下

规则引擎

 什么是规则引擎

 规则引擎是一种工具,它使得这种计算模型编程变得更容易。它可能是一个完整的开发环境,或者一个可以在传统平台上工作的框架。生产规则计算模型最适合仅解决一部分计算问题,因此规则引擎可以更好地嵌入到较大的系统中。

规则引擎解决了什么痛点

实际业务中存在大量的前置条件,以及应用的授权逻辑,都有非常多的规则管理,由于业务的变化大,需求迭代快,需要不断的嵌套规则,硬编码开发。基于业务需要,希望能建立规则引擎,将规则代码从业务中抽离出来,降低规则迭代成本,降低if else等的规则嵌套,增强代码的维护性和复用性。

    例如:年龄大于50岁,坐公交 8折

               70岁   免费

              残疾  免费

               退伍军人 免费

             学生   半价

             。。。。。

往往存在大量的判断  实际写入业务,到后期就会出现维护太麻烦,稍微不注意忘记写注释的话,后期还非常有可能自己写的业务自己也看不懂了。

倘若有一个专门维护这些规则的那就好了

规则引擎

 Easy Rules,Drools,Aviator表达式求值引擎,Rule Book、Oracle Rules SDK、Blaze (fico)、IBM Decision Manager,DTRules,DSL规则引擎

规则引擎由三部分

  • 事实(Fact):已知对象,比如以上刷卡的行为,即成事实
  • 规则(rule):是由条件和结论构成的推理语句,一般表示为if…Then。一个规则的if部分称为LHS(left-hand-side),then部分称为RHS(right hand side)。
  • 模式(module):就是指IF语句的条件。这里IF条件可能是有几个更小的条件组成的大条件。模式就是指的不能在继续分割下去的最小的原子条件。

应用场景

    商品搜索----按地址---价格区间--店铺--商品类型--品牌--包装形式

     公交刷卡,促销活动等等等等

只要多种条件组合限定什么的都可以采用规则引擎来控制

ice探索

引入关系节点

关系节点为了控制业务流转

        AND所有子节点中,有一个返回false 该节点也将是false,全部是true才是true,在执行到false的地方终止执行,类似于Java的&&

        ANY所有子节点中,有一个返回true 该节点也将是true,全部false则false,在执行到true的地方终止执行,类似于Java的||

        ALL所有子节点都会执行,有任意一个返回true该节点也是true,没有true有一个节点是false则false,没有true也没有false则返回none,所有子节点执行完毕终止

        NONE所有子节点都会执行,无论子节点返回什么,都返回none

        TRUE所有子节点都会执行,无论子节点返回什么,都返回true,没有子节点也返回true(其他没有子节点返回none)

引入叶子节点

叶子节点为真正处理的节点

        Flow一些条件与规则节点,如例子中的ScoreFlow

        Result一些结果性质的节点,如例子中的AmountResult,PointResult

        None一些不干预流程的动作,如装配工作等

好了咱们直接来操作一下

      源代码:ice: Java规则引擎-ice(用全新的思想编排规则)对于业务中需要设计复杂/灵活变动业务(规则/流程),提供一个全新的抽象编排解决方案新的编排思想,轻量级,高性能,提供可视化操作页面

    下载好了之后

   

 可以看到ice里面带了一个调用例子,非常方便我们来学习

然后我们发现例子还是有些不方便我们使用,好了我们改造一下

咱们在ice-test的pom.xml里面,直接拿我的替换就ok,当然要看看版本哈

我写这个博客的时候使用的springboot版本是2.6.8这个版本,可以用swagger3.0.0来就好

<?xml version="1.0" encoding="UTF-8"?>
<project xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns="http://maven.apache.org/POM/4.0.0"
         xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
    <modelVersion>4.0.0</modelVersion>

    <parent>
        <groupId>com.waitmoon.ice</groupId>
        <artifactId>ice</artifactId>
        <version>1.1.0</version>
    </parent>

    <artifactId>ice-test</artifactId>
    <packaging>jar</packaging>

    <name>ice-test</name>
    <properties>
        <swagger-ui.version>1.5.22</swagger-ui.version>
        <springfox.version>3.0.0</springfox.version>
        <swagger-bootstrap-ui.version>1.9.1</swagger-bootstrap-ui.version>
        <fastjson.version>1.2.47</fastjson.version>
    </properties>

    <dependencies>
        <dependency>
            <groupId>com.waitmoon.ice</groupId>
            <artifactId>ice-client-spring-boot-starter</artifactId>
            <version>1.1.0</version>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>com.googlecode.aviator</groupId>
            <artifactId>aviator</artifactId>
            <version>5.3.1</version>
        </dependency>
<!--        <dependency>-->
<!--            <groupId>org.apache.curator</groupId>-->
<!--            <artifactId>curator-recipes</artifactId>-->
<!--            <version>${curator.version}</version>-->
<!--        </dependency>-->


        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger2</artifactId>
            <version>${springfox.version}</version>
            <exclusions>
                <exclusion>
                    <groupId>io.swagger</groupId>
                    <artifactId>swagger-annotations</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>io.swagger</groupId>
                    <artifactId>swagger-models</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-annotations</artifactId>
            <version>${swagger-ui.version}</version>
        </dependency>

        <dependency>
            <groupId>io.swagger</groupId>
            <artifactId>swagger-models</artifactId>
            <version>${swagger-ui.version}</version>
        </dependency>


        <!-- https://mvnrepository.com/artifact/io.springfox/springfox-swagger-ui -->
        <dependency>
            <groupId>io.springfox</groupId>
            <artifactId>springfox-swagger-ui</artifactId>
            <version>${springfox.version}</version>
        </dependency>

        <dependency>
            <groupId>com.github.xiaoymin</groupId>
            <artifactId>swagger-bootstrap-ui</artifactId>
            <version>${swagger-bootstrap-ui.version}</version>
        </dependency>
        <dependency>
            <groupId>com.alibaba</groupId>
            <artifactId>fastjson</artifactId>
            <version>${fastjson.version}</version>
        </dependency>
    </dependencies>

    <build>
        <plugins>
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
        <finalName>ice-test</finalName>
    </build>
</project>

 

 可以看到我较demo多写了两个config的类文件

package com.ice.test.config;

import io.swagger.annotations.ApiOperation;
import io.swagger.models.auth.In;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import springfox.documentation.builders.ApiInfoBuilder;
import springfox.documentation.builders.PathSelectors;
import springfox.documentation.builders.RequestHandlerSelectors;
import springfox.documentation.service.ApiInfo;
import springfox.documentation.service.ApiKey;
import springfox.documentation.service.Contact;
import springfox.documentation.service.SecurityScheme;
import springfox.documentation.spi.DocumentationType;
import springfox.documentation.spring.web.plugins.Docket;
import springfox.documentation.swagger2.annotations.EnableSwagger2;

import java.util.Arrays;
import java.util.List;

@Configuration
@EnableSwagger2
public class SwaggerConfig {

    @Bean
    public Docket createRestApi1() {
        return new Docket(DocumentationType.SWAGGER_2).enable(true).apiInfo(apiInfo()).select()
                .apis(RequestHandlerSelectors.withMethodAnnotation(ApiOperation.class))
                .apis(RequestHandlerSelectors.basePackage("com.ice.test.controller"))
                .paths(PathSelectors.any()).build().securitySchemes(apiKeyList()).groupName("系统接口");
    }

    private ApiInfo apiInfo() {
        return new ApiInfoBuilder()
                .title("系统接口文档")
                .description("这是系统接口文档说明")
                .contact(new Contact("h2", "", ""))
                .version("1.0")
                .build();
    }

    private List<SecurityScheme> apiKeyList() {
        return Arrays.asList(new ApiKey("登录token", "token", In.HEADER.name()),
                new ApiKey("设备类型(android,ios,pc)---必填", "deviceType", In.HEADER.name()));
    }
}



package com.ice.test.config;

import org.springframework.beans.BeansException;
import org.springframework.beans.factory.config.BeanPostProcessor;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.util.ReflectionUtils;
import org.springframework.web.servlet.mvc.method.RequestMappingInfoHandlerMapping;
import springfox.documentation.spring.web.plugins.WebFluxRequestHandlerProvider;
import springfox.documentation.spring.web.plugins.WebMvcRequestHandlerProvider;

import java.lang.reflect.Field;
import java.util.List;
import java.util.stream.Collectors;

@Configuration
public class BeanPostProcessorConfig {

    @Bean
    public static BeanPostProcessor springfoxHandlerProviderBeanPostProcessor() {
        return new BeanPostProcessor() {

            @Override
            public Object postProcessAfterInitialization(Object bean, String beanName) throws BeansException {
                if (bean instanceof WebMvcRequestHandlerProvider || bean instanceof WebFluxRequestHandlerProvider) {
                    customizeSpringfoxHandlerMappings(getHandlerMappings(bean));
                }
                return bean;
            }

            private <T extends RequestMappingInfoHandlerMapping> void customizeSpringfoxHandlerMappings(List<T> mappings) {
                List<T> copy = mappings.stream()
                        .filter(mapping -> mapping.getPatternParser() == null)
                        .collect(Collectors.toList());
                mappings.clear();
                mappings.addAll(copy);
            }

            @SuppressWarnings("unchecked")
            private List<RequestMappingInfoHandlerMapping> getHandlerMappings(Object bean) {
                try {
                    Field field = ReflectionUtils.findField(bean.getClass(), "handlerMappings");
                    field.setAccessible(true);
                    return (List<RequestMappingInfoHandlerMapping>) field.get(bean);
                } catch (IllegalArgumentException | IllegalAccessException e) {
                    throw new IllegalStateException(e);
                }
            }
        };
    }

}
server:
  port: 8082

spring:
  profiles:
    active: dev
  mvc:
    pathmatch:
      matching-strategy: ant_path_matcher

ice:
  app: 1
#  server: zookeeper:localhost:2181,localhost:2182,localhost:2183
  server: localhost:18121
  scan: com.ice.test
  pool:
    parallelism: -1
test:
  value: 123
environment: dev

 然后开启注解

package com.ice.test.controller;


import com.alibaba.fastjson.JSONArray;
import com.fasterxml.jackson.core.JsonProcessingException;
import com.ice.common.utils.JacksonUtils;
import com.ice.core.Ice;
import com.ice.core.context.IceContext;
import com.ice.core.context.IcePack;
import com.ice.core.context.IceRoam;
import io.swagger.annotations.ApiOperation;
import lombok.extern.slf4j.Slf4j;
import org.springframework.web.bind.annotation.*;

import java.util.List;
import java.util.Map;

/**
 * @author waitmoon
 */
@Slf4j
@RestController
public class TestController {

    @PostMapping("/test")
    @ApiOperation(value = "test")
    public List<IceContext> test(@RequestBody Map<String, Object> map) throws JsonProcessingException {
        IcePack pack = JacksonUtils.readJson(JacksonUtils.toJsonString(map), IcePack.class);
        return Ice.processCtx(pack);
    }

    @GetMapping( "/recharge")
    @ApiOperation(value = "recharge")
    public String recharge(@RequestParam Integer cost, @RequestParam Integer uid) {
        IcePack pack = new IcePack();
        pack.setScene("recharge");
        IceRoam roam = new IceRoam();
        roam.put("cost", cost);
        roam.put("uid", uid);
        pack.setRoam(roam);
        Ice.syncProcess(pack);
        return JacksonUtils.toJsonString(roam.get("result"));
    }

    @RequestMapping(value = "/consume", method = RequestMethod.GET)
    public String consume(@RequestParam Integer cost, @RequestParam Integer uid) {
        IcePack pack = new IcePack();
        pack.setScene("consume");
        IceRoam roam = new IceRoam();
        roam.put("cost", cost);
        roam.put("uid", uid);
        pack.setRoam(roam);
        Ice.syncProcess(pack);
        return JacksonUtils.toJsonString(roam.get("result"));
    }
}

这个位置我也稍微改动了一下

ice-server的数据什么的改一下这个我就不多说了

运行IceServerApplication就可以直接访问

官网demo的规则引擎配置参考以下步骤,到后面视频作者用postman搞事情的时候,我们就可以关掉了,我们实现了swagger,我真是个小聪明

你也可以直接导入我的配置

{
    "app": 1,
    "base": {
        "confId": 1,
        "debug": 7,
        "id": 1,
        "name": "重置活动",
        "scenes": "recharge-1,recharge"
    },
    "confUpdates": [
        {
            "confField": "{"time":"2022-09-26 00:00:00"}",
            "confId": 2,
            "confName": "com.ice.test.none.TimeChangeNone",
            "iceId": 1,
            "id": 10,
            "name": "改时间节点",
            "type": 7
        }
    ],
    "confs": [
        {
            "confField": "{"time":"2022-09-24 00:00:00"}",
            "confName": "com.ice.test.none.TimeChangeNone",
            "id": 2,
            "name": "改时间节点",
            "type": 7
        },
        {
            "confField": "{"key":"uid","value":5}",
            "confName": "com.ice.test.result.AmountResult",
            "id": 7,
            "name": "满100元 发放5元优惠额",
            "type": 6
        },
        {
            "id": 5,
            "name": "50元10积分",
            "sonIds": "8,9",
            "start": 1663948800000,
            "timeType": 5,
            "type": 1
        },
        {
            "confField": "{"key":"cost","score":50}",
            "confName": "com.ice.test.flow.ScoreFlow",
            "id": 8,
            "name": "满50元",
            "type": 5
        },
        {
            "confField": "{"score":100,"key":"cost"}",
            "confName": "com.ice.test.flow.ScoreFlow",
            "id": 6,
            "name": "满100元",
            "type": 5
        },
        {
            "confField": "{"key":"uid","value":10}",
            "confName": "com.ice.test.result.PointResult",
            "id": 9,
            "name": "满50元发放10积分",
            "type": 6
        },
        {
            "id": 1,
            "sonIds": "2,3",
            "type": 0
        },
        {
            "id": 4,
            "name": "100元5余额",
            "sonIds": "6,7",
            "type": 1
        },
        {
            "end": 1664467200000,
            "id": 3,
            "name": "9.24-9.30",
            "sonIds": "4,5",
            "start": 1663948800000,
            "timeType": 7,
            "type": 4
        }
    ]
}

也可以参考视频设置 

 规则引擎-ice开发&配置简述_哔哩哔哩_bilibili

好了规则配置好了以后,就可以

我们拿ice-test来试一下 

因为我们改造了这个demo,可以直接访问swagger的接口文档

 

 

 当我们金额超过50了,就出现 "SEND_POINT": true

          输入100就返回了  "SEND_AMOUNT": true

当然你也可以参考引入关系节点里面的关键字修改  来设置你要的结果

 如果有多个场景一致的情况回出现叠加的结果

本demo可以参考https://download.csdn.net/download/qq_14926283/86695151

 也可以参考官方地址首页 | ice

Logo

为开发者提供学习成长、分享交流、生态实践、资源工具等服务,帮助开发者快速成长。

更多推荐