简单的规则引擎Drools实现

简单的规则引擎实现

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

还可以关联现有规则,具体操作如下图关联即可
在这里插入图片描述
客户端使用,标记自定义注解即可,如下
在这里插入图片描述

  • 12
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值