? ? ? 突然发现一个非常棒的框架,迫不及待的想要和大家分享一下
规则引擎
?什么是规则引擎
?规则引擎是一种工具,它使得这种计算模型编程变得更容易。它可能是一个完整的开发环境,或者一个可以在传统平台上工作的框架。生产规则计算模型最适合仅解决一部分计算问题,因此规则引擎可以更好地嵌入到较大的系统中。
规则引擎解决了什么痛点
实际业务中存在大量的前置条件,以及应用的授权逻辑,都有非常多的规则管理,由于业务的变化大,需求迭代快,需要不断的嵌套规则,硬编码开发。基于业务需要,希望能建立规则引擎,将规则代码从业务中抽离出来,降低规则迭代成本,降低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
|