简单的系统拖拽规则

简单的系统拖拽规则

1. 需求

公司想要一个规则控制功能,在功能执行过程中,可以动态的改变原来的执行逻辑,让业务人员可以进行简单的配置,并且实现拖拽功能,原有的drools规则引擎部分因为编译结果未知,执行出问题不太好定位问题,以及提示不能最快定为到底是什么规则触发引起的报错,代码部分也不能快速定为,所以对规则部分进行了一个二次开发,此次放弃使用规则引擎,直接全部自己手动实现,功能相对简单。

2. 实现思路

实现的步骤以及思路还是跟以前的drools规则实现差不多,主要分为规则模块以及非规则业务模块,最终简单的实现思路以及执行过程如下:

  • 启动规则模块服务加载全部规则,将规则信息(入参,出参,描述等)加载到缓存中,提供规则详情调用具体实现方法
  • 启动业务子模块,加载规则调用入口(既各个按钮等功能接口),将入口inVO参数以及入口信息描述等加入缓存中
  • 业务人员进行规则链编辑,将需要加入规则的controller接口,配置上需要调用的规则链,同时校验controller的方法入参是否满足规则链中各个规则的入参信息
  • 客户端调用规则链入口方法,首先执行切面方法,获取当前请求uri是否配置规则链,如果配置则依次循环调用规则模块规则详情方法,执行通过则进入controller对应的method方法,失败则进行失败信息提示即可,不进入controller方法

简单的实现思路图:
在这里插入图片描述
简单的逻辑调用图:
在这里插入图片描述

3. 页面设计

业务实现基本设置四个简单页面,大体有规则列表页面,规则编辑页面,规则链列表页面,规则链编辑页面,具体页面设计如下:

  • 规则列表页面
    在这里插入图片描述
  • 规则详情页面:可以进行规则链
    在这里插入图片描述
  • 规则链列表页面:进行规则链的分页展示
    在这里插入图片描述
  • 规则链页面:可以进行规则链的编辑
    在这里插入图片描述
  • 规则链编辑提示页面:这部分会提示规则链的规则是否合法,不合法的话提示具体哪些规则哪些字段不合法
    在这里插入图片描述
  • 产品规则列表展示:此页面进行产品规则的分页以及展示,点击新增可以新增产品规则链,操作与普通规则链相同
    在这里插入图片描述

4. 具体实现

实现部分主要包含客户端以及规则服务端,客户端提供规则接口入口信息,服务启动将接口信息上传至缓存中供规则模块规则链管理,服务端提供规则管理,规则链管理以及规则执行的具体实现。

4.1 客户端

具体逻辑为启动服务,扫描带有**@DragRule**的注解接口,将接口的对应的入参参数inVO,接口名称,描述信息等统一存入缓存,redis缓存使用结构为hash形式,我们使用的是微服务,服务启动执行需要枪锁执行。具体实现的主要代码如下所示

  1. DragRule注解信息,此注解是接口扫描注解,只有标记此注解才会被扫描进入缓存信息中

    package cn.git.common.annotation;
    
    import java.lang.annotation.*;
    
    /**
     * @description: 拖拽规则注解
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-20 09:38:40
     */
    @Target({ElementType.METHOD})
    @Retention(RetentionPolicy.RUNTIME)
    @Documented
    public @interface DragRule {
    
        /**
         * 描述信息
         * @return
         */
        String ruleDesc() default "";
    
    }
    
    
  2. DragRuleBeanDefinitionProcessor,此类在服务启动时候,获取全部规定目录下的所有有效bean的定义信息,并且封装成map

    package cn.git.common.drag.processor;
    
    import cn.git.common.annotation.DragRule;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.BeansException;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.beans.factory.config.ConfigurableListableBeanFactory;
    import org.springframework.beans.factory.support.BeanDefinitionRegistry;
    import org.springframework.beans.factory.support.BeanDefinitionRegistryPostProcessor;
    import org.springframework.stereotype.Component;
    
    import java.lang.reflect.Method;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @description: 获取拖拽规则注解定义类
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-29 04:33:16
     */
    @Slf4j
    @Component
    public class DragRuleBeanDefinitionProcessor implements BeanDefinitionRegistryPostProcessor {
    
        /**
         * controller类前缀
         */
        private static final String CONTROLLER_PREFIX = "cn.git";
    
        /**
         * controller类类名后缀
         */
        private static final String CONTROLLER_SUFFIX = "Controller";
    
        /**
         * 基础拖拽规则bean定义列表
         * 扫描所有controller类,如果类中包含@DragRule注解的,就添加到beanDefinitionList中
         */
        public static List<BeanDefinition> BASE_DRAG_RULE_BEAN_DEFINITION = new ArrayList<>();
    
        /**
         * Modify the application context's internal bean definition registry after its
         * standard initialization. All regular bean definitions will have been loaded,
         * but no beans will have been instantiated yet. This allows for adding further
         * bean definitions before the next post-processing phase kicks in.
         *
         * @param registry the bean definition registry used by the application context
         * @throws BeansException in case of errors
         */
        @Override
        public void postProcessBeanDefinitionRegistry(BeanDefinitionRegistry registry) throws BeansException {
            for (String beanName : registry.getBeanDefinitionNames()) {
                // 判定类是否为cn.git路径下以及controller类名后缀
                BeanDefinition definition = registry.getBeanDefinition(beanName);
                Class<?> clazz;
                try {
                    clazz = Class.forName(definition.getBeanClassName());
                    for (Method method : clazz.getDeclaredMethods()) {
                        if (method.isAnnotationPresent(DragRule.class) && !BASE_DRAG_RULE_BEAN_DEFINITION.contains(definition)) {
                            BASE_DRAG_RULE_BEAN_DEFINITION.add(definition);
                        }
                    }
                } catch (Exception ignored) {
                    // 加载失败,无需做处理
                }
            }
        }
    
        /**
         * Modify the application context's internal bean factory after its standard
         * initialization. All bean definitions will have been loaded, but no beans
         * will have been instantiated yet. This allows for overriding or adding
         * properties even to eager-initializing beans.
         *
         * @param beanFactory the bean factory used by the application context
         * @throws BeansException in case of errors
         */
        @Override
        public void postProcessBeanFactory(ConfigurableListableBeanFactory beanFactory) throws BeansException {
        }
    }
    
    
  3. CommonClientRuleUriInit ,服务初始化执行方法,主要进行bean定义信息解析,并且筛选出@DragRule标记类,存入缓存中

    package cn.git.common.drag;
    
    import cn.git.common.annotation.DragRule;
    import cn.git.common.constant.CommonConstant;
    import cn.git.common.drag.dto.DragChainDTO;
    import cn.git.common.drag.mapper.CommonDragRuleMapper;
    import cn.git.common.drag.processor.DragRuleBeanDefinitionProcessor;
    import cn.git.common.exception.ServiceException;
    import cn.git.common.lock.LockTypeEnum;
    import cn.git.common.lock.SimpleDistributeLock;
    import cn.git.common.util.CommonDragRuleUtil;
    import cn.git.redis.RedisUtil;
    import cn.hutool.core.util.ObjectUtil;
    import cn.hutool.core.util.StrUtil;
    import com.alibaba.csp.sentinel.slots.block.RuleConstant;
    import com.alibaba.fastjson.JSONObject;
    import io.swagger.annotations.Api;
    import io.swagger.annotations.ApiModelProperty;
    import io.swagger.annotations.ApiOperation;
    import io.swagger.annotations.ApiParam;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.beans.factory.config.BeanDefinition;
    import org.springframework.boot.ApplicationArguments;
    import org.springframework.boot.ApplicationRunner;
    import org.springframework.scheduling.annotation.EnableScheduling;
    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 java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.lang.reflect.Parameter;
    import java.util.ArrayList;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    
    /**
     * @description: 拖拽规则规则链初始化
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-28 01:40:24
     */
    @Slf4j
    @Component
    @EnableScheduling
    public class CommonClientRuleUriInit implements ApplicationRunner {
    
        @Value("${spring.application.name}")
        private String applicationName;
    
        /**
         * 规则服务名称
         */
        private static final String RULE_SERVER_NAME = "rule-manage-server";
    
        /**
         * 风控服务名称
         */
        private static final String RISK_SERVER_NAME = "risk-server";
    
        @Autowired
        private CommonDragRuleMapper commonDragRuleMapper;
    
        @Autowired
        private RedisUtil redisUtil;
    
        @Autowired
        private CommonDragRuleUtil commonDragRuleUtil;
    
        @Autowired
        private SimpleDistributeLock simpleLock;
    
        /**
         * 拖拽规则规则链初始化
         * 获取拖拽规则对应的入参参数包含值,方便后续保存规则的时候进行满足参数的校验
         * @throws Exception
         */
        @Override
        public void run(ApplicationArguments args) throws Exception {
            // 加锁,服务发版启动,只有一个服务可以执行
            String lock = simpleLock.tryLock(LockTypeEnum.DRAG_RULE_URI_LOCK, applicationName);
            try {
                // 加锁成功并且服务为业务子模块服务则可以进行规则模块信息加载,否则不进行加载
                if (StrUtil.isNotBlank(lock) && commonDragRuleUtil.checkIfLoadDragChain(applicationName)) {
                    log.info("拖拽规则[{}]初始化规则链开始!", applicationName);
                    // 获取定义好的拖拽注解实现方法类类型
                    List<BeanDefinition> beanDefinitionList = DragRuleBeanDefinitionProcessor.BASE_DRAG_RULE_BEAN_DEFINITION;
    
                    // mapper中获取规则链信息,并且将list转换为map信息,其中key为规则链uri,value为规则链信息,并且过滤掉其他模块的规则链信息
                    String chainUriPrefix = applicationName.replace(StrUtil.DASHED, StrUtil.UNDERLINE).toUpperCase();
                    List<DragChainDTO> dragChainDTOList = commonDragRuleMapper.getDragChainList();
                    Map<String, DragChainDTO> dragChainMap = dragChainDTOList
                            .stream()
                            .filter(chainDTO -> {
                                // 只获取当前服务自己的规则链信息
                                return chainDTO.getUri().startsWith(chainUriPrefix);
                            })
                            .collect(Collectors.toMap(
                                    DragChainDTO::getUri, Function.identity(), (k1, k2) -> k2)
                            );
    
                    // 获取beanDefinitionList中的全部规则链信息,封装成map结构,key为规则链uri
                    Map<String, DragChainDTO> uriMap = new HashMap<>(CommonConstant.DEFAULT_MAP_SIZE_16);
    
                    // 获取 DragRule.class注解对应的方法以及参数信息
                    for (BeanDefinition beanDefinition : beanDefinitionList) {
                        // 获取类名
                        String className = beanDefinition.getBeanClassName();
                        // 获取类对象
                        Class<?> clazz = Class.forName(className);
    
                        // 获取controller名称
                        String controllerName = clazz.getSimpleName();
    
                        // 获取请求头上面的RequestMapping路径信息
                        RequestMapping controllerRequestMapping = clazz.getAnnotation(RequestMapping.class);
                        String uriPrefix = controllerRequestMapping.value()[0];
    
                        // 获取controller描述信息
                        String controllerDesc = null;
                        if (clazz.isAnnotationPresent(Api.class)) {
                            Api api = clazz.getAnnotation(Api.class);
                            controllerDesc = api.tags()[0];
                        }
                        // 循环获取DragRule注解的方法信息以及参数信息
                        for (Method method : clazz.getDeclaredMethods()) {
                            if (method.isAnnotationPresent(DragRule.class)) {
                                // 获取方法的描述信息
                                String methodDesc = null;
                                if (method.isAnnotationPresent(ApiOperation.class)) {
                                    ApiOperation apiOperation = method.getAnnotation(ApiOperation.class);
                                    methodDesc = apiOperation.value();
                                }
                                String methodName = method.getName();
    
                                // 获取方法请求uri后缀部分
                                String suffixUri = null;
                                if (method.isAnnotationPresent(RequestMapping.class)) {
                                    // request请求方法uri
                                    suffixUri = method.getAnnotation(RequestMapping.class).value()[0];
                                } else if (method.isAnnotationPresent(GetMapping.class)) {
                                    // get请求方法uri
                                    suffixUri = method.getAnnotation(GetMapping.class).value()[0];
                                } else if (method.isAnnotationPresent(PostMapping.class)) {
                                    // post请求方法uri
                                    suffixUri = method.getAnnotation(PostMapping.class).value()[0];
                                }
                                // 判断是否为path路径带有参数信息
                                if (suffixUri.contains(StrUtil.DELIM_START)) {
                                    suffixUri = suffixUri.substring(0, suffixUri.indexOf(StrUtil.DELIM_START) - 1);
                                }
                                // 整体全路径为
                                String allPathUri = uriPrefix.concat(suffixUri);
    
                                // 获取参数信息
                                JSONObject paramJSON = new JSONObject(true);
                                // 参数描述信息
                                List<DragChainDTO.DragRuleInVOParamsDesc> inVOParamsDescList = new ArrayList<>();
                                Parameter[] parameters = method.getParameters();
                                Class<?>[] parameterTypes = method.getParameterTypes();
                                for (int i = 0; i < parameters.length; i++) {
                                    // 当前参数
                                    Parameter parameter = parameters[i];
                                    // 当前参数类型
                                    Class<?> parameterType = parameterTypes[i];
                                    // 简单类型处理
                                    if ("String".equals(parameterType.getSimpleName())
                                            || "Integer".equals(parameterType.getSimpleName())
                                            || "Long".equals(parameterType.getSimpleName())
                                            || "Double".equals(parameterType.getSimpleName())
                                            || "Float".equals(parameterType.getSimpleName())
                                            || "Boolean".equals(parameterType.getSimpleName())
                                            || "Date".equals(parameterType.getSimpleName())) {
                                        ApiParam annotation = parameter.getAnnotation(ApiParam.class);
                                        if (ObjectUtil.isNotNull(annotation)) {
                                            DragChainDTO.DragRuleInVOParamsDesc inVOParamsDesc = new DragChainDTO.DragRuleInVOParamsDesc();
                                            inVOParamsDesc.setInParamDesc(annotation.value());
                                            inVOParamsDesc.setInParamName(parameter.getName());
                                            inVOParamsDesc.setInParamType(parameterType.getSimpleName());
                                            inVOParamsDescList.add(inVOParamsDesc);
                                        }
                                        paramJSON.put(parameter.getName(), StrUtil.EMPTY);
                                    } else {
                                        // 复杂类类型
                                        Field[] declaredFields = parameterType.getDeclaredFields();
                                        for (Field field : declaredFields) {
                                            DragChainDTO.DragRuleInVOParamsDesc inVOParamsDesc = null;
                                            ApiModelProperty apiModelProperty = field.getAnnotation(ApiModelProperty.class);
                                            if (ObjectUtil.isNotNull(apiModelProperty)) {
                                                inVOParamsDesc = new DragChainDTO.DragRuleInVOParamsDesc();
                                                inVOParamsDesc.setInParamDesc(apiModelProperty.value());
                                                inVOParamsDesc.setInParamName(field.getName());
                                                inVOParamsDesc.setInParamType(field.getType().getSimpleName());
                                                inVOParamsDescList.add(inVOParamsDesc);
                                            }
                                            paramJSON.put(field.getName(), StrUtil.EMPTY);
                                        }
                                    }
                                }
                                // 设置最终路径信息,模块名:uri
                                String finalUri = chainUriPrefix
                                        .toUpperCase()
                                        .concat(StrUtil.COLON)
                                        .concat(allPathUri);
    
                                // 信息包含controller方法入参信息,规则链信息
                                DragChainDTO dragChainDTO = new DragChainDTO();
                                dragChainDTO.setMethodDesc(methodDesc);
                                dragChainDTO.setMethodName(methodName);
                                dragChainDTO.setControllerDesc(controllerDesc);
                                dragChainDTO.setControllerName(controllerName);
                                dragChainDTO.setMethodInParam(paramJSON.toJSONString());
                                dragChainDTO.setInVOParamsDescList(inVOParamsDescList);
                                dragChainDTO.setUri(finalUri);
                                dragChainDTO.setIfLoadDB(CommonDragRuleConstant.NUM_STR_0);
                                dragChainDTO.setDragRuleChainId(controllerName.concat(StrUtil.DOT).concat(methodName));
                                dragChainDTO.setDragChainModule(applicationName.replace(StrUtil.DASHED, StrUtil.UNDERLINE).toUpperCase());
                                if (ObjectUtil.isNotNull(dragChainMap.get(finalUri))) {
                                    // 设置规则链信息
                                    dragChainDTO.setIsDel(dragChainMap.get(finalUri).getIsDel());
                                    dragChainDTO.setIfLoadDB(CommonDragRuleConstant.NUM_STR_1);
                                    dragChainDTO.setDragRuleChain(dragChainMap.get(finalUri).getDragRuleChain());
                                    // 规则链校验 dragRule1,dragRule2,dragRule3联调校验入参,回参信息
                                    commonDragRuleUtil.checkDragRuleWithParamLegal(dragChainDTO);
                                }
    
                                // 设置uriMap信息
                                uriMap.put(finalUri, dragChainDTO);
    
                                // 设置规则链参数信息
                                redisUtil.hset(CommonDragRuleConstant.DRAG_RULE_CHAIN_FLAG,
                                        finalUri, JSONObject.toJSONString(dragChainDTO));
                            }
                        }
                    }
    
                    // 进行规则链失效信息校验,表中有规则数据,但是beanDefinitionList中已经删除规则链入口,需要清理 todo: 暂时注释,生产环境可以开启
                    /**
                    if (ObjectUtil.isNotEmpty(dragChainDTOList)) {
                        dragChainDTOList.forEach(dragChainDBDTO -> {
                            // 表存在数据,bean中不存在,进行删除操作,同时需要判定为自己模块,否则其他模块启动会删除本模块已经落库的规则链信息
                            String modelName = applicationName.replace(StrUtil.DASHED, StrUtil.UNDERLINE).toUpperCase();
                            if (ObjectUtil.isEmpty(uriMap.get(dragChainDBDTO.getUri()))
                                    && modelName.equalsIgnoreCase(dragChainDBDTO.getDragChainModule())) {
                                // 删除规则链以及产品规则链数据
                                commonDragRuleMapper.deleteDragChainByUri(dragChainDBDTO.getUri());
                                commonDragRuleMapper.deleteDragProductChainByUri(dragChainDBDTO.getUri());
    
                                // 删除缓存数据
                                redisUtil.hdel(CommonDragRuleConstant.DRAG_RULE_CHAIN_FLAG, dragChainDBDTO.getUri());
                            }
                        });
                    }
                     **/
    
                    // 释放锁信息
                    simpleLock.releaseLock(LockTypeEnum.DRAG_RULE_URI_LOCK, applicationName, lock);
                }
            } catch (Exception e) {
                throw new ServiceException(e.getMessage());
            } finally {
                // 释放锁信息
                simpleLock.releaseLock(LockTypeEnum.DRAG_RULE_URI_LOCK, applicationName, lock);
            }
        }
    }
    
    
  4. CommonDragRuleUtil ,client工具类,主要进行规则链的规则是否合法校验,如果不合法则服务停止启动并且进行提示

    package cn.git.common.util;
    
    import cn.git.common.drag.CommonDragRuleConstant;
    import cn.git.common.drag.dto.DragChainDTO;
    import cn.git.common.drag.dto.DragRuleDTO;
    import cn.git.common.exception.ServiceException;
    import cn.git.redis.RedisUtil;
    import cn.hutool.core.util.ObjectUtil;
    import cn.hutool.core.util.StrUtil;
    import com.alibaba.fastjson.JSONObject;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.stereotype.Component;
    
    import java.util.Arrays;
    import java.util.List;
    import java.util.Set;
    import java.util.stream.Collectors;
    
    /**
     * @description: 拖拽规则通用工具类
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-03-04 09:21:01
     */
    @Component
    public class CommonDragRuleUtil {
    
        @Autowired
        private RedisUtil redisUtil;
    
        /**
         * 入参规则链校验,其中必须有规则调用切入点(既调用controller方法uri)
         * dragRuleChain规则链,多个规则使用逗号分割
         * @param dragChainDTO 入参规则链信息
         */
        public void checkDragRuleWithParamLegal(DragChainDTO dragChainDTO) {
            // 参数信息校验
            if (ObjectUtil.isNull(dragChainDTO)
                    || StrUtil.isBlank(dragChainDTO.getUri())
                    || StrUtil.isBlank(dragChainDTO.getDragRuleChain())
                    || StrUtil.isBlank(dragChainDTO.getMethodInParam())) {
                throw new ServiceException(StrUtil.format("拖拽规则[{}],方法[{}]入参参数校验失败,请确认!",
                        dragChainDTO.getControllerDesc(), dragChainDTO.getMethodDesc()));
            }
    
            // 分割出详细规则链
            final String[] dragRuleChainArray = dragChainDTO.getDragRuleChain().split("\\|");
    
            // 并行规则校验优化
            for (String dragRuleChain : dragRuleChainArray) {
                // 开始参数校验,入参参数需要满足调用规则参数校验,并且入参参数+规则返回参数(传递参数)需要满足规则链下一个规则的入参
                List<String> dragRuleChainList = Arrays.asList(dragRuleChain.split(StrUtil.COMMA).clone())
                        .stream()
                        .map(String::trim)
                        .collect(Collectors.toList());
    
                // 只有一个规则,直接校验入参是否满足规则链第一个规则入参即可
                String errorMessage;
                if (dragRuleChainList.size() == 1) {
                    String dragRuleId = dragRuleChainList.get(0);
                    DragRuleDTO dragRuleDTO = getDragRuleDTO(dragRuleId);
                    // 分别获取规则参数以及规则链入参进行比较,如果入参不满足规则链第一个规则入参,直接抛出异常
                    JSONObject dragRuleInParam = JSONObject.parseObject(dragRuleDTO.getInputParam());
                    JSONObject dragChainInParam = JSONObject.parseObject(dragChainDTO.getMethodInParam());
                    // 参数空值校验
                    if (ObjectUtil.isEmpty(dragRuleInParam)) {
                        errorMessage = StrUtil.format("拖拽规则规则链[{}]对应规则方法[{}]获取规则入参参数为空,规则id为[{}]请确认!",
                                dragChainDTO.getControllerName(),
                                dragChainDTO.getMethodName(),
                                dragRuleDTO.getDragRuleId());
                        throw new ServiceException(errorMessage);
                    }
                    if (ObjectUtil.isEmpty(dragChainInParam)) {
                        errorMessage = StrUtil.format("拖拽规则规则链[{}]切入点[{}]入参参数为空,请确认!",
                                dragChainDTO.getControllerName(),
                                dragChainDTO.getMethodName());
                        throw new ServiceException(errorMessage);
                    }
                    // 参数包含校验,比较参数,切入点入参必须包含规则链第一个规则入参,不满足直接抛出异常
                    Set<String> dragRuleInParamKeySet = dragRuleInParam.keySet();
                    Set<String> dragChainInParamKeySet = dragChainInParam.keySet();
                    Set<String> diffKeySet = dragRuleInParamKeySet.stream()
                            .filter(element -> !dragChainInParamKeySet.contains(element))
                            .collect(Collectors.toSet());
                    if (ObjectUtil.isNotEmpty(diffKeySet)) {
                        // Set集合数据转换为字符串,逗号分割
                        String dragRuleDiffKeyStr = diffKeySet.stream()
                                .map(String::valueOf)
                                .collect(Collectors.joining(StrUtil.COMMA));
                        errorMessage = StrUtil.format("规则链[{}],第[1]个规则[{}],其入参参数不满足要求,\n缺少入参信息为[{}],请确认!",
                                dragRuleChain,
                                dragRuleDTO.getDragRuleId(),
                                dragRuleDiffKeyStr);
                        throw new ServiceException(errorMessage);
                    }
                } else {
                    // 空值校验
                    JSONObject dragChainInParam = JSONObject.parseObject(dragChainDTO.getMethodInParam());
                    if (ObjectUtil.isEmpty(dragChainInParam)) {
                        errorMessage = StrUtil.format("规则链[{}]控制层方法切入点[{}]入参参数为空,请确认!",
                                dragRuleChain,
                                dragChainDTO.getControllerName()
                                        .concat(StrUtil.DOT)
                                        .concat(dragChainDTO.getMethodName()));
                        throw new ServiceException(errorMessage);
                    }
                    // 入参结果集,第一次入参参数为规则链切入点的入参参数,之后循环规则链规则,为规则链返回传递参数+切入点入参参数,再与下一个规则入参进行对比
                    JSONObject resultInParam = dragChainInParam.clone();
                    for (int i = 0; i < dragRuleChainList.size(); i++) {
                        // 通过规则id,获取规则入参信息
                        String dragRuleId = dragRuleChainList.get(i);
                        DragRuleDTO dragRuleDTO = getDragRuleDTO(dragRuleId);
                        JSONObject dragRuleInParam = JSONObject.parseObject(dragRuleDTO.getInputParam());
                        // 进行参数校验,如果入参不满足规则链循环规则入参,直接抛出异常
                        Set<String> dragRuleInParamKeySet = dragRuleInParam.keySet();
                        Set<String> dragChainInParamKeySet = resultInParam.keySet();
                        Set<String> diffKeySet = dragRuleInParamKeySet.stream()
                                .filter(element -> !dragChainInParamKeySet.contains(element))
                                .collect(Collectors.toSet());
                        if (ObjectUtil.isNotEmpty(diffKeySet)) {
                            // Set集合数据转换为字符串,逗号分割
                            String dragRuleDiffKeyStr = diffKeySet.stream()
                                    .map(String::valueOf)
                                    .collect(Collectors.joining(StrUtil.COMMA));
                            errorMessage = StrUtil.format("规则链[{}],表第[{}]个规则[{}],入参参数不满足要求,\n缺少入参信息为[{}]",
                                    dragRuleChain,
                                    i + 1,
                                    dragRuleDTO.getDragRuleId(),
                                    dragRuleDiffKeyStr);
                            throw new ServiceException(errorMessage);
                        }
                        // 设置规则返回参数作为传递参数设定到切入点入参参数中,作为下一入参参数校验集合,传递参数为字符串,多个参数以逗号分割
                        if (StrUtil.isNotBlank(dragRuleDTO.getCrossParam())) {
                            String[] crossParams = dragRuleDTO.getCrossParam().split(StrUtil.COMMA);
                            for (int j = 0; j < crossParams.length; j++) {
                                resultInParam.put(crossParams[j], StrUtil.EMPTY);
                            }
                        }
                    }
                }
            }
        }
    
        /**
         * 通过规则id获取规则信息
         * @param dragRuleId
         */
        public DragRuleDTO getDragRuleDTO(String dragRuleId) {
            Object dragRuleObj = redisUtil.hget(CommonDragRuleConstant.DRAG_RULES, dragRuleId);
            if (ObjectUtil.isNotNull(dragRuleObj)) {
                return JSONObject.parseObject(StrUtil.toString(dragRuleObj), DragRuleDTO.class);
            }
            throw new ServiceException(StrUtil.format("拖拽规则通过缓存获取规则Id[{}]不存在,请确认规则服务成功启动!", dragRuleId));
        }
    
        /**
         * 检查当前服务是否需要进行加载规则链
         * @param serverName 服务名称
         * @return
         */
        public boolean checkIfLoadDragChain(String serverName) {
            // 空值校验
            if (StrUtil.isBlank(serverName)) {
                return false;
            }
    
            // 设定加载的模块名称
            boolean ifLoad = serverName.equalsIgnoreCase("account-server")
                    || serverName.equalsIgnoreCase("afterloan-server")
                    || serverName.equalsIgnoreCase("collateral-server")
                    || serverName.equalsIgnoreCase("credit-server")
                    || serverName.equalsIgnoreCase("loan-server")
                    || serverName.equalsIgnoreCase("management-server")
                    || serverName.equalsIgnoreCase("rat-server")
                    || serverName.equalsIgnoreCase("store-server")
                    || serverName.equalsIgnoreCase("warning-server")
                    || serverName.equalsIgnoreCase("product-server");
    
            return ifLoad;
        }
    
    }
    
    
  5. 规则链mapper类,进行规则链的列表查询

    package cn.git.common.drag.mapper;
    
    import cn.git.common.drag.dto.DragChainDTO;
    import org.apache.ibatis.annotations.Select;
    
    import java.util.List;
    
    /**
     * @description: 拖拽规则通用mapper
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-29 02:44:11
     */
    public interface CommonDragRuleMapper {
    
        /**
         * 获取拖拽规则链列表信息
         * @return
         */
        @Select("SELECT * FROM MANAGE.TB_SYS_DRAG_CHAIN")
        List<DragChainDTO> getDragChainList();
    
    }
    
    
  6. DTO参数类,拖拽规则链DTO以及拖拽规则详情DTO

    package cn.git.common.drag.dto;
    
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.util.List;
    
    /**
     * @description: 拖拽规则链表
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-20 03:32:00
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ApiModel(value = "拖拽规则链表信息", description = "拖拽规则链表信息")
    public class DragChainDTO {
    
        /**
         * 规则链id
         */
        @ApiModelProperty("规则链id,由controllerName + methodName组合而成,eg: DragRuleTestController.test0001")
        private String dragRuleChainId;
    
        /**
         * 产品cd
         */
        @ApiModelProperty("产品cd")
        private String productCd;
    
        /**
         * 产品规则链id
         */
        @ApiModelProperty("产品规则链id")
        private String productChainId;
    
        /**
         * 切入点controller描述信息
         */
        @ApiModelProperty("切入点controller描述信息")
        private String controllerDesc;
    
        /**
         * 切入点controller名称
         */
        @ApiModelProperty("切入点controller名称")
        private String controllerName;
    
        /**
         * 规则链调用方法描述
         */
        @ApiModelProperty("规则链调用方法描述")
        private String methodDesc;
    
    
        /**
         * 规则链调用方法名称
         */
        @ApiModelProperty("规则链调用方法名称")
        private String methodName;
    
        /**
         * 方法入参参数
         */
        @ApiModelProperty("方法入参参数")
        private String methodInParam;
    
        /**
         * 作用方法地址,其实为controller对应method地址
         * 具体格式为: server-name:uri
         */
        @ApiModelProperty("作用方法地址,其实为controller对应method地址,server-name:uri")
        private String uri;
    
        /**
         * 规则链详情,多个规则按序逗号分割
         */
        @ApiModelProperty("规则链详情,多个规则按序逗号分割")
        private String dragRuleChain;
    
        /**
         * 产品规则链详情,多个规则按序逗号分割
         */
        @ApiModelProperty("规则链详情,多个规则按序逗号分割")
        private String dragProductRuleChain;
    
        /**
         * 删除标识
         */
        @ApiModelProperty("删除标识,非展示字段")
        private String isDel;
    
        /**
         * 模块信息
         */
        @ApiModelProperty("模块信息 eg: ACCOUNT_SERVER")
        private String dragChainModule;
    
        /**
         * 规则是否已经保存落库,如果没有落库,规则则展示灰色,如果落库,则展示正常颜色
         */
        @ApiModelProperty("规则链是否已经保存落库,如果没有落库,规则则展示灰色,如果落库,则展示正常颜色")
        private String ifLoadDB;
    
        /**
         * 拖拽规则inVO描述信息
         */
        @ApiModelProperty("拖拽规则inVO描述信息")
        private List<DragRuleInVOParamsDesc> inVOParamsDescList;
    
        /**
         * @description: 规则入参描述信息
         * @program: bank-credit-sy
         * @author: lixuchun
         * @create: 2024-03-05 10:43:51
         */
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        @ApiModel(value = "规则入参描述信息", description = "规则入参描述信息")
        public static class DragRuleInVOParamsDesc {
    
            /**
             * 描述信息
             */
            @ApiModelProperty("描述信息,属性值中文含义")
            private String inParamDesc;
    
            /**
             * 参数名称
             */
            @ApiModelProperty("属性名称")
            private String inParamName;
    
            /**
             * 参数类型
             */
            @ApiModelProperty("参数类型")
            private String inParamType;
        }
    
    }
    
    
    package cn.git.common.drag.dto;
    
    import io.swagger.annotations.ApiModel;
    import io.swagger.annotations.ApiModelProperty;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import java.util.List;
    
    
    /**
     * @description: 拖拽规则详情DTO
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-03-01 02:07:06
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    @ApiModel(value = "拖拽规则详情信息", description = "拖拽规则详情信息")
    public class DragRuleDTO {
    
        /**
         * 规则id,eg: LOANRule0001
         */
        @ApiModelProperty("规则id,eg: LOANRule0001")
        private String dragRuleId;
    
        /**
         * 规则描述
         */
        @ApiModelProperty("规则描述")
        private String ruleDesc;
    
        /**
         * 传递参数,多个以逗号分割
         */
        @ApiModelProperty("传递参数,多个以逗号分割,同crossParamList")
        private String crossParam;
    
        /**
         * 传递参数list
         */
        @ApiModelProperty("传递参数list")
        private List<String> crossParamList;
    
        /**
         * 拖拽规则方法入参
         */
        @ApiModelProperty("拖拽规则方法入参")
        private String inputParam;
    
        /**
         * 拖拽规则入参描述信息
         */
        @ApiModelProperty("拖拽规则入参描述信息")
        private List<DragRuleDTO.DragRuleParamsDesc> inputParamDescList;
    
        /**
         * 规则自定义入参描述信息
         */
        @ApiModelProperty("规则自定义入参描述信息")
        private List<DragRuleDTO.DragRuleParamsDesc> inputCustomParamDescList;
    
        /**
         * 拖拽规则方法出参
         */
        @ApiModelProperty("拖拽规则方法出参")
        private String outputParam;
    
        /**
         * 拖拽规则出参描述信息
         */
        @ApiModelProperty("拖拽规则出参描述信息")
        private List<DragRuleDTO.DragRuleParamsDesc> outputParamDescList;
    
        /**
         * 拖拽规则传递参数描述信息
         */
        @ApiModelProperty("拖拽规则传递参数描述信息")
        private List<DragRuleDTO.DragRuleParamsDesc> crossParamDescList;
    
        /**
         * 自定义参数比较规则信息,为jsonArray格式
         */
        @ApiModelProperty("自定义参数比较规则信息,多个用逗号隔开,同customOptionParamList")
        private String customOptionParam;
    
        /**
         * 删除标识
         */
        @ApiModelProperty("删除标识,非展示字段")
        private String isDel;
    
        /**
         * 规则是否已经保存落库,如果没有落库,规则则展示灰色,如果落库,则展示正常颜色
         */
        @ApiModelProperty("规则是否已经保存落库,如果没有落库,规则则展示灰色,如果落库,则展示正常颜色")
        private String ifLoadDB;
    
        /**
         * 自定义参数比较规则信息列表
         */
        @ApiModelProperty("自定义参数比较规则信息列表")
        private List<List<CustomOptionParam>> customOptionParamList;
    
        /**
         * @description: 自定义参数比较规则信息
         * @program: bank-credit-sy
         * @author: lixuchun
         * @create: 2024-03-05 11:26:44
         */
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        @ApiModel(value = "自定义参数比较规则信息", description = "自定义参数比较规则信息")
        public static class CustomOptionParam {
    
            @ApiModelProperty("自定义操作类型: 0-操作标识符, 1-outVO参数信息, 2-自定义值")
            private String optionType;
    
            @ApiModelProperty("参数类型 eg: BigDecimal, Date, Long...")
            private String paramType;
    
            @ApiModelProperty("参数描述,eg: 合同金额,如果为操作标识,则设定 ''")
            private String paramDesc;
    
            @ApiModelProperty("参数名称,eg: contractAmt")
            private String paramName;
    
            @ApiModelProperty("码值codeTypeCd: eg: YesornoInd")
            private String sysCode;
    
            @ApiModelProperty("自定义值时候给送,其余不送")
            private String value;
    
        }
    
        /**
         * @description: 拖拽规则参数描述信息
         * @program: bank-credit-sy
         * @author: lixuchun
         * @create: 2024-03-05 10:43:51
         */
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        @ApiModel(value = "拖拽规则参数描述信息", description = "拖拽规则参数描述信息")
        public static class DragRuleParamsDesc {
    
            /**
             * 描述信息
             */
            @ApiModelProperty("描述信息,属性值中文含义")
            private String paramDesc;
    
            /**
             * 参数名称
             */
            @ApiModelProperty("属性名称")
            private String paramName;
    
            /**
             * 参数类型
             */
            @ApiModelProperty("参数类型")
            private String paramType;
    
            /**
             * 是否规则自定义入参参数
             * true是 false 否
             */
            @ApiModelProperty("是否规则自定义入参参数")
            private boolean ifCustomDefine;
    
            /**
             * 此字段码值信息
             */
            @ApiModelProperty("码值信息")
            private String sysCode;
    
            /**
             * 自定义入参参数值信息
             */
            @ApiModelProperty("自定义入参参数值信息")
            private String customDefineParamValue;
        }
    }
    
    
  7. client客户端常量类

    package cn.git.common.drag;
    
    /**
     * @description: 拖拽规则通用常量类
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-29 02:58:34
     */
    public class CommonDragRuleConstant {
    
        /**
         * 规则链标识
         * 存储结构 redis hash hashKey customKey customValue
         * eg: DRAG_RULE_CHAIN server-name:uri -> {methodParams,controllerName,controllerDesc,methodDesc,methodName}
         */
        public static final String DRAG_RULE_CHAIN_FLAG = "DRAG_RULE_CHAIN";
    
        /**
         * 产品规则链标识
         * 存储结构 redis hash hashKey customKey customValue
         * eg: DRAG_RULE_PRODUCT_CHAIN server-name:uri:productCd -> {uri,productCd,productDragChain}
         */
        public static final String DRAG_RULE_PRODUCT_CHAIN_FLAG = "DRAG_RULE_PRODUCT_CHAIN";
    
        /**
         * 产品编码标识
         */
        public static final String PRODUCT_CD = "productCd";
    
        /**
         * 拖拽规则redis hash 结构 名称
         */
        public static final String DRAG_RULES = "DRAG_RULES";
    
        /**
         * 规则链
         */
        public static final String DRAG_RULE_CHAIN = "dragRuleChain";
    
        /**
         * 存入拖拽规则入参参数
         */
        public static final String DRAG_RULE_CHAIN_DTO = "dragChainDTO";
    
        /**
         * 方法参数信息
         */
        public static final String METHOD_PARAMS = "methodParams";
    
        /**
         * controller名称
         */
        public static final String CONTROLLER_NAME = "controllerName";
    
        /**
         * controller描述
         */
        public static final String CONTROLLER_DESC = "controllerDesc";
    
        /**
         * 方法描述
         */
        public static final String METHOD_DESC = "methodDesc";
    
        /**
         * 方法名称
         */
        public static final String METHOD_NAME = "methodName";
    
        /**
         * URI
         */
        public static final String CONTROLLER_URI = "URI";
    
        /**
         * 数字类型标识
         */
        public static final String NUM_STR_0 = "0";
        public static final String NUM_STR_1 = "1";
    
        /**
         * 下载文件类型XML
         */
        public static final String APPLICATION_XML = "application/xml";
    
        /**
         * 描述信息标识
         */
        public static final String CONTENT_DISPOSITION = "Content-Disposition";
    }
    
    
  8. DragRuleAspect 客户端切面类具体实现
    封装GET,POST请求参数,将参数统一转换为JSON格式,发送到规则模块处理

    package cn.git.rules.aspect;
    
    import cn.git.common.constant.CacheManageConstant;
    import cn.git.common.constant.HeaderConstant;
    import cn.git.common.drag.CommonDragRuleConstant;
    import cn.git.common.drag.dto.DragChainDTO;
    import cn.git.common.exception.ServiceException;
    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.dto.BaseAspectDTO;
    import cn.git.rules.feign.DragRuleFeignApi;
    import cn.git.rules.util.BaseAspectUtil;
    import cn.git.rules.util.RuleUtil;
    import cn.hutool.core.util.ObjectUtil;
    import cn.hutool.core.util.StrUtil;
    import com.alibaba.fastjson.JSONObject;
    import lombok.extern.slf4j.Slf4j;
    import org.aspectj.lang.JoinPoint;
    import org.aspectj.lang.annotation.Aspect;
    import org.aspectj.lang.annotation.Before;
    import org.aspectj.lang.reflect.MethodSignature;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.beans.factory.annotation.Value;
    import org.springframework.context.ApplicationContext;
    import org.springframework.core.annotation.Order;
    import org.springframework.stereotype.Component;
    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.Date;
    import java.util.HashMap;
    import java.util.List;
    import java.util.Map;
    
    /**
     * 通用规则调用切面方法
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2022-04-01
     */
    @Slf4j
    @Component
    @Aspect
    @Order(value = RuleConstant.ASPECT_BEFORE_ORDER)
    public class DragRuleAspect {
    
        @Value("${spring.application.name}")
        private String applicationName;
    
        @Autowired
        private RedisUtil redisUtil;
    
        @Autowired
        private RuleUtil ruleUtil;
    
        @Autowired
        private ApplicationContext context;
    
        @Autowired
        private BaseAspectUtil baseAspectUtil;
    
        @Autowired
        private HttpServletRequest httpServletRequest;
    
        /**
         * 规则引擎提前通知切面
         * @param joinPoint 切面参数
         */
        @Before("@annotation(cn.git.common.annotation.DragRule)")
        public void doBeforeRuleCheck(JoinPoint joinPoint) throws Throwable {
            // 通过postMapping或者getMapping标签获取请求uri,对应uri为不带参数对应uri
            ServletRequestAttributes attributes = (ServletRequestAttributes) RequestContextHolder.getRequestAttributes();
            HttpServletRequest request = attributes.getRequest();
            MethodSignature signature = (MethodSignature) joinPoint.getSignature();
            Method method = signature.getMethod();
    
            // 获取正确的请径地址求路信息
            BaseAspectDTO baseAspectDTO = baseAspectUtil.getEffectiveURI(method, request);
            // 完整请求路径,包含传递参数信息
            String uri = baseAspectDTO.getUri();
            // 带参数url请求地址
            String requestWithParamUri = baseAspectDTO.getRequestWithParamUri();
    
            // 设置最终路径信息,模块名:uri
            String finalUri = applicationName
                    .replace(StrUtil.DASHED, StrUtil.UNDERLINE)
                    .toUpperCase()
                    .concat(StrUtil.COLON)
                    .concat(uri);
    
            // 获取全链路sign信息
            String sign = null;
            String token = null;
            if (ObjectUtil.isNotNull(httpServletRequest)) {
                sign = httpServletRequest.getHeader(HeaderConstant.HEADER_SIGN);
                token = httpServletRequest.getHeader(HeaderConstant.HEADER_TOKEN);
            }
    
            // 如果redis中含有对应规则
            if (redisUtil.hHasKey(CommonDragRuleConstant.DRAG_RULE_CHAIN_FLAG, finalUri)) {
                // 校验无论post或者get请求都必填
                if (ObjectUtil.isEmpty(joinPoint.getArgs())) {
                    throw new ServiceException("规则校验对应 controller 方法参数必须有请求参数!");
                }
    
                // 获取缓存信息中的拖拽规则缓存链信息
                DragChainDTO dragChainDTO = JSONObject.parseObject(redisUtil.hget(CommonDragRuleConstant.DRAG_RULE_CHAIN_FLAG, finalUri).toString(),
                        DragChainDTO.class);
    
                String cacheChainStr = dragChainDTO.getDragRuleChain();
                if (StrUtil.isNotBlank(cacheChainStr)) {
                    // 获取请求参数并且转换位jsonObject格式
                    Object argsObject = null;
                    if (joinPoint.getArgs().length > 1) {
                        // 定义为请求url中带有参数,进行参数匹配,重新封装为key,value类型数据
                        List<String> uriParamList = baseAspectUtil.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 = baseAspectUtil.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));
    
                    // 获取产品信息校验,缓存的key为 uri + ":" + productCd,如果当前产品配置规则链,则使用产品配置规则链信息,否则使用默认规则链
                    String productCd = jsonParam.getString(CommonDragRuleConstant.PRODUCT_CD);
                    // 从整理号的请求参数中获取产品cd,如果有则查看当前产品是否配置产品规则链
                    if (StrUtil.isNotBlank(productCd)) {
                        String cacheKey = dragChainDTO.getUri().concat(StrUtil.COLON).concat(productCd);
                        Object productRuleObj = redisUtil.hget(CommonDragRuleConstant.DRAG_RULE_PRODUCT_CHAIN_FLAG, cacheKey);
                        if (ObjectUtil.isNotNull(productRuleObj)) {
                            // 获取产品规则链
                            JSONObject productJson = JSONObject.parseObject(productRuleObj.toString());
                            String productChain = productJson.getString(CommonDragRuleConstant.DRAG_RULE_CHAIN);
                            if (StrUtil.isNotBlank(productChain)) {
                                // 替换默认规则链到产品规则链
                                dragChainDTO.setDragRuleChain(productChain);
                            }
                        }
                    }
    
                    jsonParam.put(CommonDragRuleConstant.DRAG_RULE_CHAIN_DTO, JSONObject.toJSONString(dragChainDTO));
    
                    // 错误前缀
                    String ruleErrorPrefix = "拖拽规则校验提示: ";
                    // 调用规则模块,进行规则验证
                    DragRuleFeignApi dragRuleFeignApi = context.getBean(DragRuleFeignApi.class);
    
                    // 设置X_sign
                    log.info("拖拽规则DragRuleAspect获取x_sign:{},token信息为[{}]", sign, token);
                    if (StrUtil.isNotBlank(sign)) {
                        jsonParam.put("dragSign", sign);
                    }
                    if (StrUtil.isNotBlank(token)) {
                        jsonParam.put("dragToken", token);
                    }
    
                    // 调用远程方法
                    Result rspRuleResult = dragRuleFeignApi.checkDragRule(jsonParam);
                    if (ObjectUtil.isNotEmpty(rspRuleResult) && rspRuleResult.getCode() == ResultStatusEnum.ERROR.getCode()) {
                        throw new ServiceException(ruleErrorPrefix.concat(rspRuleResult.getMsg()));
                    }
                }
            }
        }
    }
    
    
  9. 公共dragFeign接口

    package cn.git.rules.feign;
    
    import cn.git.common.result.Result;
    import com.alibaba.fastjson.JSONObject;
    import org.springframework.cloud.openfeign.FeignClient;
    import org.springframework.web.bind.annotation.PostMapping;
    import org.springframework.web.bind.annotation.RequestBody;
    import org.springframework.web.bind.annotation.RequestMapping;
    
    /**
     * @description:
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-22 02:20:31
     */
    @FeignClient(value = "rule-manage-server", fallbackFactory = RuleFeignApiFallCallBackFactory.class)
    @RequestMapping("/rule/feign")
    public interface DragRuleFeignApi {
    
        /**
         * 校验拖拽规则feign方法
         *
         * @param dragRuleJsonParam 请求对象
         * @return Result 校验结果
         */
        @PostMapping("/drag/rule")
        Result checkDragRule(@RequestBody JSONObject dragRuleJsonParam);
    
    }
    

4.2 拖拽规则服务端

服务端主要进行提供规则链规则的管理,以及子模块进行规则逻辑执行调用Feign接口的具体实现逻辑。服务端的代码pom为:

<?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-server</artifactId>
    <description>规则引擎服务端</description>

    <dependencies>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>business-common</artifactId>
            <version>1.0-SNAPSHOT</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>com.alibaba.cloud</groupId>
                    <artifactId>spring-cloud-starter-alibaba-sentinel</artifactId>
                </exclusion>
                <exclusion>
                    <groupId>cn.git</groupId>
                    <artifactId>database-synchronize-client</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>credit-log-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>credit-swagger-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>rule-manage-api</artifactId>
            <version>1.0-SNAPSHOT</version>
        </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>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-redis-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>credit-discovery-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <!-- 监听nacos配置信息变更 -->
        <dependency>
            <groupId>com.purgeteam</groupId>
            <artifactId>dynamic-config-spring-boot-starter</artifactId>
            <version>0.1.1.RELEASE</version>
            <exclusions>
                <exclusion>
                    <groupId>org.springframework.boot</groupId>
                    <artifactId>spring-boot-starter-logging</artifactId>
                </exclusion>
            </exclusions>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>credit-monitor-starter</artifactId>
            <version>1.0-SNAPSHOT</version>
        </dependency>
        <dependency>
            <groupId>org.apache.skywalking</groupId>
            <artifactId>apm-toolkit-log4j-2.x</artifactId>
            <version>8.6.0</version>
        </dependency>
        <!-- 影像平台使用jar -->
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>clientQuery</artifactId>
            <version>v1.0.0</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>SunECMClient</artifactId>
            <version>v3.1.5</version>
        </dependency>
        <dependency>
            <groupId>log4j</groupId>
            <artifactId>log4j</artifactId>
            <version>1.2.14</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>sun-ws-all-v2.0</artifactId>
            <version>v2.0</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>sun-ws-all-v3.1.5</artifactId>
            <version>v3.1.5</version>
        </dependency>

        <!-- app影像上传使用 -->
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-frontend-jaxws</artifactId>
            <version>3.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http</artifactId>
            <version>3.0.9</version>
        </dependency>
        <dependency>
            <groupId>org.apache.cxf</groupId>
            <artifactId>cxf-rt-transports-http-jetty</artifactId>
            <version>3.0.9</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>dom4j</artifactId>
            <version>v1.6.1</version>
        </dependency>
        <dependency>
            <groupId>cn.git</groupId>
            <artifactId>converter-api</artifactId>
            <version>1.0-SNAPSHOT</version>
            <scope>compile</scope>
        </dependency>
        <!--app影像上传使用 -->
        <!-- 拖拽规则使用 -->
        <dependency>
            <groupId>org.camunda.bpm.model</groupId>
            <artifactId>camunda-bpmn-model</artifactId>
            <version>7.19.0</version>
        </dependency>
    </dependencies>
    <build>
        <plugins>
            <!-- compiler -->
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>3.8.1</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                    <annotationProcessorPaths>
                        <path>
                            <groupId>org.mapstruct</groupId>
                            <artifactId>mapstruct-processor</artifactId>
                            <version>${mapstruct.version}</version>
                        </path>
                        <path>
                            <groupId>org.projectlombok</groupId>
                            <artifactId>lombok</artifactId>
                            <version>${lombok.version}</version>
                        </path>
                    </annotationProcessorPaths>
                </configuration>
            </plugin>
            <!-- package -->
            <plugin>
                <groupId>org.springframework.boot</groupId>
                <artifactId>spring-boot-maven-plugin</artifactId>
            </plugin>
        </plugins>
    </build>

</project>

其余对应具体的主要实现代码如下所示

4.2.1 规则链管理相关类
  1. DragRuleController,拖拽规则管理controller,主要管理增删改查入口类

    package cn.git.rules.controller;
    
    import cn.git.common.annotation.TableCode;
    import cn.git.common.drag.dto.DragChainDTO;
    import cn.git.common.drag.dto.DragRuleDTO;
    import cn.git.common.exception.ServiceException;
    import cn.git.common.idempotent.ApiIdempotent;
    import cn.git.common.page.PageBean;
    import cn.git.common.result.Result;
    import cn.git.rules.drag.enums.DragRuleSequenceEnum;
    import cn.git.rules.dto.drag.DragRuleChainAllParamDTO;
    import cn.git.rules.mapstruct.DragRuleConvert;
    import cn.git.rules.service.drag.DragRuleService;
    import cn.git.rules.vo.drag.*;
    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.web.bind.annotation.*;
    
    import javax.validation.Valid;
    import java.io.IOException;
    import java.util.ArrayList;
    import java.util.List;
    
    /**
     * @description: 拖拽规则controller
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-23 04:46:26
     */
    @RestController
    @RequestMapping("/rule")
    @Api(tags = "拖拽规则服务controller", value = "拖拽规则服务controller")
    public class DragRuleController {
    
        @Autowired
        private DragRuleConvert dragRuleConvert;
    
        @Autowired
        private DragRuleService dragRuleService;
    
        /**
         * 添加拖拽规则
         * @param dragRuleInVO
         * @return
         */
        @ApiOperation(value = "添加拖拽规则", notes = "添加拖拽规则")
        @PostMapping(value = "/drag/add")
        public Result addDragRule(
                @ApiParam(name = "dragRuleAddInVO", value = "添加拖拽规则inVO", required = true)
                @Valid @RequestBody DragRuleInVO dragRuleInVO) {
            // 获取传递参数
            DragRuleDTO dragRuleDTO = dragRuleConvert.dragRuleInVOToDTO(dragRuleInVO);
            // 进行自定义参数信息赋值
            if (ObjectUtil.isNotEmpty(dragRuleInVO.getCustomOptionParamList())) {
                List<List<DragRuleDTO.CustomOptionParam>> dtoCustomParamList = new ArrayList<>();
                dragRuleInVO.getCustomOptionParamList().forEach(inVOParamList -> {
                    // 空值判定
                    if (ObjectUtil.isEmpty(inVOParamList)) {
                        throw new ServiceException("保存自定义参数值为空,请正确设置自定义参数!");
                    }
    
                    // 设置dto自定义参数列表信息
                    List<DragRuleDTO.CustomOptionParam> dtoParamList = new ArrayList<>();
                    inVOParamList.forEach(inVOParam -> {
                        DragRuleDTO.CustomOptionParam dtoParam = dragRuleConvert.inVOCustomOptionParamToDTO(inVOParam);
                        dtoParamList.add(dtoParam);
                    });
                    dtoCustomParamList.add(dtoParamList);
                });
                dragRuleDTO.setCustomOptionParamList(dtoCustomParamList);
            }
    
            dragRuleService.addDragRule(dragRuleDTO);
            return Result.ok("添加拖拽规则成功!", null);
        }
    
        /**
         * 查询单条拖拽规则
         * @param id
         * @return
         */
        @ApiOperation(value = "查询单条拖拽规则", notes = "查询单条拖拽规则")
        @GetMapping(value = "/drag/{id}")
        public Result<DragRuleOutVO> findDragRule(@PathVariable("id") String id) {
            // 获取单条拖拽规则,并且转换为outVO
            DragRuleDTO dragRuleDTO = dragRuleService.findById(id);
            DragRuleOutVO outVO = dragRuleConvert.dragRuleDTOToDragRuleOutVO(dragRuleDTO);
    
            // dto 自定义参数信息转换为 outVO自定义信息参数
            if (ObjectUtil.isNotEmpty(dragRuleDTO.getCustomOptionParamList())) {
                List<List<DragRuleOutVO.CustomOptionParam>> outVOParamsList = new ArrayList<>();
                dragRuleDTO.getCustomOptionParamList().forEach(dtoParamList -> {
                    List<DragRuleOutVO.CustomOptionParam> outVOParamList = new ArrayList<>();
                    dtoParamList.forEach(dtoParam -> {
                        DragRuleOutVO.CustomOptionParam outVOParam = dragRuleConvert.dtoCustomOptionParamToDTO(dtoParam);
                        outVOParamList.add(outVOParam);
                    });
                    outVOParamsList.add(outVOParamList);
                });
                outVO.setCustomOptionParamList(outVOParamsList);
            }
            return Result.ok(outVO);
        }
    
        /**
         * 修改拖拽规则
         * @param dragRuleInVO
         * @return
         */
        @ApiOperation(value = "修改拖拽规则", notes = "修改拖拽规则")
        @PostMapping(value = "/drag/update")
        public Result updateDragRule(
                @ApiParam(name = "dragRuleInVO", value = "修改拖拽规则inVO", required = true)
                @Valid @RequestBody DragRuleInVO dragRuleInVO) {
            // 获取传递参数
            DragRuleDTO dragRuleDTO = dragRuleConvert.dragRuleInVOToDTO(dragRuleInVO);
    
            // 进行自定义参数信息赋值
            if (ObjectUtil.isNotEmpty(dragRuleInVO.getCustomOptionParamList())) {
                List<List<DragRuleDTO.CustomOptionParam>> dtoCustomParamList = new ArrayList<>();
                dragRuleInVO.getCustomOptionParamList().forEach(inVOParamList -> {
                    // 空值判定
                    if (ObjectUtil.isEmpty(inVOParamList)) {
                        throw new ServiceException("修改自定义参数值为空,请正确设置自定义参数!");
                    }
    
                    // 设置dto自定义参数列表信息
                    List<DragRuleDTO.CustomOptionParam> dtoParamList = new ArrayList<>();
                    inVOParamList.forEach(inVOParam -> {
                        DragRuleDTO.CustomOptionParam dtoParam = dragRuleConvert.inVOCustomOptionParamToDTO(inVOParam);
                        dtoParamList.add(dtoParam);
                    });
                    dtoCustomParamList.add(dtoParamList);
                });
                dragRuleDTO.setCustomOptionParamList(dtoCustomParamList);
            }
            dragRuleService.updateDragRule(dragRuleDTO);
            return Result.ok("修改成功!", null);
        }
    
        /**
         * 删除拖拽规则
         * @param id
         * @return
         */
        @ApiOperation(value = "删除拖拽规则", notes = "删除拖拽规则")
        @PostMapping(value = "/drag/delete/{id}")
        public Result deleteDragById(
                @ApiParam(name = "id", value = "删除拖拽规则id", required = true)
                @PathVariable("id") String id) {
            dragRuleService.deleteDragRule(id);
            return Result.ok("删除成功!", null);
        }
    
    
        /**
         * 优化并行规则链添加
         * @param chainAddInVO
         * @return
         */
        @ApiIdempotent
        @ApiOperation(value = "优化并行规则链添加", notes = "优化并行规则链添加")
        @PostMapping(value = "/drag/chain/add")
        public Result<String> addDragChain(@Valid DragGateChainAddInVO chainAddInVO) throws IOException {
            // 参数非空校验
            if (ObjectUtil.isNotNull(chainAddInVO.getFile()) && chainAddInVO.getFile().isEmpty()) {
                throw new ServiceException(StrUtil.format("传入文件为空,请确认!"));
            }
    
            // 开始调用添加规则链信息
            dragRuleService.addGatewayChain(chainAddInVO.getFile(), chainAddInVO.getUri(), chainAddInVO.getProductCd());
            return Result.ok("添加拖拽规则链成功!", null);
        }
    
        /**
         * 修改规则链
         * @param chainAddInVO 参数inVO
         * @return
         */
        @ApiOperation(value = "修改规则链", notes = "修改规则链")
        @PostMapping(value = "/drag/chain/update")
        public Result updateGatewayDragChain(@Valid DragGateChainAddInVO chainAddInVO) throws IOException {
            // 参数非空校验
            if (ObjectUtil.isNotNull(chainAddInVO.getFile()) && chainAddInVO.getFile().isEmpty()) {
                throw new ServiceException(StrUtil.format("传入文件为空,请确认!"));
            }
    
            // 获取传递参数
            dragRuleService.updateGatewayChain(chainAddInVO.getFile(), chainAddInVO.getUri(), chainAddInVO.getProductCd());
            return Result.ok("修改规则链成功!", null);
        }
    
        /**
         * 拖拽规则链删除
         * @param dragRuleChainOpInVO
         * @return
         */
        @ApiOperation(value = "删除拖拽规则", notes = "删除拖拽规则")
        @PostMapping(value = "/drag/chain/delete")
        public Result deleteChainById(
                @ApiParam(name = "dragRuleChainOpInVO", value = "拖拽规则链查询删除操作inVO", required = true)
                @Valid @RequestBody DragRuleChainOpInVO dragRuleChainOpInVO) {
            dragRuleService.deleteChainByUri(dragRuleChainOpInVO.getUri());
            return Result.ok("删除拖拽规则成功!", null);
        }
    
        /**
         * 拖拽产品规则链单个删除
         * @param dragRuleChainOpInVO
         * @return
         */
        @ApiOperation(value = "拖拽产品规则链单个删除", notes = "拖拽产品规则链单个删除")
        @PostMapping(value = "/drag/product/chain/delete")
        public Result deleteProductChainByURIAndProductCd(
                @ApiParam(name = "dragRuleChainOpInVO", value = "拖拽规则链查询删除操作inVO", required = true)
                @Valid @RequestBody DragRuleChainOpInVO dragRuleChainOpInVO) {
            // 空值校验
            if (StrUtil.isBlank(dragRuleChainOpInVO.getProductCd())) {
                throw new ServiceException("规则链对应产品cd不能为空!");
            }
            dragRuleService.deleteProductChainByUriAndProductCd(dragRuleChainOpInVO.getUri(),
                    dragRuleChainOpInVO.getProductCd());
            return Result.ok("产品删除拖拽规则成功!", null);
        }
    
        /**
         * 拖拽产品规则链集合删除
         * @param dragRuleChainOpInVO
         * @return
         */
        @ApiOperation(value = "拖拽产品规则链集合删除", notes = "拖拽产品规则链集合删除")
        @PostMapping(value = "/drag/product/chain/uri/delete")
        public Result deleteProductChainByURI(
                @ApiParam(name = "dragRuleChainOpInVO", value = "拖拽规则链查询删除操作inVO", required = true)
                @Valid @RequestBody DragRuleChainOpInVO dragRuleChainOpInVO) {
            // 空值校验
            if (StrUtil.isNotBlank(dragRuleChainOpInVO.getProductCd())) {
                throw new ServiceException("拖拽产品规则链集合删除,产品cd必须为空!");
            }
            dragRuleService.deleteProductChainByUri(dragRuleChainOpInVO.getUri());
            return Result.ok("产品删除拖拽规则成功!", null);
        }
    
        /**
         * 规则列表获取
         * @param dragRulePageInVO
         * @return
         */
        @ApiOperation(value = "拖拽规则分页查询", notes = "拖拽规则分页查询")
        @PostMapping(value = "/drag/page")
        @TableCode(keyNameMapping = "ifLoadDB=YesornoInd")
        public Result<DragRulePageOutVO> dragPage(
                @ApiParam(name = "dragRuleInVO", value = "拖拽规则分页查询inVO", required = true)
                @Valid @RequestBody DragRulePageInVO dragRulePageInVO) {
            // 获取传递参数
            DragRuleDTO dragRuleDTO = dragRuleConvert.dragRulePageInVOToDTO(dragRulePageInVO);
            PageBean<DragRuleDTO> pageBean = dragRuleService.getDragRulePage(dragRuleDTO);
            DragRulePageOutVO pageOutVO = new DragRulePageOutVO();
            pageOutVO.setDragRuleDTOPageBean(pageBean);
            return Result.ok(pageOutVO);
        }
    
        /**
         * 单个规则链获取
         * @param dragRuleChainOpInVO
         * @return
         */
        @ApiOperation(value = "单个规则链获取", notes = "单个规则链获取")
        @PostMapping(value = "/drag/chain")
        public Result<DragRuleChainOutVO> getDragRuleChainByUri(
                @ApiParam(name = "dragRuleChainOpInVO", value = "拖拽规则链查询删除操作inVO", required = true)
                @Valid @RequestBody DragRuleChainOpInVO dragRuleChainOpInVO) {
            // 获取单条规则链
            DragChainDTO dragChainDTO = dragRuleService.getDragChain(dragRuleChainOpInVO.getUri(),
                    dragRuleChainOpInVO.getProductCd());
            DragRuleChainOutVO chainOutVO = dragRuleConvert.dragChainDTOtoOutVO(dragChainDTO);
            return Result.ok(chainOutVO);
        }
    
        /**
         * 下载文件
         * @param dragRuleChainOpInVO
         * @return
         */
        @ApiOperation(value = "单个规则链文件获取", notes = "单个规则链文件获取")
        @PostMapping(value = "/drag/chain/file")
        public void downloadFile(
                @ApiParam(name = "dragRuleChainOpInVO", value = "拖拽规则链查询删除操作inVO", required = true)
                @Valid @RequestBody DragRuleChainOpInVO dragRuleChainOpInVO) throws IOException {
            // 获取文件
            dragRuleService.downloadFile(dragRuleChainOpInVO.getUri(), dragRuleChainOpInVO.getProductCd());
        }
    
        /**
         * 获取文件xml信息
         * @param dragRuleChainOpInVO
         * @return
         */
        @ApiOperation(value = "获取文件xml信息", notes = "获取文件xml信息")
        @PostMapping(value = "/drag/chain/xml")
        public Result<String> getXMLStr(
                @ApiParam(name = "dragRuleChainOpInVO", value = "拖拽规则链查询删除操作inVO", required = true)
                @Valid @RequestBody DragRuleChainOpInVO dragRuleChainOpInVO) throws IOException {
            // 获取文件
            String xmlStr = dragRuleService.getXMLStr(dragRuleChainOpInVO.getUri(), dragRuleChainOpInVO.getProductCd());
            return Result.ok(xmlStr);
        }
    
        /**
         * 添加产品拖拽规则链初始方法
         * @param module 模块
         * @param methodPath 请求路径
         * @return
         */
        @ApiOperation(value = "添加产品拖拽规则链初始方法", notes = "添加产品拖拽规则链初始方法")
        @PostMapping(value = "/drag/chain/product/add/init")
        public Result addDragProductChainInit(
                @ApiParam(name = "productCd", value = "产品cd", required = true)
                @RequestParam(value = "productCd") String productCd,
                @ApiParam(name = "module", value = "模块uri用 : 分割前缀 ge: ACCOUNT_SERVER", required = true)
                @RequestParam(value = "module") String module,
                @ApiParam(name = "methodPath", value = "uri除去模块后缀,将请求地址中的 '/' 更换为 '_',eg: _account_drag_test0002", required = true)
                @RequestParam(value = "methodPath") String methodPath) throws IOException {
    
            // 组成uri地址
            String uri = module.concat(StrUtil.COLON).concat(methodPath.replaceAll(StrUtil.UNDERLINE, StrUtil.SLASH));
    
            // 获取传递参数
            dragRuleService.addProductChainInit(uri, productCd);
            return Result.ok("初始化校验通过!", null);
        }
    
        /**
         * 规则链列表获取
         * @param dragRuleChainInVO
         * @return
         */
        @ApiOperation(value = "规则链列表获取", notes = "规则链列表获取")
        @PostMapping(value = "/drag/chain/page")
        @TableCode(keyNameMapping = "ifLoadDB=YesornoInd")
        public Result<DragRuleChainPageOutVO> dragChainPage(
                @ApiParam(name = "DragRuleChainPageInVO", value = "规则链分页操作inVO", required = true)
                @Valid @RequestBody DragRuleChainPageInVO dragRuleChainInVO) {
            // 获取传递参数
            DragChainDTO dragChainDTO = dragRuleConvert.dragRulePageInVOtoDTO(dragRuleChainInVO);
            PageBean<DragChainDTO> pageBean = dragRuleService.getDragChainPage(dragChainDTO);
            // 回参参数
            DragRuleChainPageOutVO chainPageOutVO = new DragRuleChainPageOutVO();
            chainPageOutVO.setPageBean(pageBean);
            return Result.ok(chainPageOutVO);
        }
    
        /**
         * 产品规则链列表获取
         * @param pageInVO
         * @return
         */
        @ApiOperation(value = "产品规则链列表获取", notes = "产品规则链列表获取")
        @PostMapping(value = "/drag/product/chain/page")
        public Result<DragRuleChainPageOutVO> dragProductChainPage(
                @ApiParam(name = "DragRuleProductPageInVO", value = "产品规则链列表获取", required = true)
                @Valid @RequestBody DragRuleProductPageInVO pageInVO) {
            // 产品cd非空校验
            if (StrUtil.isBlank(pageInVO.getProductCd())) {
                throw new ServiceException("产品cd不能为空!");
            }
    
            // 获取传递参数,转换为DTO
            DragChainDTO dragChainDTO = dragRuleConvert.dragRuleProductPageInVOtoDTO(pageInVO);
    
            // 开始获取分页列表细信息
            PageBean<DragChainDTO> pageBean = dragRuleService.getDragProductChainPage(dragChainDTO);
            // 回参参数
            DragRuleChainPageOutVO chainPageOutVO = new DragRuleChainPageOutVO();
            chainPageOutVO.setPageBean(pageBean);
            return Result.ok(chainPageOutVO);
        }
    
        /**
         * 通过文件以及节点校验规则链是否合法
         * @param dragChainFileCheckInVO
         * @return
         */
        @ApiOperation(value = "通过文件以及节点校验规则链是否合法", notes = "通过文件以及节点校验规则链是否合法")
        @PostMapping(value = "/drag/chain/params/gateway")
        public Result<DragRuleChainAllParamDescOutVO> checkDragRuleChainByFile(@Valid DragChainFileCheckInVO dragChainFileCheckInVO) {
            // 参数非空校验
            if (ObjectUtil.isNotNull(dragChainFileCheckInVO.getFile()) && dragChainFileCheckInVO.getFile().isEmpty()) {
                throw new ServiceException(StrUtil.format("传入文件为空,请确认!"));
            }
    
            // 参数转换
            DragRuleChainAllParamDTO allParamDTO = new DragRuleChainAllParamDTO();
            allParamDTO.setChooseDragRuleId(dragChainFileCheckInVO.getChooseDragRuleId());
            allParamDTO.setFile(dragChainFileCheckInVO.getFile());
            allParamDTO.setUri(dragChainFileCheckInVO.getUri());
            allParamDTO.setFileNodeId(dragChainFileCheckInVO.getFileNodeId());
    
            // 校验规则文件是否合法
            allParamDTO = dragRuleService.checkDragRuleChainByFile(allParamDTO);
            DragRuleChainAllParamDescOutVO allParamDescOutVO = dragRuleConvert.dragRuleChainAllParamDescToOutVO(allParamDTO);
            return Result.ok(allParamDescOutVO);
        }
    
        /**
         * 获取规则链名称
         * @param dragRuleSequenceEnum
         * @return
         */
        @ApiOperation(value = "开发人员使用获取拖拽规则规则名称", notes = "开发人员使用获取拖拽规则规则名称")
        @GetMapping(value = "/drag/sequence/name")
        public Result<String> getDragSequenceName(@RequestParam DragRuleSequenceEnum dragRuleSequenceEnum) {
            // 通过枚举获取各模块规则信息
            String dragRuleName = dragRuleService.getDragSequenceName(dragRuleSequenceEnum);
            return Result.ok(dragRuleName);
        }
    }
    
    
  2. dragRuleService以及对应的serviceImpl实现类

    package cn.git.rules.service.drag;
    
    import cn.git.common.drag.dto.DragChainDTO;
    import cn.git.common.drag.dto.DragRuleDTO;
    import cn.git.common.page.PageBean;
    import cn.git.rules.drag.enums.DragRuleSequenceEnum;
    import cn.git.rules.dto.drag.DragRuleChainAllParamDTO;
    import org.springframework.web.multipart.MultipartFile;
    
    import java.io.IOException;
    
    /**
     * @description: 拖拽规则service
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-22 11:55:33
     */
    public interface DragRuleService {
    
        /**
         * 添加拖拽规则方法
         * @param dragRuleDTO
         */
        void addDragRule(DragRuleDTO dragRuleDTO);
    
        /**
         * 通过id查询拖拽规则
         * @param dragRuleId
         * @return
         */
        DragRuleDTO findById(String dragRuleId);
    
        /**
         * 修改拖拽规则
         * @param dragRuleDTO
         */
        void updateDragRule(DragRuleDTO dragRuleDTO);
    
        /**
         * 删除拖拽规则
         * @param id
         */
        void deleteDragRule(String id);
    
        /**
         * 分页查询拖拽规则信息
         * 参数有规则描述信息,模糊查询
         * 参数有规则id信息,精确查询
         * 查询结果由两部分拼接而成,注意:只有表数据中有数据才能进行规则链添加
         *  1:通过缓存信息,可以获取规则入参,出参等信息
         *  2:通过表数据,可以获取传递参数,自定义比较参数等
         *
         * @param dragRuleDTO
         * @return
         */
        PageBean<DragRuleDTO> getDragRulePage(DragRuleDTO dragRuleDTO);
    
        /**
         * 添加并行规则链信息
         * @param file
         * @param uri
         * @param productCd
         */
        void addGatewayChain(MultipartFile file, String uri, String productCd) throws IOException;
    
        /**
         * 添加产品链信息初始化校验
         * @param uri uri
         * @param productCd 产品cd
         */
        void addProductChainInit(String uri, String productCd) throws IOException;
    
        /**
         * 修改并行规则链信息
         * @param file 文件
         * @param uri uri
         */
        void updateGatewayChain(MultipartFile file, String uri, String productCd) throws IOException;
    
        /**
         * 通过uri删除链信息
         * @param uri
         * @return
         */
        void deleteChainByUri(String uri);
    
        /**
         * 通过uri以及产品cd删除产品链信息
         * @param uri
         * @return
         */
        void deleteProductChainByUriAndProductCd(String uri, String productCd);
    
        /**
         * 通过uri删除多条产品链信息
         * @param uri
         * @return
         */
        void deleteProductChainByUri(String uri);
    
        /**
         * 通过uri查询链信息
         * @param uri
         * @param productCd
         * @return
         */
        DragChainDTO getDragChain(String uri, String productCd);
    
        /**
         * 获取规则文件信息
         * @param uri
         * @throws IOException
         */
        void downloadFile(String uri, String productCd) throws IOException;
    
        /**
         * 获取规则文件xml信息
         * @param uri
         * @throws IOException
         */
        String getXMLStr(String uri, String productCd) throws IOException;
    
        /**
         * 分页查询链信息
         * @param dragChainDTO
         * @return
         */
        PageBean<DragChainDTO> getDragChainPage(DragChainDTO dragChainDTO);
    
        /**
         * 分页查询产品链信息
         * @param dragChainDTO
         * @return
         */
        PageBean<DragChainDTO> getDragProductChainPage(DragChainDTO dragChainDTO);
    
        /**
         * 通过文件以及节点校验规则链是否合法
         * @param allParamDTO
         * @return
         */
        DragRuleChainAllParamDTO checkDragRuleChainByFile(DragRuleChainAllParamDTO allParamDTO);
    
        /**
         * 开发人员使用获取拖拽规则规则名称
         * @param sequenceEnum
         * @return
         */
        String getDragSequenceName(DragRuleSequenceEnum sequenceEnum);
    
    }
    
    

    规则实现类代码过多,所以新增一个单独记录地址记录,DragRuleServiceImpl 详情请点击

  3. 规则注解类,标注此类的接口会被扫描到规则缓存中

    package cn.git.rules.drag;
    
    import java.lang.annotation.ElementType;
    import java.lang.annotation.Retention;
    import java.lang.annotation.RetentionPolicy;
    import java.lang.annotation.Target;
    
    /**
     * @description: 规则属性解释注解
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-22 08:44:44
     */
    @Retention(RetentionPolicy.RUNTIME)
    @Target({ElementType.FIELD, ElementType.TYPE})
    public @interface DragRuleDesc {
    
        /**
         * 拖拽规则field作用注释
         * @return
         */
        String fieldDesc() default "属性字段描述";
    
        /**
         * 字段码值
         * @return
         */
        String sysCode() default "";
    
        /**
         * 是否自定义属性值
         * @return
         */
        boolean ifCustomDefine() default false;
    
        /**
         * 拖拽规则bean作用注释
         * @return
         */
        String beanDesc() default "拖拽规则类描述";
    
    }
    
  4. server端工具类

    package cn.git.rules.drag;
    
    import cn.git.api.util.NewCoreProperties;
    import cn.git.common.drag.CommonDragRuleConstant;
    import cn.git.common.drag.dto.DragChainDTO;
    import cn.git.common.drag.dto.DragRuleDTO;
    import cn.git.common.exception.ServiceException;
    import cn.git.common.lock.LockTypeEnum;
    import cn.git.common.lock.SimpleDistributeLock;
    import cn.git.common.util.LogUtil;
    import cn.git.redis.RedisUtil;
    import cn.git.rules.constant.RuleConstant;
    import cn.git.rules.entity.TbSysDragChain;
    import cn.git.rules.entity.TbSysDragRule;
    import cn.git.rules.entity.TbSysProductDragChain;
    import cn.git.rules.mapper.TbSysDragRuleMapper;
    import cn.git.rules.mapper.TbSysProductDragChainMapper;
    import cn.git.rules.mapstruct.DragRuleConvert;
    import cn.git.rules.util.DataSourceTypeEnum;
    import cn.git.rules.util.DatabaseUtil;
    import cn.hutool.core.collection.CollUtil;
    import cn.hutool.core.date.DatePattern;
    import cn.hutool.core.date.DateUtil;
    import cn.hutool.core.util.NumberUtil;
    import cn.hutool.core.util.ObjectUtil;
    import cn.hutool.core.util.StrUtil;
    import com.alibaba.fastjson.JSONObject;
    import lombok.extern.slf4j.Slf4j;
    import org.camunda.bpm.model.bpmn.instance.FlowNode;
    import org.camunda.bpm.model.bpmn.instance.SequenceFlow;
    import org.camunda.bpm.model.bpmn.instance.StartEvent;
    import org.springframework.aop.framework.Advised;
    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 javax.script.Compilable;
    import javax.script.ScriptEngine;
    import javax.script.ScriptEngineManager;
    import javax.script.ScriptException;
    import javax.servlet.http.HttpServletRequest;
    import java.lang.reflect.Field;
    import java.lang.reflect.Method;
    import java.time.LocalDate;
    import java.time.ZoneOffset;
    import java.time.format.DateTimeFormatter;
    import java.time.format.DateTimeParseException;
    import java.util.*;
    import java.util.function.Function;
    import java.util.regex.Matcher;
    import java.util.regex.Pattern;
    import java.util.stream.Collectors;
    
    /**
     * @description: DragRule拖拽规则工具类
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-21 03:01:12
     */
    @Slf4j
    @Component
    public class DragRuleServerUtil {
    
        /**
         * 数组正则表达式
         */
        public final String ARRAY_REGEX = "^\\[(('([^']|\\\\')*')|[a-zA-Z0-9\\.]+)(,('([^']|\\\\')*')|[a-zA-Z0-9\\.]+)*\\]$";
    
        @Autowired
        private DragRuleConvert dragRuleConvert;
    
        @Autowired
        private RedisUtil redisUtil;
    
        @Autowired
        private TbSysDragRuleMapper tbSysDragRuleMapper;
    
        @Autowired
        private TbSysProductDragChainMapper productDragChainMapper;
    
        @Autowired
        private ApplicationContext applicationContext;
    
        @Autowired
        private SimpleDistributeLock simpleDistributeLock;
    
        @Value("${spring.application.name}")
        private String applicationName;
    
        @Autowired
        private DatabaseUtil databaseUtil;
    
        @Autowired
        private HttpServletRequest httpServletRequest;
    
        /**
         * 初始化拖拽规则详情信息,设置规则信息DRAG_RULE供其他子模块获取使用
         * 规则格式如下: hset DRAG_RULE DragRuleId RuleInfo
         */
        public void initDragRule() throws Exception {
            // 获取锁信息
            String lockResult = simpleDistributeLock.tryLock(LockTypeEnum.DRAG_RULE_INIT_LOCK, applicationName);
            try {
                if (StrUtil.isNotBlank(lockResult)) {
                    // 扫描服务所有DragRule注解类信息,此处需要将获取bean由cglib转换为真实对象
                    Map<String, Object> beansWithAnnotation = applicationContext.getBeansWithAnnotation(DragRuleDesc.class);
                    beansWithAnnotation.replaceAll((key, value) -> {
                        if (value instanceof Advised) {
                            Advised advised = (Advised) value;
                            Object target = null;
                            try {
                                target = advised.getTargetSource().getTarget();
                            } catch (Exception e) {
                                e.printStackTrace();
                            }
                            return target;
                        } else {
                            return value;
                        }
                    });
    
                    // 选择数据库
                    databaseUtil.chooseDB(DataSourceTypeEnum.MANAGE);
    
                    // 获取全部拖拽规则信息
                    List<TbSysDragRule> dragRuleList = tbSysDragRuleMapper.selectList(null);
    
                    // 切换默认数据源
                    databaseUtil.clearToDefault();
    
                    Map<String, TbSysDragRule> dragRuleMap = new HashMap<>(NewCoreProperties.FLAG_INT_16);
    
                    // 如果规则不为空,则证明规则进行过设置,需要校验规则类是否有修改,规则是否存在校验
                    if (ObjectUtil.isNotEmpty(dragRuleList)) {
                        // 获取注解类名称,判定数据库是否有配置,如果没有则抛出异常
                        for (TbSysDragRule dragRule : dragRuleList) {
                            dragRuleMap.put(dragRule.getDragRuleId(), dragRule);
                            // 获取规则id eg: TESTDragRule0001
                            String dragRuleId = dragRule.getDragRuleId();
                            // 查看beansWithAnnotation中是否有当前处理规则,如果没有则抛出异常信息
                            if (!beansWithAnnotation.containsKey(dragRuleId.concat(RuleConstant.TRAN_SUFFIX))) {
                                throw new ServiceException(StrUtil
                                        .format("数据库表[MANAGE.TB_SYS_DRAG_RULE]中规则[{}]代码已经删除,确定规则是否无用,请手动删除此规则再启动!", dragRuleId));
                            }
                        }
                    }
    
                    // 设置规则入参出参信息到规则缓存中
                    Map<String, Object> dragRuleDTOMap = new HashMap<>(NewCoreProperties.FLAG_INT_16);
                    for (Map.Entry<String, Object> entry : beansWithAnnotation.entrySet()) {
                        Class<?> dragRuleTranClass = entry.getValue().getClass();
                        // 获取process方法
                        Method[] methods = dragRuleTranClass.getMethods();
                        if (ObjectUtil.isEmpty(methods)) {
                            log.error("规则[{}]没有设定方法,请正确设置方法!", dragRuleTranClass.getName());
                            continue;
                        }
                        // methods转换为List并且过滤出process方法
                        Method method = Arrays.stream(methods).filter(inMethod -> {
                            // 进行参数校验,要求参数不能为Object类型
                            if (inMethod.getName().equals("process") && inMethod.getParameterTypes().length == 1) {
                                // 校验参数不是Object类型
                                Class<?> parameterClass = inMethod.getParameterTypes()[0];
                                return parameterClass.getSimpleName().endsWith("DTO");
                            }
                            return false;
                        }).findFirst().orElse(null);
                        if (ObjectUtil.isNull(method)) {
                            log.error("规则[{}]没有设定process方法,请正确设置process方法!", dragRuleTranClass.getName());
                            continue;
                        }
    
                        // 获取方法参数信息
                        Class<?> parameterType = method.getParameterTypes()[0];
                        // 复杂类类型
                        Field[] declaredFields = parameterType.getDeclaredFields();
                        // 入参信息处理
                        JSONObject inputParamJSON = new JSONObject();
                        List<DragRuleDTO.DragRuleParamsDesc> inputParamDescList = new ArrayList<>();
    
                        // 入参自定义信息设置
                        List<DragRuleDTO.DragRuleParamsDesc> inputCustomParamList = new ArrayList<>();
                        for (Field field : declaredFields) {
                            // 获取描述信息
                            DragRuleDesc dragRuleDesc = field.getAnnotation(DragRuleDesc.class);
                            if (ObjectUtil.isNotNull(dragRuleDesc) && StrUtil.isNotBlank(dragRuleDesc.fieldDesc())) {
                                DragRuleDTO.DragRuleParamsDesc fieldDesc = new DragRuleDTO.DragRuleParamsDesc();
                                fieldDesc.setParamDesc(dragRuleDesc.fieldDesc());
                                fieldDesc.setParamType(field.getType().getSimpleName());
                                fieldDesc.setParamName(field.getName());
                                fieldDesc.setSysCode(dragRuleDesc.sysCode());
                                fieldDesc.setIfCustomDefine(dragRuleDesc.ifCustomDefine());
                                // 判断是否为自定义参数
                                if (dragRuleDesc.ifCustomDefine()) {
                                    // 自定义参数类型校验
                                    boolean ifParamTypeAllow = "Date".equals(field.getType().getSimpleName())
                                            || "BigDecimal".equals(field.getType().getSimpleName())
                                            || "Long".equals(field.getType().getSimpleName())
                                            || "Integer".equals(field.getType().getSimpleName())
                                            || "String".equals(field.getType().getSimpleName());
                                    if (!ifParamTypeAllow) {
                                        throw new ServiceException(StrUtil.format("拖拽规则入参DTO[{}]自定义参数[{}]类型非法,合法参数类型为[Date,BigDecimal,Long,Integer,String],请确认!",
                                                parameterType.getSimpleName(),
                                                field.getName()));
                                    }
                                    inputCustomParamList.add(fieldDesc);
                                } else {
                                    inputParamJSON.put(field.getName(), StrUtil.EMPTY);
                                    inputParamDescList.add(fieldDesc);
                                }
                            } else {
                                throw new ServiceException(StrUtil.format("拖拽规则入参DTO[{}]对应属性[{}]未设置@DragRuleDesc的fieldDesc属性",
                                        parameterType.getSimpleName(),
                                        field.getName()));
                            }
                        }
                        // 设置出参信息,首先获取出参内部类
                        String simpleClassName = parameterType.getSimpleName().replace("DTO", "OUTDTO");
                        String className = parameterType.getName();
                        JSONObject outputParamJSON = new JSONObject();
                        List<DragRuleDTO.DragRuleParamsDesc> outputParamDescList = new ArrayList<>();
                        try {
                            Class<?> innerClass = Class.forName(className.concat("$").concat(simpleClassName));
                            outputParamJSON = new JSONObject();
    
                            for (Field field : innerClass.getDeclaredFields()) {
                                DragRuleDesc dragRuleDesc = field.getAnnotation(DragRuleDesc.class);
                                if (ObjectUtil.isNotNull(dragRuleDesc) && StrUtil.isNotBlank(dragRuleDesc.fieldDesc())) {
                                    DragRuleDTO.DragRuleParamsDesc fieldDesc = new DragRuleDTO.DragRuleParamsDesc();
                                    fieldDesc.setParamDesc(dragRuleDesc.fieldDesc());
                                    fieldDesc.setParamType(field.getType().getSimpleName());
                                    fieldDesc.setParamName(field.getName());
                                    fieldDesc.setSysCode(dragRuleDesc.sysCode());
                                    outputParamDescList.add(fieldDesc);
                                } else {
                                    throw new ServiceException(StrUtil.format("拖拽规则出参DTO[{}]对应属性[{}]未设置@DragRuleDesc的fieldDesc属性",
                                            innerClass.getSimpleName(),
                                            field.getName()));
                                }
                                outputParamJSON.put(field.getName(), StrUtil.EMPTY);
                            }
                        } catch (ClassNotFoundException e) {
                            log.info("拖拽规则参数DTO[{}]未定义内部类出参信息,请确认!", method.getName());
                            throw new ServiceException(StrUtil.format("拖拽规则参数DTO[{}]未定义内部类出参信息,请确认!", method.getName()));
                        }
    
                        // 获取bean描述信息
                        String beanDesc = dragRuleTranClass.getAnnotation(DragRuleDesc.class).beanDesc();
                        if (StrUtil.isBlank(beanDesc)) {
                            throw new ServiceException(StrUtil.format("规则类[{}]对应@DragRuleDesc注解beanDesc没有设置", dragRuleTranClass.getName()));
                        }
                        // 封装参数信息,入参出参从反射中获取,其余参数如果数据库查询有值则设定,如果没有则不设定
                        String ruleId = StrUtil.upperFirst(entry.getKey()).replace("Tran", StrUtil.EMPTY);
                        TbSysDragRule dragRule = dragRuleMap.get(ruleId);
                        DragRuleDTO dragRuleDTO;
                        if (ObjectUtil.isNotNull(dragRule)) {
                            // 转换拖拽规则
                            dragRuleDTO = dragRuleConvert.dragRuleToDragRuleDTO(dragRule);
                            // 设置描述信息
                            dragRuleDTO.setRuleDesc(beanDesc);
                            // 是否落库
                            dragRuleDTO.setIfLoadDB(RuleConstant.NUM_1_STR);
                            // 设置crossParam列表以及自定义操作参数列表
                            if (StrUtil.isNotBlank(dragRule.getCrossParam())) {
                                List<String> crossParamList = Arrays.asList(dragRule.getCrossParam().split(StrUtil.COMMA));
                                dragRuleDTO.setCrossParamList(crossParamList);
                                dragRuleDTO.setCrossParam(dragRule.getCrossParam());
                                // 将outParamDescList转换为map,获取crossParamDesc描述信息
                                Map<String, DragRuleDTO.DragRuleParamsDesc> outParamDescMap = outputParamDescList.stream()
                                        .collect(
                                                Collectors.toMap(DragRuleDTO.DragRuleParamsDesc::getParamName,
                                                        Function.identity())
                                        );
                                List<DragRuleDTO.DragRuleParamsDesc> crossParamDescList = new ArrayList<>();
                                crossParamList.forEach(paramName -> {
                                    DragRuleDTO.DragRuleParamsDesc dragRuleParamsDesc = outParamDescMap.get(paramName);
                                    if (ObjectUtil.isNotNull(dragRuleParamsDesc)) {
                                        crossParamDescList.add(dragRuleParamsDesc);
                                    }
                                });
                                dragRuleDTO.setCrossParamDescList(crossParamDescList);
                            }
    
                            // 设置自定义参数信息
                            if (StrUtil.isNotBlank(dragRule.getCustomOptionParam())) {
                                // 将json类型转换为 List<List<DragRuleDTO.CustomOptionParam>> 类型
                                List customOptionParamsList = JSONObject.parseArray(dragRule.getCustomOptionParam(), List.class);
                                List<List<DragRuleDTO.CustomOptionParam>> typeList = new ArrayList<>();
                                for (Object customParamList : customOptionParamsList) {
                                    List<JSONObject> jsonParamList = (List<JSONObject>) customParamList;
                                    List<DragRuleDTO.CustomOptionParam> innerParamList = jsonParamList.stream().map(jsonParam -> {
                                        return JSONObject.parseObject(jsonParam.toJSONString(), DragRuleDTO.CustomOptionParam.class);
                                    }).collect(Collectors.toList());
                                    typeList.add(innerParamList);
                                }
                                dragRuleDTO.setCustomOptionParamList(typeList);
                            }
                        } else {
                            // 新增dragRuleDTO,添加入参出参以及其他通用参数信息
                            dragRuleDTO = new DragRuleDTO();
                            dragRuleDTO.setDragRuleId(ruleId);
                            dragRuleDTO.setRuleDesc(beanDesc);
                            dragRuleDTO.setIfLoadDB(RuleConstant.NUM_0_STR);
                        }
                        // 设置入参出参信息
                        dragRuleDTO.setInputParamDescList(inputParamDescList);
                        dragRuleDTO.setOutputParamDescList(outputParamDescList);
    
                        // 规则自定义入参描述信息
                        dragRuleDTO.setInputCustomParamDescList(inputCustomParamList);
    
                        dragRuleDTO.setInputParam(inputParamJSON.toJSONString());
                        dragRuleDTO.setOutputParam(outputParamJSON.toJSONString());
                        dragRuleDTOMap.put(ruleId, JSONObject.toJSONString(dragRuleDTO));
                    }
                    if (ObjectUtil.isNotEmpty(dragRuleDTOMap)) {
                        // 删除原有规则
                        redisUtil.del(RuleConstant.DRAG_RULES);
                        // 重新加载
                        redisUtil.hmset(RuleConstant.DRAG_RULES, dragRuleDTOMap);
                    }
    
                    // 清除产品规则链缓存
                    redisUtil.del(CommonDragRuleConstant.DRAG_RULE_PRODUCT_CHAIN_FLAG);
    
                    // 选择数据库
                    databaseUtil.chooseDB(DataSourceTypeEnum.MANAGE);
    
                    // 开始初始化产品相关规则链信息到缓存中,缓存key为 uri + ":" + productCd格式
                    List<TbSysProductDragChain> productDragChainList = productDragChainMapper.selectList(null);
                    databaseUtil.clearToDefault();
    
                    if (ObjectUtil.isNotEmpty(productDragChainList)) {
                        Map<String, Object> productMap = productDragChainList.stream()
                                .collect(Collectors.toMap(
                                        product -> product.getUri().concat(StrUtil.COLON).concat(product.getProductCd()),
                                        Function.identity()
                                ));
                        redisUtil.hmset(CommonDragRuleConstant.DRAG_RULE_PRODUCT_CHAIN_FLAG, productMap);
                    }
    
                    // 释放锁
                    simpleDistributeLock.releaseLock(LockTypeEnum.DRAG_RULE_INIT_LOCK, applicationName, lockResult);
                } else {
                    log.error("当前服务拖拽规则引擎服务初始化加锁失败,未执行拖拽规则服务初始化!");
                }
            } catch (Exception e) {
                e.printStackTrace();
                throw new ServiceException(e.getMessage());
            } finally {
                // 释放锁
                simpleDistributeLock.releaseLock(LockTypeEnum.DRAG_RULE_INIT_LOCK, applicationName, lockResult);
            }
        }
    
        /**
         * 自定义检查自定义参数比较规则
         * 规则格式 eg: [(, a, +, b, <, c , &&, c, ==, 2, ), ||, a, <, b, ||, loanType, in, [1,2,3,4] ]
         *
         * @param paramList 参数列表
         * @param index     当前规则索引
         */
        public void checkCustomOptionParam(List<DragRuleDTO.CustomOptionParam> paramList, int index) {
            // 参数list转map信息
            Map<String, DragRuleDTO.CustomOptionParam> paramMap = paramList.stream().collect(
                    Collectors.toMap(DragRuleDTO.CustomOptionParam::getParamName, Function.identity(), (k1, k2) -> k2)
            );
    
            // 自定义比较参数,至少包含一个outDTO参数校验
            DragRuleDTO.CustomOptionParam atLeastOneOutDTOParam = paramList.stream().filter(param ->
                    param.getOptionType().equals(RuleConstant.NUM_1_STR)
            ).findFirst().orElse(null);
            if (ObjectUtil.isNull(atLeastOneOutDTOParam)) {
                throw new ServiceException(StrUtil.format("第[{}]个自定义规则,自定义参数比较规则至少包含一个出参参数,请确认规则信息", index));
            }
    
            // 首先拼装参数信息,然后进行编译,观察编译是否通过
            StringBuilder paramBuilder = new StringBuilder();
            for (int i = 0; i < paramList.size(); i++) {
                DragRuleDTO.CustomOptionParam optionParam = paramList.get(i);
                // 操作标识符非法校验
                if (RuleConstant.NUM_0_STR.equals(optionParam.getOptionType())) {
                    boolean ifOptionValue = isLegalOperator(optionParam.getParamName());
                    if (!ifOptionValue) {
                        throw new ServiceException(StrUtil.format("第[{}]个自定义规则,自定义规则脚本操作符[{}]非法,请确认!",
                                index,
                                optionParam.getParamName()));
                    }
                }
    
                // 操作标识符拼接名称eg : "("
                if (RuleConstant.NUM_0_STR.equals(optionParam.getOptionType())) {
                    paramBuilder.append(optionParam.getParamName()).append(StrUtil.SPACE);
                } else if (RuleConstant.NUM_2_STR.equals(optionParam.getOptionType())) {
                    // 自定义值,拼接value值信息 eg: 2024-03-01
                    paramBuilder.append(optionParam.getValue()).append(StrUtil.SPACE);
                } else {
                    // 其余为outDTO参数信息
                    paramBuilder.append(optionParam.getParamName()).append(StrUtil.SPACE);
                }
            }
    
            // 编译语句
            String compareString = paramBuilder.toString();
    
            // 进行in以及 !in 操作符,字段正确性校验,要求in以及!n 后面的字段必须为码值并且是数组格式[a,b,c,d]
            String[] splitSpace = paramBuilder.toString().split(StrUtil.SPACE);
    
            // 数组前端必须为操作符 in !in校验
            Pattern pattern = Pattern.compile(ARRAY_REGEX);
    
            // 找到 in以及 !in 的索引位置,如果没有in,则判定数组类型数据 ,如果有数组类型数据,则前面必须为 in或者 !in 操作符
            List<Integer> inParamIndexList = new ArrayList<>();
            for (int paramIndex = 0; paramIndex < splitSpace.length; paramIndex++) {
                if ("in".equals(splitSpace[paramIndex]) || "!in".equals(splitSpace[paramIndex])) {
                    inParamIndexList.add(paramIndex);
                    // in !in 不能作为开头以及不能作为结尾校验
                    if (paramIndex == splitSpace.length - 1) {
                        throw new ServiceException(StrUtil.format("第[{}]个自定义规则,in或者!in不能作为表达式结尾,请确认!",
                                index,
                                paramBuilder.toString()));
                    } else if (paramIndex == 0) {
                        throw new ServiceException(StrUtil.format("第[{}]个自定义规则,in或者!in不能作为头信息,请确认!", index));
                    }
                }
    
                // 数组逗号分割数据前面必须为操作标识符 in 或者 !in
                Matcher matcher = pattern.matcher(splitSpace[paramIndex]);
                if (matcher.matches()) {
                    // 第一个参数不能为数组校验
                    if (paramIndex <= 1) {
                        throw new ServiceException(StrUtil.format("第[{}]个自定义规则,数组类型参数不能作为首个参数,请确认!", index));
                    }
    
                    // 前一个参数是否为 in !in校验
                    String paramBeforeArray = splitSpace[paramIndex - 1];
                    if (!"in".equals(paramBeforeArray) && !"!in".equals(paramBeforeArray) ) {
                        throw new ServiceException(StrUtil.format("第[{}]个自定义规则,数组类型参数前面必须为in或者!in操作符,请确认!", index));
                    }
                }
    
            }
    
            // 遍历找到的索引位置,判断是否为数组格式
            if (ObjectUtil.isNotEmpty(inParamIndexList)) {
                inParamIndexList.forEach(inParamIndex -> {
                    String nextParam = splitSpace[inParamIndex + 1];
                    // 编译正则表达式
                    Matcher matcher = pattern.matcher(nextParam);
                    if (!matcher.matches()) {
                        throw new ServiceException(StrUtil.format("第[{}]个自定义规则,in或者!in后面的参数[{}]必须是数组格式,请确认!", index, nextParam));
                    }
    
                    // in !in 前面不能是操作符
                    if (RuleConstant.NUM_0_STR.equals(splitSpace[inParamIndex - 1])) {
                        throw new ServiceException(StrUtil.format("第[{}]个自定义规则,in或者!in前面不能是操作符,请确认!", index));
                    }
                });
                
                // in !in 类型重编译执行语句 eg : a in [1,2,3,4] => [1,2,3,4].indexOf(a)
                for (Integer inParamIndex : inParamIndexList) {
                    // [1,2,3,4]
                    String arrayParam = splitSpace[inParamIndex + 1];
                    // a
                    String paramBeforeIn = splitSpace[inParamIndex - 1];
                    // !in
                    String operator = splitSpace[inParamIndex];
                    // 老参数信息 a in [1,2,3,4]
                    String oldParam = paramBeforeIn.concat(StrUtil.SPACE).concat(operator).concat(StrUtil.SPACE).concat(arrayParam);
                    // 优化后编译信息
                    String newParam = arrayParam
                            .concat(".indexOf( ")
                            .concat(paramBeforeIn)
                            .concat(" )")
                            .concat("!in".equals(operator) ? " < 0" : " >= 0");
    
                    compareString = compareString.replace(oldParam, newParam);
                }
            }
            
            
            // 进行param表达式编译校验,确保表达式正确
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName("JavaScript");
            Compilable compilableEngine = (Compilable) engine;
            try {
                compilableEngine.compile(compareString);
            } catch (ScriptException e) {
                throw new ServiceException(StrUtil.format("第[{}]个自定义规则,自定义规则脚本表达式[{}]非法,请确认!",
                        index,
                        paramBuilder.toString()));
            }
    
            // 提取表达式信息
            List<String> expressions = extractExpressions(paramBuilder.toString());
            if (ObjectUtil.isEmpty(expressions)) {
                return;
            }
    
            // expressions中每一个表达式中,必须包含一个outDTO参数校验
            expressions.forEach(expression -> {
                // 单个表达式信息 eg: conDate < 2025-05-03
                List<String> checkParamNameList = CollUtil.newArrayList(expression.split(StrUtil.SPACE));
                String checkOKParam = checkParamNameList.stream().filter(paramName -> {
                    DragRuleDTO.CustomOptionParam customOptionParam = paramMap.get(paramName);
                    return ObjectUtil.isNotNull(customOptionParam)
                            && RuleConstant.NUM_1_STR.equals(customOptionParam.getOptionType());
                }).findAny().orElse(null);
                if (StrUtil.isBlank(checkOKParam)) {
                    throw new ServiceException(StrUtil.format("第[{}]个自定义规则,自定义规则脚本表达式[{}]中未包含出参参数,请确认!",
                            index, expression));
                }
            });
    
            // 遍历校验表达式信息
            expressions.forEach(expression -> {
                // 确定表达式为什么类型表达式,一共分为三种,日期,数字以及字符串,我们获取首个outDTO参数,然后通过类型进行判定
                List<String> checkParamNameList = CollUtil.newArrayList(expression.split(StrUtil.SPACE));
                String checkOKParam = checkParamNameList.stream().filter(paramName -> {
                    DragRuleDTO.CustomOptionParam customOptionParam = paramMap.get(paramName);
                    return ObjectUtil.isNotNull(customOptionParam)
                            && RuleConstant.NUM_1_STR.equals(customOptionParam.getOptionType());
                }).findFirst().orElse(null);
    
                if (StrUtil.isBlank(checkOKParam)) {
                    throw new ServiceException(StrUtil.format("第[{}]个自定义规则,自定义规则脚本表达式[{}]中未包含出参参数,请确认!",
                            index, expression));
                }
    
                // 获取参数type,确定为什么类型操作表达式
                String paramType = paramMap.get(checkOKParam).getParamType();
    
                // 获取参数其中有日期类型参数,则证明为日期校验,进行参数校验,如果参数为自定义参数,则对表达式进行校验
                if ("Date".equals(paramType)) {
                    // 如果有其他类型数据,则必须为日期以及自定义日期格式类型
                    checkParamNameList.forEach(checkParam -> {
                        // 获取单个自定义参数信息
                        DragRuleDTO.CustomOptionParam customOptionParam = paramMap.get(checkParam);
                        if (ObjectUtil.isNull(customOptionParam)) {
                            customOptionParam = paramList.stream().filter(param -> {
                                if (checkParam.equals(param.getValue())) {
                                    return true;
                                }
                                return false;
                            }).findFirst().orElse(null);
                        }
    
                        // 自定义类型参数,日期格式校验
                        if (RuleConstant.NUM_2_STR.equals(customOptionParam.getOptionType())) {
                            String dateStr = customOptionParam.getValue();
                            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                            if (!"''".equals(dateStr) && StrUtil.isNotBlank(dateStr) && dateStr.length() > 10) {
                                try {
                                    LocalDate.parse(dateStr, formatter);
                                } catch (DateTimeParseException e) {
                                    throw new ServiceException(StrUtil.format("第[{}]个自定义规则,自定义入参参数[{}]需要为日期格式[{}],当前值为[{}],请确认!",
                                            index,
                                            customOptionParam.getParamDesc(),
                                            "yyyy-MM-dd HH:mm:ss",
                                            customOptionParam.getValue()));
                                }
                            } else if (!"''".equals(dateStr) && StrUtil.isNotBlank(dateStr) && dateStr.length() < 10) {
                                formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                                try {
                                    LocalDate.parse(dateStr, formatter);
                                } catch (DateTimeParseException e) {
                                    throw new ServiceException(StrUtil.format("第[{}]个自定义规则,自定义入参参数[{}]需要为日期格式[{}],当前值为[{}],请确认!",
                                            index,
                                            customOptionParam.getValue(),
                                            "yyyy-MM-dd",
                                            customOptionParam.getValue()));
                                }
                            }
                        }
    
                        // 其他类型参数校验,类型为outDTO参数信息,并且不是Date类型
                        if (RuleConstant.NUM_1_STR.equals(customOptionParam.getOptionType()) && !"Date".equals(customOptionParam.getParamType())) {
                            throw new ServiceException(StrUtil.format("第[{}]个自定义规则,自定义日期类型规则参数[{}]需为日期类型,请确认!",
                                    index,
                                    customOptionParam.getParamDesc()));
                        }
                    });
                }
    
                // 数字类型校验
                boolean ifTypeNum = "BigDecimal".equals(paramType)
                                || "Long".equals(paramType)
                                || "Integer".equals(paramType)
                                || "Double".equals(paramType);
    
                if (ifTypeNum) {
                    // 校验类型必须为数字类型,并且自定义参数格式也进行校验
                    checkParamNameList.forEach(checkParam -> {
                        // 获取单个自定义参数信息
                        DragRuleDTO.CustomOptionParam customOptionParam = paramMap.get(checkParam);
                        if (ObjectUtil.isNull(customOptionParam)) {
                            customOptionParam = paramList.stream().filter(param -> {
                                if (checkParam.equals(param.getValue())) {
                                    return true;
                                }
                                return false;
                            }).findFirst().orElse(null);
                        }
                        // 参数类型自定义,类型必须为数字类型校验
                        if (RuleConstant.NUM_2_STR.equals(customOptionParam.getOptionType())) {
                            String numStr = customOptionParam.getValue();
                            if (!"''".equals(numStr) && !NumberUtil.isNumber(numStr)) {
                                throw new ServiceException(StrUtil.format("第[{}]个自定义规则,自定义数字类型规则参数[{}]需要为数字类型,当前值为[{}],请确认!",
                                        index,
                                        customOptionParam.getValue(),
                                        customOptionParam.getValue()));
                            }
                        }
    
                        // outVO参数校验,其参数类型必须为数字类型
                        if (RuleConstant.NUM_1_STR.equals(customOptionParam.getOptionType())) {
                            boolean ifNum = "BigDecimal".equals(customOptionParam.getParamType())
                                    || "Long".equals(customOptionParam.getParamType())
                                    || "Integer".equals(customOptionParam.getParamType())
                                    || "Double".equals(customOptionParam.getParamType());
                            if (!ifNum) {
                                throw new ServiceException(StrUtil.format("第[{}]个自定义规则,自定义数字类型规则参数[{}]需为数字类型,请确认!",
                                        index,
                                        customOptionParam.getParamDesc()));
                            }
                        }
                    });
                }
    
                // 字符串类型校验
                if ("String".equals(paramType)) {
                    List<DragRuleDTO.CustomOptionParam> strParamList = checkParamNameList.stream().filter(checkParamName -> {
                        DragRuleDTO.CustomOptionParam customOptionParam = paramMap.get(checkParamName);
                        if (ObjectUtil.isNotNull(customOptionParam)) {
                            return "String".equals(customOptionParam.getParamType());
                        } else {
                            return false;
                        }
                    }).map(
                            paramMap::get
                    ).collect(Collectors.toList());
    
                    // 字符串校验,比较语句一般为 a == b ,a != b,其中参数不能为多个,outDTO中,字符串类型个数不能超过2个
                    if (strParamList.size() > 2) {
                        throw new ServiceException("第[{}]个自定义规则,自定义字符串类型规则参数比较条件最多只能为2个,请确认!");
                    }
                    // 字符串类型校验,操作符只能为括号以及等于,不等于,其余不可以
                    checkParamNameList.forEach(checkParam -> {
                        DragRuleDTO.CustomOptionParam customOptionParam = paramMap.get(checkParam);
                        if (ObjectUtil.isNull(customOptionParam)) {
                            customOptionParam = paramList.stream().filter(param -> {
                                if (checkParam.equals(param.getValue())) {
                                    return true;
                                }
                                return false;
                            }).findFirst().orElse(null);
                        }
                        if (RuleConstant.NUM_1_STR.equals(customOptionParam.getOptionType())
                                && !"String".equals(customOptionParam.getParamType())) {
                            throw new ServiceException(StrUtil.format("第[{}]个自定义规则,自定义字符串类型规则参数[{}]需为字符串类型,请确认!",
                                    index,
                                    customOptionParam.getParamDesc()));
                        }
                    });
                }
            });
        }
    
        /**
         * 是否合法操作符
         * @param operator 操作符
         */
        public boolean isLegalOperator(String operator) {
            return ">=".equals(operator)
                    || ">".equals(operator)
                    || "<=".equals(operator)
                    || "<".equals(operator)
                    || "==".equals(operator)
                    || "!=".equals(operator)
                    || "&&".equals(operator)
                    || ")".equals(operator)
                    || "(".equals(operator)
                    || "+".equals(operator)
                    || "-".equals(operator)
                    || "*".equals(operator)
                    || "/".equals(operator)
                    || "!in".equals(operator)
                    || "in".equals(operator)
                    || "||".equals(operator);
        }
    
        /**
         * 提取表达式
         *
         * @param script
         * @return
         */
        public List<String> extractExpressions(String script) {
            // 匹配最内层的括号表达式
            String[] expressionsArray = script.split("\\s*(?:(&&)|\\|\\|)\\s*");
            List<String> expressionList = new ArrayList<>();
            if (ObjectUtil.isNotEmpty(expressionsArray)) {
                expressionList = CollUtil.newArrayList(expressionsArray);
    //            expressionList = CollUtil.newArrayList(expressionsArray).stream().map(exp -> {
    //                String replaceExp = exp.replace("(", StrUtil.EMPTY).replace(")", StrUtil.EMPTY);
    //                if (replaceExp.startsWith(StrUtil.SPACE)) {
    //                    replaceExp = replaceExp.replaceFirst(StrUtil.SPACE, StrUtil.EMPTY);
    //                }
    //                // 去除最后一个空格
    //                if (replaceExp.endsWith(StrUtil.SPACE)) {
    //                    replaceExp = replaceExp.substring(0, replaceExp.length() - 1);
    //                }
    //
    //                // 如果字符串包含多个连贯的空格,则统一替换为单个空格
    //                replaceExp = replaceExp.replaceAll("\\s+", StrUtil.SPACE);
    //
    //                return replaceExp;
    //            }).collect(Collectors.toList());
            }
            return expressionList;
        }
    
        /**
         * 自定义检查拖拽规则选项比较
         *
         * @param rspJsonObject 响应参数信息
         * @param paramList 自定义参数信息 eg: [(, a, +, b, <, c , &&, c, ==, 2, ), ||, a, <, b ]
         * @param dragRuleId 规则名称
         * @param typeCdValueKeyMap 码值信息map 以typeCd+"_"+codeCd[值] 为key  CodeName 为Value
         * @return
         */
        public void checkOptionCompare(JSONObject rspJsonObject,
                                       List<DragRuleDTO.CustomOptionParam> paramList,
                                       String dragRuleId,
                                       Map<String, String> typeCdValueKeyMap,
                                       String dragSign) {
            // 非空校验
            if (ObjectUtil.isEmpty(paramList)) {
                return;
            }
    
            // 开始进行调用参数拼接操作
            StringBuilder checkParamStr = new StringBuilder();
            StringBuilder checkParamMessage = new StringBuilder();
    
            // 循环单个参数信息,进行数据拼接
            paramList.forEach(param -> {
                // 如果是操作标识符,则直接拼接即可
                if (RuleConstant.NUM_0_STR.equals(param.getOptionType())) {
                    checkParamStr.append(param.getParamName()).append(StrUtil.SPACE);
                    if ("||".equals(param.getParamName())) {
                        checkParamMessage.append("或者").append(StrUtil.SPACE);
                    } else if ("&&".equals(param.getParamName())) {
                        checkParamMessage.append("并且").append(StrUtil.SPACE);
                    } else if ("==".equals(param.getParamName())) {
                        checkParamMessage.append("等于").append(StrUtil.SPACE);
                    } else if (">".equals(param.getParamName())) {
                        checkParamMessage.append("大于").append(StrUtil.SPACE);
                    } else if (">=".equals(param.getParamName())) {
                        checkParamMessage.append("大于等于").append(StrUtil.SPACE);
                    } else if ("<".equals(param.getParamName())) {
                        checkParamMessage.append("小于").append(StrUtil.SPACE);
                    } else if ("<=".equals(param.getParamName())) {
                        checkParamMessage.append("小于等于").append(StrUtil.SPACE);
                    } else if ("!=".equals(param.getParamName())) {
                        checkParamMessage.append("不等于").append(StrUtil.SPACE);
                    } else if ("in".equals(param.getParamName())) {
                        checkParamMessage.append("属于").append(StrUtil.SPACE);
                    } else if ("!in".equals(param.getParamName())) {
                        checkParamMessage.append("不属于").append(StrUtil.SPACE);
                    } else {
                        checkParamMessage.append(param.getParamDesc()).append(StrUtil.SPACE);
                    }
                }
                // 获取码值信息
                String sysCodeName = "";
                // outDTO参数,进行类型判定
                if (RuleConstant.NUM_1_STR.equals(param.getOptionType())) {
                    // 获取响应参数
                    Object rspObject = rspJsonObject.get(param.getParamName());
                    /**
                     * 暂时注释空值校验
                    if (ObjectUtil.isNull(rspObject)) {
                        throw new ServiceException(StrUtil.format("规则[{}],自定义规则响应参数[{}:{}]获取为空,规则校验失败!",
                                dragRuleId,
                                param.getParamDesc(),
                                param.getParamName()));
                    }
                     **/
                    if (ObjectUtil.isNull(rspObject)) {
                        rspObject = "''";
                    }
    
                    boolean isNumberType = "BigDecimal".equals(param.getParamType())
                            || "Long".equals(param.getParamType())
                            || "Integer".equals(param.getParamType())
                            || "Double".equals(param.getParamType());
                    if (isNumberType) {
                        // 通过rspJSON信息获取响应参数信息
                        String rspValue = StrUtil.toString(rspObject);
                        if (StrUtil.isNotBlank(param.getSysCode())) {
                            sysCodeName = typeCdValueKeyMap.get(param.getSysCode().concat(StrUtil.UNDERLINE).concat(rspValue));
                        }
                        checkParamStr.append(rspValue).append(StrUtil.SPACE);
                        checkParamMessage.append(param.getParamDesc())
                                .append("[")
                                .append(StrUtil.isBlank(sysCodeName) ? ("''".equals(rspValue) ? "空" : rspValue) : sysCodeName)
                                .append("]")
                                .append(StrUtil.SPACE);
                    } else if ("String".equals(param.getParamType())) {
                        // 通过rspJSON信息获取响应参数信息
                        String rspValue = StrUtil.toString(rspObject);
                        if (StrUtil.isNotBlank(param.getSysCode())) {
                            sysCodeName = typeCdValueKeyMap.get(param.getSysCode().concat(StrUtil.UNDERLINE).concat(rspValue));
                        }
                        // 字符串类型,则添加 '' 进行表示为字符串类型
                        checkParamStr.append("'").append(rspValue).append("'").append(StrUtil.SPACE);
                        checkParamMessage.append(param.getParamDesc())
                                .append("[")
                                .append(StrUtil.isBlank(sysCodeName) ? rspValue : sysCodeName)
                                .append("]")
                                .append(StrUtil.SPACE);
                    } else if ("Date".equals(param.getParamType())) {
                        // 通过rspJSON信息获取响应参数信息
                        long rspValue = rspJsonObject.getLongValue(param.getParamName());
    
                        // date类型,转换为yyyy-MM-dd类型
                        Date rspDate = DateUtil.date(rspValue);
                        String formatDate = DateUtil.format(rspDate, DatePattern.NORM_DATE_PATTERN);
    
                        // 日期类型则全部转换为数字类型,进行数字比较
                        checkParamStr.append(rspValue).append(StrUtil.SPACE);
                        checkParamMessage.append(param.getParamDesc()).append("[").append(formatDate).append("]").append(StrUtil.SPACE);
                    }
                }
    
                // 进行自定义参数类型字段拼接
                if (RuleConstant.NUM_2_STR.equals(param.getOptionType())) {
                    // 通过自定义值信息获取码值名称
                    String customValue = param.getValue();
                    // 如果是码值,则需要转换提示信息
                    if (ObjectUtil.isNotNull(param.getSysCode())) {
                        if (StrUtil.isNotBlank(customValue) && StrUtil.isNotBlank(customValue)) {
                            // 如果value值为 ['1', '2'] 这种形式,需要进行循环解析操作
                            if (customValue.startsWith("[") && customValue.endsWith("]") && customValue.length() > 2) {
                                // value 修改为数组格式为 1 , 2
                                String[] codeCds = customValue
                                        .replace("[", "")
                                        .replace("]", "")
                                        .replace("'", "")
                                        .split(StrUtil.COMMA);
                                // 拼接码值,拼接结果eg: 新增贷款,重组贷款,续贷贷款
                                for (int index = 0; index < codeCds.length; index++) {
                                    String codeCd = codeCds[index];
                                    String codeCdName = typeCdValueKeyMap.get(param.getSysCode().concat(StrUtil.UNDERLINE).concat(codeCd));
                                    sysCodeName = sysCodeName.concat(StrUtil.isBlank(codeCdName) ? codeCd : codeCdName);
                                    // 尾部加逗号
                                    if (index != codeCds.length - 1) {
                                        sysCodeName = sysCodeName.concat(StrUtil.COMMA);
                                    }
                                }
                            } else {
                                // 普通模式
                                sysCodeName = typeCdValueKeyMap.get(param.getSysCode().concat(StrUtil.UNDERLINE).concat(param.getValue()));
                            }
                        }
                    }
                    // 如果为自定义数字类型,则直接拼接即可
                    if (NumberUtil.isNumber(customValue)) {
                        checkParamStr.append(customValue).append(StrUtil.SPACE);
                        checkParamMessage.append(StrUtil.isNotBlank(sysCodeName) ? sysCodeName : customValue).append(StrUtil.SPACE);
                    } else {
                        // 不是数字类型,则为字符串类型,字符串类型则进行是否日期类型校验,如果为日期类型,则转换为数字类型比较
                        LocalDate localDate = null;
                        if (!"''".equals(customValue) && customValue.split(StrUtil.DASHED, -1).length == 3) {
                            DateTimeFormatter formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd HH:mm:ss");
                            if ((StrUtil.isNotBlank(customValue) && customValue.length() <= 10)) {
                                formatter = DateTimeFormatter.ofPattern("yyyy-MM-dd");
                            }
                            try {
                                localDate = LocalDate.parse(customValue, formatter);
                            } catch (DateTimeParseException ignored) {
                            }
    
                            // 如果是日期类型,则转换为数字类型进行比较,否则直接拼接即可
                            if (ObjectUtil.isNotNull(localDate)) {
                                // 日期自定义格式,转换为数字类型
                                long timeMillis = localDate.atStartOfDay().toInstant(ZoneOffset.of("+8")).toEpochMilli();
                                checkParamStr.append(timeMillis).append(StrUtil.SPACE);
                                checkParamMessage.append(customValue).append(StrUtil.SPACE);
                            } else {
                                checkParamStr.append("'").append(customValue).append("'").append(StrUtil.SPACE);
                                checkParamMessage.append(StrUtil.isBlank(sysCodeName) ? customValue : sysCodeName).append(StrUtil.SPACE);
                            }
                        } else {
                            checkParamStr.append(customValue).append(StrUtil.SPACE);
                            // 提示信息优化
                            customValue = "''".equals(customValue) ? "空" : customValue;
                            checkParamMessage.append(StrUtil.isBlank(sysCodeName) ? customValue : sysCodeName).append(StrUtil.SPACE);
                        }
                    }
                }
            });
    
            // 数字类型比较,创建ScriptEngine对象,使用eval表达式进行比较
            ScriptEngineManager manager = new ScriptEngineManager();
            ScriptEngine engine = manager.getEngineByName(RuleConstant.JAVA_SCRIPT);
            try {
                // 实际执行表达式语句
                String executeStr = checkParamStr.toString();
    
                // 进行['1','2','3']此类参数转义 eg: loanType in ['1','2','3'] => ['1','2','3'].indexOf(loanType) >= 0
                if (executeStr.contains("['") && executeStr.contains("']")) {
                    // 通过空格分割成数组,后续转义使用
                    String[] splitSpace = executeStr.split(StrUtil.SPACE);
                    List<Integer> inParamIndexList = new ArrayList<>();
                    for (int paramIndex = 0; paramIndex < splitSpace.length; paramIndex++) {
                        if ("in".equals(splitSpace[paramIndex]) || "!in".equals(splitSpace[paramIndex])) {
                            inParamIndexList.add(paramIndex);
                        }
                    }
    
                    // in !in 类型重编译执行语句 eg : a in [1,2,3,4] => [1,2,3,4].indexOf(a)
                    for (Integer inParamIndex : inParamIndexList) {
                        // [1,2,3,4]
                        String arrayParam = splitSpace[inParamIndex + 1];
                        // a
                        String paramBeforeIn = splitSpace[inParamIndex - 1];
                        // !in
                        String operator = splitSpace[inParamIndex];
                        // 老参数信息 a in [1,2,3,4]
                        String oldParam = paramBeforeIn.concat(StrUtil.SPACE).concat(operator).concat(StrUtil.SPACE).concat(arrayParam);
                        // 优化后编译信息
                        String newParam = arrayParam
                                .concat(".indexOf( ")
                                .concat(paramBeforeIn)
                                .concat(" )")
                                .concat("!in".equals(operator) ? " < 0" : " >= 0");
    
                        executeStr = executeStr.replace(oldParam, newParam);
                    }
                }
    
                // 开始执行eval函数解析表达式
                log.info("X_sign[{}]执行规则脚本 => [{}]", dragSign, executeStr);
                log.info("执行规则展示信息 => [{}]", checkParamMessage);
                if (engine.eval(executeStr) == Boolean.FALSE) {
                    String errorMessage = StrUtil.format("规则[{}]执行结果为\n[{}]\n,规则表达式校验失败,请确认!",
                                    dragRuleId,
                                    checkParamMessage.toString());
                    throw new ServiceException(errorMessage);
                }
            } catch (ScriptException e) {
                String errorMessage = LogUtil.getStackTraceInfo(e);
                log.error(errorMessage);
                e.printStackTrace();
                throw new ServiceException("规则开始执行eval函数解析表达式异常,异常信息为 : ".concat(errorMessage));
            }
        }
    
        /**
         * 获取缓存拖拽规则信息
         * @param dragRuleId
         * @return
         */
        public DragRuleDTO getCacheDragRule(String dragRuleId) {
            // 返回信息
            DragRuleDTO dragRule = null;
            // 通过缓存获取拖拽信息
            Object dragRuleJsonObject = redisUtil.hget(RuleConstant.DRAG_RULES, dragRuleId);
            if (ObjectUtil.isNotNull(dragRuleJsonObject)) {
                // 转换json为对象信息
                dragRule = JSONObject.parseObject(StrUtil.toString(dragRuleJsonObject), DragRuleDTO.class);
            }
            return dragRule;
        }
    
        /**
         * 获取缓存拖拽规则信息列表
         * @param dragRuleIdList
         * @return
         */
        public List<DragRuleDTO> getCacheDragRuleList(List<Object> dragRuleIdList) {
            // 空值判定
            if (ObjectUtil.isEmpty(dragRuleIdList)) {
                return null;
            }
            // 返回参数信息
            List<DragRuleDTO> dragRuleDTOList = new ArrayList<>();
            // 通过缓存获取拖拽信息
            List<Object> dragListObject = redisUtil.hmget(RuleConstant.DRAG_RULES, dragRuleIdList);
            if (ObjectUtil.isNotNull(dragListObject)) {
                for (Object dragObject : dragListObject) {
                    // 转换json为对象信息
                    DragRuleDTO dragRule = JSONObject.parseObject(StrUtil.toString(dragObject), DragRuleDTO.class);
                    dragRuleDTOList.add(dragRule);
                }
            }
            return dragRuleDTOList;
        }
    
        /**
         * 检查拖拽规则更新信息,一个规则修改,需要检查所有引入当前规则的规则链是否参数都合法
         * @param dragRuleChain 规则链
         * @param dragRuleDTO 当前修改规则
         */
        public void checkDragRuleUpdateLegal(TbSysDragChain dragRuleChain, DragRuleDTO dragRuleDTO) {
            // 获取规则链数组,首先使用 | 进行分割
            String[] dragRuleChainArray = dragRuleChain.getDragRuleChain().split("\\|");
    
            // 循环进行规则信息校验
            for (String dragRuleChainStr : dragRuleChainArray) {
                // 获取规则链,多个规则使用逗号分割,然后封装成list
                List<String> dragRuleChainList = Arrays.asList(dragRuleChainStr.split(StrUtil.COMMA).clone())
                        .stream()
                        .map(String::trim)
                        .collect(Collectors.toList());
    
                // 通过uri获取规则链入参缓存信息,cacheDragChainDTO
                Object dragRuleChainObj = redisUtil.hget(CommonDragRuleConstant.DRAG_RULE_CHAIN_FLAG, dragRuleChain.getUri());
                if (ObjectUtil.isNull(dragRuleChainObj)) {
                    throw new ServiceException(StrUtil.format("通过规则链uri[{}]获取缓存规则链信息失败,请确认!", dragRuleChain.getUri()));
                }
                DragChainDTO cacheDragChainDTO = JSONObject.parseObject(StrUtil.toString(dragRuleChainObj), DragChainDTO.class);
    
                // 如果规则链长度为1,则只有一个规则既当前规则,无需校验,如果修改规则在最后一个,没有下一个规则使用传递参数,则也无需校验
                if (dragRuleChainList.size() == 1 || dragRuleChainList.indexOf(dragRuleDTO.getDragRuleId()) == dragRuleChainList.size() - 1) {
                    return;
                }
    
                // 开始规则链校验,既当前修改规则在规则链的中间位置执行,需要完整校验规则链参数返回值是否合法
                String errorMessage = null;
                JSONObject dragChainInParam = JSONObject.parseObject(cacheDragChainDTO.getMethodInParam());
                if (ObjectUtil.isEmpty(dragChainInParam)) {
                    errorMessage = StrUtil.format("拖拽规则修改,校验拖拽规则规则链[{}]控制层方法切入点[{}]入参参数为空,请确认!",
                            cacheDragChainDTO.getControllerName(),
                            cacheDragChainDTO.getMethodName());
                    throw new ServiceException(errorMessage);
                }
    
                // 入参结果集,第一次入参参数为规则链切入点的入参参数,之后循环规则链规则,为规则链返回传递参数+切入点入参参数,再与下一个规则入参进行对比
                JSONObject resultInParam = dragChainInParam.clone();
                for (int i = 0; i < dragRuleChainList.size(); i++) {
                    String dragRuleId = dragRuleChainList.get(i);
                    DragRuleDTO checkDragRuleDTO = null;
                    if (!dragRuleDTO.getDragRuleId().equals(dragRuleId)) {
                        checkDragRuleDTO = getCacheDragRule(dragRuleId);
                    } else {
                        checkDragRuleDTO = dragRuleDTO;
                    }
                    JSONObject dragRuleInParam = JSONObject.parseObject(checkDragRuleDTO.getInputParam());
                    // 进行参数校验,如果入参不满足规则链循环规则入参,直接抛出异常
                    Set<String> dragRuleInParamKeySet = dragRuleInParam.keySet();
                    Set<String> dragChainInParamKeySet = resultInParam.keySet();
                    Set<String> diffKeySet = dragRuleInParamKeySet.stream()
                            .filter(element -> !dragChainInParamKeySet.contains(element))
                            .collect(Collectors.toSet());
                    if (ObjectUtil.isNotEmpty(diffKeySet)) {
                        // Set集合数据转换为字符串,逗号分割
                        String dragRuleDiffKeyStr = diffKeySet.stream()
                                .map(String::valueOf)
                                .collect(Collectors.joining(StrUtil.COMMA));
                        errorMessage = StrUtil.format("规则修改后,影响规则链[{}]第[{}]个规则[{}]入参参数缺少[{}],修改失败!",
                                cacheDragChainDTO.getDragRuleChainId(),
                                i + 1,
                                checkDragRuleDTO.getDragRuleId(),
                                dragRuleDiffKeyStr);
                        throw new ServiceException(errorMessage);
                    }
    
                    // 设置规则返回参数作为传递参数设定到切入点入参参数中,作为下一入参参数校验集合,传递参数为字符串,多个参数以逗号分割
                    if (StrUtil.isNotBlank(checkDragRuleDTO.getCrossParam())) {
                        String[] crossParams = checkDragRuleDTO.getCrossParam().split(StrUtil.COMMA);
                        for (int j = 0; j < crossParams.length; j++) {
                            resultInParam.put(crossParams[j], StrUtil.EMPTY);
                        }
                    }
                }
            }
        }
    
        /**
         * 通过规则id获取规则信息
         * @param dragRuleId
         */
        public DragRuleDTO getDragRuleDTO(String dragRuleId) {
            Object dragRuleObj = redisUtil.hget(CommonDragRuleConstant.DRAG_RULES, dragRuleId);
            if (ObjectUtil.isNotNull(dragRuleObj)) {
                return JSONObject.parseObject(StrUtil.toString(dragRuleObj), DragRuleDTO.class);
            }
            throw new ServiceException(StrUtil.format("拖拽规则通过缓存获取规则Id[{}]不存在,请确认规则服务成功启动!", dragRuleId));
        }
    
        /**
         * 英文模块描述转换为中文描述
         * @param modelEnDesc
         */
        public String modelEnDescToCHDesc(String modelEnDesc) {
            // 空值校验
            if (StrUtil.isBlank(modelEnDesc)) {
                return null;
            }
    
            // 将模块描述进行小写转换
            String enDescLowerCase = modelEnDesc.toLowerCase();
            if (enDescLowerCase.contains("account")) {
                return "客户模块";
            } else if (enDescLowerCase.contains("collateral")) {
                return "押品模块";
            } else if (enDescLowerCase.contains("risk")) {
                return "风险模块";
            } else if (enDescLowerCase.contains("credit")) {
                return "评级授信模块";
            } else if (enDescLowerCase.contains("loan")) {
                return "用信放款模块";
            } else if (enDescLowerCase.contains("management")) {
                return "公共管理模块";
            } else if (enDescLowerCase.contains("afterloan")) {
                return "贷后模块";
            } else if (enDescLowerCase.contains("warning")) {
                return "风险预警模块";
            } else if (enDescLowerCase.contains("query")) {
                return "综合查询模块";
            } else if (enDescLowerCase.contains("foreign")) {
                return "外围服务模块";
            } else {
                return null;
            }
        }
    
        /**
         * 校验规则链节点类型
         *
         * @param nodeTypeName
         */
        public Boolean checkProcessFileType(String nodeTypeName) {
            // 空值校验
            if (StrUtil.isBlank(nodeTypeName)) {
                return false;
            }
    
            // 当前校验节点类型 task,start,end,sequence
            switch (nodeTypeName) {
                case "SequenceFlowImpl":
                case "TaskImpl":
                case "EndEventImpl":
                case "StartEventImpl":
                case "ExclusiveGatewayImpl" :
                    return true;
                default:
                    return false;
            }
        }
    
        /**
         * 获取所有路由
         * @param startEvent 开始节点
         */
        public List<List<FlowNode>> findRoutes(StartEvent startEvent) {
            List<List<FlowNode>> allRoutes = new ArrayList<>();
            findRoutesRecursive(startEvent, new ArrayList<>(), allRoutes);
            return allRoutes;
        }
    
        /**
         * 递归获取所有路由
         * @param currentNode 当前节点
         * @param currentPath 路径
         * @param allRoutes 路由集合
         */
        public void findRoutesRecursive(FlowNode currentNode, List<FlowNode> currentPath, List<List<FlowNode>> allRoutes) {
            // 如果当前节点是任务节点,将其添加到路径中
            if ("task".equals(currentNode.getElementType().getTypeName())) {
                currentPath.add(currentNode);
            }
    
            // 获取当前节点的所有出序列流
            Object[] outgoingFlows = currentNode.getOutgoing().toArray();
    
            // 如果没有出序列流,说明到达了一个终点
            if (ObjectUtil.isEmpty(outgoingFlows)) {
                allRoutes.add(new ArrayList<>(currentPath));
            } else {
                // 遍历所有出序列流
                for (Object flowObj : outgoingFlows) {
                    SequenceFlow flow = (SequenceFlow) flowObj;
                    FlowNode nextNode = flow.getTarget();
                    findRoutesRecursive(nextNode, currentPath, allRoutes);
                }
            }
    
            // 回溯
            currentPath.remove(currentNode);
        }
    }
    
  5. 规则具体类通用父类

    package cn.git.rules.drag.tran.base;
    
    import cn.git.common.result.Result;
    
    import javax.validation.Valid;
    
    /**
     * @description: 拖拽规则执行方法基类
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-22 10:33:09
     */
    public interface BaseDragRuleTran<T> {
    
        /**
         * 决策执行方法
         * @param requestParam 请求参数
         * @return
         */
        Result process(@Valid T requestParam);
    
    }
    
    
  6. 规则详情实例,注意规则详情名称命名要求必须此格式,controller中可以调用开发人员使用获取拖拽规则规则名称方法获取

    package cn.git.rules.drag.tran.demo;
    
    import cn.git.common.result.Result;
    import cn.git.rules.drag.DragRuleDesc;
    import cn.git.rules.drag.dto.TES0001DTO;
    import cn.git.rules.drag.tran.BaseDragRuleTran;
    import cn.hutool.core.date.DateUtil;
    import org.springframework.stereotype.Component;
    import org.springframework.validation.annotation.Validated;
    
    import javax.validation.Valid;
    import java.math.BigDecimal;
    
    /**
     * @description:
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-22 10:13:27
     */
    @Validated
    @Component
    @DragRuleDesc(beanDesc = "借据申请日期金额校验")
    public class TES0001Tran implements BaseDragRuleTran<TES0001DTO> {
    
        /**
         * 决策执行方法
         *
         * @param requestParam 请求参数
         * @return
         */
        @Override
        public Result<TES0001DTO.TES0001OUTDTO> process(@Valid TES0001DTO requestParam) {
            // 响应参数
            TES0001DTO.TES0001OUTDTO outDTO = new TES0001DTO.TES0001OUTDTO();
            outDTO.setLoanType("2");
            outDTO.setLoanShape("1");
            System.out.println(requestParam.getDragSign());
            outDTO.setBorrowAmt(new BigDecimal(500));
            outDTO.setContractDate(DateUtil.parseDate("2020-11-5"));
            outDTO.setContractTotalAmt(BigDecimal.valueOf(50000));
            outDTO.setCheckDragRule0001ResultDate(DateUtil.parseDate("2020-11-5"));
            return Result.ok(outDTO);
        }
    }
    
    
  7. 参数信息,注意参数名称命名要求必须此格式

    package cn.git.rules.drag.dto;
    
    import cn.git.rules.drag.DragRuleDesc;
    import lombok.AllArgsConstructor;
    import lombok.Data;
    import lombok.NoArgsConstructor;
    
    import javax.validation.constraints.NotBlank;
    import java.math.BigDecimal;
    import java.util.Date;
    
    /**
     * @description: 拖拽规则测试方法入参出参信息
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-22 10:31:37
     */
    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public class TES0001DTO extends DragBaseDTO {
    
        @NotBlank(message = "TESTDragRule0001DTO规则入参客户编号不能为空!")
        @DragRuleDesc(fieldDesc = "客户编号")
        private String customerNum;
    
        @NotBlank(message = "TESTDragRule0001DTO规则入参合同编号不能为空!")
        @DragRuleDesc(fieldDesc = "合同编号")
        private String contractNum;
    
        @DragRuleDesc(fieldDesc = "是否绿色企业", sysCode = "YesornoInd")
        private String ifGreen;
    
        @DragRuleDesc(fieldDesc = "贷款形式", sysCode = "LoanFormCd", ifCustomDefine = true)
        private String loanShape;
    
        @DragRuleDesc(fieldDesc = "贷款发放时间", ifCustomDefine = true)
        private Date contractStartDate;
    
        /**
         * @description: 出参信息
         * @program: bank-credit-sy
         * @author: lixuchun
         * @create: 2024-02-22 10:31:25
         */
        @Data
        @NoArgsConstructor
        @AllArgsConstructor
        public static class TES0001OUTDTO {
    
            @DragRuleDesc(fieldDesc = "合同金额")
            private BigDecimal contractTotalAmt;
    
            @DragRuleDesc(fieldDesc = "借据金额")
            private BigDecimal borrowAmt;
    
            @DragRuleDesc(fieldDesc = "合同日期")
            private Date contractDate;
    
            @DragRuleDesc(fieldDesc = "测试响应日期")
            private Date checkDragRule0001ResultDate;
    
            @DragRuleDesc(fieldDesc = "贷款类型", sysCode = "LoanTypeCd")
            private String loanType;
    
            @DragRuleDesc(fieldDesc = "贷款形式", sysCode = "LoanFormCd")
            private String loanShape;
    
        }
    
    }
    
    
  8. 通用的枚举类型

    package cn.git.rules.drag.enums;
    
    import lombok.Getter;
    
    /**
     * @description: 拖拽规则链模块枚举
     * @program: bank_credit_sy
     * @author: lixuchun
     * @create: 2024_03_07 10:59:00
     */
    @Getter
    public enum DragChainModulesEnum {
    
        /**
         * 子模块数据源信息
         */
        MANAGE("MANAGEMENT_SERVER", "通用管理模块"),
        ACCOUNT("ACCOUNT_SERVER", "客户信息模块"),
        LOAN("LOAN_SERVER", "用信放款模块"),
        WORKFLOW("WORKFLOW_SERVER", "流程模块"),
        COLLATERAL("COLLATERAL_SERVER", "押品模块"),
        AFTER_LOAN("AFTERLOAN_SERVER", "贷后模块"),
        QUERY("QUERY_SERVER", "综合查询模块"),
        CREDIT("CREDIT_SERVER", "评级授信模块"),
        RISK("RISK_SERVER", "风险模块"),
        WARNING("WARNING_SERVER", "预警模块");
    
        /**
         * 拖拽规则链服务名称
         */
        @Getter
        private String dragServerName;
    
        /**
         * 服务描述
         */
        @Getter
        private String serverDesc;
    
        /**
         * 构造函数
         * @param dragServerName
         * @param serverDesc
         */
        DragChainModulesEnum(String dragServerName, String serverDesc) {
            this.dragServerName = dragServerName;
            this.serverDesc = serverDesc;
        }
    
    }
    
    
    package cn.git.rules.drag.enums;
    
    import lombok.Getter;
    
    /**
     * @description: 拖拽规则序列信息生成枚举类型
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-06-03
     */
    @Getter
    public enum DragRuleSequenceEnum {
    
        /**
         * 序列模块分类描述信息
         */
        ACCOUNT("account-server", "ACC","客户模块"),
        AFTER("afterloan-server", "AFT", "贷后模块"),
        COLL("collateral-server", "COL", "押品模块"),
        CREDIT("credit-server", "CRE", "授信模块"),
        LOAN("loan-server", "LOA", "用信模块"),
        MANAGE("management-server", "MAN", "公共模块"),
        RAT("rat-server", "RAT", "新评级模块"),
        STORE("store-server", "STO", "储备模块"),
        WARNING("warning-server", "WAR", "预警模块"),
        PRODUCT("product-server", "PRO", "产品工厂模块")
    
        ;
    
        /**
         * 模块
         */
        private String module;
    
        /**
         * 模块别名
         */
        private String moduleAlias;
    
        /**
         * 描述
         */
        private String desc;
    
        DragRuleSequenceEnum(String module, String moduleAlias, String desc) {
            this.module = module;
            this.moduleAlias = moduleAlias;
            this.desc = desc;
        }
    
    }
    
    
4.2.2 规则链规则feign调用实现
  1. Feign接口调用impl实现类
    可以实现常用规则以及并行规则调用,显著加速规则执行速度、
    package cn.git.rules.manage;
    
    import cn.git.cache.api.BaseCacheApi;
    import cn.git.common.drag.CommonDragRuleConstant;
    import cn.git.common.drag.dto.DragChainDTO;
    import cn.git.common.drag.dto.DragRuleDTO;
    import cn.git.common.exception.ServiceException;
    import cn.git.common.result.Result;
    import cn.git.common.util.DragRequestContext;
    import cn.git.common.util.LogUtil;
    import cn.git.rules.drag.DragRuleServerUtil;
    import cn.git.rules.drag.tran.BaseDragRuleTran;
    import cn.git.rules.feign.DragRuleFeignApi;
    import cn.git.rules.thread.DragRuleThreadPool;
    import cn.hutool.core.collection.CollUtil;
    import cn.hutool.core.date.DateUtil;
    import cn.hutool.core.util.ObjectUtil;
    import cn.hutool.core.util.StrUtil;
    import com.alibaba.fastjson.JSON;
    import com.alibaba.fastjson.JSONObject;
    import lombok.extern.slf4j.Slf4j;
    import org.springframework.beans.factory.annotation.Autowired;
    import org.springframework.context.ApplicationContext;
    import org.springframework.web.bind.annotation.RestController;
    import org.springframework.web.context.request.RequestAttributes;
    import org.springframework.web.context.request.RequestContextHolder;
    
    import javax.validation.ConstraintViolationException;
    import java.util.*;
    import java.util.concurrent.CompletableFuture;
    import java.util.function.Function;
    import java.util.stream.Collectors;
    
    /**
     * @description:
     * @program: bank-credit-sy
     * @author: lixuchun
     * @create: 2024-02-22 02:32:24
     */
    @Slf4j
    @RestController
    public class DragRuleFeignApiImpl implements DragRuleFeignApi {
    
        /**
         * 拖拽规则参数入参出参dto包名
         */
        private static final String DRAG_DTO_PACKAGE = "cn.git.rules.drag.dto.";
    
        /**
         * dto尾缀
         */
        public static final String SUFFIX_DTO = "DTO";
    
        @Autowired
        private DragRuleServerUtil dragRuleServerUtil;
    
        @Autowired
        private ApplicationContext applicationContext;
    
        @Autowired
        private BaseCacheApi baseCacheApi;
    
        /**
         * 校验拖拽规则feign方法
         *
         * @param dragRuleJsonParam 请求对象
         * @return Result 校验结果
         */
        @Override
        public Result checkDragRule(JSONObject dragRuleJsonParam) {
            // 获取X_sign参数
            String dragSign = dragRuleJsonParam.getString("dragSign");
            String dragToken = dragRuleJsonParam.getString("dragToken");
    
            // 获取规则链表 多个规则用逗号分割
            String dragRuleDTOJSON = dragRuleJsonParam.getString(CommonDragRuleConstant.DRAG_RULE_CHAIN_DTO);
            DragChainDTO dragChainDTO = JSON.parseObject(dragRuleDTOJSON, DragChainDTO.class);
    
            // 获取规则链信息,使用竖线进行分割,规则链格式: dragRuleId1,dragRuleId2,dragRuleId3|dragRuleId4,dragRuleId5
            String[] dragRuleChainArray = dragChainDTO.getDragRuleChain().split("\\|");
    
            // 将竖线分割规则链再次使用逗号分割,获取所有规则id信息
            List<Object> allDragRuleList = Arrays.stream(dragRuleChainArray).map(
                    dragRuleChainStr -> dragRuleChainStr.split(StrUtil.COMMA)
            ).flatMap(Arrays::stream).collect(Collectors.toList());
    
            // 设置入参json的克隆参数
            JSONObject dragRuleJsonParamClone = dragRuleJsonParam.clone();
    
            // 通过规则id,获取全部规则详情信息,并且设置map信息,方便后续方法使用
            List<DragRuleDTO> allCacheDragRuleList = dragRuleServerUtil.getCacheDragRuleList(allDragRuleList);
            Map<String, DragRuleDTO> cacheDragRuleDTOMap = allCacheDragRuleList.stream().collect(Collectors.toMap(
                    DragRuleDTO::getDragRuleId, Function.identity(), (k1, k2) -> k1)
            );
    
            // 获取规则中码值列表
            List<String> sysCodeTypeList = allCacheDragRuleList.stream()
                    .flatMap(dragRuleList ->
                            Optional.ofNullable(dragRuleList.getCustomOptionParamList()).orElse(Collections.emptyList()).stream()
                    )
                    .flatMap(Collection::stream)
                    .map(DragRuleDTO.CustomOptionParam::getSysCode)
                    .filter(
                            // 过滤掉 getSysCode 为 null 的情况
                            Objects::nonNull
                    )
                    .filter(
                            // 过滤掉空字符串
                            StrUtil::isNotBlank
                    )
                    .collect(Collectors.toList());
    
            // 获取全部带码值的参数码值名称信息
            Map<String, String> typeCdValueKeyMap = baseCacheApi.getTypeCdValueKeyMap(sysCodeTypeList);
    
            // 如果规则拆分为1个,则直接执行单条规则,如果拆分出多个,则需要并行执行规则信息
            if (dragRuleChainArray.length == 1) {
                return executeDragRule(dragRuleChainArray[0],
                        cacheDragRuleDTOMap,
                        typeCdValueKeyMap,
                        dragRuleJsonParamClone,
                        dragSign);
            } else {
                // 提取请求上下文信息
                RequestAttributes requestAttributes = RequestContextHolder.getRequestAttributes();
                // 创建多个 CompletableFuture 任务
                List<CompletableFuture<Result>> futures = CollUtil.newArrayList(dragRuleChainArray).stream()
                    .map(dragRuleChain -> CompletableFuture.supplyAsync(() -> {
                        try {
                            // 在异步任务中恢复请求上下文,保证非web请求,请求request上下文信息不完整报错
                            if (requestAttributes != null) {
                                RequestContextHolder.setRequestAttributes(requestAttributes);
                            }
    
                            // 设置请求上下文信息,用于处理异步线程请求头丢失问题
                            DragRequestContext.DragTokenInfo dragTokenInfo = new DragRequestContext.DragTokenInfo();
                            dragTokenInfo.setSign(dragSign);
                            dragTokenInfo.setToken(dragToken);
                            DragRequestContext.setInfo(dragTokenInfo);
    
                            // 设置请求上下文信息
                            return executeDragRule(dragRuleChain,
                                    cacheDragRuleDTOMap,
                                    typeCdValueKeyMap,
                                    dragRuleJsonParamClone,
                                    dragSign);
                        } finally {
                            // 清除请求上下文信息
                            DragRequestContext.clear();
                            // 清除请求上下文
                            if (requestAttributes != null) {
                                RequestContextHolder.resetRequestAttributes();
                            }
                        }
                    },
                    DragRuleThreadPool.DRAG_RULE_THREAD_POOL)
                    // 异常处理
                    .exceptionally(exception -> {
                        String errorMessage = StrUtil.format("X_sign[{}],异常信息[{}]", dragSign,
                                LogUtil.getStackTraceInfo(new Exception(exception)));
                        log.error(errorMessage);
                        // 返回封装异常信息
                        return Result.error(StrUtil.format("并行规则exceptionally异常处理,执行 X_sign为[{}]", dragSign));
                    }))
                .collect(Collectors.toList());
    
                // 创建一个CompletableFuture,用于等待所有任务完成
                CompletableFuture<Void> allFutures = CompletableFuture.allOf(futures.toArray(new CompletableFuture[0]));
    
                // 阻塞等待所有任务完成
                try {
                    allFutures.join();
                } catch (Exception e) {
                    e.printStackTrace();
                    String errorMessage = StrUtil.format("并行规则join异常处理,x_sign[{}],具体信息为[{}]",
                            dragSign, LogUtil.getStackTraceInfo(e));
                    log.error(errorMessage);
                    return Result.error(StrUtil.format("并行规则join异常处理,执行X_sign为[{}]", dragSign));
                }
    
                // 获取所有任务的结果
                List<Result> executeResultList = futures.stream()
                        .map(CompletableFuture::join)
                        // 过滤掉因异常返回的
                        .filter(Objects::nonNull)
                        .collect(Collectors.toList());
    
                // 结果分析,找到error规则则返回错误规则提示信息,没有错误则表示所有规则正常通过
                return executeResultList.stream().filter(result -> {
                    return result.getCode() != 1;
                }).findFirst().orElseGet(() -> Result.ok("拖拽规则规则验证通过"));
            }
        }
    
        /**
         * 执行规则
         *
         * @param dragRuleChain 规则链
         * @param cacheDragRuleDTOMap 规则缓存
         * @param typeCdValueKeyMap 类型编码与值对应关系
         * @param dragRuleJsonParamClone 规则入参
         * @return
         */
        public Result executeDragRule(String dragRuleChain,
                                      Map<String, DragRuleDTO> cacheDragRuleDTOMap,
                                      Map<String, String> typeCdValueKeyMap,
                                      JSONObject dragRuleJsonParamClone,
                                      String dragSign) {
            // 获取规则链中的规则id集合
            List<String> dragRuleIdList = CollUtil.newArrayList(dragRuleChain.split(StrUtil.COMMA));
            // 循环执行规则链的规则信息
            for (String dragRuleId : dragRuleIdList) {
                DragRuleDTO dragRule = cacheDragRuleDTOMap.get(dragRuleId);
                String dragRuleName = dragRule.getDragRuleId();
                // 通过规则名称获取规则信息,然后获取规则入参自定义参数,加入规则入参参数中
                List<DragRuleDTO.DragRuleParamsDesc> inputCustomParamDescList = dragRule.getInputCustomParamDescList();
                if (ObjectUtil.isNotEmpty(inputCustomParamDescList)) {
                    // 过滤自未定义入参参数信息
                    List<DragRuleDTO.DragRuleParamsDesc> filterParamList = inputCustomParamDescList.stream().filter(inputCustomParam -> {
                        return StrUtil.isNotBlank(inputCustomParam.getCustomDefineParamValue());
                    }).collect(Collectors.toList());
                    // 通过参数类型进行参数赋值
                    if (ObjectUtil.isNotEmpty(filterParamList)) {
                        for (DragRuleDTO.DragRuleParamsDesc inputCustomParam : filterParamList) {
                            // 日期类型,将日期参数由yyyy-MM-dd格式修改为 currentTimeMillis格式,类型设置为Long类型
                            if ("Date".equals(inputCustomParam.getParamType())) {
                                Long dateTimeMillis = DateUtil.parse(inputCustomParam.getCustomDefineParamValue()).getTime();
                                dragRuleJsonParamClone.put(inputCustomParam.getParamName(), dateTimeMillis);
                            } else if ("BigDecimal".equals(inputCustomParam.getParamType())) {
                                // 数字类型 BigDecimal
                                dragRuleJsonParamClone.put(inputCustomParam.getParamName(), Integer.valueOf(inputCustomParam.getCustomDefineParamValue()));
                            } else if ("Long".equals(inputCustomParam.getParamType())) {
                                // 数字类型 Long
                                dragRuleJsonParamClone.put(inputCustomParam.getParamName(), Long.valueOf(inputCustomParam.getCustomDefineParamValue()));
                            } else if ("Integer".equals(inputCustomParam.getParamType())) {
                                // 数字类型 Integer
                                dragRuleJsonParamClone.put(inputCustomParam.getParamName(), Integer.valueOf(inputCustomParam.getCustomDefineParamValue()));
                            } else if ("String".equals(inputCustomParam.getParamType())) {
                                dragRuleJsonParamClone.put(inputCustomParam.getParamName(), inputCustomParam.getCustomDefineParamValue());
                            } else {
                                // 类型校验不同通过
                                return Result.error(StrUtil.format("规则[{}]自定义参数[{}]类型校验不通过,请确认是否正确!", dragRuleName, inputCustomParam.getParamName()));
                            }
                        }
                    }
                }
    
                // 规则入参参数转换为dto对象
                String fullClassName = DRAG_DTO_PACKAGE.concat(dragRuleName).concat(SUFFIX_DTO);
    
                Object reqObject;
                try {
                    // 通过类名获取类类型,再获取参数实例信息
                    Class reqClazz = Class.forName(fullClassName);
                    reqObject = JSONObject.parseObject(dragRuleJsonParamClone.toJSONString(), reqClazz);
                } catch (ClassNotFoundException e) {
                    return Result.error(StrUtil
                            .format("拖拽规则[{}]规则校验名称DTO[{}]解析失败,请确认是否正确!", dragRuleName, fullClassName));
                }
    
                // 获取规则执行类
                BaseDragRuleTran dragRuleTran;
                try {
                    dragRuleTran = applicationContext.getBean(dragRuleName.concat("Tran"), BaseDragRuleTran.class);
                } catch (Exception e) {
                    String errorMessage = StrUtil.format("拖拽规则[{}]获取规则执行类Tran失败,请确认参数是否正确!", dragRuleName);
                    return Result.error(errorMessage);
                }
    
                // 开始执行规则
                Result result = null;
                try {
                    result = dragRuleTran.process(reqObject);
                } catch (ConstraintViolationException e) {
                    e.printStackTrace();
                    String errorMessage = StrUtil
                            .format("拖拽规则[{}]请求参数校验异常,异常信息为[{}]",
                                    dragRuleName,
                                    LogUtil.getStackTraceInfo(e));
                    log.error(errorMessage);
                    return Result.error(e.getMessage());
                } catch (ServiceException e) {
                    e.printStackTrace();
                    // 抛出自定义异常信息
                    log.error("拖拽规则自定义异常信息[{}], x_sign为[{}]", e.getMessage(), dragSign);
                    String errorMessage = StrUtil.format("拖拽规则[{}]请求执行异常,异常信息为[{}]", dragRuleName, e.getMessage());
                    return Result.error(errorMessage);
                } catch (Exception e) {
                    e.printStackTrace();
                    // 其他异常信息
                    String errorMessage = LogUtil.getStackTraceInfo(e);
                    String dragSignMessage = StrUtil
                            .format("拖拽规则[{}]请求执行异常,x_sign[{}],异常信息为[{}]", dragRuleName, dragSign, errorMessage);
                    log.error(dragSignMessage);
                    return Result.error(dragSignMessage);
                }
    
                // 检查是否有传递参数,如果有则将传递参数设置为下一规则的入参
                if (StrUtil.isNotBlank(dragRule.getCrossParam())) {
                    String[] crossParams = dragRule.getCrossParam().split(StrUtil.COMMA);
                    for (String crossParam : crossParams) {
                        // 获取返回参数中传输数据信息
                        JSONObject rspJsonObject = JSON.parseObject(JSONObject.toJSONString(result.getData()));
                        if (rspJsonObject.containsKey(crossParam)) {
                            dragRuleJsonParamClone.put(crossParam, rspJsonObject.get(crossParam));
                        } else {
                            return Result.error(StrUtil
                                    .format("拖拽规则[{}]从响应参数中获取传递参数获取为空,请确认[{}]传递参数!", dragRuleName, crossParam));
                        }
                    }
                }
    
                // 自定义比较方法,执行自定义比较逻辑
                List<List<DragRuleDTO.CustomOptionParam>> customOptionParamsList = dragRule.getCustomOptionParamList();
                if (ObjectUtil.isNotEmpty(customOptionParamsList)) {
                    // 获取返回参数中传输数据信息
                    JSONObject rspJsonObject = JSON.parseObject(JSONObject.toJSONString(result.getData()));
                    if (ObjectUtil.isNull(rspJsonObject)) {
                        return Result.error(StrUtil.format("拖拽规则[{}]"));
                    }
                    // 参数进行转化
                    for (List<DragRuleDTO.CustomOptionParam> paramList : customOptionParamsList) {
                        // 进行自定义操作参数比对,可以比对日期与数值与字符串
                        try {
                            dragRuleServerUtil.checkOptionCompare(rspJsonObject, paramList, dragRuleName, typeCdValueKeyMap, dragSign);
                        } catch (ServiceException e) {
                            return Result.error(e.getMessage());
                        } catch (Exception e) {
                            e.printStackTrace();
                            String errorMessage = LogUtil.getStackTraceInfo(e);
                            log.error(StrUtil.format("规则[{}]自定义操作参数比对异常,异常信息为[{}]", dragRuleName, errorMessage));
                            return Result.error(StrUtil.format("自定义拖拽规则执行异常,x_sign为[{}]", dragSign));
                        }
                    }
                }
            }
            return Result.ok("拖拽规则规则验证通过");
        }
    }
    
    

4.3 数据库表结构

主要包含三张表,分别为拖拽规则表,规则链表以及产品规则链表

--拖拽规则表
DROP TABLE TB_SYS_DRAG_RULE
CREATE TABLE TB_SYS_DRAG_RULE (
	DRAG_RULE_ID varchar(32) NOT NULL,
	RULE_DESC varchar(100),
	CROSS_PARAM varchar(200),
	CUSTOM_OPTION_PARAM varchar(3000),
	ctime date DEFAULT sysdate,
	mtime date DEFAULT sysdate,
	is_del varchar(1) DEFAULT 0,
	primary key(DRAG_RULE_ID)
)

COMMENT ON TABLE MANAGE.TB_SYS_DRAG_RULE IS '拖拽规则表';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_RULE.DRAG_RULE_ID IS '规则id,名称,同组件名称eg:LOANTest0001';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_RULE.RULE_DESC IS '规则描述';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_RULE.CROSS_PARAM IS '多个规则规则链传递值,多个以英文逗号分割';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_RULE.CUSTOM_OPTION_PARAM IS '自定义参数比较规则信息,多个用逗号隔开, 响应信息返回值 loanAmt<= 5000, eg:{"optionDesc": "描述信息", "customKey": "loanAmt","optionFlag": ">=","compareValue": 5000}';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_RULE.CTIME IS '创建时间';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_RULE.MTIME IS '修改时间';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_RULE.IS_DEL IS '是否删除标识';

--规则链表
DROP TABLE TB_SYS_DRAG_CHAIN
CREATE TABLE TB_SYS_DRAG_CHAIN (
	DRAG_RULE_CHAIN_ID varchar(200),
	CONTROLLER_DESC varchar(300),
	CONTROLLER_NAME varchar(100),
	METHOD_NAME varchar(100),
	METHOD_DESC varchar(500),
	METHOD_IN_PARAM varchar(3000),
	URI varchar(500),
	DRAG_RULE_CHAIN varchar(500),
	CHAIN_FILE clob,
	CHAIN_FILE_NAME varchar(50),
	ctime date DEFAULT sysdate,
	mtime date DEFAULT sysdate,
	is_del varchar(1) DEFAULT 0,
	primary key(DRAG_RULE_CHAIN_ID)
)

COMMENT ON TABLE MANAGE.TB_SYS_DRAG_CHAIN IS '拖拽规则链表';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_CHAIN.DRAG_RULE_CHAIN_ID IS '规则链id';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_CHAIN.CONTROLLER_DESC IS '切入点controller描述信息';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_CHAIN.CONTROLLER_NAME IS '切入点controller名称';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_CHAIN.METHOD_NAME IS '规则链调用方法名称';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_CHAIN.METHOD_DESC IS '规则链调用方法描述';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_CHAIN.METHOD_IN_PARAM IS '方法入参参数';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_CHAIN.URI IS '作用方法地址';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_CHAIN.DRAG_RULE_CHAIN IS '规则链详情,多个规则按序逗号分割';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_CHAIN.CHAIN_FILE IS '文件内容base64字符串信息';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_CHAIN.CHAIN_FILE_NAME IS '文件名称';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_CHAIN.CTIME IS '创建时间';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_CHAIN.MTIME IS '修改时间';
COMMENT ON COLUMN MANAGE.TB_SYS_DRAG_CHAIN.IS_DEL IS '是否删除';

--产品规则信息
DROP TABLE TB_SYS_PRODUCT_DRAG_CHAIN
CREATE TABLE TB_SYS_PRODUCT_DRAG_CHAIN (
	PRODUCT_CHAIN_ID varchar(200),
	PRODUCT_CD varchar(50),
	URI varchar(500),
	DRAG_RULE_CHAIN varchar(500),
	CHAIN_FILE clob,
	CHAIN_FILE_NAME varchar(50),
	ctime date DEFAULT sysdate,
	mtime date DEFAULT sysdate,
	is_del varchar(1) DEFAULT 0,
	primary key(PRODUCT_CHAIN_ID)
)

COMMENT ON TABLE MANAGE.TB_SYS_PRODUCT_DRAG_CHAIN IS '拖拽规则作用产品定义信息表';
COMMENT ON COLUMN MANAGE.TB_SYS_PRODUCT_DRAG_CHAIN.PRODUCT_CHAIN_ID IS '主键id';
COMMENT ON COLUMN MANAGE.TB_SYS_PRODUCT_DRAG_CHAIN.PRODUCT_CD IS '产品cd';
COMMENT ON COLUMN MANAGE.TB_SYS_PRODUCT_DRAG_CHAIN.URI IS '产品作用点请求路径';
COMMENT ON COLUMN MANAGE.TB_SYS_PRODUCT_DRAG_CHAIN.DRAG_RULE_CHAIN IS '规则链,具体规则信息,多个使用逗号分割';
COMMENT ON COLUMN MANAGE.TB_SYS_PRODUCT_DRAG_CHAIN.CHAIN_FILE IS '规则文件';
COMMENT ON COLUMN MANAGE.TB_SYS_PRODUCT_DRAG_CHAIN.CHAIN_FILE_NAME IS '规则文件名称';
COMMENT ON COLUMN MANAGE.TB_SYS_PRODUCT_DRAG_CHAIN.CTIME IS '创建时间';
COMMENT ON COLUMN MANAGE.TB_SYS_PRODUCT_DRAG_CHAIN.MTIME IS '修改时间';
COMMENT ON COLUMN MANAGE.TB_SYS_PRODUCT_DRAG_CHAIN.IS_DEL IS '删除标识';

5. 管理页面展示

5.1 规则管理page页面介绍

此页面为规则分页查询,注意操作栏落库按钮代表此规则是否存入数据库,未存入的不能进行后续规则链操作,已经入库的可以进行修改以及清空

管理页面可以观察当前所有规则信息,需要使用管理员账号登录 310907 ,管理页面位置位于 系统管理 => 参数管理 => 规则管理
在这里插入图片描述

5.2 落库修改新增操作页面介绍

规则校验部分,主要是针对出参参数信息,可以对出参信息进行自定义校验,需要注意需要满足正常操作符规则

  • 入参参数:入参参数即为规则自定义的入参参数信息,此例为 TES0001DTO 参数信息

  • 自定义入参:自定义入参参数可以多选,将会逗号分割送入Tran规则类中,设定码值前端将反显码值信息

  • 出参参数:出参参数即为规则自定义的出参参数信息,此例为 TES0001OUTDTO 出参参数信息

  • 中转参数:中转参数,必须为OUTDTO中的参数,此值没有类型要求,会传递到下一规则中,供下一规则使用

    注意:如果规则中的中转参数下一规则入参使用,那么在规则链固定好后,此中转参数不能随意删除,必须先解除规则链,再删除中转参数

  • 自定义校验参数:此参数必须为OUTDTO中的数字类型参数,或者为日期类型参数。日期以及数字类型参数会有校验,如果为码值下拉则可以进行多选

    设置常用比较格式如下所列:

    表达式案例解释
    ( 合同金额 + 借据金额 ) / 2 < 合同金额 / 2000复杂数字计算,包含参数比较,设置自定义参数值
    合同金额 < 50000简单数字类型比较
    合同日期 < 合同到期日期日期类型参数比较
    合同日期 < 2023-05-03日期类型自定义参数比较
    (贷款类型 == ‘新增’ && 合同金额 > 500) || 借据金额 == ‘’与或运算,空值判定

    在这里插入图片描述

5.3 规则链列表页面

此页面为规则调用入口列表页面,位于 系统管理 => 参数管理 => 规则链管理,管理了可以进行规则调用的入口接口列表页面,同样如果规则链的操作栏配置落库后,才能进行修改删除操作,未配置落库则不能进行操作

在这里插入图片描述

5.4 拖拽规则页面

此页面可以进行规则链详情配置,展示信息包含两部分

  • 首先是规则链基本信息,包含id信息(controller.method)以及规则链描述信息,还有规则链入参信息
    在这里插入图片描述

  • 第二部分为规则拖拽部分,可以进行规则链拖拽选择,可以自己定义规则执行逻辑,双击规则可以进行规则转换
    在这里插入图片描述
    也可以设置并行规则,并行规则如下所示将会分成三个规则链,并且三个规则链(TES0001,TES0002|TES0003|TES0004)将会并发执行,最后全部通过则算校验通过,否则从多个结果中选择一个错误结果弹出。
    在这里插入图片描述

  • 双击后展示规则选择页面,主要可以观察当前规则链包含哪些规则信息,并且选择规则时候,会提示当前规则调用入口(controller.method)方法是否满足当前规则,如果不满足条件,会提示出为何不满足
    在这里插入图片描述

  • 在下面的规则列表中可以选择规则信息,点击规则id,可以进行规则信息查看,展示信息与规则管理页面相同
    在这里插入图片描述

  • 最终保存的规则链信息如下:规则调用会按照顺序执行 TESTDragRule0001 -> TESTDragRule0002 -> TESTDragRule0003 -> TESTDragRule0004
    在这里插入图片描述

注意:上图的并行规则则最终保存效果则是 TES0001,TES0002|TES0003|TES0004 多个规则链用竖线分割,表示三个并行规则

5.5 产品规则

产品规则位于 系统管理 => 参数管理 => 业务品种管理 嵌套的tab页面中,代码部分与常有规则链一致,可以对固定产品进行规则配置,但是有以下前提

  • 产品规则对应作用接口需要配置好默认的执行规则
  • 产品规则接口中必须要有productCd参数,以便于规则判定时获取产品对应的规则链,然后执行对应的规则链,在调用过程中,如果接口inVO带有productCd参数,并且对应规则配置了响应的productCd产品对应规则链,则执行产品规则链,未匹配到productCd,则执行默认规则链
5.5.1 产品规则列表
  • 按照标注按钮可以找到对应的产品规则链
    在这里插入图片描述
  • 点击查看按钮,进入到当前规则链详情信息,如果产品规则未配置,则可以进行产品规则新增,如果已经配置,则产品规则展示规则id,多个id逗号分割,并且可以进行修改以及删除操作
    在这里插入图片描述
  • 点击修改或者新增按钮,进入到规则链编辑页面,与之前规则链编辑页面一致,编辑保存会有规则是否合法校验,校验通过则可以进行规则链调用
    在这里插入图片描述
    产品规则同样可以设置并行规则,如下所示:
    在这里插入图片描述
5.5.2 编辑规则页面

此页面展示当前规则链可以选择哪些规则进行设置,并且提示规则是否合法,如果不合法则显示具体哪个规则对应哪个字段不满足规则入参条件
在这里插入图片描述

6. 测试部分

规则链配置成功之后,直接在规则链入口对应的controller.method进行调用即可 DragRuleTestController.test0001
在这里插入图片描述
执行结果展示如下:
在这里插入图片描述

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值