Drools业务规则管理系统25_Drools实战9

一、个人所得税计算器

 本小节我们需要通过Drools规则引擎来根据规则计算个人所得税,最终页面效果如下:

  

 1、名词解释

  税前月收入:即税前工资,指交纳个人所得税之前的总工资

  应纳税所得额:指按照税法规定确定纳税人在一定期间所获得的所有应税收入减除在该纳税期间依法允许减除的各种支出后的余额

  税率:是对征税对象的征收比例或征收额度

  速算扣除数:指为解决超额累进税率分级计算税额的复杂技术问题,而预先计算出的一个数据,可以简化计算过程

  扣税额:是指实际缴纳的税额

  税后工资:是指扣完税后实际到手的工资收入

 2、计算规则

  要实现个人所得税计算器,需要了解如下计算规则:(2011)

   

  税前工资:10000

  应缴纳所得税:10000-3500 = 6500

  税率:0.2

  速算扣除数:555

  扣税额:6500 * 0.2 - 555 = 745

  税后工资:10000 - 745 = 9255

 3、实现步骤

  本实战案例我们基于Spring Boot整合Drools的方式来实现。

    1. 创建maven工程calculation并配置pom.xml文件

    2. 创建/resources/application.yml文件

    3. 编写配置类DroolsConfig

    4. 编写实体类Calculation

    5. 在resources/rules下创建规则文件calculation.drl文件

    6. 创建RuleService

    7. 创建RuleController

    8. 创建启动类DroolsApplication

    9. 导入静态资源文件到resources/static目录下

  第一步:创建maven工程calculation并配置pom.xml文件

<?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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starters</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>
    <groupId>com.itheima</groupId>
    <artifactId>calculation</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <!--drools规则引擎-->
        <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>
            <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>
            <version>7.6.0.Final</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>${project.artifactId}</finalName>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

  第二步:创建/resources/application.yml文件

server:
  port: 8080
spring:
  application:
    name: calculation

  第三步:编写配置类DroolsConfig

package com.itheima.drools.config;

import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieRepository;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.kie.spring.KModuleBeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.Resource;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import java.io.IOException;

/**
 * 规则引擎配置类
 */
@Configuration
public class DroolsConfig {

    //指定规则文件存放的目录
    private static final String RULES_PATH = "rules/";
    private final KieServices kieServices = KieServices.Factory.get();
    @Bean
    @ConditionalOnMissingBean
    public KieFileSystem kieFileSystem() throws IOException {
        System.setProperty("drools.dateformat","yyyy-MM-dd");
        KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
        ResourcePatternResolver resourcePatternResolver =
                new PathMatchingResourcePatternResolver();
        Resource[] files =
                resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "*.*");
        String path = null;
        for (Resource file : files) {
            path = RULES_PATH + file.getFilename();
            kieFileSystem.write(ResourceFactory.newClassPathResource(path, "UTF-8"));
        }
        return kieFileSystem;
    }
    @Bean
    @ConditionalOnMissingBean
    public KieContainer kieContainer() throws IOException {
        KieRepository kieRepository = kieServices.getRepository();
        kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem());
        kieBuilder.buildAll();
        return kieServices.newKieContainer(kieRepository.getDefaultReleaseId());
    }
    @Bean
    @ConditionalOnMissingBean
    public KieBase kieBase() throws IOException {
        return kieContainer().getKieBase();
    }
    @Bean
    @ConditionalOnMissingBean
    public KModuleBeanFactoryPostProcessor kiePostProcessor() {
        return new KModuleBeanFactoryPostProcessor();
    }
}

  第四步:编写实体类Calculation

package com.itheima.drools.entity;

public class Calculation {

    private double wage;//税前工资
    private double wagemore;//应纳税所得额
    private double cess;//税率
    private double preminus;//速算扣除数
    private double wageminus;//扣税额
    private double actualwage;//税后工资

    public double getWage() {
        return wage;
    }

    public void setWage(double wage) {
        this.wage = wage;
    }

    public double getWagemore() {
        return wagemore;
    }

    public void setWagemore(double wagemore) {
        this.wagemore = wagemore;
    }

    public double getCess() {
        return cess;
    }

    public void setCess(double cess) {
        this.cess = cess;
    }

    public double getPreminus() {
        return preminus;
    }

    public void setPreminus(double preminus) {
        this.preminus = preminus;
    }

    public double getWageminus() {
        return wageminus;
    }

    public void setWageminus(double wageminus) {
        this.wageminus = wageminus;
    }

    public double getActualwage() {
        return actualwage;
    }

    public void setActualwage(double actualwage) {
        this.actualwage = actualwage;
    }

    @Override
    public String toString() {
        return "Calculation{" +
                "wage=" + wage +
                ", wagemore=" + wagemore +
                ", cess=" + cess +
                ", preminus=" + preminus +
                ", wageminus=" + wageminus +
                ", actualwage=" + actualwage +
                '}';
    }
}

  第五步:在resources/rules下创建规则文件calculation.drl文件

//当前规则文件用于计算个人所得税
package calculation

import com.itheima.drools.entity.Calculation

/**
  当前规则文件中的规则主要分为三类:
    1、计算应纳税所得额       有1个规则
    2、设置税率、速算扣除数    有7个规则
    3、计算税后工资           有1个规则
*/
//计算应纳税所得额
rule "计算应纳税所得额"
    salience 100
    no-loop true
    date-effective "2011-09-01"
    when
        $cal:Calculation(wage > 0)
    then
        $cal.setWagemore($cal.getWage()-3500);
        update($cal);
end


//设置税率、速算扣除数
rule "设置税率,应纳税所得额<=1500"
    salience 90
    no-loop true
    when
        $cal:Calculation(wagemore <= 1500)
    then
        $cal.setCess(0.03);
        $cal.setPreminus(0);
        update($cal);
end


rule "设置税率,应纳税所得额在1500至4500之间"
    salience 90
    no-loop true
    when
        $cal:Calculation(wagemore > 1500 && wagemore <= 4500)
    then
        $cal.setCess(0.1);
        $cal.setPreminus(105);
        update($cal);
end

rule "设置税率,应纳税所得额在4500至9000之间"
    salience 90
    no-loop true
    when
        $cal:Calculation(wagemore > 4500 && wagemore <= 9000)
    then
        $cal.setCess(0.2);
        $cal.setPreminus(555);
        update($cal);
end

rule "设置税率,应纳税所得额在9000至35000之间"
    salience 90
    no-loop true
    when
        $cal:Calculation(wagemore > 9000 && wagemore <= 35000)
    then
        $cal.setCess(0.25);
        $cal.setPreminus(1005);
        update($cal);
end

rule "设置税率,应纳税所得额在35000至55000之间"
    salience 90
    no-loop true
    when
        $cal:Calculation(wagemore > 35000 && wagemore <= 55000)
    then
        $cal.setCess(0.3);
        $cal.setPreminus(2755);
        update($cal);
end

rule "设置税率,应纳税所得额在55000至80000之间"
    salience 90
    no-loop true
    when
        $cal:Calculation(wagemore > 55000 && wagemore <= 80000)
    then
        $cal.setCess(0.35);
        $cal.setPreminus(5505);
        update($cal);
end

rule "设置税率,应纳税所得额在80000以上"
    salience 90
    no-loop true
    when
        $cal:Calculation(wagemore > 80000)
    then
        $cal.setCess(0.45);
        $cal.setPreminus(13505);
        update($cal);
end

rule "计算税后工资"
    salience 80
    when
        $cal: Calculation(wage>0 && wagemore>0 && cess>0)
    then
        $cal.setWageminus($cal.getWagemore()*$cal.getCess()-$cal.getPreminus());
        $cal.setActualwage($cal.getWage()-$cal.getWageminus());

        System.out.println("-----税前工资:"+$cal.getWage());
        System.out.println("-----应纳税所得额:"+$cal.getWagemore());
        System.out.println("-----税率:" + $cal.getCess());
        System.out.println("-----速算扣除数:" + $cal.getPreminus());
        System.out.println("-----扣税额:" + $cal.getWageminus());
        System.out.println("-----税后工资:" + $cal.getActualwage());
end

  第六步:创建RuleService

package com.itheima.drools.service;

import com.itheima.drools.entity.Calculation;
import org.kie.api.KieBase;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class RuleService {

    @Autowired
    private KieBase kieBase;

    //调用Drools规则引擎实现个人所得税计算
    public Calculation calculation(Calculation calculation){
        KieSession kieSession = kieBase.newKieSession();
        kieSession.insert(calculation);
        kieSession.fireAllRules();
        kieSession.dispose();
        return  calculation;
    }
}

  第七步:创建RuleController

package com.itheima.drools.controller;

import com.itheima.drools.entity.Calculation;
import com.itheima.drools.service.RuleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/rule")
public class RuleController {

    @Autowired
    private RuleService ruleService;

    @RequestMapping("/calculate")
    public Calculation calculation(double wage){
        Calculation calculation = new Calculation();
        calculation.setWage(wage);
        calculation = ruleService.calculation(calculation);
        System.out.println(calculation);
        return  calculation;
    }
}

  第八步:创建启动类DroolsApplication

package com.itheima.drools;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DroolsApplication {

    public static void main(String[] args) {
        SpringApplication.run(DroolsApplication.class);
    }
}

  第九步:导入静态资源文件到resources/static目录下

   

  启动服务DroolsApplication,浏览器访问http://localhost:8080/calculation.html

   

   输入税前月收入,查看浏览器及控制台:

     

   

二、信用卡申请

 本小节我们需要通过Drools规则引擎来根据规则进行申请人的合法性检查,检查通过后再根据规则确定信用卡额度,最终页面效果如下:

  

 1、计算规则

  合法性检查规则如下:

   

  信用卡额度确定规则:

   

 2、实现步骤 

  步骤分析:

    1. 创建maven工程creditCardApply并配置pom.xml文件

    2. 创建/resources/application.yml文件

    3. 编写配置类DroolsConfig

    4. 编写实体类CreditCardApplyInfo在resources/rules下

    5. 创建规则文件creditCardApply.drl文件

    6. 创建RuleService

    7. 创建RuleController

    8. 创建启动类DroolsApplication

    9. 导入静态资源文件到resources/static目录下

  第一步:创建maven工程creditCardApply并配置pom.xml文件

<?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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starters</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>
    <groupId>com.itheima</groupId>
    <artifactId>creditCardApply</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <!--drools规则引擎-->
        <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>
            <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>
            <version>7.6.0.Final</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>${project.artifactId}</finalName>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

  第二步:创建/resources/application.yml文件

server:
  port: 8080
spring:
  application:
    name: creditCardApply

  第三步:编写配置类DroolsConfig

package com.itheima.drools.config;

import org.kie.api.KieBase;
import org.kie.api.KieServices;
import org.kie.api.builder.KieBuilder;
import org.kie.api.builder.KieFileSystem;
import org.kie.api.builder.KieRepository;
import org.kie.api.runtime.KieContainer;
import org.kie.internal.io.ResourceFactory;
import org.kie.spring.KModuleBeanFactoryPostProcessor;
import org.springframework.boot.autoconfigure.condition.ConditionalOnMissingBean;
import org.springframework.context.annotation.Bean;
import org.springframework.context.annotation.Configuration;
import org.springframework.core.io.support.PathMatchingResourcePatternResolver;
import org.springframework.core.io.support.ResourcePatternResolver;
import org.springframework.core.io.Resource;
import java.io.IOException;
/**
 * 规则引擎配置类
 */
@Configuration
public class DroolsConfig {
    //指定规则文件存放的目录
    private static final String RULES_PATH = "rules/";
    private final KieServices kieServices = KieServices.Factory.get();
    @Bean
    @ConditionalOnMissingBean
    public KieFileSystem kieFileSystem() throws IOException {
        KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
        ResourcePatternResolver resourcePatternResolver =
                new PathMatchingResourcePatternResolver();
        Resource[] files =
                resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "*.*");
        String path = null;
        for (Resource file : files) {
            path = RULES_PATH + file.getFilename();
            kieFileSystem.write(ResourceFactory.newClassPathResource(path, "UTF-8"));
        }
        return kieFileSystem;
    }
    @Bean
    @ConditionalOnMissingBean
    public KieContainer kieContainer() throws IOException {
        KieRepository kieRepository = kieServices.getRepository();
        kieRepository.addKieModule(kieRepository::getDefaultReleaseId);
        KieBuilder kieBuilder = kieServices.newKieBuilder(kieFileSystem());
        kieBuilder.buildAll();
        return kieServices.newKieContainer(kieRepository.getDefaultReleaseId());
    }
    @Bean
    @ConditionalOnMissingBean
    public KieBase kieBase() throws IOException {
        return kieContainer().getKieBase();
    }
    @Bean
    @ConditionalOnMissingBean
    public KModuleBeanFactoryPostProcessor kiePostProcessor() {
        return new KModuleBeanFactoryPostProcessor();
    }
}

  第四步:编写实体类CreditCardApplyInfo

package com.itheima.drools.entity;

/**
 * 信用卡申请信息
 */
public class CreditCardApplyInfo {

    public static final String EDUCATION_1 = "专科以下";
    public static final String EDUCATION_2 = "专科";
    public static final String EDUCATION_3 = "本科";
    public static final String EDUCATION_4 = "本科以上";

    private String name;
    private String sex;
    private int age;
    private String education;
    private String telephone;
    private double monthlyIncome = 0;//月收入
    private String address;

    private boolean hasHouse = false;//是否有房
    private boolean hasCar = false;//是否有车
    private int hasCreditCardCount = 0;//现持有信用卡数量

    private boolean checkResult = true;//审核是否通过
    private double quota = 0;//额度


    public String getName() {
        return name;
    }

    public void setName(String name) {
        this.name = name;
    }

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public String getEducation() {
        return education;
    }

    public void setEducation(String education) {
        this.education = education;
    }

    public String getTelephone() {
        return telephone;
    }

    public void setTelephone(String telephone) {
        this.telephone = telephone;
    }

    public double getMonthlyIncome() {
        return monthlyIncome;
    }

    public void setMonthlyIncome(double monthlyIncome) {
        this.monthlyIncome = monthlyIncome;
    }

    public String getAddress() {
        return address;
    }

    public void setAddress(String address) {
        this.address = address;
    }

    public boolean isHasHouse() {
        return hasHouse;
    }

    public void setHasHouse(boolean hasHouse) {
        this.hasHouse = hasHouse;
    }

    public boolean isHasCar() {
        return hasCar;
    }

    public void setHasCar(boolean hasCar) {
        this.hasCar = hasCar;
    }

    public int getHasCreditCardCount() {
        return hasCreditCardCount;
    }

    public void setHasCreditCardCount(int hasCreditCardCount) {
        this.hasCreditCardCount = hasCreditCardCount;
    }

    public boolean isCheckResult() {
        return checkResult;
    }

    public void setCheckResult(boolean checkResult) {
        this.checkResult = checkResult;
    }

    public double getQuota() {
        return quota;
    }

    public void setQuota(double quota) {
        this.quota = quota;
    }

    public String toString() {
        if(checkResult){
            return "审核通过,信用卡额度为:" + quota;
        }else {
            return "审核不通过";
        }
    }
}

  第五步:在resources/rules下创建规则文件creditCardApply.drl文件

//当前规则文件用于处理两类规则:合法性检查、确定信用卡额度规则
package creditCardApplay
import com.itheima.drools.entity.CreditCardApplyInfo

//合法性检查-共4个规则
rule "如果申请人既没房也没车,同时学历为大专以下,并且月薪少于5000,那么不通过"
    salience 100
    no-loop true
    when
        $c:CreditCardApplyInfo(hasCar == false && hasHouse == false && education == CreditCardApplyInfo.EDUCATION_1 && monthlyIncome < 5000)
    then
        $c.setCheckResult(false);
        drools.halt();
end

rule "如果申请人既没房也没车,同时学历为大专或本科,并且月薪少于3000,那么不通过"
    salience 100
    no-loop true
    when
        $c:CreditCardApplyInfo(hasCar == false && hasHouse == false && (education == CreditCardApplyInfo.EDUCATION_2 || education == CreditCardApplyInfo.EDUCATION_3) && monthlyIncome < 3000)
    then
        $c.setCheckResult(false);
        drools.halt();
end

rule "如果申请人既没房也没车,同时学历为本科以上,并且月薪少于2000,同时之前没有信用卡的,那么不通过"
    salience 100
    no-loop true
    when
        $c:CreditCardApplyInfo(hasCar == false && hasHouse == false && education == CreditCardApplyInfo.EDUCATION_4 && monthlyIncome < 2000 && hasCreditCardCount == 0)
    then
        $c.setCheckResult(false);
        drools.halt();
end

rule "如果申请人现有的信用卡数量大于10,那么不通过"
    salience 100
    no-loop true
    when
        $c:CreditCardApplyInfo(hasCreditCardCount > 10)
    then
        $c.setCheckResult(false);
        drools.halt();
end

//信用卡额度确定-共5个规则
rule "如果申请人有房有车,或者月收入在20000以上,那么发放的信用卡额度为15000"
    salience 10
    no-loop true
    activation-group "quota_group"  //组内只有一个规则触发
    when
        $c:CreditCardApplyInfo(checkResult == true && ((hasHouse == true && hasCar == true) || (monthlyIncome > 20000)))
    then
        $c.setQuota(15000);
end

rule "如果申请人没房没车,但月收入在10000~20000之间,那么发放的信用卡额度为6000"
    salience 10
    no-loop true
    activation-group "quota_group"
    when
        $c:CreditCardApplyInfo(checkResult == true && hasHouse == false && hasCar == false &&  monthlyIncome >= 10000 && monthlyIncome <= 20000)
    then
        $c.setQuota(6000);
end

rule "如果申请人没房没车,月收入在10000以下,那么发放的信用卡额度为3000"
    salience 10
    no-loop true
    activation-group "quota_group"
    when
        $c:CreditCardApplyInfo(checkResult == true && hasHouse == false && hasCar == false && monthlyIncome < 10000)
    then
        $c.setQuota(3000);
end

rule "如果申请人有房没车或者没房但有车,月收入在10000以下,那么发放的信用卡额度为5000"
    salience 10
    no-loop true
    activation-group "quota_group"
    when
        $c:CreditCardApplyInfo(checkResult == true && ((hasHouse == true && hasCar == false) || (hasHouse == false && hasCar == true)) && monthlyIncome < 10000)
    then
        $c.setQuota(5000);
end

rule "如果申请人有房没车或者是没房但有车,月收入在10000~20000之间,那么发放的信用卡额度为8000"
    salience 10
    no-loop true
    activation-group "quota_group"
    when
        $c:CreditCardApplyInfo(checkResult == true && ((hasHouse == true && hasCar == false) || (hasHouse == false && hasCar == true)) && monthlyIncome >= 10000 && monthlyIncome <= 20000)
    then
        $c.setQuota(8000);
end

  第六步:创建RuleService

package com.itheima.drools.service;

import com.itheima.drools.entity.CreditCardApplyInfo;
import org.kie.api.KieBase;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

@Service
public class RuleService {
    @Autowired
    private KieBase kieBase;

    //调用Drools规则引擎实现信用卡申请
    public CreditCardApplyInfo creditCardApply(CreditCardApplyInfo creditCardApplyInfo){
        KieSession session = kieBase.newKieSession();
        session.insert(creditCardApplyInfo);
        session.fireAllRules();
        session.dispose();
        return creditCardApplyInfo;
    }
}

  第七步:创建RuleController

package com.itheima.drools.controller;

import com.itheima.drools.entity.CreditCardApplyInfo;
import com.itheima.drools.service.RuleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestBody;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;

@RestController
@RequestMapping("/rule")
public class RuleController {

    @Autowired
    private RuleService ruleService;

    @RequestMapping("/creditCardApply")
    public CreditCardApplyInfo creditCardApply(@RequestBody
                                                       CreditCardApplyInfo creditCardApplyInfo){
        creditCardApplyInfo = ruleService.creditCardApply(creditCardApplyInfo);
        return creditCardApplyInfo;
    }
}

  第八步:创建启动类DroolsApplication

package com.itheima.drools;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DroolsApplication {
    public static void main(String[] args) {
        SpringApplication.run(DroolsApplication.class);
    }
}

  第九步:导入静态资源文件到resources/static目录下  

   

   creditCardApply.html

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>XX银行信用卡申请</title>
    <meta name="description" content="XX银行信用卡申请算">
    <meta name="keywords" content="XX银行信用卡申请">
    <meta content="width=device-width,initial-scale=1,maximum-scale=1,user-scalable=no" name="viewport">
</head>
<body class="mainBg">
<div id="app">
    <h3 align="center">XX银行信用卡申请</h3>
    <table align="center" width="50%" border="0">
        <tr>
            <td>姓名</td>
            <td>
                <input type="text" v-model="creditCardApplyInfo.name">
            </td>
            <td>性别</td>
            <td>
                <input type="text" v-model="creditCardApplyInfo.sex">
            </td>
        </tr>
        <tr>
            <td>年龄</td>
            <td>
                <input type="text" v-model="creditCardApplyInfo.age">
            </td>
            <td>手机号</td>
            <td>
                <input type="text" v-model="creditCardApplyInfo.telephone">
            </td>
        </tr>
        <tr>
            <td>住址</td>
            <td>
                <input type="text" v-model="creditCardApplyInfo.address">
            </td>
            <td>学历</td>
            <td>
                <select v-model="creditCardApplyInfo.education">
                    <option value="专科以下">专科以下</option>
                    <option value="专科">专科</option>
                    <option value="本科">本科</option>
                    <option value="本科以上">本科以上</option>
                </select>
            </td>
        </tr>
        <tr>
            <td>月收入</td>
            <td>
                <input type="text" v-model="creditCardApplyInfo.monthlyIncome">
            </td>
            <td>现持有信用卡数量</td>
            <td>
                <input type="text" v-model="creditCardApplyInfo.hasCreditCardCount">
            </td>
        </tr>
        <tr>
            <td>是否有房</td>
            <td>
                <select v-model="creditCardApplyInfo.hasHouse">
                    <option value="true">是</option>
                    <option value="false">否</option>
                </select>
            </td>
            <td>是否有车</td>
            <td>
                <select v-model="creditCardApplyInfo.hasCar">
                    <option value="true">是</option>
                    <option value="false">否</option>
                </select>
            </td>
        </tr>
        <tr>
            <td colspan="4" align="center">
                <br>
                <input type="button" value="    申请    " @click="creditCardApply()">
            </td>
        </tr>
        <tr>
            <td colspan="4" align="center">
                    {{applyResultMessage}}
            </td>
        </tr>
    </table>
</div>
</body>
<!--引入组件库-->
<script src="js/vue.js"></script>
<script src="js/axios.js"></script>
<script>
    new Vue({
        el:'#app',
        data:{
            creditCardApplyInfo:{
                education:"本科",
                hasHouse:true,
                hasCar:true
            },
            applyResultMessage:''
        },
        methods:{
            creditCardApply(){
                axios.post("/rule/creditCardApply",this.creditCardApplyInfo).then((res)=>{
                    if(res.data.checkResult){
                        //审核通过
                        this.applyResultMessage = "恭喜你信用卡申请成功,信用卡额度为:"+res.data.quota;
                    }else{
                        //审核失败
                        this.applyResultMessage = "抱歉,您提交的信息审核未通过,您不能申请我行信用卡!";
                    }
                });
            }
        }
    });
</script>
</html>

  浏览器访问http://localhost:8080/creditCardApply.html,浏览器展示:

   

   点击申请或修改信息重新申请

      

三、保险产品准入规则

 1、决策表

  前面的课程中我们编写的规则文件都是drl形式的文件,Drools除了支持drl形式的文件外还支持xls格式的文件(即Excel文件)。这种xls格式的文件通常称为决策表(decision table)。

  决策表(decision table)是一个“精确而紧凑的”表示条件逻辑的方式,非常适合商业级别的规则。决策表与现有的drl文件可以无缝替换。Drools提供了相应的API可以将xls文件编译为drl格式的字符串。

  一个决策表的例子如下:

   

  决策表语法:

   

   在决策表中还经常使用到占位符,语法为$后面加数字,用于替换每条规则中设置的具体值。$1表示取规则中的第一个值,$2表示取规则中的第二个值(22,25取25)。

  上面的决策表例子转换为drl格式的规则文件内容如下:

package rules;

import com.itheima.drools.entity.PersonInfoEntity;
import java.util.List;
global java.util.List listRules;

rule "personCheck_10"
    salience 65535
    agenda-group "sign"
    when
        $person : PersonInfoEntity(sex != "男")
    then
        listRules.add("性别不对");
end

rule "personCheck_11"
    salience 65534
    agenda-group "sign"
    when
        $person : PersonInfoEntity(age < 22 || age > 25)
    then
        listRules.add("年龄不合适");
end

rule "personCheck_12"
    salience 65533
    agenda-group "sign"
    when
        $person : PersonInfoEntity(salary < 10000)
    then
        listRules.add("工资太低了");
end

  要进行决策表相关操作,需要导入如下maven坐标:

<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-decisiontables</artifactId>
    <version>7.10.0.Final</version>
</dependency>

  通过下图可以发现,由于maven的依赖传递特性在导入drools-decisiontables坐标后,drools-core和drools-compiler等坐标也被传递了过来

   

  Drools提供的将xls文件编译为drl格式字符串的API如下:

String realPath = "C:\\testRule.xls";//指定决策表xls文件的磁盘路径
File file = new File(realPath);
InputStream is = new FileInputStream(file);
SpreadsheetCompiler compiler = new SpreadsheetCompiler();
String drl = compiler.compile(is, InputType.XLS);

  Drools还提供了基于drl格式字符串创建KieSession的API:

KieHelper kieHelper = new KieHelper();
kieHelper.addContent(drl, ResourceType.DRL);
KieSession session = kieHelper.build().newKieSession();

  基于决策表的入门案例:

   第一步:创建maven工程drools_decisiontable_demo并配置pom.xml文件

<dependency>
    <groupId>org.drools</groupId>
    <artifactId>drools-decisiontables</artifactId>
    <version>7.10.0.Final</version>
</dependency>
<dependency>
    <groupId>junit</groupId>
    <artifactId>junit</artifactId>
    <version>4.12</version>
</dependency>

   第二步:创建实体类PersonInfoEntity

package com.itheima.drools.entity;

public class PersonInfoEntity {
    private String sex;
    private int age;
    private double salary;

    public String getSex() {
        return sex;
    }

    public void setSex(String sex) {
        this.sex = sex;
    }

    public int getAge() {
        return age;
    }

    public void setAge(int age) {
        this.age = age;
    }

    public double getSalary() {
        return salary;
    }

    public void setSalary(double salary) {
        this.salary = salary;
    }
}

   第三步:创建xls规则文件(可以直接使用资料中提供的testRule.xls文件)

    

   第四步:创建单元测试

@Test
public void test1() throws Exception{
    String realPath = "d:\\testRule.xlsx";//指定决策表xls文件的磁盘路径
    File file = new File(realPath);
    InputStream is = new FileInputStream(file);
    SpreadsheetCompiler compiler = new SpreadsheetCompiler();
    String drl = compiler.compile(is, InputType.XLS);
    System.out.println(drl);
KieHelper kieHelper
= new KieHelper(); kieHelper.addContent(drl, ResourceType.DRL); KieSession session = kieHelper.build().newKieSession(); PersonInfoEntity personInfoEntity = new PersonInfoEntity(); personInfoEntity.setSex("男"); personInfoEntity.setAge(35); personInfoEntity.setSalary(1000); List<String> list = new ArrayList<String>(); session.setGlobal("listRules",list); session.insert(personInfoEntity); session.getAgenda().getAgendaGroup("sign").setFocus(); session.fireAllRules(); for (String s : list) { System.out.println(s); } session.dispose(); }

     执行test1,控制台输出excel转成的drl规则,及匹配规则结果

package rules;
//generated from Decision Table
import com.itheima.drools.entity.PersonInfoEntity;
import java.util.List;
global java.util.List listRules;
// rule values at B10, header at B5
rule "personCheck_10"
    salience 65535
    agenda-group "sign"
    when
        $person:PersonInfoEntity(sex != "男")
    then
        listRules.add("性别不对");
end

// rule values at B11, header at B5
rule "personCheck_11"
    salience 65534
    agenda-group "sign"
    when
        $person:PersonInfoEntity(age < 22 || age > 25)
    then
        listRules.add("年龄不合适");
end

// rule values at B12, header at B5
rule "personCheck_12"
    salience 65533
    agenda-group "sign"
    when
        $person:PersonInfoEntity(salary < 10000)
    then
        listRules.add("工资太低了");
end


年龄不合适
工资太低了

   当需要修改规则时不需要重新部署项目就能生效。

    

    重新执行test1,查看生成的drl规则变化:

    

 2、规则介绍

   各保险公司针对人身、财产推出了不同的保险产品,作为商业保险公司,筛选出符合公司利益最大化的客户是非常重要的,即各保险产品的准入人群是不同的,也就是说保险公司会针对不同的人群特征,制定不同的产品缴费和赔付规则。

  我们来看一下某保险产品准入规则的简化版,当不满足以下规则时,系统模块需要返回准入失败标识和失败原因

规则1:  保险公司是:PICC
规则2:  销售区域是:北京、天津
规则3:  投保人年龄:0 ~ 17岁
规则4:  保险期间是:20年、25年、30年
规则5:  缴费方式是:趸交(一次性交清)或年交
规则6:  保险期与交费期规则一:保险期间为20年期交费期间最长10年交且不能选择[趸交]
规则7:  保险期与交费期规则二:保险期间为25年期交费期间最长15年交且不能选择[趸交]
规则8:  保险期与交费期规则三:保险期间为30年期交费期间最长20年交且不能选择[趸交]
规则9:  被保人要求:(投保年龄+保险期间)不得大于40周岁
规则10: 保险金额规则:投保时约定,最低为5万元,超过部分必须为1000元的整数倍
规则11: 出单基本保额限额规则:线上出单基本保额限额62.5万元,超62.5万元需配合契调转线下出单

  在本案例中规则文件是一个Excel文件,业务人员可以直接更改这个文件中指标的值,系统不需要做任何变更。

 3、实现步骤

  本案例还是基于Spring Boot整合Drools的架构来实现。

   1. 创建maven工程insuranceInfoCheck并配置pom.xml文件

   2. 创建/resources/application.yml文件

   3. 创建实体类InsuranceInfo

   4. 创建决策表文件

   5. 封装工具类KieSessionUtils

   6. 创建RuleService类

   7. 创建RuleController类

   8. 创建启动类DroolsApplication

  第一步:创建maven工程insuranceInfoCheck并配置pom.xml文件

<?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">
    <modelVersion>4.0.0</modelVersion>
    <parent>
        <groupId>org.springframework.boot</groupId>
        <artifactId>spring-boot-starters</artifactId>
        <version>2.0.6.RELEASE</version>
    </parent>
    <groupId>com.itheima</groupId>
    <artifactId>insuranceInfoCheck</artifactId>
    <version>1.0-SNAPSHOT</version>

    <dependencies>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-web</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-aop</artifactId>
        </dependency>
        <dependency>
            <groupId>org.springframework.boot</groupId>
            <artifactId>spring-boot-starter-test</artifactId>
        </dependency>
        <dependency>
            <groupId>commons-lang</groupId>
            <artifactId>commons-lang</artifactId>
            <version>2.6</version>
        </dependency>
        <!--drools规则引擎-->
        <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>
            <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>
            <version>7.6.0.Final</version>
        </dependency>
    </dependencies>
    <build>
        <finalName>${project.artifactId}</finalName>
        <resources>
            <resource>
                <directory>src/main/java</directory>
                <includes>
                    <include>**/*.xml</include>
                </includes>
                <filtering>false</filtering>
            </resource>
            <resource>
                <directory>src/main/resources</directory>
                <includes>
                    <include>**/*.*</include>
                </includes>
                <filtering>false</filtering>
            </resource>
        </resources>
        <plugins>
            <plugin>
                <groupId>org.apache.maven.plugins</groupId>
                <artifactId>maven-compiler-plugin</artifactId>
                <version>2.3.2</version>
                <configuration>
                    <source>1.8</source>
                    <target>1.8</target>
                </configuration>
            </plugin>
        </plugins>
    </build>
</project>

  第二步:创建/resources/application.yml文件

server:
  port: 8080
spring:
  application:
    name: insuranceInfoCheck

  第三步:创建实体类InsuranceInfo

package com.itheima.drools.entity;

/**
 * 保险信息
 */
public class InsuranceInfo {
    private String param1;//保险公司
    private String param2;//方案代码
    private String param3;//渠道号
    private String param4;//销售区域
    private String param5;//投保年龄
    private String param6;//保险期间
    private String param7;//缴费期间
    private String param8;//缴费方式
    private String param9;//保障类型
    private String param10;//等待期
    private String param11;//犹豫期
    private String param12;//职业类型
    private String param13;//保额限制
    private String param14;//免赔额
    private String param15;//主险保额
    private String param16;//主险保费
    private String param17;//附加险保额
    private String param18;//附加险保费
    private String param19;//与投保人关系
    private String param20;//与被保人关系
    private String param21;//性别
    private String param22;//证件
    private String param23;//保费
    private String param24;//保额

    //getter setter省略
}

  第四步:创建决策表文件(也可以直接使用实战资料中提供的insuranceInfoCheck.xls文件)

   

  第五步:封装工具类KieSessionUtils

 
package com.itheima.drools.utils;

import org.drools.decisiontable.InputType;
import org.drools.decisiontable.SpreadsheetCompiler;
import org.kie.api.builder.Message;
import org.kie.api.builder.Results;
import org.kie.api.io.ResourceType;
import org.kie.api.runtime.KieSession;
import org.kie.internal.utils.KieHelper;
import java.io.File;
import java.io.FileInputStream;
import java.io.FileNotFoundException;
import java.io.InputStream;
import java.util.List;

public class KieSessionUtils {

    private KieSessionUtils() {

    }
    // 把xls文件解析为String
    public static String getDRL (String realPath) throws FileNotFoundException {
        File file = new File(realPath); // 例如:C:\\abc.xls
        InputStream is = new FileInputStream(file);
        SpreadsheetCompiler compiler = new SpreadsheetCompiler();
        String drl = compiler.compile(is, InputType.XLS);
        System.out.println(drl);
        return drl;
    }

    // drl为含有内容的字符串
    public static KieSession createKieSessionFromDRL(String drl) throws Exception{
        KieHelper kieHelper = new KieHelper();
        kieHelper.addContent(drl, ResourceType.DRL);
        Results results = kieHelper.verify();//校验drl语法
        if (results.hasMessages(Message.Level.WARNING, Message.Level.ERROR)) {
            List<Message> messages = results.getMessages(Message.Level.WARNING, Message.Level.ERROR);
            for (Message message : messages) {
                System.out.println("Error: "+message.getText());
            }
            // throw new IllegalStateException("Compilation errors were found. Check the logs.");
        }
        return kieHelper.build().newKieSession();
    }

    // realPath为Excel文件绝对路径
    public static KieSession getKieSessionFromXLS(String realPath) throws Exception {
        return createKieSessionFromDRL(getDRL(realPath));
    }
}

  第六步:创建RuleService类

 
package com.itheima.drools.service;

import com.itheima.drools.entity.InsuranceInfo;
import com.itheima.drools.utils.KieSessionUtils;
import org.kie.api.runtime.KieSession;
import org.springframework.stereotype.Service;
import java.util.ArrayList;
import java.util.List;

@Service
public class RuleService {
    public List<String> insuranceInfoCheck(InsuranceInfo insuranceInfo) throws Exception{
        KieSession session = KieSessionUtils.getKieSessionFromXLS("c:\\insuranceInfoCheck.xlsx");
        session.getAgenda().getAgendaGroup("sign").setFocus();

        session.insert(insuranceInfo);

        List<String> listRules = new ArrayList<>();
        session.setGlobal("listRules", listRules);

        session.fireAllRules();
        session.dispose();

        return listRules;
    }
}

  第七步:创建RuleController类

 
package com.itheima.drools.controller;

import com.itheima.drools.entity.InsuranceInfo;
import com.itheima.drools.service.RuleService;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RestController;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

@RestController
@RequestMapping("/rule")
public class RuleController {
    @Autowired
    private RuleService ruleService;

    @RequestMapping("/insuranceInfoCheck")
    public Map insuranceInfoCheck(){
        Map map = new HashMap();

        //模拟数据,实际应为页面传递过来
        InsuranceInfo insuranceInfo = new InsuranceInfo();
        insuranceInfo.setParam1("picc");
        insuranceInfo.setParam4("上海");
        insuranceInfo.setParam5("101");
        insuranceInfo.setParam6("12");
        insuranceInfo.setParam7("222");
        insuranceInfo.setParam8("1");
        insuranceInfo.setParam13("3");

        try {
            List<String> list = ruleService.insuranceInfoCheck(insuranceInfo);
            if(list != null && list.size() > 0){
                map.put("checkResult",false);
                map.put("msg","准入失败");
                map.put("detail",list);
            }else{
                map.put("checkResult",true);
                map.put("msg","准入成功");
            }
            return map;
        } catch (Exception e) {
            e.printStackTrace();
            map.put("checkResult",false);
            map.put("msg","未知错误");
            return map;
        }
    }
}

  第八步:创建启动类DroolsApplication

package com.itheima.drools;

import org.springframework.boot.SpringApplication;
import org.springframework.boot.autoconfigure.SpringBootApplication;

@SpringBootApplication
public class DroolsApplication {
    public static void main(String[] args) {
        SpringApplication.run(DroolsApplication.class);
    }
}

  执行启动类,浏览器访问http://localhost:8080/rule/insuranceInfoCheck,浏览器展示:

   

 

 

 

posted on 2024-04-09 19:38  花溪月影  阅读(17)  评论(0编辑  收藏  举报