几种规则策略

规则的起因

软件开发中经常会有很多复杂的条件判断,满足不同条件的时候执行不同的操作,而这些规则可能并不是一直不变的。

以某市几条积分规则为例:

年龄(最高30分)

  * 56-60岁   积5分
  * 每减少1岁 加2分

教育背景(最高110分)
  * 1:大专(高职); 50分
  * 2:本科; 60分
  * 3:本科+学历; 90分
  * 4:硕士; 100分
  * 5:博士 110分

技能类国家职业资格等级(最高140分)
  * 1:一级或高级职称; 140分
  * 2:二级或中级职称; 100分
  * 3:三级; 60分
  * 4:四级; 30分
  * 5:五级 15分

社保缴纳年限
  * year * 3

基于method/bean的简易规则

 规则配置:

public class ScoreRule {
    /**
     * 规则id
     */
    private Integer ruleId;
    /**
     * 规则名
     */
    private String ruleName;
    /**
     * 规则执行者
     */
    private String ruleExecuter;
    /**
     * 规则表达式(可以提供后台修改配置)
     * eg:{"maxScore":30,"minRegion":56,"maxRegion":60,"regionScore":5,"scorePeYear":2}
     */
    private String ruleExpression;
    /**
     * 规则执行顺序
     */
    private Integer sort;
    /**
     * 是否可用
     */
    private Boolean enable;
}

规则执行:

    /**
     * 评估积分
     */
    public Integer getUserSettleRules(UserInfoDto dto) throws NoSuchMethodException, InvocationTargetException, IllegalAccessException {
        Integer baseScore = 0;
        List<ScoreRule> scoreRuleList = getScoreRuleList().stream().filter(r -> r.getEnable().equals(Boolean.TRUE)).sorted((x, y) -> x.getSort().compareTo(y.getSort())).collect(Collectors.toList());
        for (ScoreRule scoreRule : scoreRuleList) {
            Method method = this.getClass().getDeclaredMethod(scoreRule.getRuleExecuter(), UserInfoDto.class);
            Integer result = (Integer) method.invoke(this, dto);
            if (result > 0) {
                log.info("methodName:" + scoreRule.getRuleExecuter() + ",result:" + result);
                baseScore += result;
            }
        }
        return baseScore;
    }

实现简单,将易于变化的参数提供给业务方配置;新增或修改规则需要调整代码重新发布。

类似基于method的方式,将不同的规则实现到不同的bean里,利用 SpringContextUtil.getBean(sortRule.getRuleExecuter()) 获取实现规则的bean,执行对应的规则。

基于qlexpress规则

引用依赖:

<dependency>
<groupId>com.alibaba</groupId>
<artifactId>QLExpress</artifactId>
<version>3.2.0</version>
</dependency>

调用的java的方法:

  • userService.getUserInfo(userId);//指定userId获取用户信息
  • settleScore.getBaseScoreByEdu(userInfo);//传入userInfo按年龄计算基础积分
  • settleScore.getBaseScoreByEdu(userInfo);//传入userInfo按学历计算基础积分

传入参数:

context.put("userId", 2);//给userService.getUserInfo(userId)传入参数
context.put("userService", SpringContextUtil.getBean("userService"));//注入userService。调用userService.getUserInfo(userId)方法
context.put("settleScore", SpringContextUtil.getBean("settleScore"));//注入settleScore。调用settleScore.getBaseScoreByEdu(userInfo)、settleScore.getBaseScoreByEdu(userInfo)方法

UserService获取用户信息示例:

@Service("userService")
public class UserService {
    public UserInfoDto getUserInfo(Integer userId) {
        if (userId == 1) {
            UserInfoDto userInfoDto = new UserInfoDto();
            userInfoDto.setUserName("张三");
            userInfoDto.setAge(35);
            userInfoDto.setDegreeOfEdu(1);
            userInfoDto.setSkillLevel(1);
            userInfoDto.setSocialSecurity(1);
            return userInfoDto;
        }
        UserInfoDto userInfoDto = new UserInfoDto();
        userInfoDto.setUserName("张三");
        userInfoDto.setAge(58);
        userInfoDto.setDegreeOfEdu(1);
        userInfoDto.setSkillLevel(1);
        userInfoDto.setSocialSecurity(1);
        return userInfoDto;
    }
}

规则完整示例:

    public void testQlExpress() throws Exception {
        String qlExpress = "score = 0;" +
                            "userInfo = userService.getUserInfo(userId);\n" +
                                "if(userInfo.age > 0 && userInfo.age <= 60) {\n" +
                                "result = settleScore.getBaseScoreByAge(userInfo);\n" +
                                "System.out.println('>>>cal age score..........result:'+result);" +
                                "score = score + result;" +
                            "} if(userInfo.degreeOfEdu >= 1 && userInfo.degreeOfEdu <= 5) {\n" +
                                "result = settleScore.getBaseScoreByEdu(userInfo);\n" +
                                "System.out.println('>>>cal edu score..........result:'+result);" +
                                "score = score + result;" +
                            "}\n" +
                            "System.out.println('return result:'+result);" +
                            "return score;";

        ExpressRunner runner = new ExpressRunner();
        DefaultContext<String, Object> context = new DefaultContext<>();
        context.put("userId", 2);
        context.put("userService", SpringContextUtil.getBean("userService"));
        context.put("settleScore", SpringContextUtil.getBean("settleScore"));

        Object result = runner.execute(qlExpress, context, null, true, false);
        System.out.println("score result:" + result);
    }

执行结果:

2018-06-25 13:38:07.634 [INFO][http-nio-8088-exec-1]:c.demo.scorerule.SettleScore [getBaseScoreByAge:73] 基础积分计算>>>年龄积分:5
>>>cal age score..........result:5
2018-06-25 13:38:07.639 [INFO][http-nio-8088-exec-1]:c.demo.scorerule.SettleScore [getBaseScoreByEdu:121] 基础积分计算>>>教育背景:50
>>>cal edu score..........result:50
return result:50
score result:55

基于drools规则

将规则实现在drl文件里,java读取drl文件执行规则,可以做到动态更新drl规则而不需要发布。

引用drools包(drools5.5与java8不兼容问题参考):

<dependency>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
<version>4.5.1</version>
</dependency>

<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-core</artifactId>
<version>5.5.0.Final</version>
</dependency>
<dependency>
<groupId>org.drools</groupId>
<artifactId>drools-compiler</artifactId>
<version>5.5.0.Final</version>
<exclusions>
<exclusion>
<groupId>org.eclipse.jdt.core.compiler</groupId>
<artifactId>ecj</artifactId>
</exclusion>
</exclusions>
</dependency>

drl规则实现:

package com.drools.demo.point

import com.logback.demo.scorerule.UserInfoDto;
import com.logback.demo.scorerule.UserBaseScore;

rule "ageBaseScore"
//    salience 2
    lock-on-active true
    when
        $userInfo : UserInfoDto(age >= 56 && age <= 60);
    then
        UserBaseScore.addScore(5);
        System.out.println("age between 56 and 60");
        $userInfo.recordPointLog($userInfo.getUserName(),"ageBaseScore");
end

rule "ageCalScore"
//    salience 3//优先级,值越大越先执行
    lock-on-active true
    when
        $userInfo : UserInfoDto(age < 56);
    then
        System.out.println("age less than 56. age:"+$userInfo.getAge());
        Integer ageScore = (56 - $userInfo.getAge()) * 2;
        UserBaseScore.addScore(ageScore > 30 ? 30 : ageScore);
        $userInfo.recordPointLog($userInfo.getUserName(),"ageCalScore");
end

rule "eduLevel1Score"
//    salience 3//优先级,值越大越先执行
    lock-on-active true
    when
        $userInfo : UserInfoDto(degreeOfEdu == 1);
    then
        System.out.println("edu level is:"+$userInfo.getDegreeOfEdu());
        UserBaseScore.addScore(50);
        $userInfo.recordPointLog($userInfo.getUserName(),"eduLevel1Score");
end

猜你喜欢

转载自www.cnblogs.com/mr-yang-localhost/p/9204208.html