营销系统规则引擎

一、系统介绍

规则引擎是一个用于执行营销规则的模块,其包括营销规则配置、规则校验等功能。规则引擎可以根据预先设定的条件和逻辑,自动化地执行特点的营销策略,帮助企业更好地吸引客户,增加销售和提高客户满意度。

规则引擎功能列表:

1、规则装配 - 根据活动类型组装不同营销规则

2、逻辑设计 - 支持与、或、非等复杂逻辑关系。

3、配置校验 - 支持对活动规则进行自动化校验。

二、技术实现

1.UserCenterRegistryActivityRule - 个人中心注册活动规则

java 复制代码
class UserCenterRegistryActivityRule extends ActivityRule{

    public UserCenterRegistryActivityRule(){
        super("region_rule&channel_rule&(user_group_rule|user_tag_rule)");
    }

    public String rootRulePath(){
        return "usercenter_registry_activity";
    }
}

2.ActivityRule - 活动规则抽象

java 复制代码
public abstract class ActivityRule implements RuleItem{
    /**
     * 规则表达式
     */
    private String ruleExpression;
    /**
     * 规则项配置校验
     */
    @Autowired
    private Map<String, RuleItem> ruleItemMap;
    
    public ActivityRule(String ruleExpression){
        this.ruleExpression = ruleExpression;
    }

    /**
     * 规则项校验
     */
    public boolean apply(CalculateRequest request, PeContext peContext) {
        Stack<Character> opStack = new Stack<>();
        Stack<Boolean> resStack = new Stack<>();
        Set<Character> opChSet = Set.of('(', ')', '&', '|');
        int i = 0;
        // 处理右括号
        while (i < ruleExpression.length()) {
            char ch = ruleExpression.charAt(i);
            switch (ch) {
                case '(':
                    opStack.push(ch);
                    i++;
                    break;
                case ')': 
                    while (opStack.peek() != '(') {
                        Character opChar = opStack.peek();
                        if (opChar == '&') {
                            boolean right = resStack.pop();
                            boolean left = resStack.pop();
                            resStack.push(left && right);
                        } else if (opChar == '|') {
                            boolean right = resStack.pop();
                            boolean left = resStack.pop();
                            resStack.push(left || right);
                        }
                        opStack.pop();
                    }
                    break;
                case '&': // 操作符
                    opStack.push('&');
                    i++;
                    break;
                case '|':
                    opStack.push('|');
                    i++;
                    break;
                default: // 操作数
                    int j = i;
                    while (j < ruleExpression.length() && !opChSet.contains(ruleExpression.charAt(j))) {
                        j++;
                    }
                    String rule = ruleExpression.substring(i, j);
                    resStack.push(ruleItemMap.get(rule).apply(request, peContext));
                    i = j;
            }
        }
        // 处理结果
        while (!opStack.isEmpty()) {
            Character opChar = opStack.peek();
            if (opChar == '&') {
                boolean right = resStack.pop();
                boolean left = resStack.pop();
                resStack.push(left && right);
            } else if (opChar == '|') {
                boolean right = resStack.pop();
                boolean left = resStack.pop();
                resStack.push(left || right);
            }
            opStack.pop();
        }

        return resStack.pop();
    }

    
    default String rootRulePath();


    /**
     * 规则配置校验
     */
    public PathError validate(String rulePath) {
        if (rulePath == null){
            rulePath = rootRulePath();
        }
        PathError pathError = PathError.of(null, rulePath);
        for (Map.Entry<String, RuleItem> entry : ruleItemMap.entrySet()) {
            String ruleItemPath = entry.getKey();
            RuleItem ruleItem = entry.getValue();
            PathError ruleItemError = ruleItem.validate(ruleItemPath);
            if (ruleItemError != null) {
                return pathError.with(ruleItemError);
            }
        }
        return null;
    }
}

3.UserGroupRuleItem - 用户组规则项

java 复制代码
class UserGroupRuleItem extends RuleItem{

    private List<String> groupsLimit;

    @Override
    public PathError validate(String rulePath){
        PathError pathError = PathError.of(null, rulePath);
        if (groupsLimit == null || groupsLimit.size() == 0){
            return pathError.withPath("groupsLimit").withError("groupsLimit不能为空");
        }
        return null;
    }


    @Overrider
    public boolean apply(CalculateRequest req, PeContext context){
        UserService userService = SpringUtil.getBean(UserService.class);
        // 判断用户是否符合用户组规则
        List<String> userGroups = userService.getUserGroups(req.getUserId());
        if (groupsLimit.stream().anyMatch(userGroups::contains)){
            return true;
        }
        retrun false;
    }
}

4.RuleItem - 规则项定义

java 复制代码
interface RuleItem {
    // 规则配置校验
    default PathError validate(String rulePath) {

        PathError pathError = new PathError(null, rulePath);

        // 校验规则字段
        Field[] fields = FieldUtils.getSpecFields(getClass(), RuleItem.class);

        if (fields.length == 0) {
            return null;
        }

        try {
            for (Field ruleField : fields) {
                RuleItem rule = (Rule) FieldUtils.getField(this, ruleField);
                if (rule == null){
                    continue;
                }
                String fieldName = ruleField.getName();
                // 规则字段校验失败
                PathError fieldPathError = rule.validate(fieldName);
                // 返回字段校验失败结果
                if (fieldPathError != null) {
                    return pathError.with(fieldPathError);
                }
            }
        } catch (Exception e) {
            throw new AppException(ErrorCode.SYSTEM_ERROR, String.format("校验Rule错误: %s", e.getMessage()));
        }

        return null;

    }

    // 规则应用
    default boolean apply(CalculateRequest request, PeContext peContext){
        return true;
    };

    @Data
    @NoArgsConstructor
    @AllArgsConstructor
    public static class PathError{
        
        private String error;
        
        private List<String> paths;
        
        public PathError with(PathError other){
              this.error = other.error;
              this.paths.addAll(other.paths);
              return this;
        }
        
        public PathError withError(String error){
            this.error = error;
            return this;
        }

        public PathError withPath(String path){
            this.paths.add(path);
            return this;
        }

    }
}
相关推荐
IDRSolutions_CN6 分钟前
PDF 转 HTML5 —— HTML5 填充图形不支持 Even-Odd 奇偶规则?(第二部分)
java·经验分享·pdf·软件工程·团队开发
hello早上好10 分钟前
Spring不同类型的ApplicationContext的创建方式
java·后端·架构
HelloWord~1 小时前
SpringSecurity+vue通用权限系统2
java·vue.js
让我上个超影吧1 小时前
黑马点评【基于redis实现共享session登录】
java·redis
BillKu2 小时前
Java + Spring Boot + Mybatis 插入数据后,获取自增 id 的方法
java·tomcat·mybatis
全栈凯哥2 小时前
Java详解LeetCode 热题 100(26):LeetCode 142. 环形链表 II(Linked List Cycle II)详解
java·算法·leetcode·链表
chxii2 小时前
12.7Swing控件6 JList
java
全栈凯哥2 小时前
Java详解LeetCode 热题 100(27):LeetCode 21. 合并两个有序链表(Merge Two Sorted Lists)详解
java·算法·leetcode·链表
YuTaoShao2 小时前
Java八股文——集合「List篇」
java·开发语言·list
PypYCCcccCc2 小时前
支付系统架构图
java·网络·金融·系统架构