简单的规则引擎实现
1. 需求
公司想对一些通用的规则进行统一的管理,并且可以设置为动态的配置,想用的时候就用,不想用的时候进行禁用,进行实时的配置。规则执行于controller方法之前,如果规则执行失败,则提示具体规则校验失败原因,执行通过则继续走controller中的具体业务逻辑,配置页面则需要动态配置,可以对请求路径(controller中的请求方法)进行配置,还需要可以关联现有的规则,删除以及禁用,配置动态生效。规则分为本地规则以及远程规则两种规则,本地规则优先执行,执行后再执行远程规则,并且单一url路径可以执行多个规则,规则之间可以传递参数信息。本地规则远程规则区别在于本地规则执行查询本地用户数据库,远程规则可以查询多数据源的数据库信息,实现多维度规则校验,其中多数据源部分类似于 spring security 部分,此处不再赘述
2. 选型
暂时规则平台使用Drools规则引擎进行封装开发,配置在controller方法上面,主要对form表单提交数据进行简单规则校验。可以对校验规则进行排序,唤醒 规则等。如果只是对传入vo参数类型对象进行简单校验则可以对规则进行动态的下架上架管理,方便规则管理。
3. 架构设计
3.1 架构设计图
主要执行逻辑如下
- 在需要规则引擎的模块引入规则引擎的pom文件
- 启动springboot服务时候,服务会从redis中加载服务本地规则到内存中,方便后续执行
- 在需要执行规则的controller方法上面打上自定义注解
- 调用方法时,先进入自定义切面,查询redis缓存信息,查看当前路径url是否配置规则,如果配置则执行规则,没有配置,则直接进入controller方法
- 执行规则分为为本地规则,以及远程规则,本地规则直接执行本地代码逻辑,远程则通过feign接口调用,到规则模块进行执行规则
- 本地服务为springboot微服务集群模式,修改逻辑时候需要使用kafka不同组订阅同一topic特性,进行服务分发,保证每一个节点都可以收到修改指令
3.2 执行Drools执行逻辑如下
drools规则基本语法
//包名可以随便取
package 包名
//选择语言
dialect "java";
//同java语法import
import 引入的java类
//定义规则名称,该命名空间唯一
rule 规则名
when
(条件)左手定则(LHS(Left Hand Side))
then
(动作/结果)右手定则(RHS(Right Hand Side))
end
其中规则的编写示例如下:其中规则1先调用,之后调用规则2。待两个本地规则执行后开始执行远程通用规则,执行规则3后按判断情况确定是否唤醒规则4。其中模块为DEMO,URI为/demo/testDrools
规则一:本地规则1
package cn.git.demo.rules;
import cn.hutool.core.util.StrUtil
import cn.git.demo.service.DemoService
import cn.hutool.core.util.ObjectUtil
import java.util.HashMap;
import java.util.Map;
global cn.git.demo.service.DemoService demoService
global cn.git.rules.results.RuleResult ruleResult
global com.alibaba.fastjson.JSONObject ruleJsonParam
// 本地规则测试
rule "demoLocalRuleTest"
// 优先级 越大越先执行
salience 999
// 防止循环调用 no-loop升级版
lock-on-active true
when
// 判断条件 固定写法,全局 ruleResult校验规则
// $result为在规则引擎初始化插入的result对象,result值一直是true
// 若全局ruleResult.getResult值为false与初始化擦混入result值不同则不能进行then中语句块,跳过本次规则校验
$result:RuleResult(result == ruleResult.getResult())
then
// 调用demoService本地规则1
System.out.println("调用本地规则1 : ".concat(demoService.testRule()));
// 调用demoService本地规则2,并且传递参数到后面规则
String secondResult = demoService.testRuleSecond(StrUtil.toString(ruleJsonParam.get("ruleInfo")));
System.out.println("调用完testRuleSecond后结果变更为 : ".concat(secondResult));
// 根据自定义规则条件唤醒子集规则
if (secondResult != null && secondResult.length() > 5) {
ruleResult.setMessage("修改后的ruleInfo [".concat(secondResult).concat("] > 5 部分"));
// 设置全局传递值,本地规则远程规则通用,即本地规则设置,远程规则同样可以获取
ruleResult.getResultMap().put("secondResult", secondResult);
} else {
ruleResult.setMessage("修改后的ruleInfo [".concat(secondResult).concat("] < 5 部分"));
ruleResult.setResult(false);
drools.halt();
}
System.out.println("执行demoLocalRuleTest成功!");
end
规则二:本地规则2
package cn.git.demo.rules;
import cn.hutool.core.util.StrUtil
import cn.git.demo.service.DemoService
import cn.hutool.core.util.ObjectUtil
import java.util.HashMap;
import java.util.Map;
global cn.git.demo.service.DemoService demoService
global cn.git.rules.results.RuleResult ruleResult
global com.alibaba.fastjson.JSONObject ruleJsonParam
// 本地规则测试
rule "demoLocalRuleTest2"
// 优先级 越大越先执行
salience 998
// 防止循环调用 no-loop升级版
lock-on-active true
when
// 判断条件 固定写法,全局 ruleResult校验规则
// $result为在规则引擎初始化插入的result对象,result值一直是true
// 若全局ruleResult.getResult值为false与初始化擦混入result值不同则不能进行then中语句块
$result:RuleResult(result == ruleResult.getResult())
then
// 调用demoService本地规则1
System.out.println("调用本地规则2 : ".concat(demoService.testRule()));
// 模拟设置调用规则信息
ruleResult.setMessage("调用demoLocalRuleTest2设置消息啦啦啦!");
ruleResult.setResult(true);
// 获取规则demoLocalRuleTest1自定义值
System.out.println("获取规则demoLocalRuleTest1自定义值: ".concat(StrUtil.toString(ruleResult.getResultMap().get("secondResult"))));
drools.halt();
System.out.println("执行demoLocalRuleTest2成功!");
end
规则三:远程规则1
package cn.git.demo.remote.rules;
import cn.git.rules.service.demo.DemoRemoteService
import cn.hutool.core.util.ObjectUtil
import java.util.HashMap;
import java.util.Map
import cn.hutool.core.util.StrUtil;
global cn.git.rules.service.demo.DemoRemoteService demoRemoteService
global cn.git.rules.results.RuleResult ruleResult
global com.alibaba.fastjson.JSONObject ruleJsonParam
// 远程规则则测试
rule "demoRemoteRuleTest"
// 优先级 越大越先执行
salience 999
// 防止循环调用 no-loop升级版
lock-on-active true
when
// 判断条件 固定写法,全局 ruleResult校验规则
// $result为在规则引擎初始化插入的result对象,result值一直是true
// 若全局ruleResult.getResult值为false与初始化擦混入result值不同则不能进行then中语句块,跳过本次规则校验
$result:RuleResult(result == ruleResult.getResult())
then
// 调用demoService本地规则1
System.out.println("调用远程规则testRemoteRule : ".concat(demoRemoteService.testRemoteRule()));
// 调用远程规则testRemoteRuleSecond方法
String remoteSecondResult = demoRemoteService.testRemoteRuleSecond(StrUtil.toString(ruleJsonParam.get("ruleInfo")));
System.out.println("调用完testRemoteRuleSecond后结果变更为 : ".concat(remoteSecondResult));
if (remoteSecondResult.length() < 10) {
System.out.println("调整后结果 remoteSecondResult < 10 唤醒规则");
kcontext.getKieRuntime().getAgenda().getAgendaGroup("demoRemoteRuleTest2").setFocus();
} else {
ruleResult.setMessage("调整后结果 remoteSecondResult >= 10, 唤醒规则demoRemoteRuleTest2失败!");
ruleResult.setResult(false);
drools.halt();
}
end
规则四:远程规则2
package cn.git.demo.remote.rules;
import cn.hutool.core.util.StrUtil
import cn.git.rules.service.demo.DemoRemoteService
import cn.hutool.core.util.ObjectUtil
import java.util.HashMap;
import java.util.Map;
global cn.git.rules.service.demo.DemoRemoteService demoRemoteService
global cn.git.rules.results.RuleResult ruleResult
global com.alibaba.fastjson.JSONObject ruleJsonParam
// 本地规则测试
rule "demoRemoteRuleTest2"
// 优先级 越大越先执行
salience 999
// 防止循环调用 no-loop升级版
lock-on-active true
// 规则唤醒group组名称 默认不会执行 需要其他规则唤醒 或者 使用 auto-focus true 自动唤醒
agenda-group "demoRemoteRuleTest2"
when
// 判断条件 固定写法,全局 ruleResult校验规则
// $result为在规则引擎初始化插入的result对象,result值一直是true
// 若全局ruleResult.getResult值为false与初始化擦混入result值不同则不能进行then中语句块,跳过本次规则校验
$result:RuleResult(result == ruleResult.getResult())
then
// 在远程规则中获取本地规则传入的全局变量值
System.out.println("打印demoLocalRule设置的自定义值".concat(StrUtil.toString(ruleResult.getResultMap().get("secondResult"))));
// 业务逻辑处理
System.out.println("调用远程规则2222 : ".concat(demoRemoteService.testRemoteRule()));
System.out.println("执行demoRemoteRuleTest2成功!");
end
4. 表设计
主要使用两张表即可实现规则路径的关联关系
- TB_URI_MODULE_RELATION 路径信息
- TB_SYS_COMMON_RULE_CONFIG 规则详情信息
具体表结构如下
--规则路径关联表
CREATE TABLE "MANAGE"."TB_URI_MODULE_RELATION"
( "RELATION_ID" VARCHAR2(32),
"MODULE" VARCHAR2(50),
"URI" VARCHAR2(200),
"URI_DESCRIPTION" VARCHAR2(200),
"RULE_IDS" CLOB,
"CREATE_TIME" DATE,
"UPDATE_TIME" DATE,
"CTIME" DATE DEFAULT sysdate,
"MTIME" DATE DEFAULT sysdate,
"IS_DEL" VARCHAR2(1) DEFAULT '0'
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "LOANDBS"
LOB ("RULE_IDS") STORE AS SECUREFILE (
TABLESPACE "LOANDBS" ENABLE STORAGE IN ROW CHUNK 8192 RETENTION AUTO
NOCACHE LOGGING NOCOMPRESS KEEP_DUPLICATES
STORAGE(INITIAL 106496 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)) ;
COMMENT ON COLUMN "MANAGE"."TB_URI_MODULE_RELATION"."RELATION_ID" IS 'id';
COMMENT ON COLUMN "MANAGE"."TB_URI_MODULE_RELATION"."MODULE" IS '模块';
COMMENT ON COLUMN "MANAGE"."TB_URI_MODULE_RELATION"."URI" IS '规则路径';
COMMENT ON COLUMN "MANAGE"."TB_URI_MODULE_RELATION"."URI_DESCRIPTION" IS '路径描述';
COMMENT ON COLUMN "MANAGE"."TB_URI_MODULE_RELATION"."RULE_IDS" IS '路径对应子规则';
COMMENT ON COLUMN "MANAGE"."TB_URI_MODULE_RELATION"."CREATE_TIME" IS '创建时间';
COMMENT ON COLUMN "MANAGE"."TB_URI_MODULE_RELATION"."UPDATE_TIME" IS '修改时间';
COMMENT ON COLUMN "MANAGE"."TB_URI_MODULE_RELATION"."CTIME" IS '创建时间';
COMMENT ON COLUMN "MANAGE"."TB_URI_MODULE_RELATION"."MTIME" IS '修改时间';
COMMENT ON COLUMN "MANAGE"."TB_URI_MODULE_RELATION"."IS_DEL" IS '删除标识';
-- 规则详情表
CREATE TABLE "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"
( "RULE_ID" VARCHAR2(32),
"RULE_NAME" VARCHAR2(100),
"CHILD_RULE_NAME" VARCHAR2(100),
"RULE_DESCRIPTION" VARCHAR2(200),
"RULE_INFO" CLOB,
"SERVICES" VARCHAR2(400),
"IF_DISABLE" VARCHAR2(1),
"IF_LOCAL_RULE" VARCHAR2(1),
"EXECUTE_ORDER" NUMBER(5,0),
"CREATE_TIME" DATE,
"UPDATE_TIME" DATE,
"CTIME" DATE DEFAULT sysdate,
"MTIME" DATE DEFAULT sysdate,
"IS_DEL" VARCHAR2(1) DEFAULT '0'
) SEGMENT CREATION IMMEDIATE
PCTFREE 10 PCTUSED 40 INITRANS 1 MAXTRANS 255
NOCOMPRESS LOGGING
STORAGE(INITIAL 65536 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0 FREELISTS 1 FREELIST GROUPS 1
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)
TABLESPACE "LOANDBS"
LOB ("RULE_INFO") STORE AS SECUREFILE (
TABLESPACE "LOANDBS" ENABLE STORAGE IN ROW CHUNK 8192 RETENTION AUTO
NOCACHE LOGGING NOCOMPRESS KEEP_DUPLICATES
STORAGE(INITIAL 106496 NEXT 1048576 MINEXTENTS 1 MAXEXTENTS 2147483645
PCTINCREASE 0
BUFFER_POOL DEFAULT FLASH_CACHE DEFAULT CELL_FLASH_CACHE DEFAULT)) ;
COMMENT ON COLUMN "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"."RULE_ID" IS 'id';
COMMENT ON COLUMN "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"."RULE_NAME" IS '规则名称';
COMMENT ON COLUMN "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"."CHILD_RULE_NAME" IS '子规则名称';
COMMENT ON COLUMN "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"."RULE_DESCRIPTION" IS '规则描述';
COMMENT ON COLUMN "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"."RULE_INFO" IS '规则详情';
COMMENT ON COLUMN "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"."SERVICES" IS 'sevice信息';
COMMENT ON COLUMN "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"."IF_DISABLE" IS '是否禁用 0否1是';
COMMENT ON COLUMN "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"."IF_LOCAL_RULE" IS '是否本地规则 0否1是';
COMMENT ON COLUMN "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"."EXECUTE_ORDER" IS '执行顺序';
COMMENT ON COLUMN "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"."CREATE_TIME" IS '创建时间';
COMMENT ON COLUMN "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"."UPDATE_TIME" IS '修改时间';
COMMENT ON COLUMN "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"."CTIME" IS '创建时间';
COMMENT ON COLUMN "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"."MTIME" IS '修改时间';
COMMENT ON COLUMN "MANAGE"."TB_SYS_COMMON_RULE_CONFIG"."IS_DEL" IS '删除标识';
5. 代码实现
5.1 client端实现
编写通用jar包供其他子模块使用,规则采用api server模式,提供通用api,大家引入即可实现对应的规则功能
首先引入jar包,
注意:在引入rule-manage-api的启动类上加上如下注解,如果原有项目有feign接口,则原有项目feign路径也需要加入
eg: @EnableFeignClients(value = {“cn.git.rules.feign”, “cn.git.demo.feign”})
<?xml version="1.0" encoding="UTF-8"?>
<project xmlns="http://maven.apache.org/POM/4.0.0"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://maven.apache.org/POM/4.0.0 http://maven.apache.org/xsd/maven-4.0.0.xsd">
<parent>
<artifactId>credit-rule-manage</artifactId>
<groupId>cn.git</groupId>
<version>1.0-SNAPSHOT</version>
</parent>
<modelVersion>4.0.0</modelVersion>
<artifactId>rule-manage-api</artifactId>
<dependencies>
<dependency>
<groupId>org.springframework.cloud</groupId>
<artifactId>spring-cloud-starter-openfeign</artifactId>
</dependency>
<!-- 规则引擎jar包 -->
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>7.6.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>7.6.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-templates</artifactId>
<version>7.6.0.Final</version>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-api</artifactId>
<version>7.6.0.Final</version>
</dependency>
<dependency>
<groupId>com.baomidou</groupId>
<artifactId>mybatis-plus-boot-starter</artifactId>
</dependency>
<dependency>
<groupId>org.kie</groupId>
<artifactId>kie-spring</artifactId>
<version>7.6.0.Final</version>
<exclusions>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-tx</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-beans</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-core</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework</groupId>
<artifactId>spring-context</artifactId>
</exclusion>
</exclusions>
</dependency>
<dependency>
<groupId>cn.git</groupId>
<artifactId>credit-kafka-starter</artifactId>
<version>1.0-SNAPSHOT</version>
</dependency>
<dependency>
<groupId>cn.git</groupId>
<artifactId>business-common</artifactId>
<version>1.0-SNAPSHOT</version>
<exclusions>
<exclusion>
<groupId>cn.git</groupId>
<artifactId>database-synchronize-client</artifactId>
</exclusion>
<exclusion>
<groupId>org.springframework.boot</groupId>
<artifactId>spring-boot-starter-logging</artifactId>
</exclusion>
<exclusion>
<groupId>cn.git</groupId>
<artifactId>credit-oracle-starter</artifactId>
</exclusion>
</exclusions>
<optional>true</optional>
</dependency>
</dependencies>
</project>
5.1.1 注解切面实现
首先是注解,注解比较简单
package cn.git.rules.annotation;
import java.lang.annotation.*;
/**
* 通用规则引用注解
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2021-05-10
*/
@Documented
@Retention(RetentionPolicy.RUNTIME)
@Target({ElementType.TYPE, ElementType.METHOD})
public @interface Rule {
}
其次是切面部分,切面部分需要解析请求信息,包括get以及post两种类型,将请求参数通用设置为JSONObject类型,从redis中获取当前请求路径是否有配置规则,如果有这执行规则,否则进入controller继续逻辑执行
package cn.git.rules.aspect;
import cn.git.common.result.Result;
import cn.git.common.result.ResultStatusEnum;
import cn.git.redis.RedisUtil;
import cn.git.rules.constant.RuleConstant;
import cn.git.rules.feign.RuleFeignApi;
import cn.git.rules.results.RuleResult;
import cn.git.rules.util.RuleUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import org.aspectj.lang.ProceedingJoinPoint;
import org.aspectj.lang.annotation.Around;
import org.aspectj.lang.annotation.Aspect;
import org.aspectj.lang.reflect.MethodSignature;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.context.ApplicationContext;
import org.springframework.core.annotation.Order;
import org.springframework.stereotype.Component;
import org.springframework.web.bind.annotation.GetMapping;
import org.springframework.web.bind.annotation.PostMapping;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.context.request.RequestContextHolder;
import org.springframework.web.context.request.ServletRequestAttributes;
import javax.servlet.http.HttpServletRequest;
import java.lang.reflect.Method;
import java.math.BigDecimal;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* 通用规则调用切面方法
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2022-04-01
*/
@Component
@Aspect
@Order(value = Integer.MAX_VALUE)
public class BaseRuleAspect {
@Autowired
private RedisUtil redisUtil;
@Autowired
private RuleUtil ruleUtil;
@Autowired
private ApplicationContext context;
/**
* 规则引擎提前通知切面
* @param joinPoint 切面参数
*/
@Around("@annotation(cn.git.rules.annotation.Rule)")
public Result doBeforeRuleCheck(ProceedingJoinPoint joinPoint) throws Throwable {
// 通过postMapping或者getMapping标签获取请求uri,对应uri为不带参数对应uri
ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
HttpServletRequest request = attributes.getRequest();
String requestType = request.getMethod();
MethodSignature signature = (MethodSignature) joinPoint.getSignature();
Method method = signature.getMethod();
// 完整请求路径,包含传递参数信息
String uri = request.getRequestURI();
// 带参数url请求地址
String requestWithParamUri = null;
// 如果请求uri中带有'{',则需要过滤掉后面对应参数信息,并且去除参数信息前的第一个'/'
if (RuleConstant.REQUEST_TYPE_POST_FLAG.equals(requestType)) {
PostMapping postMapping = method.getAnnotation(PostMapping.class);
// 通过requestMapping设定请求方式为POST
if (ObjectUtil.isNull(postMapping)) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
String requestMappingUri = requestMapping.value()[0];
if (requestMappingUri.contains(StrUtil.DELIM_START)) {
requestWithParamUri = requestMappingUri;
uri = requestMappingUri.substring(0,
requestMappingUri.indexOf(StrUtil.DELIM_START) - RuleConstant.NUM_1);
}
} else {
// 正常PostMapping请求
String postMappingUri = postMapping.value()[0];
if (postMappingUri.contains(StrUtil.DELIM_START)) {
requestWithParamUri = postMappingUri;
uri = postMappingUri.substring(0, postMappingUri.indexOf(StrUtil.DELIM_START) - RuleConstant.NUM_1);
}
}
} else if (RuleConstant.REQUEST_TYPE_GET_FLAG.equals(requestType)) {
GetMapping getMapping = method.getAnnotation(GetMapping.class);
// 通过requestMapping设定请求方式为GET
if (ObjectUtil.isNull(getMapping)) {
RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);
String requestMappingUri = requestMapping.value()[0];
if (requestMappingUri.contains(StrUtil.DELIM_START)) {
requestWithParamUri = requestMappingUri;
uri = requestMappingUri.substring(0,
requestMappingUri.indexOf(StrUtil.DELIM_START) - RuleConstant.NUM_1);
}
} else {
// 正常GetMapping请求
String getMappingUri = getMapping.value()[0];
if (getMappingUri.contains(StrUtil.DELIM_START)) {
requestWithParamUri = getMappingUri;
uri = getMappingUri.substring(0, getMappingUri.indexOf(StrUtil.DELIM_START) - RuleConstant.NUM_1);
}
}
}
// 组装完整请求uri参数信息
String allPath = request.getRequestURI();
if (!allPath.equals(uri)) {
String prefixPath = allPath.substring(0, allPath.indexOf(uri));
uri = prefixPath.concat(uri);
}
// 如果redis中含有对应规则
if (redisUtil.hHasKey(RuleConstant.DROOLS_RULES, uri)) {
// 通过缓存平台获取缓存数据列表
List<Map<String, Object>> ruleInfoList = (List<Map<String, Object>>)
redisUtil.hget(RuleConstant.DROOLS_RULES, uri);
// 获取本地规则
List<Map<String, Object>> localRuleList = ruleInfoList.stream().filter(ruleConfig ->
RuleConstant.NUM_1_STR.equals(ruleConfig.get(RuleConstant.IF_LOCAL_RULE))
).collect(Collectors.toList());
// 获取通用规则
List<Map<String, Object>> remoteRuleList = ruleInfoList.stream().filter(ruleConfig ->
RuleConstant.NUM_0_STR.equals(ruleConfig.get(RuleConstant.IF_LOCAL_RULE))
).collect(Collectors.toList());
// 校验无论post或者get请求都必填
if (ObjectUtil.isEmpty(joinPoint.getArgs())) {
return Result.error(ResultStatusEnum.RULE_CHECK_FAIL.getCode(),
"规则校验对应controller方法参数必须有请求参数");
}
// 获取请求参数并且转换位jsonObject格式
Object argsObject = null;
if (joinPoint.getArgs().length > 1) {
// 定义为请求url中带有参数,进行参数匹配,重新封装为key,value类型数据
List<String> uriParamList = getPathParamConfig(requestWithParamUri);
Map<String, Object> paramMap = new HashMap<>(RuleConstant.DEFAULT_MAP_SIZE);
for (int i = 0; i < uriParamList.size(); i ++) {
paramMap.put(uriParamList.get(i), joinPoint.getArgs()[i]);
}
argsObject = paramMap;
} else if (joinPoint.getArgs().length == 1) {
argsObject = joinPoint.getArgs()[0];
// 如果为简单类型,必须设定为key value 对应json数据格式
if (argsObject instanceof String
|| argsObject instanceof BigDecimal
|| argsObject instanceof Integer
|| argsObject instanceof Date
|| argsObject instanceof Long
|| argsObject instanceof Double) {
List<String> uriParamList = getPathParamConfig(requestWithParamUri);
Map<String, Object> paramMap = new HashMap<>(RuleConstant.NUM_1);
paramMap.put(uriParamList.get(RuleConstant.NUM_0), joinPoint.getArgs()[0]);
argsObject = paramMap;
}
}
// argsObject 转换为json并且去除 null 值
JSONObject jsonParam = JSONObject.parseObject(JSONObject.toJSONString(argsObject));
jsonParam.put(RuleConstant.RULE_URI, uri);
// 错误前缀
String ruleErrorPrefix = "规则校验提示: ";
if (ObjectUtil.isNotEmpty(localRuleList)) {
// 本地标识
jsonParam.put(RuleConstant.IF_LOCAL_RULE, RuleConstant.LOCAL_FLAG);
jsonParam.put(RuleConstant.RULE_LIST, localRuleList);
// 通过uri进行本地规则校验
RuleResult localResult = ruleUtil.setRuleCheckInfo(jsonParam);
// 进行本地规则校验结果反馈,如果失败则直接返回,不进行后续远程规则部分校验
if (ObjectUtil.isNotEmpty(localResult) && !localResult.getResult()) {
return Result.error(ResultStatusEnum.ERROR.getCode(),
ruleErrorPrefix.concat(localResult.getMessage()));
}
// 本地规则校验通过,设置自定义值,后续远程调用远程规则可能会使用
if (ObjectUtil.isNotEmpty(localResult.getResultMap())) {
jsonParam.put(RuleConstant.RESULT_MAP, localResult.getResultMap());
}
}
// 通过uri进行远程规则调用
if (ObjectUtil.isNotEmpty(remoteRuleList)) {
RuleFeignApi ruleFeignApi = context.getBean(RuleFeignApi.class);
// 远程标识
jsonParam.put(RuleConstant.IF_LOCAL_RULE, RuleConstant.REMOTE_FLAG);
jsonParam.put(RuleConstant.RULE_LIST, remoteRuleList);
RuleResult rspRuleResult = ruleFeignApi.checkRuleByUriInfo(jsonParam);
if (ObjectUtil.isNotEmpty(rspRuleResult) && !rspRuleResult.getResult()) {
return Result.error(ResultStatusEnum.ERROR.getCode(),
ruleErrorPrefix.concat(rspRuleResult.getMessage()));
}
}
}
Object returnObject = joinPoint.proceed();
return (Result) returnObject;
}
/**
* 获取请求信息参数标识项
* @param requestParamPath 请求路径
* @return 标识项数组
*/
public List<String> getPathParamConfig(String requestParamPath) {
// 请求参数标识list
List<String> paramFlagList = new ArrayList<>();
Pattern pattern = Pattern.compile(RuleConstant.URL_PARAM_PATTEN);
Matcher matcher = pattern.matcher(requestParamPath);
while(matcher.find()){
paramFlagList.add(matcher.group());
}
return paramFlagList;
}
}
5.1.2 规则引擎Drools初始化
编写drools配置类,其中配置文件如下application-rule.properties
drools.mode=stream
drools.listener="off"
drools.verify="off"
配置类内容如下
package cn.git.rules.properties;
import lombok.Data;
import org.springframework.boot.context.properties.ConfigurationProperties;
import org.springframework.context.annotation.Configuration;
import org.springframework.context.annotation.PropertySource;
/**
* 规则引擎配置参数类
* @program: rule-manage-api
* @author: lixuchun
* @create: 2022-03-24
*/
@Data
@Configuration
@PropertySource("classpath:application-rule.properties")
@ConfigurationProperties("drools")
public class KieProperties {
/**
* 规则监听开关: on: 开 off: 关闭
*/
private String listener;
/**
* 模式,stream 或 cloud
*/
private String mode;
/**
* 规则检查
*/
private String verify;
}
规则的对应的模板类KieTemplate,不需要每一次编译一次,一次编译,多次使用
package cn.git.rules.template;
import cn.git.rules.constant.RuleConstant;
import cn.git.rules.lisener.DefaultAgendaEventListener;
import cn.git.rules.lisener.DefaultProcessEventListener;
import cn.git.rules.lisener.DefaultRuleRuntimeEventListener;
import cn.git.rules.properties.KieProperties;
import cn.hutool.core.util.StrUtil;
import lombok.Data;
import lombok.NoArgsConstructor;
import lombok.extern.slf4j.Slf4j;
import org.kie.api.KieBase;
import org.kie.api.KieBaseConfiguration;
import org.kie.api.builder.Message;
import org.kie.api.builder.Results;
import org.kie.api.conf.EventProcessingOption;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.internal.utils.KieHelper;
import java.util.List;
import java.util.Map;
/**
* 规则引擎通用工具类
* 2022-09-28引擎优化重构,新增base缓存,不用每一次编译
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2021-05-11
*/
@Slf4j
@Data
@NoArgsConstructor
public class KieTemplate extends KieProperties {
/**
* 通过请求模块uri获取请求session
* @param ruleInfoList 规则信息列表
* @return KieSession 规则引擎session
*/
public KieSession getKieSession(List<Map<String, Object>> ruleInfoList) {
// 通过uri 获取缓存中的规则校验信息列表
KieHelper kieHelper = new KieHelper();
ruleInfoList.forEach(ruleConfig -> {
String ruleInfo = StrUtil.toString(ruleConfig.get(RuleConstant.RULE_INFO));
kieHelper.addContent(ruleInfo, ResourceType.DRL);
});
if (getVerify() != null && RuleConstant.LISTENER_OPEN.equalsIgnoreCase(getVerify())) {
Results results = kieHelper.verify();
if (results.hasMessages(Message.Level.WARNING, Message.Level.ERROR)) {
List<Message> messages = results.getMessages(Message.Level.WARNING, Message.Level.ERROR);
for (Message message : messages) {
log.error(StrUtil.format("规则格式校验错误信息: {}!", message.getText()));
}
throw new RuntimeException("规则引擎校验信息失败");
}
}
// kieBase信息配置
KieBaseConfiguration config = kieHelper.ks.newKieBaseConfiguration();
if (EventProcessingOption.STREAM.getMode().equalsIgnoreCase(getMode())) {
config.setOption(EventProcessingOption.STREAM);
} else {
config.setOption(EventProcessingOption.CLOUD);
}
KieBase kieBase = kieHelper.build(config);
KieSession kieSession = kieBase.newKieSession();
// 是否监听配置部分
if (getListener() == null || !RuleConstant.LISTENER_CLOSE.equalsIgnoreCase(getListener())) {
kieSession.addEventListener(new DefaultRuleRuntimeEventListener());
kieSession.addEventListener(new DefaultAgendaEventListener());
kieSession.addEventListener(new DefaultProcessEventListener());
}
return kieSession;
}
/**
* 获取kieBase信息
* @param ruleInfoList 规则列表信息
* @return kieBase信息
*/
public KieBase getKieBase(List<Map<String, Object>> ruleInfoList) {
// 通过uri 获取缓存中的规则校验信息列表
KieHelper kieHelper = new KieHelper();
ruleInfoList.forEach(ruleConfig -> {
String ruleInfo = StrUtil.toString(ruleConfig.get(RuleConstant.RULE_INFO));
kieHelper.addContent(ruleInfo, ResourceType.DRL);
});
if (getVerify() != null && RuleConstant.LISTENER_OPEN.equalsIgnoreCase(getVerify())) {
Results results = kieHelper.verify();
if (results.hasMessages(Message.Level.WARNING, Message.Level.ERROR)) {
List<Message> messages = results.getMessages(Message.Level.WARNING, Message.Level.ERROR);
for (Message message : messages) {
log.error(StrUtil.format("规则格式校验错误信息: {}!", message.getText()));
}
throw new RuntimeException("规则引擎校验信息失败");
}
}
// kieBase信息配置
KieBaseConfiguration config = kieHelper.ks.newKieBaseConfiguration();
if (EventProcessingOption.STREAM.getMode().equalsIgnoreCase(getMode())) {
config.setOption(EventProcessingOption.STREAM);
} else {
config.setOption(EventProcessingOption.CLOUD);
}
return kieHelper.build(config);
}
}
template使用的三个监听类,AgendaEventListener是一个接口,我们可以用它去通知在Drools的agenda组内发生的事件
package cn.git.rules.lisener;
import lombok.extern.slf4j.Slf4j;
import org.kie.api.event.rule.*;
/**
* AgendaEventListener是一个接口,我们可以用它去通知在Drools的agenda组内发生的事件
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2021-05-11
*/
@Slf4j
public class DefaultAgendaEventListener implements AgendaEventListener {
@Override
public void matchCreated(MatchCreatedEvent event) {
log.debug("匹配的规则:{}", event.getMatch().getRule());
}
@Override
public void matchCancelled(MatchCancelledEvent event) {
}
@Override
public void beforeMatchFired(BeforeMatchFiredEvent event) {
log.debug("开始执行Java代码块,匹配规则:{},评估对象:{}",
event.getMatch().getRule(), event.getMatch().getFactHandles());
}
@Override
public void afterMatchFired(AfterMatchFiredEvent event) {
log.debug("结束执行Java代码块,匹配规则:{},评估对象:{}",
event.getMatch().getRule(), event.getMatch().getFactHandles());
}
@Override
public void agendaGroupPopped(AgendaGroupPoppedEvent event) {
}
@Override
public void agendaGroupPushed(AgendaGroupPushedEvent event) {
}
@Override
public void beforeRuleFlowGroupActivated(RuleFlowGroupActivatedEvent event) {
}
@Override
public void afterRuleFlowGroupActivated(RuleFlowGroupActivatedEvent event) {
}
@Override
public void beforeRuleFlowGroupDeactivated(RuleFlowGroupDeactivatedEvent event) {
}
@Override
public void afterRuleFlowGroupDeactivated(RuleFlowGroupDeactivatedEvent event) {
}
}
ProcessEventListeners与jBPM事件,允许我们在当流程实例启动或完成时,或在流程实例中的单个节点被触发之前/触发之后得到通知
package cn.git.rules.lisener;
import org.kie.api.event.process.*;
/**
* ProcessEventListeners与jBPM事件,允许我们在当流程实例启动或完成时,
* 或在流程实例中的单个节点被触发之前/触发之后得到通知
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2021-05-11
*/
public class DefaultProcessEventListener implements ProcessEventListener {
@Override
public void beforeProcessStarted(ProcessStartedEvent event) {
}
@Override
public void afterProcessStarted(ProcessStartedEvent event) {
}
@Override
public void beforeProcessCompleted(ProcessCompletedEvent event) {
}
@Override
public void afterProcessCompleted(ProcessCompletedEvent event) {
}
@Override
public void beforeNodeTriggered(ProcessNodeTriggeredEvent event) {
}
@Override
public void afterNodeTriggered(ProcessNodeTriggeredEvent event) {
}
@Override
public void beforeNodeLeft(ProcessNodeLeftEvent event) {
}
@Override
public void afterNodeLeft(ProcessNodeLeftEvent event) {
}
@Override
public void beforeVariableChanged(ProcessVariableChangedEvent event) {
}
@Override
public void afterVariableChanged(ProcessVariableChangedEvent event) {
}
}
RuleRuntimeEventListener可以被用于与在KieSession中事实对象的状态有关的事件的通知
package cn.git.rules.lisener;
import lombok.extern.slf4j.Slf4j;
import org.kie.api.event.rule.ObjectDeletedEvent;
import org.kie.api.event.rule.ObjectInsertedEvent;
import org.kie.api.event.rule.ObjectUpdatedEvent;
import org.kie.api.event.rule.RuleRuntimeEventListener;
/**
* RuleRuntimeEventListener可以被用于与在KieSession中事实对象的状态有关的事件的通知
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2021-05-11
*/
@Slf4j
public class DefaultRuleRuntimeEventListener implements RuleRuntimeEventListener {
@Override
public void objectInserted(ObjectInsertedEvent event) {
log.debug("插入对象:{};操作规则:{}", event.getFactHandle(), event.getRule());
}
@Override
public void objectUpdated(ObjectUpdatedEvent event) {
log.debug("更新对象:{};操作规则:{}", event.getFactHandle(), event.getRule());
}
@Override
public void objectDeleted(ObjectDeletedEvent event) {
log.debug("删除对象:{};操作规则:{}", event.getFactHandle(), event.getRule());
}
}
引入rule-api的服务,都会执行api的初始化方法,从数据库中获取当前模块的规则信息,存入到本地服务的cache中,ruleUtil.makeKieBaseCache参考上面给出的ruleUtil类,具体代码如下
package cn.git.rules.init;
import cn.git.common.thread.ThreadPoolUtil;
import cn.git.redis.RedisUtil;
import cn.git.rules.constant.RuleConstant;
import cn.git.rules.util.RuleUtil;
import cn.hutool.core.util.ObjectUtil;
import lombok.extern.slf4j.Slf4j;
import org.kie.api.KieBase;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
import java.util.LinkedHashMap;
import java.util.Map;
/**
* @description: 规则引擎初始化编译现有存在规则,进行规则编译缓存
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2022-09-28 06:29:12
*/
@Slf4j
@Component
public class RuleInfoInit implements ApplicationRunner {
/**
* 本地缓存信息
* 1层key 模块, 2层key uri, 3层key 本地远程
*/
public static Map<String, Map<String, Map<String, KieBase>>> commonKieBaseMap = new LinkedHashMap<>();
@Autowired
private RuleUtil ruleUtil;
@Autowired
private RedisUtil redisUtil;
/**
* 规则引擎本地缓存初始化
* 各个子服务请求uri头信息
* /account/
* /afterloan/
* /credit/
* /demo/
* /loan/
* /manage/
* /warning/
* /query/
* /risk/
* /collateral/
* /credit/
* @param args
* @throws Exception
*/
@Override
public void run(ApplicationArguments args) throws Exception {
log.info("服务启动开始执行本地规则缓存方法");
while (true) {
Object dicsRuleInitFlag = redisUtil.get(RuleConstant.DICS_RULE_INIT_FLAG);
if (ObjectUtil.isNotNull(dicsRuleInitFlag)
&& RuleConstant.NUM_1_STR.equals(String.valueOf(dicsRuleInitFlag))
&& redisUtil.hasKey(RuleConstant.DROOLS_RULES)) {
log.info("获取缓存获取标识[DICS_RULE_INIT_FLAG]成功,并且缓存redis中规则信息加载完成!");
break;
}
log.info("服务未获取到规则信息,五秒后重新获取缓存信息!");
Thread.sleep(RuleConstant.DICS_RULE_INIT_WAIT_TIME);
}
ThreadPoolUtil.THREAD_POOL.execute(() -> {
ruleUtil.makeKieBaseCache(commonKieBaseMap);
});
}
}
其中本地规则校验ruleUtil部分代码如下,远程规则则也是使用feign接口调用到rule-server端,再在server端进行ruleUtil的setRuleCheckInfo方法调用,相当于执行server端的本地方法
package cn.git.rules.util;
import cn.git.common.exception.ServiceException;
import cn.git.redis.RedisUtil;
import cn.git.rules.constant.RuleConstant;
import cn.git.rules.init.RuleInfoInit;
import cn.git.rules.results.RuleResult;
import cn.git.rules.template.KieTemplate;
import cn.hutool.core.text.StrSpliter;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.alibaba.fastjson.JSONObject;
import lombok.extern.slf4j.Slf4j;
import org.kie.api.KieBase;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.ApplicationContext;
import org.springframework.stereotype.Component;
import java.io.PrintWriter;
import java.io.StringWriter;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;
import java.util.stream.Collectors;
/**
* 规则引擎模块通用方法类
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2022-04-01
*/
@Slf4j
@Component
public class RuleUtil {
@Autowired
private RedisUtil redisUtil;
@Value("${spring.application.name}")
private String applicationName;
/**
* 错误信息长度
*/
private static final Integer ERROR_MESSAGE_LENGTH = 1500;
private static final Integer LENGTH_ZERO = 0;
/**
* spring容器上下文
*/
@Autowired
private ApplicationContext applicationContext;
/**
* 规则引擎调用模板
*/
@Autowired
private KieTemplate kieTemplate;
/**
* 通过uri找寻对应规则进行总体规则校验
* @param ruleJsonParam 规则参数
* @return RuleResult 校验结果
*/
public RuleResult setRuleCheckInfo(JSONObject ruleJsonParam) {
// 如果有自定义参数,则设定为全局变量值
Map<String, Object> resultMap = null;
if (ObjectUtil.isNotEmpty(ruleJsonParam.get(RuleConstant.RESULT_MAP))) {
resultMap = (Map<String, Object>) ruleJsonParam.get(RuleConstant.RESULT_MAP);
} else {
resultMap = new HashMap<>(RuleConstant.DEFAULT_MAP_SIZE);
}
// 设置通用校验结果信息
RuleResult ruleResult = new RuleResult(RuleConstant.TRUE, RuleConstant.RULE_SUCCESS_MESSAGE, resultMap);
// 规则引擎kieSession
KieSession kieSession = null;
try {
// 通过缓存平台获取缓存数据列表
List<Map<String, Object>> ruleInfoList = (List<Map<String, Object>>)
ruleJsonParam.get(RuleConstant.RULE_LIST);
ruleJsonParam.put(RuleConstant.RULE_LIST, null);
// 获取session进行规则引擎引用
KieBase kieBase = getKieBase(applicationName,
ruleJsonParam.getString(RuleConstant.RULE_URI),
ruleJsonParam.getString(RuleConstant.IF_LOCAL_RULE));
if (ObjectUtil.isNull(kieBase)) {
String errorMessage = StrUtil.format("通过服务[{}]以及uri[{}]是否本地[{}]获取规则kieBase信息为空!",
applicationName,
ruleJsonParam.getString(RuleConstant.RULE_URI),
ruleJsonParam.getString(RuleConstant.IF_LOCAL_RULE));
ruleResult.setResult(RuleConstant.FALSE);
ruleResult.setMessage(errorMessage);
return ruleResult;
}
log.info("加载uri[{}]对应规则信息成功!", ruleJsonParam.getString(RuleConstant.RULE_URI));
kieSession = kieBase.newKieSession();
kieSession.setGlobal(RuleConstant.RULE_PARAM, ruleJsonParam);
// 获取service
KieSession finalKieSession = kieSession;
ruleInfoList.forEach(ruleConfig -> {
if (ObjectUtil.isNotNull(ruleConfig.getOrDefault(RuleConstant.SERVICES, null))) {
String servicesStr = StrUtil.toString(ruleConfig.getOrDefault(RuleConstant.SERVICES,
null));
List<String> services = StrSpliter.split(servicesStr,
RuleConstant.COMMA, RuleConstant.NUM_0, true, true);
services.forEach(service -> {
// 具体放入为规则用到service实现类
finalKieSession.setGlobal(service, applicationContext.getBean(StrUtil.format(
RuleConstant.DOUBLE_APPEND_TEMPLATE,
service,
RuleConstant.IMPL_SUFFIX)));
});
}
});
// 设置通用校验结果信息
kieSession.setGlobal(RuleConstant.RULE_RESULT, ruleResult);
kieSession.insert(new RuleResult());
// 执行所有校验信息
kieSession.fireAllRules();
} catch (Exception e) {
e.printStackTrace();
ruleResult.setResult(RuleConstant.FALSE);
// 打印异常信息
getStackTraceInfo(e);
ruleResult.setMessage(RuleConstant.RULE_DEFAULT_FAIL_MESSAGE_FORMAT);
} finally {
// 释放资源
if (ObjectUtil.isNotNull(kieSession)) {
kieSession.dispose();
}
}
return ruleResult;
}
/**
* 打印异常信息转字符串
* @param exception 异常
* @return 异常信息 长度设置为300
*/
public String getStackTraceInfo(Exception exception) {
StringWriter stringWriter = new StringWriter();
PrintWriter printWriter = new PrintWriter(stringWriter);
try {
exception.printStackTrace(printWriter);
printWriter.flush();
stringWriter.flush();
String errorMessage = stringWriter.toString();
log.error("调用规则引擎异常,异常信息为[{}]", errorMessage);
if (StrUtil.isNotBlank(errorMessage) && errorMessage.length() > ERROR_MESSAGE_LENGTH) {
errorMessage = errorMessage.substring(LENGTH_ZERO, ERROR_MESSAGE_LENGTH);
}
return errorMessage;
} finally {
try{
printWriter.close();
stringWriter.close();
} catch (Exception ex) {
ex.printStackTrace();
}
}
}
/**
* 添加规则信息到通用缓存中
* @param ruleInfo 规则信息
* @param ruleInfoMap 组装结构
*/
public void setRuleInfo(Map<String, Object> ruleInfo, Map<String, Object> ruleInfoMap) {
List<Map<String, Object>> ruleList;
if (ObjectUtil.isNotEmpty(ruleInfoMap.get(ruleInfo.get(RuleConstant.RULE_URI)))) {
ruleList = (List<Map<String, Object>>) ruleInfoMap.get(ruleInfo.get(RuleConstant.RULE_URI));
} else {
ruleList = new ArrayList<>(1);
}
ruleList.add(ruleInfo);
ruleInfoMap.put((String) ruleInfo.get(RuleConstant.RULE_URI), ruleList);
}
/**
* 获取规则order排序
* @param ruleInfo 规则详情
* @return 规则排序顺序
*/
public Integer getRuleSalience(String ruleInfo) {
String patternStr = RuleConstant.ORDER_SALIENCE_PATTEN;
Pattern pattern = Pattern.compile(patternStr);
Matcher matcher = pattern.matcher(ruleInfo);
String order = null;
while (matcher.find()) {
order = matcher.group(3);
}
if (ObjectUtil.isEmpty(order)) {
return -1;
}
return Integer.valueOf(order);
}
/**
* 获取规则名称
* @param ruleInfo 规则详情
* @return 规则名称
*/
public String getRuleName(String ruleInfo) {
String patternStr = RuleConstant.RULE_NAME_PATTEN;
Pattern pattern = Pattern.compile(patternStr);
Matcher matcher = pattern.matcher(ruleInfo);
String ruleName = null;
while (matcher.find()) {
ruleName = matcher.group(4);
}
return ruleName;
}
/**
* 获取子规则名称
* @param ruleInfo 规则详情
* @return 子规则名称
*/
public String getChildRuleName(String ruleInfo) {
String patternStr = RuleConstant.CHILD_RULE_NAME_PATTEN;
Pattern pattern = Pattern.compile(patternStr);
Matcher matcher = pattern.matcher(ruleInfo);
String childRuleName = null;
while (matcher.find()) {
childRuleName = matcher.group(3);
}
return childRuleName;
}
/**
* 重新加载本地缓存信息
* @param commonKieBaseMap 本地规则kieBase缓存信息
*/
public void makeKieBaseCache(Map<String, Map<String, Map<String, KieBase>>> commonKieBaseMap) {
if (StrUtil.isBlank(applicationName)) {
log.error("获取服务名称异常,规则缓存初始化异常!");
return;
}
// 获取服务名称,并且截断server部分
String prefixServerName;
if (RuleConstant.MANAGE_SERVER_NAME.equals(applicationName)) {
prefixServerName = StrUtil.SLASH.concat(applicationName.replaceAll(RuleConstant.MANAGE_SERVER_FLAG, StrUtil.EMPTY));
} else if (RuleConstant.RULE_MANAGE_SERVER.equals(applicationName)) {
prefixServerName = StrUtil.SLASH.concat(applicationName.replaceAll(RuleConstant.RULE_SERVER_FLAG, StrUtil.EMPTY));
} else {
prefixServerName = StrUtil.SLASH.concat(applicationName.replaceAll(RuleConstant.SERVER_FLAG, StrUtil.EMPTY));
}
// 规则信息
Map<String, Map<String, KieBase>> uriMap = new LinkedHashMap<>();
// 获取规则信息
Map<Object, Object> initRuleMap = redisUtil.hmget(RuleConstant.DROOLS_RULES);
if (ObjectUtil.isNotEmpty(initRuleMap)) {
log.info("获取redis缓存规则[{}]条", initRuleMap.size());
} else {
log.info("加载本地规则,获取redis缓存规则失败");
}
// 非规则模块,只需要获取本地规则即可
if (!RuleConstant.RULE_MANAGE_SERVER.equals(applicationName)) {
// 规则模块,需要获取所有远程规则
for (Object key : initRuleMap.keySet()) {
Map<String, KieBase> localKieBaseMap = new LinkedHashMap<>();
// 规则uri地址
String keyStr = StrUtil.toString(key);
if (keyStr.startsWith(prefixServerName)) {
// 获取规则详细信息
List<Map<String, Object>> ruleInfoList = (List<Map<String, Object>>) initRuleMap.get(key);
// 获取本地规则
List<Map<String, Object>> localRuleList = ruleInfoList.stream().filter(ruleConfig ->
RuleConstant.NUM_1_STR.equals(ruleConfig.get(RuleConstant.IF_LOCAL_RULE))
).collect(Collectors.toList());
if (ObjectUtil.isNotEmpty(localRuleList)) {
log.info("服务模块{}开始加载[{}]uri规则,规则详情为[{}]",
prefixServerName,
keyStr,
JSONObject.toJSONString(ruleInfoList));
try {
KieBase localKieBase = kieTemplate.getKieBase(localRuleList);
localKieBaseMap.put(RuleConstant.LOCAL_FLAG, localKieBase);
uriMap.put(keyStr, localKieBaseMap);
} catch (Exception e) {
e.printStackTrace();
return;
}
}
}
}
} else {
// 规则模块,需要获取所有远程规则
for (Object key : initRuleMap.keySet()) {
Map<String, KieBase> remoteKieBaseMap = new LinkedHashMap<>();
// 规则uri地址
String keyStr = StrUtil.toString(key);
// 获取规则详细信息
List<Map<String, Object>> ruleInfoList = (List<Map<String, Object>>) initRuleMap.get(key);
// 获取通用规则
List<Map<String, Object>> remoteRuleList = ruleInfoList.stream().filter(ruleConfig ->
RuleConstant.NUM_0_STR.equals(ruleConfig.get(RuleConstant.IF_LOCAL_RULE))
).collect(Collectors.toList());
if (ObjectUtil.isNotEmpty(remoteRuleList)) {
KieBase remoteKieBase = null;
try {
log.info("服务模块{}开始加载[{}]uri规则,规则详情为[{}]",
prefixServerName,
keyStr,
JSONObject.toJSONString(ruleInfoList));
remoteKieBase = kieTemplate.getKieBase(remoteRuleList);
} catch (Exception e) {
e.printStackTrace();
return;
}
remoteKieBaseMap.put(RuleConstant.REMOTE_FLAG, remoteKieBase);
uriMap.put(keyStr, remoteKieBaseMap);
}
}
}
// 判断当前业务模块kieBase信息是否为空
if (ObjectUtil.isNotEmpty(uriMap)) {
log.info("开始加载本地缓存[{}]规则信息, 本地缓存规则规则count为[{}]", applicationName, uriMap.size());
commonKieBaseMap.put(prefixServerName, uriMap);
} else {
log.info("[{}]本地未获取到规则信息!", applicationName);
}
}
/**
* 获取kieBase信息
* @param applicationName 系统名称
* @param uri uri地址
* @param localFlag 本地标识 REMOTE远程,LOCAL本地
* @return 规则kieBase信息
*/
public KieBase getKieBase(String applicationName, String uri, String localFlag) {
// 空值判断
if (StrUtil.isBlank(applicationName) || StrUtil.isBlank(uri) || ObjectUtil.isNull(localFlag)) {
throw new ServiceException("获取kieBase信息参数信息为空,请确认!");
}
// 获取服务名称,并且截断server部分
String prefixServerName;
if (RuleConstant.MANAGE_SERVER_NAME.equals(applicationName)) {
prefixServerName = StrUtil.SLASH.concat(applicationName.replaceAll(RuleConstant.MANAGE_SERVER_FLAG, StrUtil.EMPTY));
} else if (RuleConstant.RULE_MANAGE_SERVER.equals(applicationName)) {
prefixServerName = StrUtil.SLASH.concat(applicationName.replaceAll(RuleConstant.RULE_SERVER_FLAG, StrUtil.EMPTY));
} else {
prefixServerName = StrUtil.SLASH.concat(applicationName.replaceAll(RuleConstant.SERVER_FLAG, StrUtil.EMPTY));
}
// 通过模块获取uri信息
Map<String, Map<String, KieBase>> uriRuleMap = RuleInfoInit.commonKieBaseMap.get(prefixServerName);
if (ObjectUtil.isEmpty(uriRuleMap)) {
return null;
}
// 通过uri获取对应规则信息
Map<String, KieBase> kieBaseMap = uriRuleMap.get(uri);
if (ObjectUtil.isEmpty(kieBaseMap)) {
return null;
}
// 通过本地远程标识
return kieBaseMap.get(localFlag);
}
}
规则常量类如下 RuleConstant
package cn.git.rules.constant;
/**
* 规则引擎工具常量类
*
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2021-05-11
*/
public class RuleConstant {
/**
* hashMap 默认大小
*/
public static final int DEFAULT_MAP_SIZE = 16;
/**
* 通用 on off 常量
*/
public static final String ON_FLAG = "1";
public static final String OFF_FLAG = "0";
/**
* 规则redis hash 结构 名称
*/
public static final String DROOLS_RULES = "DROOLS_RULES";
/**
* uri
*/
public static final String RULE_URI = "URI";
/**
* 校验规则自定义参数
*/
public static final String RESULT_MAP = "RESULT_MAP";
/**
* 规则uri描述
*/
public static final String RULE_DESCRIPTION = "RULE_DESCRIPTION";
/**
* RULE_ID
*/
public static final String RULE_ID = "RULE_ID";
/**
* utf-8 编码格式
*/
public static final String UTF_8 = "UTF-8";
/**
* 开启监听器的标识符
*/
public static final String LISTENER_OPEN = "on";
/**
* 关闭监听器的标识符
*/
public static final String LISTENER_CLOSE = "off";
/**
* rule info
*/
public static final String RULE_INFO = "RULE_INFO";
/**
* services
*/
public static final String SERVICES = "SERVICES";
/**
* 逗号
*/
public static final String COMMA = ",";
/**
* num 数据接口
*/
public static final Integer NUM_0 = 0;
public static final Integer NUM_1 = 1;
/**
* 两个字段拼接模板
*/
public static final String DOUBLE_APPEND_TEMPLATE = "{}{}";
public static final String DOUBLE_APPEND_TEMPLATE_COMMA = "{}: {}";
/**
* Impl
*/
public static final String IMPL_SUFFIX = "Impl";
/**
* json格式请求参数
*/
public static final String JSON_PARAM = "jsonParam";
/**
* 转义分隔符
*/
public static final String SPLIT_FLAG = "\\.";
/**
* 默认校验状态 成功
*/
public static final Boolean TRUE = true;
/**
* 校验状态 失败
*/
public static final Boolean FALSE = false;
/**
* 规则校验默认信息
*/
public static final String RULE_SUCCESS_MESSAGE = "规则校验成功";
/**
* 调用规则平台失败,未执行校验
*/
public static final String RULE_DEFAULT_FAIL_MESSAGE_FORMAT = "调用规则平台异常,请联系技术人员处理!";
/**
* 规则校验结果
*/
public static final String RULE_RESULT = "ruleResult";
/**
* rule-manage-server
*/
public static final String RULE_MANAGE_SERVER = "rule-manage-server";
/**
* 规则通用参数名称
*/
public static final String RULE_PARAM = "ruleJsonParam";
/**
* disable 标识
*/
public static final String DISABLE_FLAG = "DISABLE";
/**
* 默认数据库 公共管理模块
*/
public static final String DEFAULT_FLAG = "MANAGE";
/**
* 系统默认数据源
*/
public static final String DATA_SOURCE_FLAG = "dataSource";
/**
* 模块标识
*/
public static final String MODULES = "modules";
/**
* 规则id
*/
public static final String RULE_ID_FLAG = "RULE_ID";
/**
* 是否本地标识
*/
public static final String IF_LOCAL_RULE = "IF_LOCAL_RULE";
/**
* 规则列表标识
*/
public static final String RULE_LIST = "RULE_LIST";
/**
* 字符串数字标识
*/
public static final String NUM_0_STR = "0";
public static final String NUM_1_STR = "1";
public static final String NUM_2_STR = "2";
public static final String NUM_3_STR = "3";
public static final String NUM_4_STR = "4";
/**
* 从规则信息中获取规则顺序正则表达式
*/
public static final String ORDER_SALIENCE_PATTEN = "(salience)( )(.*?)(\n|\r\n)";
/**
* post提交标识
*/
public static final String REQUEST_TYPE_POST_FLAG = "POST";
/**
* get请求标识
*/
public static final String REQUEST_TYPE_GET_FLAG = "GET";
/**
* url param patten
*/
public static final String URL_PARAM_PATTEN = "(?<=\\{)[^\\}]+";
/**
* 获取规则名称正则表达式
*/
public static final String RULE_NAME_PATTEN = "(rule)( )(\")(.*?)(\")(\n|\r\n)";
/**
* 自规则名称
*/
public static final String CHILD_RULE_NAME_PATTEN = "(getAgendaGroup\\()(\")(.*?)(\"\\))";
/**
* Nacos配置动态数据源信息头
*/
public static final String BUSINESS_MODULES_FLAG = "business.modules";
/**
* 添加现有规则失败
*/
public static final String ADD_EXIST_RULE_CONFIG_FAIL = "添加现有规则失败";
/**
* 规则禁用失败提示信息
*/
public static final String RULE_DISABLE_FAIL_MESSAGE = "消息规则禁用失败,其中\"{}\" 规则对应主规则uri:\"{}\",请先在主规则禁用此类规则!";
/**
* 规则删除失败提示信息
*/
public static final String RULE_DELETE_FAIL_MESSAGE = "消息规则删除失败,其中 \"{}\" 规则对应主规则uri: \"{}\",请先在主规则删除此类规则!";
/**
* 通过id禁用失败
*/
public static final String RULE_DISABLE_BY_ID_FAIL_MESSAGE = "通过id禁用失败,当前规则对应主uri为: \"{}\",请用主uri进行禁用!";
/**
* 通过id删除失败
*/
public static final String RULE_DELETE_BY_ID_FAIL_MESSAGE = "通过id删除失败,当前规则对应主uri为: \"{}\",请用主uri进行删除!";
/**
* 解除关联失败信息
*/
public static final String DELETE_RELATION_SUB_ERROR_MESSAGE = "关联关系uri与规则uri相同只能进行删除操作,不能进行解除关联操作!";
/**
* 父级id信息对应无数据错误提示
*/
public static final String PARENT_INFO_NONE_MESSAGE = "父级id信息对应无数据!";
/**
* 修改父规则对应子规则与当前修改规则不对应错误提示
*/
public static final String UPDATE_PARENT_SUN_RULE_MISS_MESSAGE = "修改父规则对应子规则与当前修改规则不对应!";
/**
* 父规则已经有对应子规则则不能进行添加错误提示
*/
public static final String PARENT_HAS_SUN_RULE_MESSAGE = "父规则已经有对应子规则则不能进行添加!";
/**
* 只有主uri对应关联才能修改规则信息
*/
public static final String MAIN_RULE_URI_UPDATE_MESSAGE = "修改规则失败,只有主uri: \"{}\" 才能修改规则,当前uri: \"{}\" 不能修改!";
/**
* 重复添加模块信息
*/
public static final String ADD_EXIST_RULE_REPEAT_MESSAGE = "当前uri已经添加过此信息,不能重复添加!";
/**
* 添加存在规则失败
*/
public static final String ADD_EXIST_RULE_FAIL_MESSAGE = "添加存在规则失败,请选择一条数据再添加!";
/**
* 添加规则信息不完整
*/
public static final String ADD_RULE_UN_COMPLETE_MESSAGE = "添加规则信息不完整!";
/**
* 删除条件不满足
*/
public static final String DELETE_CONDITION_MISS_MESSAGE = "删除规则参数不正确!";
/**
* 禁用条件不满足
*/
public static final String DISABLE_CONDITION_MISS_MESSAGE = "删除规则参数不正确!";
/**
* 设置重连次数1次
*/
public static final int ERROR_RETRY_ATTEMPTS = 1;
/**
* 影像平台IP
*/
public static final String IMAGE_FILE_IP = "6.1.14.2";
/**
* 生产端口8021
*/
public static final int IMAGE_FILE_SOCKET = 8023;
public static final String STATE = "SUCCESS";
/**
* 影像平台用户
*/
public static final String IMAGE_FILE_USER_NAME = "xdadmin";
/**
* 影像平台默认密码
*/
public static final String IMAGE_FILE_USER_PASS = "111";
/**
* 登录用户名 UID xdadmin,固定值
*/
public static final String UID = "?UID=xdadmin";
/**
* 登录密码 PWD 111,固定值
*/
public static final String PWD = "&PWD=111";
/**
* 登录密码 PWD 111,固定值
*/
public static final String GROUPNAME = "FWQZ";
/**
* 固定值XD
*/
public static final String APP_ID = "&AppID=XD";
/**
* 用户id标识
*/
public static final String USER_ID_FLAG = "&UserID=";
/**
* 是否合并标识
*/
public static final String IS_COMBINED_FLAG = "&isCombined=";
/**
* 用户名称标识
*/
public static final String USER_NAME_FLAG = "&UserName=";
/**
* 机构编号标识
*/
public static final String ORG_ID_FLAG = "&OrgID=";
/**
* 机构名称标识
*/
public static final String ORG_NAME_FLAG = "&OrgName=";
/**
* 品种名称
*/
public static final String BUSINESS_NAME = "";
/**
* 等级匹配
*/
public static final String LEVEL = "";
/**
* 权限设定
*/
public static final String RIGHT_VOTE = "";
/**
* 流水号
*/
public static final String SERIAL_NO = "";
/**
* 请求路径前缀信息
*/
public static final String URL_PREFIX_0 = ":7011/SunIAS/SunIASRequestServlet.do";
public static final String SIT2_URL_PREFIX_0 = ":7011/SunIAS/SunIASRequestServlet.do";
/**
* 查看影像是否存在生产配置
*/
public static final String PROD_URL_PREFIX_0 = ":7012/SunIAS/SunIASRequestServlet.do";
public static final String URL_PREFIX_1 = "/SunIAS/SunIASRequestServlet.do";
public static final String App_list_PREFIX_0 = "/SunUST/services/WSDataServer";
/**
* url请求前缀
*/
public static final String HTTP_PREFIX = "http://";
/**
* url拼接信息
*/
public static final String INFO_FLAG = "&info";
public static final String BUS_SERIAL_NO_FLAG = "=BUSI_SERIAL_NO:";
public static final String OBJECT_NAME_FLAG = ";OBJECT_NAME:";
public static final String QUERY_TIME_FLAG = ";QUERY_TIME:";
public static final String FILE_LEVEL_FLAG = ";FILELEVEL:";
public static final String RIGHT_FLAG = ";RIGHT:";
/**
* 影像权限
* [0-1][0-1][0-1][0-1][0-1][0-1][0-1]对应的权限分别为:新增权限、查看权限、删除权限、修改权限、打印权限、批注权限、管理员权限。0无权,1有权
*/
public static final String IMAGE_RIGHTS_0100111 = "0100111";
public static final String IMAGE_RIGHTS_0100110 = "0100110";
public static final String IMAGE_RIGHTS_1100111 = "1100111";
public static final String IMAGE_RIGHTS_1111111 = "1111111";
public static final String IMAGE_RIGHTS_1100100 = "1100100";
public static final String IMAGE_RIGHTS_0100100 = "0100100";
public static final String IMAGE_RIGHTS_1100011 = "1100011";
// 没有保存提交
public static final String IMAGE_RIGHTS_0100000 = "0100000";
/**
* 影像平台常量
* 1、OBJECT_NAME业务名称
* 2、FILE_LEVEL等级
* 3、IS_COMBINED是否合并
*/
// 法人客户信息
public static final String OBJECT_NAME_FRKHXX = "FRKHXX";
// 法定代表人资料(FDDBRZL)
public static final String OBJECT_NAME_FDDBRZL = "FDDBRZL";
// 实控人资料(SKRZL)
public static final String OBJECT_NAME_SKRZL = "SKRZL";
// 股东资料(GDZL)
public static final String OBJECT_NAME_GDZL = "GDZL";
// 抵质押人资料(DZYRZL)
public static final String OBJECT_NAME_DZYRZL = "DZYRZL";
// 关联企业资料(GLQYZL)
public static final String OBJECT_NAME_GLQYZL = "GLQYZL";
// 保证人资料(BZRZL)
public static final String OBJECT_NAME_BZRZL = "BZRZL";
// 法人客户评级
public static final String OBJECT_NAME_FRKHPJ = "FRKHPJ";
// 法人授信管理
public static final String OBJECT_NAME_FRSXGL = "FRSXGL";
// 法人贷款申请
public static final String OBJECT_NAME_FRDKSQ = "FRDKSQ";
// 法人业务审批
public static final String OBJECT_NAME_FRYWSP = "FRYWSP";
// 法人放款核实
public static final String OBJECT_NAME_FRFKHZ = "FRFKHZ";
// 法人核保核押
public static final String OBJECT_NAME_FRHBHY = "FRHBHY";
// 法人客户担保
public static final String OBJECT_NAME_FRKHDB = "FRKHDB";
// 授信申请
public static final String OBJECT_NAME_SHOUXSQ = "SHOUXSQ";
// 法人抵押
public static final String OBJECT_NAME_FRDY = "FRDY";
// 法人质押
public static final String OBJECT_NAME_FRZY = "FRZY";
// 主管户权移交
public static final String OBJECT_NAME_GRZGHQYJ = "GRZGHQYJ";
// 定期检查报告
public static final String OBJECT_NAME_FRDHJC = "FRDHJC";
// 电子承兑汇票发票
public static final String OBJECT_NAME_CDHPXX = "CDHPXX";
// 主协议信息
public static final String OBJECT_NAME_YSDKXY = "YSDKXY";
// 借新还旧
public static final String OBJECT_NAME_FRYPJCHH = "FRYPJCHH";
// 预警
public static final String OBJECT_NAME_FRFXYJ = "FRFXYJ";
//担保关联关系/解除追加担保
public static final String OBJECT_NAME_JCDBXGCL = "JCDBXGCL";
//核保核押
public static final String OBJECT_NAME_HBHYSCZL = "HBHYSCZL";
//法人贷后管理
public static final String OBJECT_NAME_FRDHGL = "FRDHGL";
//对公客户整合授权书
public static final String OBJECT_NAME_DGKHZHSQS = "DGKHZHSQS";
//个人客户整合授权书
public static final String OBJECT_NAME_GRKHZHSQS = "GRKHZHSQS";
//贷审会影像资料
public static final String OBJECT_NAME_DHGLWYH = "DHGLWYH";
//个人客户信息影像资料
public static final String OBJECT_NAME_GRKHXX = "GRKHXX";
//个人客户评级
public static final String OBJECT_NAME_GRKHPJ = "GRKHPJ";
//个人贷款申请
public static final String OBJECT_NAME_GRDKSQ = "GRDKSQ";
//个人业务审批
public static final String OBJECT_NAME_GRYWSP = "GRYWSP";
//信贷审批
public static final String OBJECT_NAME_XDSP = "XDSP";
//视频录制
public static final String OBJECT_NAME_VIDEO = "VIDEO";
//个人客户担保
public static final String OBJECT_NAME_GRKHDB = "GRKHDB";
//个人抵押
public static final String OBJECT_NAME_GRDY = "GRDY";
//个人质押
public static final String OBJECT_NAME_GRZY = "GRZY";
//个人放款核准
public static final String OBJECT_NAME_GRFKHZ = "GRFKHZ";
// 问题单影像
public static final String OBJECT_NAME_WTDCLGL = "WTDCLGL";
// 报告查询申请书
public static final String OBJECT_NAME_BGCXSQS = "BGCXSQS";
// 个人征信 影像
public static final String OBJECT_NAME_XGZLSC = "XGZLSC";
// 法人银行承兑汇票申请 影像
public static final String OBJECT_NAME_FRYHCD = "FRYHCD";
// 法人银行承兑汇票放款核准 影像
public static final String OBJECT_NAME_FRYHFK = "FRYHFK";
// 法人银行承兑汇票放核保核押 影像
public static final String OBJECT_NAME_FRYHHB = "FRYHHB";
public static final String FILE_LEVEL_004 = "004";
public static final String FILE_LEVEL_1 = "1";
public static final String FILE_LEVEL_5 = "5";
public static final String FILE_LEVEL_6 = "6";
public static final String FILE_LEVEL_8 = "8";
public static final String FILE_LEVEL_9 = "9";
public static final String FILE_LEVEL_10 = "10";
public static final String FILE_LEVEL_11 = "11";
public static final String FILE_LEVEL_13 = "13";
public static final String FILE_LEVEL_14 = "14";
public static final String FILE_LEVEL_15 = "15";
public static final String FILE_LEVEL_16 = "16";
public static final String FILE_LEVEL_17 = "17";
public static final String FILE_LEVEL_18 = "18";
public static final String FILE_LEVEL_19 = "19";
public static final String FILE_LEVEL_22 = "22";
public static final String FILE_LEVEL_23 = "23";
public static final String FILE_LEVEL_27 = "27";
public static final String FILE_LEVEL_28 = "28";
public static final String FILE_LEVEL_30 = "30";
public static final String FILE_LEVEL_31 = "31";
public static final String FILE_LEVEL_33 = "33";
public static final String FILE_LEVEL_35 = "35";
public static final String FILE_LEVEL_37 = "37";
public static final String FILE_LEVEL_38 = "38";
public static final String FILE_LEVEL_50 = "50";
public static final String IS_COMBINED_Y = "y";
public static final String IS_COMBINED_N = "n";
//押品类型:1,抵押
public static final String COL_CGY_TP = "1";
//押品包分隔符
public static final String COL_PAGE_SEP = "@";
/**
* 熔断标识
*/
public static final String BREAK_FLAG = "break";
public static final String NO_BREAK = "noBreak";
/**
* 公共管理服务名称
*/
public static final String MANAGE_SERVER_NAME = "management-server";
/**
* 规则模块名称
*/
public static final String RULE_SERVER_NAME = "rule-manage-server";
/**
* 系统名称后缀
*/
public static final String SERVER_FLAG = "-server";
public static final String MANAGE_SERVER_FLAG = "ment-server";
public static final String RULE_SERVER_FLAG = "-manage-server";
/**
* 远程本地标识
*/
public static final String LOCAL_FLAG = "LOCAL";
public static final String REMOTE_FLAG = "REMOTE";
/**
* topic尾缀
*/
public static final String TOPIC_SUFFIX = "_TOPIC";
/**
* DICS系统标识
*/
public static final String DICS_FLAG = "DICS_RULE_CACHE_";
/**
* 规则缓存consumer对应id
*/
public static final String ID_RULE_CACHE = "RULE_ID";
/**
* 规则缓存consumer对应组
*/
public static final String GROUP_RULE_CACHE = "GROUP_RULE_CACHE_";
/**
* 规则缓存信息
*/
public static final String TOPIC_RULE_PREFIX = "rule-cache-";
/**
* 发送修改规则信息到其模块
* account
* afterloan
* credit
* demo
* loan
* manage
* warning
* query
* risk
* collateral
*/
public static final String SYSTEM_FLAG_LOAN = "loan";
public static final String SYSTEM_FLAG_ACCOUNT = "account";
public static final String SYSTEM_FLAG_AFTER = "after";
public static final String SYSTEM_FLAG_CREDIT = "credit";
public static final String SYSTEM_FLAG_MANAGE = "manage";
public static final String SYSTEM_FLAG_WARNING = "warning";
public static final String SYSTEM_FLAG_QUERY = "query";
public static final String SYSTEM_FLAG_RISK = "risk";
public static final String SYSTEM_FLAG_COLLATERAL = "collateral";
public static final String SYSTEM_FLAG_RULE = "rule";
/**
* 规则引擎初始化标识
* 0 不可以加载本地规则
* 1 可以加载本地规则
*/
public static final String DICS_RULE_INIT_FLAG = "DICS_RULE_INIT_FLAG";
/**
* 本地规则未获取到再次获取等待时间(毫秒)
*/
public static final Integer DICS_RULE_INIT_WAIT_TIME = 5000;
/**
* 本地规则初始化加载等待超时时间
*/
public static final Integer DICS_RULE_WAIT_TIME_OUT = 360;
}
5.2 server端实现
server端主要就是规则服务的管理部分,包含规则新增,删除,修改,以及查询,关联等功能,其余的则为具体的业务校验逻辑
5.2.1 controller实现
规则引擎管理的controller,主要是规则的增删改查处理
package cn.git.rules.controller;
import cn.git.common.result.Result;
import cn.git.rules.dto.RuleDTO;
import cn.git.rules.mapstruct.RuleConvert;
import cn.git.rules.service.rule.RuleService;
import cn.git.rules.util.BusinessModuleType;
import cn.git.rules.vo.*;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import io.swagger.annotations.Api;
import io.swagger.annotations.ApiOperation;
import io.swagger.annotations.ApiParam;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.validation.annotation.Validated;
import org.springframework.web.bind.annotation.*;
import javax.validation.Valid;
import javax.validation.constraints.NotNull;
import java.util.List;
import java.util.stream.Collectors;
/**
* 通用规则服务controller
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2022-03-28
*/
@Validated
@RestController
@RequestMapping("/config/rules")
@Api(tags = "通用规则服务controller", value = "通用规则服务controller")
public class CommonRuleController {
@Autowired
private RuleConvert ruleConvert;
@Autowired
private RuleService ruleService;
@Autowired
private BusinessModuleType businessModuleType;
/**
* 添加规则uri关联关系
* @param addInVO addInVO
* @return Result 添加uri结果
*/
@ApiOperation(value = "添加规则uri关联关系", notes = "添加规则uri关联关系")
@PostMapping(value = "/addRuleRelation")
public Result addTbUriModuleRelation(
@ApiParam(name = "addInVO", value = "添加规则关联关系信息inVo", required = true)
@Valid @RequestBody TbUriModuleRelationAddInVO addInVO) {
// 添加规则关联关系错误信息
String errorMessage = ruleService.addRelation(ruleConvert.relationAddInVoConvertDto(addInVO));
if (StrUtil.isNotBlank(errorMessage)) {
return Result.error(errorMessage);
}
return Result.ok();
}
/**
* 修改规则uri关联关系
* @param updateInVO updateInVO
* @return Result 修改uri结果
*/
@ApiOperation(value = "修改规则uri关联关系", notes = "修改规则uri关联关系")
@PostMapping(value = "/relation/update")
public Result updateRelation(
@ApiParam(name = "updateInVO", value = "修改规则uri关联关系inVo", required = true)
@Valid @RequestBody TbUriModuleRelationUpdateInVO updateInVO) {
// 修改规则关联关系错误信息
String errorMessage = ruleService.updateRelation(ruleConvert.relationUpdateInVOConvertRuleDto(updateInVO));
if (StrUtil.isNotBlank(errorMessage)) {
return Result.error(errorMessage);
}
return Result.ok();
}
/**
* 通过规则关联关系id获取关联关系
* @param relationId relationId
* @return 获取关联关系信息
*/
@ApiOperation(value = "通过规则关联关系id获取关联关系", notes = "通过规则关联关系id获取关联关系")
@GetMapping(value = "/findRelation/{relationId}")
public Result findRelationById(
@NotNull(message = "规则关联关系id不能为空!")
@ApiParam(name = "relationId", value = "关联关系id", required = true)
@PathVariable("relationId") String relationId) {
RuleDTO ruleDTO = ruleService.findRelationById(relationId);
if (StrUtil.isNotBlank(ruleDTO.getErrorMessage())) {
return Result.error(ruleDTO.getErrorMessage());
}
return Result.ok(ruleConvert.ruleDtoConvertRelationOutVo(ruleDTO));
}
/**
* 规则关联关系列表查询
* @param listInVO listInVO
* @return 列表信息
*/
@ApiOperation(value = "规则关联关系列表查询", notes = "规则关联关系列表查询")
@GetMapping(value = "/listTbUriModuleRelation")
public Result listTbUriModuleRelation(
@ApiParam(name = "listInVO", value = "规则关联关系列表查询InVO", required = true)
ListTbUriModuleRelationInVO listInVO) {
RuleDTO ruleDTO = ruleService.findRelationList(ruleConvert.relationListInVoConvertRuleDto(listInVO));
ListTbUriModuleRelationOutVO outVO = new ListTbUriModuleRelationOutVO();
if (ObjectUtil.isNotEmpty(ruleDTO.getRelationPageBean())) {
outVO = ruleConvert.ruleDtoConvertRelationListOutVo(ruleDTO);
}
// 拼装模块展示信息列表
List<BusinessModuleType.ViewModule> moduleList = businessModuleType.getModules().stream().map(module -> {
BusinessModuleType.ViewModule viewModule = new BusinessModuleType.ViewModule();
viewModule.setName(module.getName());
viewModule.setDesc(module.getDesc());
return viewModule;
}).collect(Collectors.toList());
outVO.setModuleList(moduleList);
return Result.ok(outVO);
}
/**
* 添加规则详情信息
* @param addInVO addInVO
* @return Result 添加结果
*/
@ApiOperation(value = "添加规则详情信息", notes = "添加规则详情信息")
@PostMapping(value = "/addRuleConfig")
public Result addRuleConfig(
@ApiParam(name = "addInVO", value = "添加规则关联关系信息inVo", required = true)
@Valid @RequestBody TbSysCommonRuleConfigAddInVO addInVO) {
// 添加规则关联关系错误信息
String errorMessage = ruleService.addRuleConfig(ruleConvert.addRuleConfigConvertDto(addInVO));
if (StrUtil.isNotBlank(errorMessage)) {
return Result.error(errorMessage);
}
return Result.ok();
}
/**
* 规则详情列表信息查询
* @param listInVO listInVO
* @return 列表信息
*/
@ApiOperation(value = "规则详情列表信息查询", notes = "规则详情列表信息查询")
@GetMapping(value = "/listRuleConfig")
public Result listRuleConfig(
@ApiParam(name = "listInVO", value = "规则详情列表信息查询inVO", required = true)
ListTbSysCommonRuleConfigInVO listInVO) {
RuleDTO ruleDTO = ruleService.findRuleConfigList(ruleConvert.ruleConfigListInVoConvertRuleDto(listInVO));
ListTbSysCommonRuleConfigOutVO outVO = new ListTbSysCommonRuleConfigOutVO();
if (ObjectUtil.isNotEmpty(ruleDTO.getRuleConfigPageBean())) {
outVO = ruleConvert.ruleDtoConvertRuleConfigListOutVo(ruleDTO);
}
return Result.ok(outVO);
}
/**
* 修改规则
* @param updateInVO updateInVO
* @return Result 修改结果
*/
@ApiOperation(value = "修改规则信息", notes = "修改规则信息")
@PostMapping(value = "/updateRuleConfig")
public Result updateRuleConfig(
@ApiParam(name = "updateInVO", value = "修改规则inVO", required = true)
@Valid @RequestBody TbSysCommonRuleConfigUpdateInVO updateInVO) {
// 修改规则关联关系错误信息
String errorMessage = ruleService.updateRuleInfo(ruleConvert.updateRuleInfoInVoConvertRuleDto(updateInVO));
if (StrUtil.isNotBlank(errorMessage)) {
return Result.error(errorMessage);
}
return Result.ok();
}
/**
* 删除或者禁用规则信息
* @param deleteInVO deleteInVO
* @return Result 删除结果
*/
@ApiOperation(value = "删除或者禁用规则信息", notes = "删除或者禁用规则信息")
@PostMapping(value = "/disableRule")
public Result disableRule(
@ApiParam(name = "deleteInVO", value = "禁用规则关联关系信息inVo", required = true)
@Valid @RequestBody TbSysCommonRuleConfigDeleteInVO deleteInVO) {
if (StrUtil.isBlank(deleteInVO.getRuleId()) && StrUtil.isBlank(deleteInVO.getUri())) {
return Result.error("删除规则对应uri与ruleId都为空,删除失败!");
} else if (StrUtil.isNotBlank(deleteInVO.getRuleId()) && StrUtil.isNotBlank(deleteInVO.getUri())) {
return Result.error("删除规则对应uri与ruleId只能传递一个,删除失败!");
}
String errorMessage = ruleService.deleteOrDisableRule(ruleConvert.deleteInVoConvertRuleDto(deleteInVO));
if (StrUtil.isNotBlank(errorMessage)) {
return Result.error(errorMessage);
}
return Result.ok();
}
/**
* 恢复禁用规则
* @param enableInVO enableInVO
* @return 错误信息
*/
@ApiOperation(value = "恢复禁用规则", notes = "恢复禁用规则")
@PostMapping(value = "/enableRule")
public Result enableRule(
@ApiParam(name = "addInVO", value = "添加规则关联关系信息inVo", required = true)
@RequestBody TbSysCommonRuleConfigEnableInVO enableInVO) {
// 全部填写校验
boolean checkPrams = (StrUtil.isBlank(enableInVO.getRuleId()) && StrUtil.isBlank(enableInVO.getUri()));
if (checkPrams) {
return Result.error("启用规则对应uri与ruleId都为空,启用规则失败!");
}
checkPrams = (StrUtil.isNotBlank(enableInVO.getRuleId()) && StrUtil.isNotBlank(enableInVO.getUri()));
if (checkPrams) {
return Result.error("启用规则uri与ruleId只能任选一个进行启用,启用失败!");
}
String errorMessage = ruleService.enableRule(ruleConvert.enableInVConvertRuleDto(enableInVO));
if (StrUtil.isNotBlank(errorMessage)) {
return Result.error(errorMessage);
}
return Result.ok();
}
/**
* 通过规则id获取规则信息
* @param ruleId ruleId
* @return 错误信息
*/
@ApiOperation(value = "通过规则id获取规则信息", notes = "通过规则id获取规则信息")
@GetMapping(value = "/findRuleInfoById/{ruleId}")
public Result findRuleInfoById(
@NotNull(message = "规则id不能为空!")
@ApiParam(name = "ruleId", value = "规则id", required = true)
@PathVariable(value = "ruleId") String ruleId) {
RuleDTO ruleDTO = ruleService.findRuleInfoById(ruleId);
if (StrUtil.isNotBlank(ruleDTO.getErrorMessage())) {
return Result.error(ruleDTO.getErrorMessage());
}
return Result.ok(ruleConvert.ruleDtoConvertDetailOutVo(ruleDTO));
}
/**
* uri关联当前存在规则
* @param relationId 关联关系id
* @param ruleId 规则id
* @return 关联结果
*/
@ApiOperation(value = "uri关联当前存在规则", notes = "uri关联当前存在规则")
@GetMapping(value = "/relation/link/{relationId}/{ruleId}")
public Result ruleConfigAndUriRelationLinked(
@NotNull(message = "uri关联id不能为空!")
@ApiParam(name = "relationId", value = "uri关联id不能为空", required = true)
@PathVariable("relationId") String relationId,
@NotNull(message = "规则id不能为空!")
@ApiParam(name = "ruleId", value = "规则id", required = true)
@PathVariable("ruleId") String ruleId) {
String errorMessage = ruleService.linkRelationAndRule(relationId, ruleId);
if (StrUtil.isNotBlank(errorMessage)) {
return Result.error(errorMessage);
}
return Result.ok();
}
/**
* 解除绑定关系
* @param relationId 关联关系id
* @param ruleId 规则id
* @return 解除结果
*/
@ApiOperation(value = "解除绑定关系", notes = "解除绑定关系")
@GetMapping(value = "/unlock/{relationId}/{ruleId}")
public Result unlockRelationAndRuleConfig(
@NotNull(message = "uri关联id不能为空!")
@ApiParam(name = "relationId", value = "uri关联id不能为空", required = true)
@PathVariable("relationId") String relationId,
@NotNull(message = "规则id不能为空!")
@ApiParam(name = "ruleId", value = "规则id", required = true)
@PathVariable("ruleId") String ruleId) {
String errorMessage = ruleService.unlockRelationAndRuleConfig(relationId, ruleId);
if (StrUtil.isNotBlank(errorMessage)) {
return Result.error(errorMessage);
}
return Result.ok();
}
}
5.2.2 service实现
执行service具体实现
package cn.git.rules.service.rule.impl;
import cn.git.common.exception.ServiceException;
import cn.git.common.lock.LockTypeEnum;
import cn.git.common.lock.SimpleDistributeLock;
import cn.git.common.page.PageBean;
import cn.git.common.page.PaginationContext;
import cn.git.kafka.KafkaProducer;
import cn.git.redis.RedisUtil;
import cn.git.rules.constant.RuleConstant;
import cn.git.rules.dto.RuleDTO;
import cn.git.rules.entity.TbSysCommonRuleConfig;
import cn.git.rules.entity.TbUriModuleRelation;
import cn.git.rules.mapper.TbSysCommonRuleConfigMapper;
import cn.git.rules.mapper.TbUriModuleRelationMapper;
import cn.git.rules.mapstruct.RuleConvert;
import cn.git.rules.service.rule.RuleService;
import cn.git.rules.util.RuleUtil;
import cn.hutool.core.bean.BeanUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import java.util.*;
import java.util.stream.Collectors;
/**
* 规则引擎表管理service实现类
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2021-05-10
*/
@Slf4j
@Service
public class RuleServiceImpl implements RuleService {
@Autowired
private KafkaProducer kafkaProducer;
@Autowired
private RuleConvert ruleConvert;
@Autowired
private RedisUtil redisUtil;
@Autowired
private RuleUtil ruleUtil;
@Autowired
private TbSysCommonRuleConfigMapper tbSysCommonRuleConfigMapper;
@Autowired
private TbUriModuleRelationMapper tbUriModuleRelationMapper;
@Autowired
private SimpleDistributeLock simpleDistributeLock;
@Value("${spring.application.name}")
private String name;
/**
* 规则服务初始化方法
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2021-05-12
*/
@Override
public void initRuleLoad() {
log.info("规则引擎初始化服务开始,进行初始化加锁!");
// 获取锁,防止多服务启动重复执行
redisUtil.set(RuleConstant.DICS_RULE_INIT_FLAG, RuleConstant.NUM_0_STR);
String lockResult = simpleDistributeLock.tryLock(LockTypeEnum.RULE_INIT_LOCK, name);
if (StrUtil.isNotBlank(lockResult)) {
log.info("加锁成功,开始初始化规则消息");
this.ruleInfoReload();
simpleDistributeLock.releaseLock(LockTypeEnum.RULE_INIT_LOCK, name, lockResult);
redisUtil.set(RuleConstant.DICS_RULE_INIT_FLAG, RuleConstant.NUM_1_STR);
} else {
log.error("当前服务规则引擎服务初始化加锁失败,未执行规则服务初始化!");
}
}
/**
* 重新载入redis规则信息
*/
public void ruleInfoReload() {
// 删除缓存规则信息
redisUtil.del(RuleConstant.DROOLS_RULES);
// 获取规则uri信息
List<TbUriModuleRelation> relationList = tbUriModuleRelationMapper.selectList(null);
// 获取规则列表
QueryWrapper<TbSysCommonRuleConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(TbSysCommonRuleConfig::getIfDisable, RuleConstant.NUM_0_STR);
List<Map<String, Object>> ruleMapList = tbSysCommonRuleConfigMapper.selectMaps(queryWrapper);
// 最终插入redis缓存信息
if (ObjectUtil.isNotEmpty(ruleMapList) && ObjectUtil.isNotEmpty(relationList)) {
// 组装规则新建对象
List<Map<String, Object>> ruleMapListFinal = new ArrayList<>();
Map<String, Map<String, Object>> ruleIdKeyMap = new HashMap<>();
ruleMapList.forEach(ruleMap -> {
ruleIdKeyMap.put(StrUtil.toString(ruleMap.get(RuleConstant.RULE_ID_FLAG)), ruleMap);
});
// 开始组装最终信息
relationList.forEach(relation -> {
if (StrUtil.isBlank(relation.getRuleIds())) {
return;
}
List<String> ruleIdList = Arrays.asList(relation.getRuleIds().split(StrUtil.COMMA));
if (ObjectUtil.isEmpty(ruleIdList)) {
return;
}
ruleIdList.forEach(ruleId -> {
Map<String, Object> ruleMap = ruleIdKeyMap.get(ruleId);
Map<String, Object> targetMap = new HashMap<>();
BeanUtil.copyProperties(ruleMap, targetMap, true);
targetMap.put(RuleConstant.RULE_URI, relation.getUri());
targetMap.put(RuleConstant.RULE_DESCRIPTION, relation.getUriDescription());
ruleMapListFinal.add(targetMap);
});
});
if (ObjectUtil.isEmpty(ruleMapListFinal)) {
log.error("获取最终uri以及对应规则信息为空,请确认!");
return;
}
// 查询出信息存入redis 信息中
Map<String, Object> ruleInfoMap = new HashMap<>(16);
ruleMapListFinal.forEach(ruleMap -> {
ruleUtil.setRuleInfo(ruleMap, ruleInfoMap);
});
if (ObjectUtil.isNotEmpty(ruleInfoMap)) {
redisUtil.hmset(RuleConstant.DROOLS_RULES, ruleInfoMap);
}
} else {
log.error("规则引擎对应规则关联关系表以及规则信息表为空,请确认信息是否完整!");
}
}
/**
* 添加规则关联关系
*
* @param ruleDTO ruleDTO
* @return 处理信息
*/
@Override
public String addRelation(RuleDTO ruleDTO) {
// 实体对象创建
TbUriModuleRelation tbUriModuleRelation = ruleConvert.ruleDtoConvertModuleRelation(ruleDTO);
// uri 重复校验
String uri = tbUriModuleRelation.getUri();
String module = tbUriModuleRelation.getModule();
QueryWrapper<TbUriModuleRelation> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(TbUriModuleRelation::getUri, uri);
if (ObjectUtil.isNotEmpty(tbUriModuleRelationMapper.selectOne(queryWrapper))) {
return StrUtil.format("当前模块[{}]以及uri[{}]已经存在,不能进行新增!", module, uri);
}
tbUriModuleRelation.setRelationId(IdUtil.simpleUUID());
tbUriModuleRelation.setCreateTime(new Date());
tbUriModuleRelation.setUpdateTime(new Date());
tbUriModuleRelationMapper.insert(tbUriModuleRelation);
return null;
}
/**
* 修改规则关联关系
* @param ruleDTO ruleDTO
* @return 错误信息
*/
@Override
public String updateRelation(RuleDTO ruleDTO) {
// 实体对象创建
TbUriModuleRelation tbUriModuleRelation = ruleConvert.ruleDtoConvertModuleRelation(ruleDTO);
// 规则修改uri是否存在校验
String uri = tbUriModuleRelation.getUri();
QueryWrapper<TbUriModuleRelation> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(TbUriModuleRelation::getUri, uri)
.ne(TbUriModuleRelation::getRelationId, ruleDTO.getRelationId());
TbUriModuleRelation dbRelation = tbUriModuleRelationMapper.selectOne(queryWrapper);
if (ObjectUtil.isNotNull(dbRelation)) {
return StrUtil.format("当前规则修改后的uri已经存在,与relationId[{}]规则关联关系相同,修改失败!",
dbRelation.getRelationId());
}
tbUriModuleRelation.setUpdateTime(new Date());
tbUriModuleRelationMapper.updateById(tbUriModuleRelation);
// 刷新redis规则信息
this.ruleInfoReload();
// 通过规则id列表信息获取uri关联信息
kieBaseCacheProducer(new ArrayList<>(Collections.singleton(uri)));
return null;
}
/**
* 通过规则关联关系获取规则关联关系信息
* @param relationId relationId
* @return 规则关联关系信息
*/
@Override
public RuleDTO findRelationById(String relationId) {
// 最终返回对象
RuleDTO ruleDTO = new RuleDTO();
// 获取规则关联关系
TbUriModuleRelation tbUriModuleRelation = tbUriModuleRelationMapper.selectById(relationId);
if (ObjectUtil.isNull(tbUriModuleRelation)) {
ruleDTO.setErrorMessage(StrUtil.format("通过传入的relationId[{}]未找到对应规则关联关系信息!", relationId));
return ruleDTO;
}
ruleDTO = ruleConvert.relationConvertRuleDTO(tbUriModuleRelation);
return ruleDTO;
}
/**
* 新增规则
* @param ruleDTO dto
* @return 错误信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public String addRuleConfig(RuleDTO ruleDTO) {
// 传入ruleDTO 转换为entity 实体类型
TbSysCommonRuleConfig tbSysCommonRuleConfig = ruleConvert.ruleDtoConvertRuleConfig(ruleDTO);
tbSysCommonRuleConfig.setRuleId(IdUtil.simpleUUID());
tbSysCommonRuleConfig.setCreateTime(new Date());
tbSysCommonRuleConfig.setUpdateTime(new Date());
// 获取当前模块uri对应所有规则
TbUriModuleRelation relation = tbUriModuleRelationMapper.selectById(ruleDTO.getRelationId());
if (ObjectUtil.isNull(relation)) {
return StrUtil.format("当前传入规则关联id[{}]错误,请检查", ruleDTO.getRelationId());
}
// 获取order规则排序顺序正确性校验
String ruleIds = relation.getRuleIds();
if (StrUtil.isNotBlank(ruleIds)) {
QueryWrapper<TbSysCommonRuleConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().in(TbSysCommonRuleConfig::getRuleId,
Arrays.asList(relation.getRuleIds().split(StrUtil.COMMA)));
List<TbSysCommonRuleConfig> ruleConfigList = tbSysCommonRuleConfigMapper.selectList(queryWrapper);
if (ObjectUtil.isNotEmpty(ruleConfigList)) {
Integer order = ruleUtil.getRuleSalience(tbSysCommonRuleConfig.getRuleInfo());
if (ObjectUtil.isNotNull(order) && order != -1) {
// order重复性校验
String ifLocalRule = tbSysCommonRuleConfig.getIfLocalRule();
for (TbSysCommonRuleConfig ruleConfig: ruleConfigList) {
if (ifLocalRule.equals(ruleConfig.getIfLocalRule())
&& ruleConfig.getExecuteOrder().equals(order)) {
return StrUtil.format("当前传入规则排序与规则id[{}]的规则[{}]执行顺序重复!",
ruleConfig.getRuleId(),
ruleConfig.getRuleDescription());
}
}
// 校验通过,设置规则执行顺序
tbSysCommonRuleConfig.setExecuteOrder(order);
} else {
return "获取当前规则排序salience失败,请检查salience是否正确编写!";
}
}
ruleIds = ruleIds.concat(StrUtil.COMMA).concat(tbSysCommonRuleConfig.getRuleId());
} else {
ruleIds = tbSysCommonRuleConfig.getRuleId();
Integer order = ruleUtil.getRuleSalience(tbSysCommonRuleConfig.getRuleInfo());
if (ObjectUtil.isNull(order)) {
return "获取当前规则排序salience失败,请检查salience是否正确编写!";
}
// 设置规则执行顺序
tbSysCommonRuleConfig.setExecuteOrder(order);
}
// 服务名称正确性校验
String ruleName = ruleUtil.getRuleName(ruleDTO.getRuleInfo());
if (StrUtil.isBlank(ruleName)) {
return "当前新增规则规则名称设置错误,请确认名称正确!";
}
QueryWrapper<TbSysCommonRuleConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(TbSysCommonRuleConfig::getRuleName, ruleName);
TbSysCommonRuleConfig ruleConfig = tbSysCommonRuleConfigMapper.selectOne(queryWrapper);
if (ObjectUtil.isNotNull(ruleConfig)) {
return StrUtil.format("当前新增规则名称[{}]已经存在,请修改后新增!", ruleConfig.getRuleName());
} else {
tbSysCommonRuleConfig.setRuleName(ruleName);
}
// 子规则合法性校验,包含是否存在以及是否禁用
String childRuleName = ruleUtil.getChildRuleName(ruleDTO.getRuleInfo());
if (StrUtil.isNotBlank(childRuleName)) {
queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(TbSysCommonRuleConfig::getRuleName, childRuleName)
.eq(TbSysCommonRuleConfig::getIfDisable, RuleConstant.NUM_0_STR);
TbSysCommonRuleConfig childRuleConfig = tbSysCommonRuleConfigMapper.selectOne(queryWrapper);
if (ObjectUtil.isNull(childRuleConfig)) {
return StrUtil.format("新增服务子服务[{}]为空或者被禁用,请确认!", childRuleName);
} else {
tbSysCommonRuleConfig.setChildRuleName(childRuleName);
}
}
// 新增服务
tbSysCommonRuleConfigMapper.insert(tbSysCommonRuleConfig);
// 修改关联信息部分的ruleIds字段
relation.setRuleIds(ruleIds);
tbUriModuleRelationMapper.updateById(relation);
// 刷新redis规则信息
this.ruleInfoReload();
// 通过规则id列表信息获取uri关联信息
kieBaseCacheProducer(new ArrayList<>(Collections.singleton(relation.getUri())));
return null;
}
/**
* 修改规则信息
* @param ruleDTO dto
* @return 错误信息
*/
@Override
public String updateRuleInfo(RuleDTO ruleDTO) {
// 转换dto为ruleConfig对象
TbSysCommonRuleConfig tbSysCommonRuleConfig = ruleConvert.ruleDtoConvertRuleConfig(ruleDTO);
tbSysCommonRuleConfig.setUpdateTime(new Date());
// 获取当前模块uri对应所有规则
TbUriModuleRelation relation = tbUriModuleRelationMapper.selectById(ruleDTO.getRelationId());
if (ObjectUtil.isNull(relation)) {
return StrUtil.format("当前传入规则关联id[{}]错误,请检查", ruleDTO.getRelationId());
}
// 获取order规则排序顺序正确性校验
String ruleIds = relation.getRuleIds();
if (StrUtil.isNotBlank(ruleIds)) {
QueryWrapper<TbSysCommonRuleConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().ne(TbSysCommonRuleConfig::getRuleId, ruleDTO.getRuleId())
.in(TbSysCommonRuleConfig::getRuleId, Arrays.asList(relation.getRuleIds().split(StrUtil.COMMA)));
List<TbSysCommonRuleConfig> ruleConfigList = tbSysCommonRuleConfigMapper.selectList(queryWrapper);
if (ObjectUtil.isNotEmpty(ruleConfigList)) {
Integer order = ruleUtil.getRuleSalience(tbSysCommonRuleConfig.getRuleInfo());
if (ObjectUtil.isNotNull(order) && order != -1) {
// order重复性校验
String ifLocalRule = tbSysCommonRuleConfig.getIfLocalRule();
for (TbSysCommonRuleConfig ruleConfig: ruleConfigList) {
if (ifLocalRule.equals(ruleConfig.getIfLocalRule())
&& ruleConfig.getExecuteOrder().equals(order)) {
return StrUtil.format("当前传入规则排序与规则id[{}]的规则[{}]执行顺序重复!",
ruleConfig.getRuleId(),
ruleConfig.getRuleDescription());
}
}
// 校验通过,设置规则执行顺序
tbSysCommonRuleConfig.setExecuteOrder(order);
} else {
return "获取当前规则排序salience失败,请检查salience是否正确编写!";
}
}
}
// 服务名称正确性校验
String ruleName = ruleUtil.getRuleName(tbSysCommonRuleConfig.getRuleInfo());
if (StrUtil.isBlank(ruleName)) {
return "当前新增规则规则名称设置错误,请确认名称正确!";
}
QueryWrapper<TbSysCommonRuleConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().ne(TbSysCommonRuleConfig::getRuleId, tbSysCommonRuleConfig.getRuleId())
.eq(TbSysCommonRuleConfig::getRuleName, ruleName);
TbSysCommonRuleConfig ruleConfig = tbSysCommonRuleConfigMapper.selectOne(queryWrapper);
if (ObjectUtil.isNotNull(ruleConfig)) {
return StrUtil.format("当前修改规则名称[{}]已经存在,修改失败!", ruleConfig.getRuleName());
} else {
tbSysCommonRuleConfig.setRuleName(ruleName);
}
// 子规则合法性校验,包含是否存在以及是否禁用
String childRuleName = ruleUtil.getChildRuleName(ruleDTO.getRuleInfo());
if (StrUtil.isNotBlank(childRuleName)) {
queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(TbSysCommonRuleConfig::getRuleName, childRuleName)
.eq(TbSysCommonRuleConfig::getIfDisable, RuleConstant.NUM_0_STR);
TbSysCommonRuleConfig childRuleConfig = tbSysCommonRuleConfigMapper.selectOne(queryWrapper);
if (ObjectUtil.isNull(childRuleConfig)) {
return StrUtil.format("修改服务子服务[{}]为空或者被禁用,请确认!", childRuleName);
} else {
tbSysCommonRuleConfig.setChildRuleName(childRuleName);
}
}
// 修改规则信息
tbSysCommonRuleConfigMapper.updateById(tbSysCommonRuleConfig);
// 刷新redis规则信息
this.ruleInfoReload();
// 通过规则id列表信息获取uri关联信息
kieBaseCacheProducer(new ArrayList<>(Collections.singleton(relation.getUri())));
return null;
}
/**
* 删除或者禁用规则
* @param ruleDTO dto
* @return 错误信息
*/
@Override
@Transactional(rollbackFor = Exception.class)
public String deleteOrDisableRule(RuleDTO ruleDTO) {
// 获取删除的id和uri以及操作类型
String uri = ruleDTO.getUri();
String ruleId = ruleDTO.getRuleId();
String optionType = ruleDTO.getOptionType();
// 获取全量信息
TbSysCommonRuleConfig ruleConfig = null;
List<String> ruleIdList = null;
// kafka规则发送信息
List<String> consumerKafkaRuleIdList = new ArrayList<>();
if (StrUtil.isNotBlank(uri)) {
QueryWrapper<TbUriModuleRelation> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(TbUriModuleRelation::getUri, uri);
TbUriModuleRelation relation = tbUriModuleRelationMapper.selectOne(queryWrapper);
if (ObjectUtil.isNull(relation)) {
return StrUtil.format("通过传入uri[{}]查询规则关联关系为空!", uri);
}
if (StrUtil.isNotBlank(relation.getRuleIds())) {
QueryWrapper<TbSysCommonRuleConfig> configQueryWrapper = new QueryWrapper<>();
configQueryWrapper.lambda().in(TbSysCommonRuleConfig::getRuleId,
Arrays.asList(relation.getRuleIds().split(StrUtil.COMMA)));
List<TbSysCommonRuleConfig> ruleConfigList = tbSysCommonRuleConfigMapper.selectList(configQueryWrapper);
if (ObjectUtil.isEmpty(ruleConfigList)) {
return StrUtil.format("uri对应关联关系对应规则ids[{}]查询规则为空!", relation.getRuleIds());
}
ruleIdList = ruleConfigList.stream().map(
TbSysCommonRuleConfig::getRuleId
).collect(Collectors.toList());
consumerKafkaRuleIdList.addAll(ruleIdList);
}
} else if (StrUtil.isNotBlank(ruleId)) {
ruleConfig = tbSysCommonRuleConfigMapper.selectById(ruleId);
if (ObjectUtil.isNull(ruleConfig)) {
return StrUtil.format("通过规则id[{}]查询规则信息为空!", ruleId);
}
consumerKafkaRuleIdList.add(ruleId);
}
// 修改规则通知uri列表信息
List<String> uriList = new ArrayList<>();
// 禁用操作
if (RuleConstant.NUM_1_STR.equals(optionType)) {
if (ObjectUtil.isNotNull(ruleConfig)) {
ruleConfig.setIfDisable(RuleConstant.NUM_1_STR);
ruleConfig.setUpdateTime(new Date());
tbSysCommonRuleConfigMapper.updateById(ruleConfig);
}
if (ObjectUtil.isNotEmpty(ruleIdList)) {
UpdateWrapper<TbSysCommonRuleConfig> updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda().in(TbSysCommonRuleConfig::getRuleId, ruleIdList);
TbSysCommonRuleConfig tbSysCommonRuleConfig = new TbSysCommonRuleConfig();
tbSysCommonRuleConfig.setIfDisable(RuleConstant.NUM_1_STR);
tbSysCommonRuleConfig.setUpdateTime(new Date());
tbSysCommonRuleConfigMapper.update(tbSysCommonRuleConfig, updateWrapper);
}
} else {
// ruleConfig删除操作
if (ObjectUtil.isNotNull(ruleConfig)) {
// 删除详情信息
tbSysCommonRuleConfigMapper.deleteById(ruleId);
ruleIdList = Collections.singletonList(ruleId);
List<String> ruleConfigUriList = commonRelationUpdate(ruleIdList);
if (ObjectUtil.isNotEmpty(ruleConfigUriList)) {
uriList.addAll(ruleConfigUriList);
}
ruleIdList = null;
} else {
if (ObjectUtil.isNotEmpty(ruleIdList)) {
List<String> uriRuleConfigList = commonRelationUpdate(ruleIdList);
if (ObjectUtil.isNotEmpty(uriRuleConfigList)) {
uriList.addAll(uriRuleConfigList);
}
tbSysCommonRuleConfigMapper.deleteBatchIds(ruleIdList);
}
QueryWrapper<TbUriModuleRelation> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(TbUriModuleRelation::getUri, uri);
tbUriModuleRelationMapper.delete(queryWrapper);
}
}
// 刷新redis规则信息
this.ruleInfoReload();
if (ObjectUtil.isNotEmpty(consumerKafkaRuleIdList)) {
// 通过规则id列表信息获取uri关联信息
List<TbUriModuleRelation> relationList = tbUriModuleRelationMapper
.getRelationListByRuleIdList(consumerKafkaRuleIdList);
if (ObjectUtil.isNotEmpty(relationList)) {
List<String> allConfigUri = relationList.stream().map(TbUriModuleRelation::getUri).collect(Collectors.toList());
allConfigUri.stream().distinct();
uriList.addAll(allConfigUri);
}
}
if (ObjectUtil.isNotEmpty(uriList)) {
// 通知外围系统进行规则修改
uriList.stream().distinct();
kieBaseCacheProducer(uriList);
}
return null;
}
/**
* 规则删除操作通用方法
* 通过ruleList查询relation信息
*/
public List<String> commonRelationUpdate(List<String> ruleIdList) {
// 通过规则idList获取所有关联的relation信息
List<TbUriModuleRelation> relationList = tbUriModuleRelationMapper.getRelationListByRuleIdList(ruleIdList);
if (ObjectUtil.isNotEmpty(relationList)) {
relationList.stream().distinct();
relationList.forEach(relation -> {
String ruleIds = relation.getRuleIds();
List<String> currentIdList = Arrays.asList(ruleIds.split(StrUtil.COMMA));
// relation对应ruleIds过滤当前删除的ruleId
currentIdList = currentIdList.stream().filter(ruleId ->
!ruleIdList.contains(ruleId)
).collect(Collectors.toList());
ruleIds = String.join(StrUtil.COMMA, currentIdList);
relation.setRuleIds(ruleIds);
tbUriModuleRelationMapper.updateById(relation);
});
// 通过规则id列表信息获取uri关联信息
if (ObjectUtil.isNotEmpty(relationList)) {
List<String> uriList = relationList.stream().map(
TbUriModuleRelation::getUri
).collect(Collectors.toList());
// 返回uri列表信息
return uriList;
}
}
return null;
}
/**
* 启用规则
* @param ruleDTO dto
* @return 错误信息
*/
@Override
public String enableRule(RuleDTO ruleDTO) {
// 获取启用的id和uri
String uri = ruleDTO.getUri();
String ruleId = ruleDTO.getRuleId();
if (StrUtil.isNotBlank(ruleId)) {
// 单个规则启用
TbSysCommonRuleConfig ruleConfig = tbSysCommonRuleConfigMapper.selectById(ruleId);
if (ObjectUtil.isNull(ruleConfig)) {
return StrUtil.format("通过规则id[]未找到对应规则,启用规则失败!", ruleId);
}
ruleConfig.setIfDisable(RuleConstant.NUM_0_STR);
tbSysCommonRuleConfigMapper.updateById(ruleConfig);
// 刷新redis规则信息
this.ruleInfoReload();
// 通过规则id列表信息获取uri关联信息
List<TbUriModuleRelation> relationList = tbUriModuleRelationMapper
.getRelationListByRuleIdList(new ArrayList<>(Collections.singletonList(ruleId)));
if (ObjectUtil.isNotEmpty(relationList)) {
List<String> uriList = relationList.stream().map(TbUriModuleRelation::getUri).collect(Collectors.toList());
uriList.stream().distinct();
// 发送修改规则信息到其模块
kieBaseCacheProducer(uriList);
}
} else if (StrUtil.isNotBlank(uri)) {
// 启用uri对应规则
QueryWrapper<TbUriModuleRelation> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(TbUriModuleRelation::getUri, uri);
TbUriModuleRelation relation = tbUriModuleRelationMapper.selectOne(queryWrapper);
if (ObjectUtil.isNull(relation)) {
return StrUtil.format("通过uri[{}]未查询规则关联关系,启动失败!", uri);
}
if (StrUtil.isNotBlank(relation.getRuleIds())) {
String[] ruleIds = relation.getRuleIds().split(StrUtil.COMMA);
if (ObjectUtil.isNotEmpty(ruleIds)) {
UpdateWrapper<TbSysCommonRuleConfig> updateWrapper = new UpdateWrapper<>();
updateWrapper.lambda().in(TbSysCommonRuleConfig::getRuleId, new ArrayList<>(Arrays.asList(ruleIds)));
TbSysCommonRuleConfig ruleConfig = new TbSysCommonRuleConfig();
ruleConfig.setIfDisable(RuleConstant.NUM_0_STR);
tbSysCommonRuleConfigMapper.update(ruleConfig, updateWrapper);
// 刷新redis规则信息
this.ruleInfoReload();
// 发送修改规则信息到其模块
kieBaseCacheProducer(new ArrayList<>(Collections.singletonList(uri)));
}
}
}
return null;
}
/**
* 通过规则id获取ruleInfo
* @param ruleId 规则id
* @return dto
*/
@Override
public RuleDTO findRuleInfoById(String ruleId) {
// 返回dto
RuleDTO ruleDTO = new RuleDTO();
// 通过ruleId查询信息
TbSysCommonRuleConfig ruleConfig = tbSysCommonRuleConfigMapper.selectById(ruleId);
if (ObjectUtil.isNull(ruleConfig)) {
ruleDTO.setErrorMessage(StrUtil.format("通过规则id[{}]查询规则信息为空!", ruleId));
return ruleDTO;
}
return ruleConvert.ruleConfigConvertRuleDto(ruleConfig);
}
/**
* 关联关系列表查询
* @param ruleDTO ruleDTO
* @return dto
*/
@Override
public RuleDTO findRelationList(RuleDTO ruleDTO) {
// 获取分页参数
Page<TbUriModuleRelation> page = new Page<>(PaginationContext.getPageNum(), PaginationContext.getPageSize());
IPage<TbUriModuleRelation> iPage = tbUriModuleRelationMapper.getRelationList(page, ruleDTO);
if (ObjectUtil.isNotNull(iPage) && ObjectUtil.isNotEmpty(iPage.getRecords())) {
PageBean<TbUriModuleRelation> pageBean = new PageBean<>(iPage);
ruleDTO.setRelationPageBean(pageBean);
}
return ruleDTO;
}
/**
* 查询规则详情列表信息
* @param ruleDTO ruleDTO
* @return dto
*/
@Override
public RuleDTO findRuleConfigList(RuleDTO ruleDTO) {
// 关联关系字段非空条件获取
String relationId = ruleDTO.getRelationId();
List<String> ruleIdList = null;
if (StrUtil.isNotBlank(relationId)) {
// 如果规则关联关系不为空,则引入关联关系查询ruleIds查询条件
QueryWrapper<TbUriModuleRelation> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(TbUriModuleRelation::getRelationId, relationId);
TbUriModuleRelation relation = tbUriModuleRelationMapper.selectOne(queryWrapper);
if (ObjectUtil.isNotNull(relation) && StrUtil.isNotBlank(relation.getRuleIds())) {
ruleIdList = Arrays.asList(relation.getRuleIds().split(StrUtil.COMMA));
} else {
return ruleDTO;
}
}
// 规则详情条件获取
QueryWrapper<TbSysCommonRuleConfig> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().orderByDesc(TbSysCommonRuleConfig::getExecuteOrder);
if (ObjectUtil.isNotEmpty(ruleIdList)) {
// relation不为空整理出的ruleIdList条件
queryWrapper.lambda().in(TbSysCommonRuleConfig::getRuleId, ruleIdList);
}
if (ObjectUtil.isNotNull(ruleDTO.getRuleName())) {
// 规则名称
queryWrapper.lambda().like(TbSysCommonRuleConfig::getRuleName, ruleDTO.getRuleName());
}
if (StrUtil.isNotBlank(ruleDTO.getRuleDescription())) {
// 规则描述
queryWrapper.lambda().like(TbSysCommonRuleConfig::getRuleDescription, ruleDTO.getRuleDescription());
}
if (StrUtil.isNotBlank(ruleDTO.getRuleInfo())) {
// 部分规则详情
queryWrapper.lambda().like(TbSysCommonRuleConfig::getRuleInfo, ruleDTO.getRuleInfo());
}
if (StrUtil.isNotBlank(ruleDTO.getServices())) {
// service条件
queryWrapper.lambda().like(TbSysCommonRuleConfig::getServices, ruleDTO.getServices());
}
if (StrUtil.isNotBlank(ruleDTO.getIfDisable())) {
// 是否禁用
queryWrapper.lambda().eq(TbSysCommonRuleConfig::getIfDisable, ruleDTO.getIfDisable());
}
if (StrUtil.isNotBlank(ruleDTO.getIfLocalRule())) {
// 是否本地规则
queryWrapper.lambda().eq(TbSysCommonRuleConfig::getIfLocalRule, ruleDTO.getIfLocalRule());
}
// 获取分页参数
Page<TbSysCommonRuleConfig> page = new Page<>(PaginationContext.getPageNum(), PaginationContext.getPageSize());
IPage<TbSysCommonRuleConfig> ruleConfigIPage = tbSysCommonRuleConfigMapper.selectPage(page, queryWrapper);
if (ObjectUtil.isNotNull(ruleConfigIPage) && ObjectUtil.isNotEmpty(ruleConfigIPage.getRecords())) {
PageBean<TbSysCommonRuleConfig> pageBean = new PageBean<>(ruleConfigIPage);
ruleDTO.setRuleConfigPageBean(pageBean);
}
return ruleDTO;
}
/**
* 关联uri关联现有存在规则
* @param relationId 关联id
* @param ruleId 规则id
* @return 错误信息
*/
@Override
public String linkRelationAndRule(String relationId, String ruleId) {
// 通过relationId查询relation关联信息
QueryWrapper<TbUriModuleRelation> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(TbUriModuleRelation::getRelationId, relationId);
TbUriModuleRelation relation = tbUriModuleRelationMapper.selectOne(queryWrapper);
if (ObjectUtil.isNull(relation)) {
// 校验relation非空校验
return StrUtil.format("通过传入规则关联id[{}]未查询到uri关联信息,绑定规则失败!", relationId);
}
// ruleId正确性校验
TbSysCommonRuleConfig ruleConfig = tbSysCommonRuleConfigMapper.selectById(ruleId);
if (ObjectUtil.isEmpty(ruleConfig)) {
return StrUtil.format("通过规则id[{}]查询规则为空,绑定规则失败!", ruleId);
}
// 规则详情校验
if (StrUtil.isNotBlank(relation.getRuleIds())) {
// uri绑定规则重复绑定校验
if (relation.getRuleIds().contains(ruleId)) {
return StrUtil.format("当前uri[{}]已经绑定ruleId[{}]对应规则,无法多次绑定!", relation.getUri(), ruleId);
}
// 规则绑定规则order排序校验
Integer currentLinkOrder = ruleUtil.getRuleSalience(ruleConfig.getRuleInfo());
QueryWrapper<TbSysCommonRuleConfig> ruleConfigQueryWrapper = new QueryWrapper<>();
ruleConfigQueryWrapper.lambda().in(TbSysCommonRuleConfig::getRuleId,
Arrays.asList(relation.getRuleIds().split(StrUtil.COMMA)));
List<TbSysCommonRuleConfig> ruleConfigList = tbSysCommonRuleConfigMapper.selectList(ruleConfigQueryWrapper);
if (ObjectUtil.isNotEmpty(ruleConfigList)) {
if (ObjectUtil.isNotNull(currentLinkOrder) && currentLinkOrder != -1) {
// order重复性校验
String ifLocalRule = ruleConfig.getIfLocalRule();
for (TbSysCommonRuleConfig eachRuleConfig: ruleConfigList) {
if (ifLocalRule.equals(eachRuleConfig.getIfLocalRule())
&& eachRuleConfig.getExecuteOrder().equals(currentLinkOrder)) {
return StrUtil.format("当前绑定规则排序与规则id[{}]的规则[{}]执行顺序重复,关联失败!",
eachRuleConfig.getRuleId(),
eachRuleConfig.getRuleDescription());
}
}
} else {
return StrUtil.format("获取要绑定规则ruleId[{}]对应salience失败,请检查要绑定的规则salience是否正确!", ruleId);
}
}
}
// 校验通过进行规则关联
if (StrUtil.isNotBlank(relation.getRuleIds())) {
relation.setRuleIds(relation.getRuleIds().concat(StrUtil.COMMA).concat(ruleId));
} else {
relation.setRuleIds(ruleId);
}
tbUriModuleRelationMapper.updateById(relation);
// 刷新redis规则信息
this.ruleInfoReload();
// 通过规则id列表信息获取uri关联信息
kieBaseCacheProducer(new ArrayList<>(Collections.singleton(relation.getUri())));
return null;
}
/**
* 解除规则现有关联关系
* @param relationId 关联id
* @param ruleId 规则id
* @return 错误信息
*/
@Override
public String unlockRelationAndRuleConfig(String relationId, String ruleId) {
// 直接解除关联
TbUriModuleRelation relation = tbUriModuleRelationMapper.selectById(relationId);
if (ObjectUtil.isNull(relation)) {
return StrUtil.format("通过relationId[{}]查询关联信息为空,解除绑定关系失败!", relationId);
}
// 包含校验
if (StrUtil.isNotBlank(relation.getRuleIds()) && !relation.getRuleIds().contains(ruleId)) {
return StrUtil.format("通过获取关联关系relationId[{}]中的ruleIds不包含ruleId[{}],无需解除绑定关系",
relationId, ruleId);
}
// 解除绑定关系
List<String> ruleIdList = new ArrayList<>(Arrays.asList(relation.getRuleIds().split(StrUtil.COMMA)));
ruleIdList.remove(ruleId);
relation.setRuleIds(ruleIdList.stream().collect(Collectors.joining(StrUtil.COMMA)));
tbUriModuleRelationMapper.updateById(relation);
// 刷新redis规则信息
this.ruleInfoReload();
// 通过规则id列表信息获取uri关联信息
if (ObjectUtil.isNotEmpty(ruleIdList)) {
List<TbUriModuleRelation> relationList = tbUriModuleRelationMapper.getRelationListByRuleIdList(ruleIdList);
if (ObjectUtil.isNotEmpty(relationList)) {
List<String> uriList = relationList.stream().map(TbUriModuleRelation::getUri).collect(Collectors.toList());
uriList.stream().distinct();
// 发送修改规则信息到其模块
kieBaseCacheProducer(uriList);
}
}
return null;
}
/**
* 发送修改规则信息到其模块
* account
* afterloan
* credit
* demo
* loan
* manage
* warning
* query
* risk
* collateral
* credit
* @param uriList 路径uriList
*/
public void kieBaseCacheProducer(List<String> uriList) {
// 循环发送kafka信息
for (String uri : uriList) {
if (uri.indexOf(StrUtil.SLASH, 2) >= RuleConstant.NUM_0) {
String serverPrefix = uri.substring(1, uri.indexOf(StrUtil.SLASH, 2));
if (StrUtil.isBlank(serverPrefix)) {
throw new ServiceException("获取服务模块信息失败");
}
String serverName = getServerNameBySysFlag(serverPrefix);
if (StrUtil.isBlank(serverName)) {
throw new ServiceException(StrUtil.format("通过服务前缀[{}]获取服务完整名称失败", serverPrefix));
}
// 查看是否包含本地规则
QueryWrapper<TbUriModuleRelation> queryWrapper = new QueryWrapper<>();
queryWrapper.lambda().eq(TbUriModuleRelation::getUri, uri);
TbUriModuleRelation tbUriModuleRelation = tbUriModuleRelationMapper.selectOne(queryWrapper);
if (ObjectUtil.isNull(tbUriModuleRelation)) {
throw new ServiceException(StrUtil.format("规则信息发送业务模块,通过uri[{}]获取规则关联关系为空!",
uri));
}
// 如果规则id信息为空则说明uri向下无规则,无需通知其他模块进行修改
if (StrUtil.isBlank(tbUriModuleRelation.getRuleIds())) {
continue;
}
// 获取规则id信息
List<String> ruleIdList = Arrays.asList(tbUriModuleRelation.getRuleIds().split(StrUtil.COMMA).clone());
QueryWrapper<TbSysCommonRuleConfig> configQueryWrapper = new QueryWrapper<>();
configQueryWrapper.lambda().in(TbSysCommonRuleConfig::getRuleId, ruleIdList);
List<TbSysCommonRuleConfig> ruleConfigList = tbSysCommonRuleConfigMapper.selectList(configQueryWrapper);
// 如果规则信息为空,无需通知其他模块进行修改
if (ObjectUtil.isEmpty(ruleConfigList)) {
continue;
}
// 是否有本地
boolean ifContainsLocalRule = ruleConfigList.stream().anyMatch(ruleConfig ->
RuleConstant.NUM_1_STR.equals(ruleConfig.getIfLocalRule())
);
// 开始发送本地规则信息
if (ifContainsLocalRule) {
kafkaProducer.syncSend(RuleConstant.TOPIC_RULE_PREFIX.concat(serverName), uri);
}
// 是否有远程规则
boolean ifContainsRemoteRule = ruleConfigList.stream().anyMatch(ruleConfig->
RuleConstant.NUM_0_STR.equals(ruleConfig.getIfLocalRule())
);
// 开始发送远程规则
if (ifContainsRemoteRule) {
kafkaProducer.syncSend(RuleConstant.TOPIC_RULE_PREFIX.concat(RuleConstant.RULE_SERVER_NAME), uri);
}
}
}
}
/**
* 通过系统标识获取服务名称
* @param sysFlag 系统标识
* @return 服务名称
*/
public String getServerNameBySysFlag(String sysFlag) {
// 空值判断
if (StrUtil.isBlank(sysFlag)) {
return null;
}
if (sysFlag.contains(RuleConstant.SYSTEM_FLAG_LOAN)) {
return RuleConstant.SYSTEM_FLAG_LOAN.concat(RuleConstant.SERVER_FLAG);
} else if (sysFlag.contains(RuleConstant.SYSTEM_FLAG_ACCOUNT)) {
return RuleConstant.SYSTEM_FLAG_ACCOUNT.concat(RuleConstant.SERVER_FLAG);
} else if (sysFlag.contains(RuleConstant.SYSTEM_FLAG_AFTER)) {
return RuleConstant.SYSTEM_FLAG_AFTER.concat(RuleConstant.SYSTEM_FLAG_LOAN).concat(RuleConstant.SERVER_FLAG);
} else if (sysFlag.contains(RuleConstant.SYSTEM_FLAG_CREDIT)) {
return RuleConstant.SYSTEM_FLAG_CREDIT.concat(RuleConstant.SERVER_FLAG);
} else if (sysFlag.contains(RuleConstant.SYSTEM_FLAG_MANAGE)) {
return RuleConstant.SYSTEM_FLAG_MANAGE.concat(RuleConstant.MANAGE_SERVER_FLAG);
} else if (sysFlag.contains(RuleConstant.SYSTEM_FLAG_WARNING)) {
return RuleConstant.SYSTEM_FLAG_WARNING.concat(RuleConstant.SERVER_FLAG);
} else if (sysFlag.contains(RuleConstant.SYSTEM_FLAG_QUERY)) {
return RuleConstant.SYSTEM_FLAG_QUERY.concat(RuleConstant.SERVER_FLAG);
} else if (sysFlag.contains(RuleConstant.SYSTEM_FLAG_RISK)) {
return RuleConstant.SYSTEM_FLAG_RISK.concat(RuleConstant.SERVER_FLAG);
} else if (sysFlag.contains(RuleConstant.SYSTEM_FLAG_COLLATERAL)) {
return RuleConstant.SYSTEM_FLAG_COLLATERAL.concat(RuleConstant.SERVER_FLAG);
} else if (sysFlag.contains(RuleConstant.SYSTEM_FLAG_RULE)) {
return RuleConstant.SYSTEM_FLAG_RULE.concat(RuleConstant.RULE_SERVER_FLAG);
} else {
return null;
}
}
}
5.2.3 其他工具类实体实现
SimpleDistributeLock分布式锁实现类
package cn.git.common.lock;
import cn.git.redis.RedisUtil;
import cn.hutool.core.util.IdUtil;
import cn.hutool.core.util.ObjectUtil;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.data.redis.core.script.RedisScript;
import org.springframework.stereotype.Component;
import java.util.Collections;
/**
* 简单分布式锁
* todo: 暂时未实现重入,阻塞队列等锁定功能后续添加
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2022-04-25
*/
@Slf4j
@Component
public class SimpleDistributeLock {
/**
* LUA解锁脚本
*/
private static final String UNLOCK_SCRIPT = "if redis.call('get', KEYS[1]) == ARGV[1] then return redis.call('del', KEYS[1]) else return 0 end";
/**
* 释放锁失败标识
*/
private static final long RELEASE_OK_FLAG = 0L;
@Autowired
private RedisUtil redisUtil;
/**
* 加锁方法
* @param lockTypeEnum 锁信息
* @param customKey 自定义锁定key
* @return true 成功,false 失败
*/
public String tryLock(LockTypeEnum lockTypeEnum, String customKey) {
// 锁对应值信息
String lockValue = IdUtil.simpleUUID();
// 对自定义key进行加锁操作,value值与key值相同
boolean result = redisUtil.setNxEx(lockTypeEnum.getLockType().concat(StrUtil.COLON).concat(customKey),
lockValue,
lockTypeEnum.getExpireTime());
if (result) {
log.info("[{}]加锁成功!", lockTypeEnum.getLockType().concat(StrUtil.COLON).concat(customKey));
return lockValue;
}
return null;
}
/**
* 解锁操作
* @param lockTypeEnum 锁定类型
* @param customKey 自定义key
* @param releaseValue 释放value
* @return true 成功,false 失败
*/
public boolean releaseLock(LockTypeEnum lockTypeEnum, String customKey, String releaseValue) {
// 各个模块服务启动时间差,预留5秒等待时间,防止重调用
if (ObjectUtil.isNotNull(lockTypeEnum.getLockedWaitTimeMiles())) {
try {
Thread.sleep(lockTypeEnum.getLockedWaitTimeMiles());
} catch (InterruptedException e) {
e.printStackTrace();
}
}
// 设置释放锁定key,value值
String releaseKey = lockTypeEnum.getLockType().concat(StrUtil.COLON).concat(customKey);
// 释放锁定资源
RedisScript<Long> longDefaultRedisScript = new DefaultRedisScript<>(UNLOCK_SCRIPT, Long.class);
Long result = redisUtil.executeLuaCustom(longDefaultRedisScript, Collections.singletonList(releaseKey), releaseValue);
// 根据返回结果判断是否成功成功匹配并删除 Redis 键值对,若果结果不为空和0,则验证通过
if (ObjectUtil.isNotNull(result) && result != RELEASE_OK_FLAG) {
log.info("[{}]释放锁成功!", releaseKey);
return true;
} else {
log.error("[{}]释放锁失败!", releaseKey);
return false;
}
}
}
两个mapper类,规则详情以及url路径关联关系类
package cn.git.rules.mapper;
import cn.git.oracle.mapper.BaseMybatisPlusMapper;
import cn.git.rules.entity.TbSysCommonRuleConfig;
import java.util.List;
import java.util.Map;
/**
* 规则通用配置表 Mapper 接口
* @author lixuchun
* @since 2021-05-11
*/
public interface TbSysCommonRuleConfigMapper extends BaseMybatisPlusMapper<TbSysCommonRuleConfig> {
}
TbUriModuleRelationMapper 关联类
package cn.git.rules.mapper;
import cn.git.oracle.mapper.BaseMybatisPlusMapper;
import cn.git.rules.dto.RuleDTO;
import cn.git.rules.entity.TbUriModuleRelation;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
import org.apache.ibatis.annotations.Param;
import java.util.List;
/**
* 模块规则uri对应关系mapper
* @author lixuchun
* @since 2022-03-28
*/
public interface TbUriModuleRelationMapper extends BaseMybatisPlusMapper<TbUriModuleRelation> {
/**
* 通过规则idList查询对应关联关系信息
* @param ruleIdList 规则idList
* @return 规则关联关系列表
*/
List<TbUriModuleRelation> getRelationListByRuleIdList(@Param("ruleIdList") List<String> ruleIdList);
/**
* 规则关联关系列表信息
* @param page 分页信息
* @param ruleDTO 参数dto
* @return 列表信息
*/
IPage<TbUriModuleRelation> getRelationList(Page<TbUriModuleRelation> page, @Param("ruleDTO") RuleDTO ruleDTO);
}
对应mybatis plus xml如下
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE mapper PUBLIC "-//mybatis.org//DTD Mapper 3.0//EN" "http://mybatis.org/dtd/mybatis-3-mapper.dtd">
<mapper namespace="cn.git.rules.mapper.TbUriModuleRelationMapper">
<select id="getRelationListByRuleIdList" resultType="cn.git.rules.entity.TbUriModuleRelation">
SELECT
RELATION_ID,
MODULE,
URI,
CREATE_TIME,
UPDATE_TIME,
RULE_IDS
FROM
TB_URI_MODULE_RELATION
WHERE
1 = 1
<if test=" ruleIdList != null and ruleIdList.size() > 0 ">
AND
<foreach item="ruleId" index="index" collection="ruleIdList" separator="OR">
RULE_IDS LIKE concat('%', concat(#{ruleId}, '%'))
</foreach>
</if>
</select>
<select id="getRelationList" resultType="cn.git.rules.entity.TbUriModuleRelation">
SELECT DISTINCT
relation.RELATION_ID,
relation.MODULE,
relation.URI,
relation.URI_DESCRIPTION,
relation.CREATE_TIME,
relation.UPDATE_TIME
FROM
TB_URI_MODULE_RELATION relation
LEFT JOIN TB_SYS_COMMON_RULE_CONFIG config ON relation.RULE_IDS LIKE '%' || config.RULE_ID || '%'
WHERE
1 = 1
<if test="ruleDTO.module != null and ruleDTO.module != ''">
AND
relation.MODULE = #{ruleDTO.module}
</if>
<if test="ruleDTO.uri != null and ruleDTO.uri != ''">
AND
relation.URI = #{ruleDTO.uri}
</if>
<if test="ruleDTO.uriDescription != null and ruleDTO.uriDescription != ''">
AND
relation.URI_DESCRIPTION LIKE '%' || #{ruleDTO.uriDescription} || '%'
</if>
<if test="ruleDTO.ruleName != null and ruleDTO.ruleName != ''">
AND
config.RULE_NAME = #{ruleDTO.ruleName}
</if>
</select>
</mapper>
5.2.4 服务端初始化
服务端初始化执行init方法如下
package cn.git.rules.init;
import cn.git.rules.service.rule.RuleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.boot.ApplicationArguments;
import org.springframework.boot.ApplicationRunner;
import org.springframework.stereotype.Component;
/**
* 规则平台初始化数据信息
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2021-05-12
*/
@Component
public class InitRuleLoad implements ApplicationRunner {
@Autowired
private RuleService ruleService;
/**
* Callback used to run the bean.
* @param args incoming application arguments
* @throws Exception on error
*/
@Override
public void run(ApplicationArguments args) {
ruleService.initRuleLoad();
}
}
5.2.5 服务端修改kafka处理
服务端修改,需要将修改指令发送到所有客户端,所以客户端引入了kafka模块,利用kafka consumer 不同组可以同时消费同一topic特性,设计消息指令的处理,具体实现如下
consumer的具体实现逻辑
package cn.git.rules.consumer;
import cn.git.rules.init.RuleInfoInit;
import cn.git.rules.util.RuleUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerRecord;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.kafka.annotation.KafkaListener;
import org.springframework.stereotype.Component;
/**
* @description: 规则缓存消费者
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2022-09-30 09:06:24
*/
@Slf4j
@Component
public class RuleCacheConsumer {
@Value("${spring.application.name}")
private String applicationName;
@Autowired
private RuleUtil ruleUtil;
/**
* cache server 缓存平台消费consumer
* 匹配 rule-cache-{服务名称信息}格式,动态生成topic
* 使用 kafkaListenerContainerFactory 动态生成group信息
* @param record 消息record记录
*/
@KafkaListener(
topics = {"rule-cache-${spring.application.name}"},
containerFactory = "kafkaListenerContainerFactory"
)
public void consumerListener(ConsumerRecord<?, ?> record) {
log.info("服务[{}]规则信息刷新开始!", applicationName);
ruleUtil.makeKieBaseCache(RuleInfoInit.commonKieBaseMap);
}
}
kafkaListenerContainerFactory 具体的实现逻辑如下,同一服务设置不同的组id
package cn.git.rules.factory;
import cn.git.redis.RedisUtil;
import cn.git.rules.constant.RuleConstant;
import cn.hutool.core.util.StrUtil;
import lombok.extern.slf4j.Slf4j;
import org.apache.kafka.clients.consumer.ConsumerConfig;
import org.apache.kafka.common.serialization.StringDeserializer;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Value;
import org.springframework.context.annotation.Bean;
import org.springframework.kafka.config.ConcurrentKafkaListenerContainerFactory;
import org.springframework.kafka.config.KafkaListenerContainerFactory;
import org.springframework.kafka.core.ConsumerFactory;
import org.springframework.kafka.core.DefaultKafkaConsumerFactory;
import org.springframework.kafka.listener.ConcurrentMessageListenerContainer;
import org.springframework.stereotype.Component;
import java.util.HashMap;
import java.util.Map;
/**
* @description: kafka 自定义消费者信息配置类
* @program: bank-credit-sy
* @author: lixuchun
* @create: 2022-09-30 02:40:09
*/
@Slf4j
@Component
public class CustomKafkaGroupConfig {
/**
* profile 环境变量
*/
private static final String PROFILE_SIT = "sit";
private static final String PROFILE_SIT_2 = "sit2";
private static final String PROFILE_UAT = "uat";
private static final String PROFILE_UAT2 = "uat2";
private static final String PROFILE_TEST = "test";
private static final String PROFILE_PROD = "prod";
/**
* 本地起服务
*/
private static final String LOCAL_UP = "LOCAL";
/**
* 远程服务
*/
private static final String REMOTE_UP = "REMOTE";
/**
* 最大分组数
*/
private static final Integer MAX_GROUP_ID = 5;
@Autowired
private RedisUtil redisUtil;
/**
* 获取当前运行环境信息
*/
@Value("${spring.profiles.active}")
private String profile;
/**
* 服务名称
*/
@Value("${spring.application.name}")
private String applicationName;
@Value("${git.kafka.bootstrap-servers}")
private String brokers;
@Value("${git.kafka.consumer.auto-offset-reset}")
private String autoOffsetReset;
/**
* 构建kafka监听工厂
* @return
*/
@Bean
public KafkaListenerContainerFactory<ConcurrentMessageListenerContainer<String, String>> kafkaListenerContainerFactory() {
ConcurrentKafkaListenerContainerFactory<String, String> factory = new ConcurrentKafkaListenerContainerFactory<>();
factory.setConsumerFactory(createConsumerFactory());
return factory;
}
/**
* 初始化消费工厂配置 其中会动态指定消费分组
* @return
*/
private ConsumerFactory<String, String> createConsumerFactory() {
// 配置consumer参数信息
Map<String, Object> properties = new HashMap<>(16);
properties.put(ConsumerConfig.BOOTSTRAP_SERVERS_CONFIG, brokers);
properties.put(ConsumerConfig.ENABLE_AUTO_COMMIT_CONFIG, true);
properties.put(ConsumerConfig.AUTO_COMMIT_INTERVAL_MS_CONFIG, 1000);
properties.put(ConsumerConfig.MAX_POLL_RECORDS_CONFIG, 500);
properties.put(ConsumerConfig.KEY_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
properties.put(ConsumerConfig.VALUE_DESERIALIZER_CLASS_CONFIG, StringDeserializer.class);
properties.put(ConsumerConfig.REQUEST_TIMEOUT_MS_CONFIG, 180000);
// 动态设置groupId信息
String currentGroupId = getGroupCurrentGroupId(RuleConstant.NUM_0);
log.info("动态生成currentGroupId为[{}]", currentGroupId);
properties.put(ConsumerConfig.GROUP_ID_CONFIG, currentGroupId);
properties.put(ConsumerConfig.AUTO_OFFSET_RESET_CONFIG, autoOffsetReset);
return new DefaultKafkaConsumerFactory<>(properties);
}
/**
* 获取单个服务group组名称
*
* @param currValue 当前值
* @return
*/
public String getGroupCurrentGroupId(Integer currValue) {
// 单个服务数量校验
if (currValue > MAX_GROUP_ID) {
throw new RuntimeException(StrUtil.format("单个服务启动消费者超过设定消费者数量最大值[{}]", MAX_GROUP_ID));
}
// 设置服务groupId
String customGroupId = RuleConstant.GROUP_RULE_CACHE.concat(applicationName.toUpperCase())
.concat(StrUtil.UNDERLINE)
.concat(currValue.toString());
// 判断服务是否本地启动
boolean ifLocalServer = PROFILE_TEST.equals(profile)
|| PROFILE_SIT.equals(profile)
|| PROFILE_SIT_2.equals(profile)
|| PROFILE_UAT.equals(profile)
|| PROFILE_UAT2.equals(profile)
|| PROFILE_PROD.equals(profile);
if (!ifLocalServer) {
customGroupId = LOCAL_UP.concat(StrUtil.UNDERLINE).concat(customGroupId);
} else {
customGroupId = REMOTE_UP.concat(StrUtil.UNDERLINE).concat(customGroupId);
}
// 设置redis分组锁信息
boolean setResult = redisUtil.setNxEx(customGroupId, currValue.toString(), 180);
if (setResult) {
// 设置成功
log.info("设置成功,返回groupId为[{}]", customGroupId);
return customGroupId;
} else {
// 设置失败,主键存在向下加1
currValue = currValue + 1;
return getGroupCurrentGroupId(currValue);
}
}
}
6. 规则管理页面
规则管理首页,可以通过模块,路径,名称等查询url规则信息
通过模块信息进行查询
点击详情进入详情查看页面,详情页面也可以使用名称,是否禁用等信息进行查询,一个url可以对应多个规则,执行顺序按照大到小顺序执行。
通过规则新增按钮可以新增规则,其中规则执行顺序,规则名称,规则对应子规则都会通过规则详情自动获取,需要注意,本地规则执行顺序不能有重复,远程所有规则执行顺序也不能有重复,规则名称不能重复,如果规则有子规则,则必须新建子规则,否则不能新建
点击规则修改,展示信息如下,可以对规则引用的service以及规则内容进行修改,实时生效
所有对应规则services中,需要使用service完整名称(不能简写),首字母小写,后面添加词采用驼峰模式命名。
- for example:如果service完整名称为 DemoService,则规则填写service则为demoService。远程规则包含多个service时,则可以用’,'分割,注意本地规则远程规则service不能共用。
- 本地规则使用本地数据库,不需要切换数据源,远程规则(即rule-manage-server中执行规则)如果使用数据源则需要调用DatabaseUtil.chooseDB方法切换数据源。 如果调用多个数据源则在方法中进行多次切换即可。
此处的规则具体内容如下
package cn.git.loan.rules;
import cn.hutool.core.util.StrUtil
import cn.git.rules.service.loan.LoanRemoteBusinessApplyService
import cn.git.rules.service.loan.impl.LoanRemoteBusinessApplyServiceImpl
import cn.hutool.core.util.ObjectUtil
import com.alibaba.fastjson.JSONObject
import java.lang.String;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
global cn.git.rules.service.loan.LoanRemoteBusinessApplyService loanRemoteBusinessApplyService
global cn.git.rules.results.RuleResult ruleResult
global com.alibaba.fastjson.JSONObject ruleJsonParam
// 远程规则则测试
rule "loanRemoteBusinessApplyRule3"
// 优先级 越大越先执行
salience 999
// 防止循环调用 no-loop升级版
lock-on-active true
when
// 判断条件 固定写法,全局 ruleResult校验规则
// $result为在规则引擎初始化插入的result对象,result值一直是true
// 若全局ruleResult.getResult值为false与初始化擦混入result值不同则不能进行then中语句块,跳过本次规则校验
$result:RuleResult(result == ruleResult.getResult())
then
Logger log = LoggerFactory.getLogger(LoanRemoteBusinessApplyServiceImpl.class);
// 调用loanRemoteBusinessApplyService远程规则3
log.info("调用loanRemoteBusinessApplyService远程规则3start");
// 获取入参信息
// 贷款形式
String loanShape = StrUtil.toString(ruleJsonParam.get("loanShape"));
// 调用远程规则checkAfterRigidControlUser方法
String result = loanRemoteBusinessApplyService.checkAfterRigidControlUser();
// 业务逻辑处理
if("1".equals(loanShape) && "1".equals(result)){
log.info("调用loanRemoteBusinessApplyService远程规则3提示:该柜员在贷后检查刚性控制名单中,不允许其发起新增贷款业务!");
ruleResult.setMessage("该柜员在贷后检查刚性控制名单中,不允许其发起新增贷款业务!");
ruleResult.setResult(false);
drools.halt();
}
log.info("执行loanRemoteBusinessApplyRule3成功!");
end
还可以关联现有规则,具体操作如下图关联即可
客户端使用,标记自定义注解即可,如下