天天看點

第2-4-8章 規則引擎Drools實戰(1)-個人所得稅電腦

文章目錄

    • 9. Drools實戰
      • 9.1 個人所得稅電腦
        • 9.1.1 名詞解釋
        • 9.1.2 計算規則
          • 9.1.2.1 新稅制主要有哪些變化?
          • 9.1.2.2 資較高人員本次個稅較少,可能到年底扣稅增加?
          • 9.1.2.3 關于年度彙算清繳
          • 9.1.2.4 個人所得稅預扣率表(居民個人工資、薪金所得預扣預繳适用)
          • 9.1.2.5 五險一金繳費比例
        • 9.1.3 實作步驟
          • 9.1.3.1 建立maven工程calculation并配置pom.xml檔案
          • 9.1.3.2 建立/resources/application.yml檔案
          • 9.1.3.3 編寫配置類DroolsConfig
          • 9.1.3.4 編寫各種實體類
          • 9.1.3.5 在resources/rules下建立規則檔案calculation.drl檔案
          • 9.1.3.6 建立RuleServiceImpl
          • 9.1.3.7 建立RuleController
          • 9.1.3.8 建立啟動類DroolsApplication

9. Drools實戰

全套代碼及資料全部完整提供,點此處下載下傳

9.1 個人所得稅電腦

本小節我們需要通過Drools規則引擎來根據規則計算個人所得稅,最終資料效果如下:

第2-4-8章 規則引擎Drools實戰(1)-個人所得稅電腦

9.1.1 名詞解釋

稅前月收入:即稅前工資,指交納個人所得稅之前的總工資

應納稅所得額:指按照稅法規定确定納稅人在一定期間所獲得的所有應稅收入減除在該納稅期間依法允許減除的各種支出後的餘額

稅率:是對征稅對象的征收比例或征收額度

速算扣除數:指為解決超額累進稅率分級計算稅額的複雜技術問題,而預先計算出的一個資料,可以簡化計算過程

扣稅額:是指實際繳納的稅額

稅後工資:是指扣完稅後實際到手的工資收入

9.1.2 計算規則

要實作個人所得稅電腦,需要了解2019個稅扣繳變化:

應納稅所得額=(月收入-五險一金-起征點-依法确定的其他扣除-專項附加扣除)*适用稅率
9.1.2.1 新稅制主要有哪些變化?
  1. 扣繳義務人在支付居民個人工資、薪金所得時,需按照“累計預扣法”核算預扣個人所得稅。較之前按月的稅率表變化成按年計稅,适用個人所得稅預扣率表
  2. 首次設立了子女教育、繼續教育、大病醫療、住房貸款利息或住房租金、贍養老人六項專項附加扣除。(在稅前工資扣除相應額度後核算個稅)
9.1.2.2 資較高人員本次個稅較少,可能到年底扣稅增加?

〔例1〕某職員2015年入職,2019年每月應發工資均為10000元,每月減除費用5000元,“三險一金”等專項扣除為1500元,從1月起享受子女教育專項附加扣除1000元,沒有減免收入及減免稅額等情況,以前三個月為例,應當按照以下方法計算預扣預繳稅額:

1月份:(10000-5000-1500-1000)×3% =75元;

2月份:(10000×2-5000×2-1500×2-1000×2)×3%-75 =75元;

3月份:(10000×3-5000×3-1500×3-1000×3)×3%-75-75 =75元;

進一步計算可知,該納稅人全年累計預扣預繳應納稅所得額為30000元,一直适用3%的稅率,是以各月應預扣預繳的稅款相同

〔例2〕某職員2015年入職,2019年每月應發工資均為30000元,每月減除費用5000元,“三險一金”等專項扣除為4500元,享受子女教育、贍養老人兩項專項附加扣除共計2000元,沒有減免收入及減免稅額等情況,以前三個月為例,應當按照以下方法計算各月應預扣預繳稅額:

1月份:(30000–5000-4500-2000)×3% = 555元;

2月份:(30000×2-5000×2-4500×2-2000×2)×10% -2520 -555 =625元;

3月份:(30000×3-5000×3-4500×3-2000×3)×10% -2520 -555-625 =1850元;

按照上述算法,每一個月應納個稅額:

項目 1月 2月 3月 4月 5月 6月 7月 8月 9月 10月 11月 12月
每月稅前工資 30000 30000 30000 30000 30000 30000 30000 30000 30000 30000 30000 30000
扣除各項減免額後應對稅率 3% 10% 10% 10% 10% 10% 10% 20% 20% 20% 20% 20%
月應預扣預繳個稅 555 625 1850 1850 1850 1850 1850 2250 3700 3700 3700 3700

上述計算結果表明,由于2月份累計預扣預繳應納稅所得額為37000元,已适用10%的稅率,是以2月份和3月份應預扣預繳有所增高。8月份已适用于20%的稅率。根據上述計算結果表明,雖每月工資薪金額和相關扣除額相同,年度内越往後,應納稅所得額越大适用稅率越高,則預扣預繳稅款可能會越大,會出現類似“12月取得工資10000元預扣預繳的個稅稅款,比1月份取得工資薪金30000元預扣預繳的個稅稅款多的”現象。

〔例3〕某職員2022年每月應發工資均為15000元,每月減除費用5000元,養老保險個人繳納8%,為15000×8%=1200元,失業保險個人繳納0.5%,為15000×0.5%=75元,醫療保險個人繳納2%+3元,為15000×2%+3=303元,住房公積金個人繳納比例12%,為15000×12%=1800元,享受住房租金專項附加扣除1500元,沒有減免收入及減免稅額等情況,以前三個月的計算為例,應當按照以下方法計算各月應預扣預繳稅額:

應納稅所得額=月收入-五險一金-起征點-依法确定的其他扣除-專項附加扣除

1月應納稅所得額=15000–5000-1200-75-303-1800-1500=5122元

1月份:1月應納稅所得額 × 3% = 153.66元;

2月份:1月應納稅所得額 × 2 × 3% -153.66 =153.66元;

3月份:1月應納稅所得額 × 3 × 3% -153.66 - 153.66=153.66元;

按照上述算法,每一個月應納個稅額:

項目 1月 2月 3月 4月 5月 6月 7月 8月 9月 10月 11月 12月
每月稅前工資 15000 15000 15000 15000 15000 15000 15000 15000 15000 15000 15000 15000
扣除各項減免額後應對稅率 3% 3% 3% 3% 3% 3% 3% 10% 10% 10% 10% 10%
月應預扣預繳個稅 153.66 153.66 153.66 153.66 153.66 153.66 153.66 501.98 512.2 512.2 512.2 512.2

上述計算結果表明,由于8月份累計預扣預繳應納稅所得額為40976元,已适用10%的稅率,是以8月份應預扣預繳有所增高。

9.1.2.3 關于年度彙算清繳

一般來講,有以下情形之一的,您可以選擇在次年3月1日至6月30日内,自行向彙繳地主管稅務機關辦理彙算清繳申報時進行專項附加扣除,稅款多退少補;(1)不願意通過機關辦理扣除,未将相關專項附加扣除資訊報送給任職受雇機關的;

(2)在同一納稅年度涉及有兩處工資薪金所得或涉及勞務報酬所得的;

(3)有大病醫療支出項目的;

(4)納稅年度内未享受或未足額享受專項附加扣除等情形。

9.1.2.4 個人所得稅預扣率表(居民個人工資、薪金所得預扣預繳适用)
級數 全年累計預扣預繳應納稅所得額 稅率(%) 速算扣除數
1 不超過36,000元的部分 3
2 超過36,000元至144,000元的部分 10 2520
3 超過144,000元至300,000元的部分 20 16920
4 超過300,000元至420,000元的部分 25 31920
5 超過420,000元至660,000元的部分 30 52920
6 超過660,000元至960,000元的部分 35 85920
7 超過960,000元的部分 45 181920
9.1.2.5 五險一金繳費比例
五險一金繳費比例:養老保險機關21%,個人8%;醫療保險機關9%,個人2%+3元;失業保險機關0.5%,個人0.5%;工傷保險和生育保險由機關繳納,勞動者不用繳;住房公積金根據企業的實際情況,選擇住房公積金繳費比例,下限為5%,最高不超12%。

9.1.3 實作步驟

本實戰案例我們基于Spring Boot整合Drools的方式來實作。

目錄結構

第2-4-8章 規則引擎Drools實戰(1)-個人所得稅電腦
9.1.3.1 建立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>cn.itcast</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>
           
9.1.3.2 建立/resources/application.yml檔案
server:
  port: 8080
spring:
  application:
    name: calculation
           
9.1.3.3 編寫配置類DroolsConfig
package com.guohaowei.drools.config;
import cn.hutool.core.date.DatePattern;
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", DatePattern.NORM_DATE_PATTERN);
        KieFileSystem kieFileSystem = kieServices.newKieFileSystem();
        ResourcePatternResolver resourcePatternResolver =
                new PathMatchingResourcePatternResolver();
        Resource[] files =
                resourcePatternResolver.getResources("classpath*:" + RULES_PATH + "*.*");
        String path;
        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();
    }
}
           
9.1.3.4 編寫各種實體類
  • 編寫實體類CalculationDto
package com.guohaowei.drools.domain.dto;

import com.guohaowei.drools.domain.vo.CalculationVo;
import lombok.Data;

import java.math.BigDecimal;
import java.util.List;

/**
 * @author guohaowei
 */
@Data
public class CalculationDto {
    /**
     * 12個月的工資繳稅額等資訊
     */
    private List<CalculationVo> calculationVoList;
//--------------------------------重複字段用于計算start--------------------------------------------------------
    /**
     * 稅前工資
     */
    private BigDecimal wageBeforeTax;
    /**
     * 預扣預繳應納稅所得額
     */
    private BigDecimal taxableIncome;
    /**
     * 累計預扣預繳應納稅所得額
     */
    private BigDecimal taxableIncomeCount;
    /**
     * 月稅後工資
     */
    private BigDecimal wageDeductedTax;
//--------------------------------重複字段用于計算end----------------------------------------------------------
    /**
     * 個人所得稅起征點
     */
    private BigDecimal threshold;
    /**
     * 養老保險個人繳納比例,預設8%
     */
    private BigDecimal oldAgeInsuranceRatio;
    /**
     * 失業保險個人繳納比例,預設0.5%
     */
    private BigDecimal unemploymentInsuranceRatio;
    /**
     * 醫療保險個人繳納比例,預設2% + 3元
     */
    private BigDecimal medicalInsuranceRatio = BigDecimal.valueOf(0.02);
    /**
     * 醫療保險個人繳納比例,預設2% + 3元
     */
    private BigDecimal medicalInsuranceAdd = BigDecimal.valueOf(3);
    /**
     * 住房公積金個人繳納比例,預設12%
     */
    private BigDecimal housingFundRatio;
    /**
     * 住房租金專項附加扣除,預設1500元
     * 其他專項附加扣除同理,此處省略
     */
    private BigDecimal specialAdditionalDeductionRent;

}
           
  • 編寫實體類CalculationVo
package com.guohaowei.drools.domain.vo;

import lombok.Builder;
import lombok.Data;

import java.math.BigDecimal;

/**
 * @author guohaowei
 */
@Data
@Builder
public class CalculationVo {
    /**
     * 月稅前工資
     */
    private BigDecimal wageBeforeTax;
    /**
     * 月預扣預繳應納稅所得額
     */
    private BigDecimal taxableIncome;
    /**
     * 月稅率
     */
    private BigDecimal taxRate;
    /**
     * 月速算扣除數
     */
    private BigDecimal quickDeduction;
    /**
     * 月扣稅額
     */
    private BigDecimal taxRebate;
    /**
     * 月稅後工資
     */
    private BigDecimal wageDeductedTax;
    /**
     * 月份
     */
    private Integer month;
}
           
  • 編寫枚舉類WithholdingTaxRateEnum
package com.guohaowei.drools.enums;

import lombok.AllArgsConstructor;
import lombok.Getter;

import java.math.BigDecimal;

/**
 * @Author: 郭浩偉 qq:912161367
 * @Date: 2022/11/17 0017 7:18
 * @Description: 個人所得稅預扣率表(居民個人工資、薪金所得預扣預繳适用)
 */
@AllArgsConstructor
@Getter
public enum WithholdingTaxRateEnum {
    /**
     * 級數
     */
    one(36_000, "0.03", 0),
    two(144_000, "0.10", 2520),
    three(300_000, "0.20", 16920),
    four(420_000, "0.25", 31920),
    five(660_000, "0.30", 52920),
    six(960_000, "0.35", 85920),
    seven(960_000, "0.45", 181920),
    ;

    /**
     * 全年累計預扣預繳應納稅所得額
     */
    private Integer taxableIncome;
    /**
     * 稅率,如0.03
     */
    private String taxRate;
    /**
     * 速算扣除數
     */
    private Integer quickDeduction;

    /**
     * 根據【應納稅所得額】得到含對應的【稅率】和【速算扣除數】的枚舉值
     *
     * @param actualTaxableIncome
     * @return
     */
    public static WithholdingTaxRateEnum getInfoByIncome(BigDecimal actualTaxableIncome) {
        if(null == actualTaxableIncome){
            return null;
        }
        for (WithholdingTaxRateEnum value : WithholdingTaxRateEnum.values()) {
            if(actualTaxableIncome.compareTo(BigDecimal.valueOf(value.getTaxableIncome())) <= 0){
                return value;
            }
        }
        return seven;
    }
}
           
  • 編寫實體類CalculationReq
package com.guohaowei.drools.domain.req;


import lombok.Data;

import java.math.BigDecimal;

/**
 * @Author: 郭浩偉 qq:912161367
 * @Date: 2022/11/16 0016 23:31
 * @Description:
 */
@Data
public class CalculationReq {
    /**
     * 稅前工資
     */
    private BigDecimal wageBeforeTax;
    /**
     * 個人所得稅起征點,預設5000元
     */
    private BigDecimal threshold = BigDecimal.valueOf(5000);
    /**
     * 養老保險個人繳納比例,預設8%
     */
    private BigDecimal oldAgeInsuranceRatio = BigDecimal.valueOf(0.08);
    /**
     * 失業保險個人繳納比例,預設0.5%
     */
    private BigDecimal unemploymentInsuranceRatio = BigDecimal.valueOf(0.005);
    /**
     * 醫療保險個人繳納比例,預設2% + 3元
     */
    private BigDecimal medicalInsuranceRatio = BigDecimal.valueOf(0.02);
    /**
     * 醫療保險個人繳納比例,預設2% + 3元
     */
    private BigDecimal medicalInsuranceAdd = BigDecimal.valueOf(3);
    /**
     * 住房公積金個人繳納比例,預設12%
     */
    private BigDecimal housingFundRatio = BigDecimal.valueOf(0.12);
    /**
     * 住房租金專項附加扣除,預設1500元
     * 其他專項附加扣除同理,此處省略
     */
    private BigDecimal specialAdditionalDeductionRent = BigDecimal.valueOf(1500);

}
           
9.1.3.5 在resources/rules下建立規則檔案calculation.drl檔案
//目前規則檔案用于計算個人所得稅
package calculation
import java.math.BigDecimal
import com.guohaowei.drools.domain.dto.CalculationDto
import com.guohaowei.drools.enums.WithholdingTaxRateEnum
import java.util.ArrayList
import com.guohaowei.drools.domain.vo.CalculationVo
import java.util.List
global com.guohaowei.drools.domain.dto.CalculationDto calculationGlobalDto
/**
    目前規則檔案中的規則主要分為三類
    1、計算應納稅所得額有1個規則
    2、設定稅率、速算扣除數有7個規則
    3、計算稅後工資有1個規則
**/

//計算應納稅所得額
rule "計算應納稅所得額-減去個稅起征點"
    salience 100
    date-effective "2019-01-01"
    no-loop true
    when
    then
        //稅前工資
        BigDecimal wageBeforeTax = calculationGlobalDto.getWageBeforeTax();
        BigDecimal taxableIncome = wageBeforeTax.subtract(calculationGlobalDto.getThreshold());

        calculationGlobalDto.setWageBeforeTax(wageBeforeTax);
        calculationGlobalDto.setWageDeductedTax(wageBeforeTax);
        calculationGlobalDto.setTaxableIncome(BigDecimal.ZERO);
        if(taxableIncome.compareTo(BigDecimal.valueOf(0)) > 0){
            calculationGlobalDto.setTaxableIncome(taxableIncome);
        }

end

rule "計算應納稅所得額-減去養老保險個人繳納金額"
    salience 99
    date-effective "2019-01-01"
    no-loop true
    when
    then
        //養老保險個人繳納額
        BigDecimal oldAgeInsurance = calculationGlobalDto.getWageBeforeTax().multiply(calculationGlobalDto.getOldAgeInsuranceRatio());
        //預扣預繳應納稅所得額
        BigDecimal taxableIncome = calculationGlobalDto.getTaxableIncome();
        //應納稅所得額如果小于0即不需要繳稅也就不需要計算了
        if(taxableIncome.compareTo(BigDecimal.valueOf(0)) > 0){
            taxableIncome = taxableIncome.subtract(oldAgeInsurance);
        calculationGlobalDto.setTaxableIncome(taxableIncome);
        }
        //稅後工資
        calculationGlobalDto.setWageDeductedTax(calculationGlobalDto.getWageBeforeTax().subtract(oldAgeInsurance));
end

rule "計算應納稅所得額-減去失業保險個人繳納金額"
    salience 98
    date-effective "2019-01-01"
    no-loop true
    when
    then
        //失業保險個人繳納額
        BigDecimal unemploymentInsurance = calculationGlobalDto.getWageBeforeTax().multiply(calculationGlobalDto.getUnemploymentInsuranceRatio());
        //應納稅所得額如果小于0即不需要繳稅也就不需要計算了
        if(calculationGlobalDto.getTaxableIncome().compareTo(BigDecimal.valueOf(0)) > 0){
            BigDecimal taxableIncome = calculationGlobalDto.getTaxableIncome().subtract(unemploymentInsurance);
            calculationGlobalDto.setTaxableIncome(taxableIncome);
        }
        //稅後工資
        calculationGlobalDto.setWageDeductedTax(calculationGlobalDto.getWageDeductedTax().subtract(unemploymentInsurance));
end

rule "計算應納稅所得額-減去醫療保險個人繳納金額"
    salience 97
    date-effective "2019-01-01"
    no-loop true
    when
    then
        //醫療保險個人繳納額
        BigDecimal medicalInsurance = calculationGlobalDto.getWageBeforeTax().multiply(calculationGlobalDto.getMedicalInsuranceRatio()).add(calculationGlobalDto.getMedicalInsuranceAdd());
        //應納稅所得額如果小于0即不需要繳稅也就不需要計算了
        if(calculationGlobalDto.getTaxableIncome().compareTo(BigDecimal.valueOf(0)) > 0){
            BigDecimal taxableIncome = calculationGlobalDto.getTaxableIncome().subtract(medicalInsurance);
            calculationGlobalDto.setTaxableIncome(taxableIncome);
        }
        //稅後工資
        calculationGlobalDto.setWageDeductedTax(calculationGlobalDto.getWageDeductedTax().subtract(medicalInsurance));
end

rule "計算應納稅所得額-減去住房公積金個人繳納金額"
    salience 96
    date-effective "2019-01-01"
    no-loop true
    when
    then
        //住房公積金個人繳納額
        BigDecimal housingFund = calculationGlobalDto.getWageBeforeTax().multiply(calculationGlobalDto.getHousingFundRatio());
        //應納稅所得額如果小于0即不需要繳稅也就不需要計算了
        if(calculationGlobalDto.getTaxableIncome().compareTo(BigDecimal.valueOf(0)) > 0){
            BigDecimal taxableIncome = calculationGlobalDto.getTaxableIncome().subtract(housingFund);
            calculationGlobalDto.setTaxableIncome(taxableIncome);
        }
        //稅後工資
        calculationGlobalDto.setWageDeductedTax(calculationGlobalDto.getWageDeductedTax().subtract(housingFund));
end

rule "計算應納稅所得額-減去住房租金專項附加扣除金額"
    salience 95
    date-effective "2019-01-01"
    no-loop true
    when
    then
    //應納稅所得額如果小于0即不需要繳稅也就不需要計算了
     if(calculationGlobalDto.getTaxableIncome().compareTo(BigDecimal.valueOf(0)) > 0){
        BigDecimal taxableIncome = calculationGlobalDto.getTaxableIncome().subtract(calculationGlobalDto.getSpecialAdditionalDeductionRent());
        calculationGlobalDto.setTaxableIncome(taxableIncome);
     }
end

rule "計算出最終的統計結果"
    salience 94
    date-effective "2019-01-01"
    no-loop true
    when
    then
        List<CalculationVo> calculationVoList = new ArrayList<>();
        BigDecimal taxRebateCount = BigDecimal.ZERO;
        //當月扣稅額=應納稅所得額x月份數x稅率-速算扣除數-累計扣稅額
        for(int i = 1; i <= 12 ; i++) {
          //累計應納稅所得額
          BigDecimal taxableIncomeCount = calculationGlobalDto.getTaxableIncome().multiply(BigDecimal.valueOf(i));
          WithholdingTaxRateEnum withholdingTaxRateEnum = WithholdingTaxRateEnum.getInfoByIncome(taxableIncomeCount);
          //月稅率
          BigDecimal taxRate = new BigDecimal(withholdingTaxRateEnum.getTaxRate());
          //月速算扣除數
          BigDecimal quickDeduction = BigDecimal.valueOf(withholdingTaxRateEnum.getQuickDeduction());
          //月扣稅額
          BigDecimal taxRebate =taxableIncomeCount.multiply(taxRate).subtract(quickDeduction).subtract(taxRebateCount);
          //累計扣稅額
          taxRebateCount = taxRebateCount.add(taxRebate);
          //組裝vo
          CalculationVo calculationVo = CalculationVo.builder().wageBeforeTax(calculationGlobalDto.getWageBeforeTax()).taxableIncome(calculationGlobalDto.getTaxableIncome()).
          taxRate(taxRate).quickDeduction(quickDeduction).taxRebate(taxRebate).wageDeductedTax(calculationGlobalDto.getWageDeductedTax().subtract(taxRebate)).month(i).build();
          calculationVoList.add(calculationVo);
        }
        calculationGlobalDto.setCalculationVoList(calculationVoList);
end

           
9.1.3.6 建立RuleServiceImpl
package com.guohaowei.drools.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.guohaowei.drools.domain.dto.CalculationDto;
import com.guohaowei.drools.domain.req.CalculationReq;
import com.guohaowei.drools.domain.vo.CalculationVo;
import com.guohaowei.drools.service.RuleService;
import org.kie.api.KieBase;
import org.kie.api.runtime.KieSession;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Service;

import java.util.List;
/**
 * @Author: 郭浩偉 qq:912161367
 * @Date: 2022/11/18 0018 19:41
 * @Description:
 */
@Service
public class RuleServiceImpl implements RuleService {
    @Autowired
    private KieBase kieBase;

    /**
     * 調用Drools規則引擎實作個人所得稅計算
     *
     * @param req
     * @return
     */
    @Override
    public List<CalculationVo> calculate(CalculationReq req) {
        KieSession session = kieBase.newKieSession();
        CalculationDto calculationDto = BeanUtil.copyProperties(req, CalculationDto.class);
        //初始化稅後工資=稅前工資
        calculationDto.setWageDeductedTax(calculationDto.getWageBeforeTax());
        //設定全局變量,名稱和類型必須和規則檔案中定義的全局變量名稱對應
        session.setGlobal("calculationGlobalDto",calculationDto);
        session.fireAllRules();
        session.dispose();
        return calculationDto.getCalculationVoList();
    }
}

           
9.1.3.7 建立RuleController
package com.guohaowei.drools.controller;

import com.guohaowei.drools.domain.req.CalculationReq;
import com.guohaowei.drools.domain.vo.CalculationVo;
import com.guohaowei.drools.service.impl.RuleServiceImpl;
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;

import java.util.List;

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

    @RequestMapping("/calculate")
    public List<CalculationVo> calculate(@RequestBody CalculationReq req){
        return ruleService.calculate(req);
    }
}

           
9.1.3.8 建立啟動類DroolsApplication
package com.guohaowei.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);
    }
}
           

全套代碼及資料全部完整提供,點此處下載下傳