【RuleUtil】适用于全业务场景的规则匹配快速开发工具

一、RuleUtil 开发背景

1.1 越来越多,越来越复杂的业务规则

1、规则的应用场景多
2、规则配置的参数类型多(ID、数值、文本、日期等等)
3、规则的参数条件多(大于、小于、等于、包含、不包含、区间等等)
4、规则的结构复杂(父规则、子规则、子场景、与或、优先级、命中数量限制)

1.2 目前规则匹配实现方式,以及对应痛点

在这里插入图片描述

现有实现方式使用了许多 if-else 对匹配参数和条件运算符进行判断,结构复杂,可读性较差

痛点一:规则条件组合方式多样,普通实现代码结构容易混乱;
痛点二:同一规则在多个应用场景的复用性较差;
痛点三:每次需求迭代都要改动核心匹配逻辑,效率低,测试成本高。

二、RuleUtil 如何解决痛点

2.1 核心思想

1、通过设计 Rule 结构和四个字段类型,对业务规则进行高度抽象和统一,提高代码可复用性

2、研发仅需实现从业务规则到 Rule 的转换方法,以及定义匹配参数结构即可,无需再编写复杂的匹配逻辑,交给 match 方法通通搞定

RuleUtil 核心规则类(Rule)

/**
 * 核心规则类
 */
public class RuleV2 {
    // 主规则id
    private String id;
    // 子规则id
    private String subId;
    // 规则参数
    private Object param;
    // 规则字段
    private List<Field> fields;
 
    public static class Field {
        // 字段名
        private String name;
        // 字段键
        private String key;
        // 字段类型
        private RuleFieldTypeEnum type;
        // 配置条件
        private RuleFieldConditionEnum condition;
        // 字段值
        private Object value;
    }
}

RuleUtil 核心匹配方法(match)

/**
 * 规则工具类V2
 */
@Slf4j
public class RuleUtilV2 {
 
    /**
     * 规则匹配
     *
     * @param param 待匹配参数
     * @param rules 待匹配规则池
     * @return 命中规则池
     */
    public static List<RuleV2> match(Object param, List<RuleV2> rules) {
        return match(param, rules, rules.size());
    }
 
    /**
     * 规则匹配
     *
     * @param param      待匹配参数
     * @param rules      待匹配规则池
     * @param matchLimit 命中规则数量限制
     * @return 命中规则池
     */
    public static List<RuleV2> match(Object param, List<RuleV2> rules, Integer matchLimit) {
        List<RuleV2> matchRules = new ArrayList<>();
        if (Objects.isNull(param) || CollUtil.isEmpty(rules) || Objects.isNull(matchLimit) || matchLimit <= NumberConst.ZERO) {
            return matchRules;
        }
        for (RuleV2 rule : rules) {
            if (CollUtil.isEmpty(rule.getFields())) {
                continue;
            }
            boolean isMatch = true;
            for (RuleV2.Field field : rule.getFields()) {
                if (StrUtil.isBlank(field.getName()) || Objects.isNull(field.getType()) || Objects.isNull(field.getCondition()) || Objects.isNull(field.getValue())) {
                    isMatch = false;
                    log.info("RuleUtil::match 规则匹配存在空字段 规则id:{} 规则字段:{}", rule.getId(), field);
                    break;
                }
                // 根据规则字段名获取参数字段值
                Object paramFieldValueObj = ReflectUtil.getFieldValue(param, field.getName());
                // 获取规则字段值
                Object ruleFieldValueObj = field.getValue();
                if (RuleFieldTypeEnum.NORMAL.equals(field.getType())) {
                    // 普通属性
                    Set<String> paramFieldValues = collConvertToSet(paramFieldValueObj);
                    Set<String> ruleFieldValues = collConvertToSet(ruleFieldValueObj);
                    if (Set.of(RuleFieldConditionEnum.LT, RuleFieldConditionEnum.LE, RuleFieldConditionEnum.GT, RuleFieldConditionEnum.GE).contains(field.getCondition())) {
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.EQ.equals(field.getCondition()) && !Objects.equals(paramFieldValues, ruleFieldValues)) {
                        // 等于
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.NE.equals(field.getCondition()) && Objects.equals(paramFieldValues, ruleFieldValues)) {
                        // 不等于
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.IN.equals(field.getCondition()) && !CollUtil.containsAny(paramFieldValues, ruleFieldValues)) {
                        // 包含
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.NIN.equals(field.getCondition()) && CollUtil.containsAny(paramFieldValues, ruleFieldValues)) {
                        // 不包含
                        isMatch = false;
                        break;
                    }
                }
                if (RuleFieldTypeEnum.NUMBER.equals(field.getType())) {
                    // 数值属性
                    if (Set.of(RuleFieldConditionEnum.IN, RuleFieldConditionEnum.NIN).contains(field.getCondition())) {
                        Set<String> paramFieldValues = collConvertToSet(paramFieldValueObj);
                        Set<String> ruleFieldValues = collConvertToSet(ruleFieldValueObj);
                        if (RuleFieldConditionEnum.IN.equals(field.getCondition()) && !CollUtil.containsAny(paramFieldValues, ruleFieldValues)) {
                            // 包含
                            isMatch = false;
                            break;
                        }
                        if (RuleFieldConditionEnum.NIN.equals(field.getCondition()) && CollUtil.containsAny(paramFieldValues, ruleFieldValues)) {
                            // 不包含
                            isMatch = false;
                            break;
                        }
                        continue;
                    }
                    String paramFieldValue = String.valueOf(paramFieldValueObj);
                    if (!NumberUtil.isNumber(paramFieldValue) || !NumberUtil.isNumber(String.valueOf(ruleFieldValueObj))) {
                        isMatch = false;
                        log.info("RuleUtil::match 规则匹配存在非数值字段 规则id:{} 规则字段值:{} 参数字段值:{}", rule.getId(), field.getValue(), paramFieldValue);
                        break;
                    }
                    double paramFieldDouble = NumberUtil.parseDouble(paramFieldValue);
                    double ruleFieldDouble = NumberUtil.parseDouble(String.valueOf(ruleFieldValueObj));
                    // 数值属性
                    if (RuleFieldConditionEnum.EQ.equals(field.getCondition()) && paramFieldDouble != ruleFieldDouble) {
                        // 等于
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.NE.equals(field.getCondition()) && paramFieldDouble == ruleFieldDouble) {
                        // 不等于
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.LT.equals(field.getCondition()) && paramFieldDouble >= ruleFieldDouble) {
                        // 小于
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.LE.equals(field.getCondition()) && paramFieldDouble > ruleFieldDouble) {
                        // 小于等于
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.GT.equals(field.getCondition()) && paramFieldDouble <= ruleFieldDouble) {
                        // 大于
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.GE.equals(field.getCondition()) && paramFieldDouble < ruleFieldDouble) {
                        // 大于等于
                        isMatch = false;
                        break;
                    }
                }
                if (RuleFieldTypeEnum.NORMAL_KV.equals(field.getType()) && StrUtil.isNotBlank(field.getKey())) {
                    // 普通键值对属性
                    if (Set.of(RuleFieldConditionEnum.LT, RuleFieldConditionEnum.LE, RuleFieldConditionEnum.GT, RuleFieldConditionEnum.GE).contains(field.getCondition())) {
                        isMatch = false;
                        break;
                    }
                    Set<String> paramFieldValues = mapConvertToSet(paramFieldValueObj, field.getKey());
                    Set<String> ruleFieldValues = collConvertToSet(ruleFieldValueObj);
                    if (RuleFieldConditionEnum.EQ.equals(field.getCondition()) && !Objects.equals(paramFieldValues, ruleFieldValues)) {
                        // 等于
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.NE.equals(field.getCondition()) && Objects.equals(paramFieldValues, ruleFieldValues)) {
                        // 不等于
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.IN.equals(field.getCondition()) && !CollUtil.containsAny(paramFieldValues, ruleFieldValues)) {
                        // 包含
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.NIN.equals(field.getCondition()) && CollUtil.containsAny(paramFieldValues, ruleFieldValues)) {
                        // 不包含
                        isMatch = false;
                        break;
                    }
                }
                if (RuleFieldTypeEnum.NUMBER_KV.equals(field.getType()) && StrUtil.isNotBlank(field.getKey())) {
                    // 数值键值对属性
                    if (Set.of(RuleFieldConditionEnum.IN, RuleFieldConditionEnum.NIN).contains(field.getCondition())) {
                        Set<Double> paramFieldDoubles = mapConvertToSet(paramFieldValueObj, field.getKey()).stream().filter(NumberUtil::isNumber).map(NumberUtil::parseDouble).collect(Collectors.toSet());
                        Set<Double> ruleFieldDoubles = collConvertToSet(ruleFieldValueObj).stream().filter(NumberUtil::isNumber).map(NumberUtil::parseDouble).collect(Collectors.toSet());
                        if (RuleFieldConditionEnum.IN.equals(field.getCondition()) && !CollUtil.containsAny(paramFieldDoubles, ruleFieldDoubles)) {
                            // 包含
                            isMatch = false;
                            break;
                        }
                        if (RuleFieldConditionEnum.NIN.equals(field.getCondition()) && CollUtil.containsAny(paramFieldDoubles, ruleFieldDoubles)) {
                            // 不包含
                            isMatch = false;
                            break;
                        }
                        continue;
                    }
                    String paramFieldValue = IterUtil.getFirst(mapConvertToSet(paramFieldValueObj, field.getKey()));
                    if (!NumberUtil.isNumber(paramFieldValue) || !NumberUtil.isNumber(String.valueOf(ruleFieldValueObj))) {
                        isMatch = false;
                        log.info("RuleUtil::match 规则匹配存在非数值字段 规则id:{} 规则字段值:{} 参数字段值:{}", rule.getId(), field.getValue(), paramFieldValue);
                        break;
                    }
                    double paramFieldDouble = NumberUtil.parseDouble(paramFieldValue);
                    double ruleFieldDouble = NumberUtil.parseDouble(String.valueOf(ruleFieldValueObj));
                    // 数值属性
                    if (RuleFieldConditionEnum.EQ.equals(field.getCondition()) && paramFieldDouble != ruleFieldDouble) {
                        // 等于
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.NE.equals(field.getCondition()) && paramFieldDouble == ruleFieldDouble) {
                        // 不等于
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.LT.equals(field.getCondition()) && paramFieldDouble >= ruleFieldDouble) {
                        // 小于
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.LE.equals(field.getCondition()) && paramFieldDouble > ruleFieldDouble) {
                        // 小于等于
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.GT.equals(field.getCondition()) && paramFieldDouble <= ruleFieldDouble) {
                        // 大于
                        isMatch = false;
                        break;
                    }
                    if (RuleFieldConditionEnum.GE.equals(field.getCondition()) && paramFieldDouble < ruleFieldDouble) {
                        // 大于等于
                        isMatch = false;
                        break;
                    }
                }
            }
            if (isMatch) {
                matchRules.add(rule);
                if (matchRules.size() >= matchLimit) {
                    return matchRules;
                }
            }
        }
        return matchRules;
    }
 
    /**
     * 把集合类型对象转换为set
     *
     * @param obj 对象
     * @return set
     */
    private static Set<String> collConvertToSet(Object obj) {
        Set<String> set = new HashSet<>();
        if (Objects.isNull(obj)) {
            return set;
        }
        if (obj instanceof Collection<?> paramFieldColl) {
            for (Object subObj : paramFieldColl) {
                set.add(String.valueOf(subObj));
            }
        } else {
            set.add(String.valueOf(obj));
        }
        return set;
    }
 
    /**
     * 把map类型对象转换为set
     *
     * @param obj 对象
     * @param key 键
     * @return set
     */
    private static Set<String> mapConvertToSet(Object obj, String key) {
        Set<String> set = new HashSet<>();
        if (Objects.isNull(obj) || StrUtil.isBlank(key) || !(obj instanceof Map<?, ?> map)) {
            return set;
        }
        for (Map.Entry<?, ?> entry : map.entrySet()) {
            if (key.equals(String.valueOf(entry.getKey()))) {
                if (entry.getValue() instanceof Collection<?> paramFieldColl) {
                    for (Object subObj : paramFieldColl) {
                        set.add(String.valueOf(subObj));
                    }
                } else {
                    set.add(String.valueOf(entry.getValue()));
                }
                break;
            }
        }
        return set;
    }
}

RuleUtil 把业务规则配置的字段类型,归纳为以下四种(代码枚举:RuleFieldTypeEnum),各个类型支持的场景如下:

/**
 * 规则项字段类型枚举
 */
@Getter
@AllArgsConstructor
public enum RuleFieldTypeEnum {

    UNKNOWN(0, ""),
    NORMAL(1, "普通"),
    NUMBER(2, "数值"),
    NORMAL_KV(3, "普通键值对"),
    NUMBER_KV(4, "数值键值对"),

    ;
    private final Integer code;
    private final String desc;
}

在这里插入图片描述

2.2 应用场景

应用场景一:列举以下四个业务规则示例,分别对应上面四种字段类型:

在这里插入图片描述
第一步:实现 convert 方法,将业务规则转换为 Rule 实体列表

根据示例转换后的 Rule 实体 json 结构如下:

在这里插入图片描述

第二步:根据【字段名-Field.name】【字段类型-Field.type】自行定义匹配 Rule 所需要的参数实体类。根据示例定义的 Param 实体结构如下:

/**
 * 规则匹配参数
 */
class Param {
    // 品类组合id(普通)
    private List<Long> combIds;
    // 库存数量(数值)
    private Long stock;
    // 属性id-属性值id(普通键值对)
    private Map<Long, List<Long>> attrIdToAttrValIdMap;
    // 成分属性值id-属性数值(数值键值对)
    private Map<Long, Double> componentAttrValIdToValMap;
}

第三步:调用 RuleUtile.match 方法,得到 Param 命中的 Rule 列表。

根据示例 Param 创建一个待匹配对象

{
  "combIds": [
    "1-男装",
    "3-上装"
  ],
  "stock": 15,
  "attrIdToAttrValIdMap": {
    "10-适合类型": [
      "11-宽松",
      "13-修身"
    ],
    "20-织造方式": [
      "21-牛仔"
    ]
  },
  "componentAttrValIdToValMap": {
    "10-棉": 15.0,
    "20-尼龙": 85.0
  }
}

调用 RuleUtile.match 方法如下,方法返回了能够命中的 Rule

在这里插入图片描述

match 方法使用反射,根据【Rule.name】获取 Param 对应字段的值进行条件匹配

应用场景二:假设业务规则发生变化,迭代为嵌套【且】【或】关系的业务规则

在这里插入图片描述
第一步:更新 convert 方法,将新的业务规则转换为 Rule 实体列表

根据示例转换后的 Rule 实体 json 结构如下:

在这里插入图片描述

⚠️注意:多个【Rule】之间是【或】关系,多个【Rule.Field】之间是【且】关系

第二步:定义的 Param 实体结构不变

第三步:调用 RuleUtile.match 方法如下,方法返回了能够命中的 Rule

在这里插入图片描述
场景一、场景二的完整单元测试用例代码:

public class RuleUtilTest {
 
    @Test
    public void demo01() {
 
        // 规则匹配参数
        TestParam param = new TestParam();
        param.setCombIds(List.of(1L, 3L));
        param.setStock(15L);
        param.setAttrIdToAttrValIdMap(Map.of(10L, List.of(11L, 13L), 20L, List.of(21L)));
        param.setComponentAttrValIdToValMap(Map.of(10L, 15.0, 20L, 85.0));
 
        // 品类组合
        RuleV2.Field fieldCombIds = new RuleV2.Field();
        fieldCombIds.setName(LambdaUtil.getFieldName(TestParam::getCombIds));
        fieldCombIds.setType(RuleFieldTypeEnum.NORMAL); // 普通
        fieldCombIds.setCondition(RuleFieldConditionEnum.IN); // 包含
        fieldCombIds.setValue(List.of(1L, 2L));
 
        RuleV2 rule01 = new RuleV2();
        rule01.setId("rule01");
        rule01.setFields(List.of(fieldCombIds));
 
        // 库存数量
        RuleV2.Field fieldStockGt = new RuleV2.Field();
        fieldStockGt.setName(LambdaUtil.getFieldName(TestParam::getStock));
        fieldStockGt.setType(RuleFieldTypeEnum.NUMBER); // 数值
        fieldStockGt.setCondition(RuleFieldConditionEnum.GT); // 大于
        fieldStockGt.setValue("10");
        RuleV2.Field fieldStockLt = new RuleV2.Field();
        fieldStockLt.setName(LambdaUtil.getFieldName(TestParam::getStock));
        fieldStockLt.setType(RuleFieldTypeEnum.NUMBER);
        fieldStockLt.setCondition(RuleFieldConditionEnum.LT); // 小于
        fieldStockLt.setValue("20");
 
        RuleV2 rule02 = new RuleV2();
        rule02.setId("rule02");
        rule02.setFields(List.of(fieldStockGt, fieldStockLt));
 
        // 属性id-属性值id(普通键值对)
        RuleV2.Field fieldAttrIdToAttrValIdMap = new RuleV2.Field();
        fieldAttrIdToAttrValIdMap.setName(LambdaUtil.getFieldName(TestParam::getAttrIdToAttrValIdMap));
        fieldAttrIdToAttrValIdMap.setKey("10");
        fieldAttrIdToAttrValIdMap.setType(RuleFieldTypeEnum.NORMAL_KV);
        fieldAttrIdToAttrValIdMap.setCondition(RuleFieldConditionEnum.IN); // 包含
        fieldAttrIdToAttrValIdMap.setValue(List.of(11L, 12L));
 
        RuleV2 rule03 = new RuleV2();
        rule03.setId("rule03");
        rule03.setFields(List.of(fieldAttrIdToAttrValIdMap));
 
        // 成分属性值id-属性数值
        RuleV2.Field fieldComponentAttrValIdToValMapGt = new RuleV2.Field();
        fieldComponentAttrValIdToValMapGt.setName(LambdaUtil.getFieldName(TestParam::getComponentAttrValIdToValMap));
        fieldComponentAttrValIdToValMapGt.setKey("10");
        fieldComponentAttrValIdToValMapGt.setType(RuleFieldTypeEnum.NUMBER_KV); // 数值键值对
        fieldComponentAttrValIdToValMapGt.setCondition(RuleFieldConditionEnum.GT); // 大于
        fieldComponentAttrValIdToValMapGt.setValue("10");
        RuleV2.Field fieldComponentAttrValIdToValMapLt = new RuleV2.Field();
        fieldComponentAttrValIdToValMapLt.setName(LambdaUtil.getFieldName(TestParam::getComponentAttrValIdToValMap));
        fieldComponentAttrValIdToValMapLt.setKey("10");
        fieldComponentAttrValIdToValMapLt.setType(RuleFieldTypeEnum.NUMBER_KV);
        fieldComponentAttrValIdToValMapLt.setCondition(RuleFieldConditionEnum.LT); // 小于
        fieldComponentAttrValIdToValMapLt.setValue("20");
 
        RuleV2 rule04 = new RuleV2();
        rule04.setId("rule04");
        rule04.setFields(List.of(fieldComponentAttrValIdToValMapGt, fieldComponentAttrValIdToValMapLt));
 
        // 调用 RuleUtil 规则匹配方法
        List<RuleV2> matchResults = RuleUtilV2.match(param, List.of(rule01, rule02, rule03, rule04));
        // 命中四种字段类型的规则
        assertEquals(List.of(rule01, rule02, rule03, rule04), matchResults);
    }
 
    @Test
    public void demo02() {
 
        // 规则匹配参数
        TestParam param = new TestParam();
        param.setCombIds(List.of(1L, 3L));
        param.setStock(15L);
        param.setAttrIdToAttrValIdMap(Map.of(10L, List.of(11L, 13L), 20L, List.of(21L)));
        param.setComponentAttrValIdToValMap(Map.of(10L, 15.0, 20L, 85.0));
 
        // 品类组合
        RuleV2.Field fieldCombIds = new RuleV2.Field();
        fieldCombIds.setName(LambdaUtil.getFieldName(TestParam::getCombIds));
        fieldCombIds.setType(RuleFieldTypeEnum.NORMAL); // 普通
        fieldCombIds.setCondition(RuleFieldConditionEnum.IN); // 包含
        fieldCombIds.setValue(List.of(1L, 2L));
 
        // 库存数量
        RuleV2.Field fieldStockGt = new RuleV2.Field();
        fieldStockGt.setName(LambdaUtil.getFieldName(TestParam::getStock));
        fieldStockGt.setType(RuleFieldTypeEnum.NUMBER); // 数值
        fieldStockGt.setCondition(RuleFieldConditionEnum.GT); // 大于
        fieldStockGt.setValue("10");
        RuleV2.Field fieldStockLt = new RuleV2.Field();
        fieldStockLt.setName(LambdaUtil.getFieldName(TestParam::getStock));
        fieldStockLt.setType(RuleFieldTypeEnum.NUMBER);
        fieldStockLt.setCondition(RuleFieldConditionEnum.LT); // 小于
        fieldStockLt.setValue("20");
 
        RuleV2 prule01_rule01 = new RuleV2();
        prule01_rule01.setId("prule01");
        prule01_rule01.setSubId("rule01");
        prule01_rule01.setFields(List.of(fieldCombIds, fieldStockGt, fieldStockLt));
 
        // 属性id-属性值id(普通键值对)
        RuleV2.Field fieldAttrIdToAttrValIdMap = new RuleV2.Field();
        fieldAttrIdToAttrValIdMap.setName(LambdaUtil.getFieldName(TestParam::getAttrIdToAttrValIdMap));
        fieldAttrIdToAttrValIdMap.setKey("10");
        fieldAttrIdToAttrValIdMap.setType(RuleFieldTypeEnum.NORMAL_KV);
        fieldAttrIdToAttrValIdMap.setCondition(RuleFieldConditionEnum.IN); // 包含
        fieldAttrIdToAttrValIdMap.setValue(List.of(11L, 12L));
 
        // 成分属性值id-属性数值
        RuleV2.Field fieldComponentAttrValIdToValMapGt = new RuleV2.Field();
        fieldComponentAttrValIdToValMapGt.setName(LambdaUtil.getFieldName(TestParam::getComponentAttrValIdToValMap));
        fieldComponentAttrValIdToValMapGt.setKey("10");
        fieldComponentAttrValIdToValMapGt.setType(RuleFieldTypeEnum.NUMBER_KV); // 数值键值对
        fieldComponentAttrValIdToValMapGt.setCondition(RuleFieldConditionEnum.GT); // 大于
        fieldComponentAttrValIdToValMapGt.setValue("10");
        RuleV2.Field fieldComponentAttrValIdToValMapLt = new RuleV2.Field();
        fieldComponentAttrValIdToValMapLt.setName(LambdaUtil.getFieldName(TestParam::getComponentAttrValIdToValMap));
        fieldComponentAttrValIdToValMapLt.setKey("10");
        fieldComponentAttrValIdToValMapLt.setType(RuleFieldTypeEnum.NUMBER_KV);
        fieldComponentAttrValIdToValMapLt.setCondition(RuleFieldConditionEnum.LT); // 小于
        fieldComponentAttrValIdToValMapLt.setValue("20");
 
        RuleV2 prule01_rule02 = new RuleV2();
        prule01_rule02.setId("prule01");
        prule01_rule02.setSubId("rule02");
        prule01_rule02.setFields(List.of(fieldAttrIdToAttrValIdMap, fieldComponentAttrValIdToValMapGt, fieldComponentAttrValIdToValMapLt));
 
        // 调用 RuleUtil 规则匹配方法
        List<RuleV2> matchResults = RuleUtilV2.match(param, List.of(prule01_rule01, prule01_rule02));
        // 命中组合字段的规则
        assertEquals(List.of(prule01_rule01, prule01_rule02), matchResults);
    }
}

三、RuleUtil 实践技巧

1、Rule 类的 param 字段可以 set 任何规则信息(如子规则配置的图片 id、枚举值等等);

2、若业务规则存在不同优先级,可以通过对 Rule 列表进行排序实现优先级匹配,排序靠前的优先匹配;

3、若存在命中规则的数量限制(比如优先或随机选择命中的第一个规则),可以通过入参 matchLimit 进行控制;

4、若业务场景对业务规则的更新不要求实时,可以对转换后的 Rule 数组进行缓存,降低数据库查询和转换次数,提高性能;

5、后续将逐步完善 RuleUtil 单元测试用例,保证核心匹配逻辑准确无误。

使用 VuePress + Github 30分钟搭建属于自己的AI网站

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值