跳转到内容

案例 8:税务合规风险打分

本案例对应 JQuick-Java 仓库 USE-CASES.md 的场景 8:税务合规风险打分,属于财税合规领域,解决按申报所属期对企业做「六维风险指标独立打分 → 风险分级 → 稽查处置路由 → 命中指标清单」的纳税合规评估问题。

税务机关或财税 SaaS 平台需要按税款所属期对企业进行批量纳税合规评估。企业税务画像包含:逾期申报次数、缺失发票次数、实际税负率与行业平均税负率、本期营业收入、进项占比、是否存在异常发票、注册年限以及纳税信用等级(A/B/M/C/D)等字段。

评估从六个维度独立打分:逾期申报每次 5 分、单项封顶 25 分;缺失发票每次 8 分、单项封顶 24 分;税负率低于行业均值一半得 20 分、低于八成得 12 分;进项占比超过 0.95 得 15 分、超过 0.85 得 8 分;存在异常发票一票记 20 分;成立不足 1 年得 10 分、不足 3 年得 5 分。六维汇总后按 70/40 两条线映射 HIGH/MEDIUM/LOW。

处置路由分为三路:存在异常发票或总分达到 70 分的,直接进入税务稽查(TAX_AUDIT)并立案调查;40 分以上的安排实地走访(FIELD_VISIT);其余企业常规自查(SELF_CHECK)。异常发票是独立于总分的「一票路由」——即使总分只有 20 分、等级 LOW,也照样移交稽查。

决策结果同时输出 7 条逐项指标清单(六个维度得分加风险总分)与关注备注(如「税负率显著低于行业均值,关注收入完整性」「进项占比过高,关注虚增进项风险」「新设企业,处于重点监控期」),支撑风险说明、约谈与事后审计回溯。场景 8 覆盖 6 组企业形态:多维全中的高风险企业(100 分)、合规良好企业(0 分自查)、中风险 48 分实地走访、异常发票单独触发稽查、进项异常 32 分、新设企业 72 分。

  • 六个维度的扣分口径(每次 5 分/8 分、封顶 25/24、0.5/0.8 倍行业均值、0.95/0.85 进项占比)散落在风控脚本与报表逻辑中,各分局或行业调整阈值都要改代码发版。
  • 税负率与行业均值比较若写成除法形式,既存在浮点精度问题又要防行业均值为零;硬编码时各处写法不一。
  • 六个维度分数叠加可能超过 100,分级阈值(70/40)与处置路由需要统一收口,否则不同入口算出的等级不一致。
  • 「异常发票一票稽查」与「按总分分级」是两条决策路径,写成普通 if-else 时容易退化为「必须高分才稽查」,放过低分高危企业。
  • 企业对评定结果有异议时,需要逐维度的得分明细作为解释材料,黑盒总分无法满足税务审计的可回溯要求。
  • 税务系统变更审批严格、发布窗口少,且大量配套系统运行在 JDK 8 与信创环境,需要轻量、可热更新、无重型中间件的规则方案。

本案例采用标准「六件套」:入参模型承载企业申报与经营快照,决策结果模型承载分数、等级、路由与指标清单,规则接口声明 10 个规则方法,XML 脚本外置全部扣分口径与阈值,Service 负责六维汇总、0-100 封顶与清单组装,JUnit 以 6 组企业形态回归。

层次 职责 对应文件
领域模型 企业税务画像、税务风险决策结果 TaxProfile.java、TaxRiskDecision.java
规则集与 XML 六维评分 + 风险等级 + 稽查/预警/立案路由共 10 个方法 TaxComplianceRules.java、tax-compliance.xml
编排服务 六维汇总并 clamp 到 0-100、7 条指标清单与关注备注组装 TaxComplianceService.java
测试 6 组企业形态(高风险/合规/中风险/异常发票/进项异常/新设) TaxComplianceServiceTest.java
规则方法 口径 分值/输出
scoreLate 逾期申报每次 5 分,单项封顶 25 0-25
scoreMissingInvoice 缺失发票每次 8 分,单项封顶 24 0-24
scoreBurden 税负率低于行业均值×0.5 得 20、低于×0.8 得 12(交叉相乘比较,不用除法) 0/12/20
scoreInputRatio 进项占比超过 0.95 得 15、超过 0.85 得 8 0/8/15
scoreAbnormal 存在异常发票一票记 20 分 0/20
scoreAge 成立不足 1 年得 10、不足 3 年得 5 0/5/10
riskLevel ≥70 HIGH / ≥40 MEDIUM / 否则 LOW HIGH/MEDIUM/LOW
auditLevel 异常发票或 ≥70 → TAX_AUDIT;≥40 → FIELD_VISIT;否则 SELF_CHECK TAX_AUDIT/FIELD_VISIT/SELF_CHECK
needWarning 总分 ≥40 推送风险提醒 true / false
needInvestigation 总分 ≥70 或存在异常发票 → 立案调查 true / false

将六个文件按仓库原有包结构放入 Maven 工程:两个模型放入 src/test/java/com/github/paohaijiao/demo/model/,规则接口与测试放入 com.github.paohaijiao.demo 包,编排服务放入 com.github.paohaijiao.demo.service 包,XML 脚本放入 src/test/resources/demo/tax-compliance.xml,包名全部保持源码原样。工程根目录执行 mvn test -Dtest=TaxComplianceServiceTest 即可运行。

package com.github.paohaijiao.demo.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 企业税务画像(税务风险打分所需的申报与经营快照)
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class TaxProfile {
/**
* 企业税号
*/
private String companyId;
/**
* 企业名称
*/
private String companyName;
/**
* 纳税人类型:GENERAL-一般纳税人 / SMALL_MICRO-小规模
*/
private String taxpayerType;
/**
* 申报所属期
*/
private String taxPeriod;
/**
* 逾期申报次数
*/
private int lateCount;
/**
* 缺失发票次数
*/
private int missingInvoice;
/**
* 实际税负率
*/
private double taxBurdenRate;
/**
* 行业平均税负率
*/
private double industryAvgBurden;
/**
* 本期营业收入(元)
*/
private double revenue;
/**
* 进项占比
*/
private double inputTaxRatio;
/**
* 是否存在异常发票
*/
private boolean hasAbnormalInvoice;
/**
* 注册年限
*/
private int yearsSinceRegister;
/**
* 纳税信用等级:A / B / M / C / D
*/
private String creditRating;
}
package com.github.paohaijiao.demo.model;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 税务合规风险决策结果
*/
@Data
public class TaxRiskDecision {
/**
* 企业税号
*/
private String companyId;
/**
* 风险总分(0-100)
*/
private int riskScore;
/**
* 风险等级:HIGH / MEDIUM / LOW
*/
private String riskLevel;
/**
* 处置方式:SELF_CHECK-自查 / FIELD_VISIT-实地核查 / TAX_AUDIT-税务稽查
*/
private String auditLevel;
/**
* 是否推送风险提醒
*/
private boolean needWarning;
/**
* 是否移交稽查
*/
private boolean needInvestigation;
/**
* 风险指标明细
*/
private List<String> indicators = new ArrayList<String>();
/**
* 处置建议
*/
private List<String> notes = new ArrayList<String>();
public void addIndicator(String indicator) {
this.indicators.add(indicator);
}
public void addNote(String note) {
this.notes.add(note);
}
}
package com.github.paohaijiao.demo;
import com.github.paohaijiao.xml.param.Param;
/**
* 场景 8(生产级样板):税务合规风险打分规则集
* <p>
* 规则按「多维风险指标 → 分级 → 处置路由」拆分,六个维度独立计分:
* 逾期申报、缺失发票、税负率异常、进项异常、异常发票、成立年限。
* 规则脚本见 {@code src/test/resources/demo/tax-compliance.xml}。
*/
public interface TaxComplianceRules {
/**
* 逾期申报扣分(每次 5 分,封顶 25)
*/
int scoreLate(@Param("lateCount") int lateCount);
/**
* 缺失发票扣分(每次 8 分,封顶 24)
*/
int scoreMissingInvoice(@Param("missingInvoice") int missingInvoice);
/**
* 税负率异常扣分:明显低于行业均值触发
*/
int scoreBurden(@Param("taxBurdenRate") double taxBurdenRate,
@Param("industryAvgBurden") double industryAvgBurden);
/**
* 进项占比异常扣分
*/
int scoreInputRatio(@Param("inputTaxRatio") double inputTaxRatio);
/**
* 异常发票扣分
*/
int scoreAbnormal(@Param("hasAbnormalInvoice") boolean hasAbnormalInvoice);
/**
* 新设企业扣分(成立时间越短风险越高)
*/
int scoreAge(@Param("yearsSinceRegister") int yearsSinceRegister);
/**
* 风险等级:HIGH / MEDIUM / LOW
*/
String riskLevel(@Param("riskScore") int riskScore);
/**
* 处置方式:SELF_CHECK / FIELD_VISIT / TAX_AUDIT
*/
String auditLevel(@Param("riskScore") int riskScore,
@Param("hasAbnormalInvoice") boolean hasAbnormalInvoice);
/**
* 是否推送风险提醒
*/
boolean needWarning(@Param("riskScore") int riskScore);
/**
* 是否移交稽查
*/
boolean needInvestigation(@Param("riskScore") int riskScore,
@Param("hasAbnormalInvoice") boolean hasAbnormalInvoice);
}
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE javas PUBLIC "-//PAOHAIJIAO//DTD API JAVA 1.0//EN"
"classpath:paohaijiao/dtd/Jquick-java.dtd">
<javas namespace="com.github.paohaijiao.demo.TaxComplianceRules">
<!-- 逾期申报扣分 -->
<java name="scoreLate" returnClass="int">
<![CDATA[
int def scoreLate(int:lateCount) {
int score = lateCount * 5;
if (score > 25) {
score = 25;
}
return score;
}
]]>
</java>
<!-- 缺失发票扣分 -->
<java name="scoreMissingInvoice" returnClass="int">
<![CDATA[
int def scoreMissingInvoice(int:missingInvoice) {
int score = missingInvoice * 8;
if (score > 24) {
score = 24;
}
return score;
}
]]>
</java>
<!-- 税负率异常扣分 -->
<java name="scoreBurden" returnClass="int">
<![CDATA[
int def scoreBurden(double:taxBurdenRate, double:industryAvgBurden) {
if (taxBurdenRate < industryAvgBurden * 0.5) {
return 20;
} else if (taxBurdenRate < industryAvgBurden * 0.8) {
return 12;
} else {
return 0;
}
}
]]>
</java>
<!-- 进项占比异常扣分 -->
<java name="scoreInputRatio" returnClass="int">
<![CDATA[
int def scoreInputRatio(double:inputTaxRatio) {
if (inputTaxRatio > 0.95) {
return 15;
} else if (inputTaxRatio > 0.85) {
return 8;
} else {
return 0;
}
}
]]>
</java>
<!-- 异常发票扣分 -->
<java name="scoreAbnormal" returnClass="int">
<![CDATA[
int def scoreAbnormal(boolean:hasAbnormalInvoice) {
if (hasAbnormalInvoice) {
return 20;
} else {
return 0;
}
}
]]>
</java>
<!-- 新设企业扣分 -->
<java name="scoreAge" returnClass="int">
<![CDATA[
int def scoreAge(int:yearsSinceRegister) {
if (yearsSinceRegister < 1) {
return 10;
} else if (yearsSinceRegister < 3) {
return 5;
} else {
return 0;
}
}
]]>
</java>
<!-- 风险等级 -->
<java name="riskLevel" returnClass="java.lang.String">
<![CDATA[
str def riskLevel(int:riskScore) {
if (riskScore >= 70) {
return "HIGH";
} else if (riskScore >= 40) {
return "MEDIUM";
} else {
return "LOW";
}
}
]]>
</java>
<!-- 处置方式 -->
<java name="auditLevel" returnClass="java.lang.String">
<![CDATA[
str def auditLevel(int:riskScore, boolean:hasAbnormalInvoice) {
if (hasAbnormalInvoice) {
return "TAX_AUDIT";
} else if (riskScore >= 70) {
return "TAX_AUDIT";
} else if (riskScore >= 40) {
return "FIELD_VISIT";
} else {
return "SELF_CHECK";
}
}
]]>
</java>
<!-- 是否推送提醒 -->
<java name="needWarning" returnClass="boolean">
<![CDATA[
boolean def needWarning(int:riskScore) {
if (riskScore >= 40) {
return true;
} else {
return false;
}
}
]]>
</java>
<!-- 是否移交稽查 -->
<java name="needInvestigation" returnClass="boolean">
<![CDATA[
boolean def needInvestigation(int:riskScore, boolean:hasAbnormalInvoice) {
if (hasAbnormalInvoice) {
return true;
} else if (riskScore >= 70) {
return true;
} else {
return false;
}
}
]]>
</java>
</javas>
package com.github.paohaijiao.demo.service;
import com.github.paohaijiao.demo.TaxComplianceRules;
import com.github.paohaijiao.demo.model.TaxProfile;
import com.github.paohaijiao.demo.model.TaxRiskDecision;
/**
* 场景 8:税务合规风险打分服务
* <p>
* 职责划分:
* <ul>
* <li><b>Java 侧</b>:六维指标汇总、封顶、处置建议组装</li>
* <li><b>规则侧</b>:各维度扣分口径、风险分级阈值、处置路由,全部外置在 XML 中</li>
* </ul>
*/
public class TaxComplianceService {
private final TaxComplianceRules rules;
public TaxComplianceService(TaxComplianceRules rules) {
this.rules = rules;
}
/**
* 评估税务合规风险
*
* @param profile 企业税务画像
* @return 风险决策
*/
public TaxRiskDecision evaluate(TaxProfile profile) {
TaxRiskDecision decision = new TaxRiskDecision();
decision.setCompanyId(profile.getCompanyId());
int lateScore = rules.scoreLate(profile.getLateCount());
int missingScore = rules.scoreMissingInvoice(profile.getMissingInvoice());
int burdenScore = rules.scoreBurden(profile.getTaxBurdenRate(), profile.getIndustryAvgBurden());
int inputScore = rules.scoreInputRatio(profile.getInputTaxRatio());
int abnormalScore = rules.scoreAbnormal(profile.isHasAbnormalInvoice());
int ageScore = rules.scoreAge(profile.getYearsSinceRegister());
int total = lateScore + missingScore + burdenScore + inputScore + abnormalScore + ageScore;
if (total > 100) {
total = 100;
} else if (total < 0) {
total = 0;
}
decision.setRiskScore(total);
decision.setRiskLevel(rules.riskLevel(total));
decision.setAuditLevel(rules.auditLevel(total, profile.isHasAbnormalInvoice()));
decision.setNeedWarning(rules.needWarning(total));
decision.setNeedInvestigation(rules.needInvestigation(total, profile.isHasAbnormalInvoice()));
decision.addIndicator("逾期申报扣分: " + lateScore);
decision.addIndicator("缺失发票扣分: " + missingScore);
decision.addIndicator("税负率异常扣分: " + burdenScore);
decision.addIndicator("进项异常扣分: " + inputScore);
decision.addIndicator("异常发票扣分: " + abnormalScore);
decision.addIndicator("成立年限扣分: " + ageScore);
decision.addIndicator("风险总分: " + total);
buildNotes(profile, decision);
return decision;
}
private void buildNotes(TaxProfile profile, TaxRiskDecision decision) {
if (profile.isHasAbnormalInvoice()) {
decision.addNote("存在异常发票,直接移交税务稽查");
}
if (profile.getLateCount() >= 3) {
decision.addNote("逾期申报 " + profile.getLateCount() + " 次,需督促按期申报");
}
if (profile.getTaxBurdenRate() < profile.getIndustryAvgBurden() * 0.5) {
decision.addNote("税负率显著低于行业均值,关注收入完整性");
}
if (profile.getInputTaxRatio() > 0.95) {
decision.addNote("进项占比过高,关注虚增进项风险");
}
if (profile.getYearsSinceRegister() < 1) {
decision.addNote("新设企业,处于重点监控期");
}
if ("D".equals(profile.getCreditRating())) {
decision.addNote("纳税信用等级 D,按最高风险级别管理");
}
if (decision.getRiskScore() < 40) {
decision.addNote("风险较低,纳入常规自查");
}
}
}
package com.github.paohaijiao.demo;
import com.github.paohaijiao.demo.model.TaxProfile;
import com.github.paohaijiao.demo.model.TaxRiskDecision;
import com.github.paohaijiao.demo.service.TaxComplianceService;
import com.github.paohaijiao.xml.JQuickJava;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* 场景 8 生产级样板:税务合规风险打分
* <p>
* 覆盖 6 组企业形态:高风险异常企业、合规良好企业、中风险临界、
* 异常发票触发稽查、进项异常、新设企业。
*/
public class TaxComplianceServiceTest {
private TaxComplianceService service;
@Before
public void setUp() {
TaxComplianceRules rules = JQuickJava.create()
.createApi(TaxComplianceRules.class, "demo/tax-compliance.xml");
this.service = new TaxComplianceService(rules);
}
/**
* 高风险异常企业:逾期 5 次、缺票 3 次、税负率极低、进项异常、异常发票 → 100 分、稽查
*/
@Test
public void shouldFlagHighRiskEnterprise() {
TaxProfile profile = new TaxProfile();
profile.setCompanyId("91310000MA1K0001X");
profile.setCompanyName("某贸易有限公司");
profile.setTaxpayerType("GENERAL");
profile.setTaxPeriod("2026Q2");
profile.setLateCount(5);
profile.setMissingInvoice(3);
profile.setTaxBurdenRate(0.01);
profile.setIndustryAvgBurden(0.03);
profile.setRevenue(8000000.0);
profile.setInputTaxRatio(0.97);
profile.setHasAbnormalInvoice(true);
profile.setYearsSinceRegister(1);
profile.setCreditRating("C");
TaxRiskDecision decision = service.evaluate(profile);
Assert.assertEquals(100, decision.getRiskScore());
Assert.assertEquals("HIGH", decision.getRiskLevel());
Assert.assertEquals("TAX_AUDIT", decision.getAuditLevel());
Assert.assertTrue(decision.isNeedWarning());
Assert.assertTrue(decision.isNeedInvestigation());
Assert.assertEquals(7, decision.getIndicators().size());
}
/**
* 合规良好企业:各项指标正常 → 0 分、自查
*/
@Test
public void shouldPassCompliantEnterprise() {
TaxProfile profile = new TaxProfile();
profile.setCompanyId("91310000MA1K0002X");
profile.setCompanyName("某科技股份有限公司");
profile.setTaxpayerType("GENERAL");
profile.setTaxPeriod("2026Q2");
profile.setLateCount(0);
profile.setMissingInvoice(0);
profile.setTaxBurdenRate(0.05);
profile.setIndustryAvgBurden(0.04);
profile.setRevenue(20000000.0);
profile.setInputTaxRatio(0.70);
profile.setHasAbnormalInvoice(false);
profile.setYearsSinceRegister(5);
profile.setCreditRating("A");
TaxRiskDecision decision = service.evaluate(profile);
Assert.assertEquals(0, decision.getRiskScore());
Assert.assertEquals("LOW", decision.getRiskLevel());
Assert.assertEquals("SELF_CHECK", decision.getAuditLevel());
Assert.assertFalse(decision.isNeedWarning());
Assert.assertFalse(decision.isNeedInvestigation());
Assert.assertTrue(decision.getNotes().contains("风险较低,纳入常规自查"));
}
/**
* 中风险临界:逾期 3 次、缺票 1 次、税负率偏低、进项偏高 → 48 分、实地核查
*/
@Test
public void shouldAssessMediumRiskEnterprise() {
TaxProfile profile = new TaxProfile();
profile.setCompanyId("91310000MA1K0003X");
profile.setCompanyName("某制造有限公司");
profile.setTaxpayerType("GENERAL");
profile.setTaxPeriod("2026Q2");
profile.setLateCount(3);
profile.setMissingInvoice(1);
profile.setTaxBurdenRate(0.03);
profile.setIndustryAvgBurden(0.04);
profile.setRevenue(5000000.0);
profile.setInputTaxRatio(0.90);
profile.setHasAbnormalInvoice(false);
profile.setYearsSinceRegister(2);
profile.setCreditRating("B");
TaxRiskDecision decision = service.evaluate(profile);
Assert.assertEquals(48, decision.getRiskScore());
Assert.assertEquals("MEDIUM", decision.getRiskLevel());
Assert.assertEquals("FIELD_VISIT", decision.getAuditLevel());
Assert.assertTrue(decision.isNeedWarning());
Assert.assertFalse(decision.isNeedInvestigation());
}
/**
* 异常发票触发稽查:总分仅 20(LOW)但存在异常发票 → 仍移交稽查
*/
@Test
public void shouldInvestigateDueToAbnormalInvoice() {
TaxProfile profile = new TaxProfile();
profile.setCompanyId("91310000MA1K0004X");
profile.setCompanyName("某服务有限公司");
profile.setTaxpayerType("SMALL_MICRO");
profile.setTaxPeriod("2026Q2");
profile.setLateCount(0);
profile.setMissingInvoice(0);
profile.setTaxBurdenRate(0.05);
profile.setIndustryAvgBurden(0.04);
profile.setRevenue(1000000.0);
profile.setInputTaxRatio(0.70);
profile.setHasAbnormalInvoice(true);
profile.setYearsSinceRegister(5);
profile.setCreditRating("B");
TaxRiskDecision decision = service.evaluate(profile);
Assert.assertEquals(20, decision.getRiskScore());
Assert.assertEquals("LOW", decision.getRiskLevel());
Assert.assertEquals("TAX_AUDIT", decision.getAuditLevel());
Assert.assertFalse(decision.isNeedWarning());
Assert.assertTrue(decision.isNeedInvestigation());
Assert.assertTrue(decision.getNotes().contains("存在异常发票,直接移交税务稽查"));
}
/**
* 进项异常:逾期 1 次、税负率偏低、进项占比 96% → 32 分、自查
*/
@Test
public void shouldWatchInputTaxRatio() {
TaxProfile profile = new TaxProfile();
profile.setCompanyId("91310000MA1K0005X");
profile.setCompanyName("某供应链有限公司");
profile.setTaxpayerType("GENERAL");
profile.setTaxPeriod("2026Q2");
profile.setLateCount(1);
profile.setMissingInvoice(0);
profile.setTaxBurdenRate(0.02);
profile.setIndustryAvgBurden(0.04);
profile.setRevenue(6000000.0);
profile.setInputTaxRatio(0.96);
profile.setHasAbnormalInvoice(false);
profile.setYearsSinceRegister(4);
profile.setCreditRating("B");
TaxRiskDecision decision = service.evaluate(profile);
Assert.assertEquals(32, decision.getRiskScore());
Assert.assertEquals("LOW", decision.getRiskLevel());
Assert.assertEquals("SELF_CHECK", decision.getAuditLevel());
Assert.assertFalse(decision.isNeedWarning());
Assert.assertFalse(decision.isNeedInvestigation());
Assert.assertTrue(decision.getNotes().contains("进项占比过高,关注虚增进项风险"));
}
/**
* 新设企业:逾期 2 次、缺票 4 次、税负率极低、进项偏高、成立不足 1 年 → 72 分、稽查
*/
@Test
public void shouldMonitorNewlyRegisteredEnterprise() {
TaxProfile profile = new TaxProfile();
profile.setCompanyId("91310000MA1K0006X");
profile.setCompanyName("某新设电商有限公司");
profile.setTaxpayerType("SMALL_MICRO");
profile.setTaxPeriod("2026Q2");
profile.setLateCount(2);
profile.setMissingInvoice(4);
profile.setTaxBurdenRate(0.02);
profile.setIndustryAvgBurden(0.05);
profile.setRevenue(3000000.0);
profile.setInputTaxRatio(0.88);
profile.setHasAbnormalInvoice(false);
profile.setYearsSinceRegister(0);
profile.setCreditRating("M");
TaxRiskDecision decision = service.evaluate(profile);
Assert.assertEquals(72, decision.getRiskScore());
Assert.assertEquals("HIGH", decision.getRiskLevel());
Assert.assertEquals("TAX_AUDIT", decision.getAuditLevel());
Assert.assertTrue(decision.isNeedWarning());
Assert.assertTrue(decision.isNeedInvestigation());
Assert.assertTrue(decision.getNotes().contains("新设企业,处于重点监控期"));
}
}

在工程根目录执行:

Terminal window
mvn test -Dtest=TaxComplianceServiceTest
测试方法 构造的业务形态 关键断言/期望决策
shouldFlagHighRiskEnterprise 贸易公司:逾期 5、缺票 3、税负 0.01/均值 0.03、进项 0.97、异常发票、成立 1 年 总分 100(封顶)、HIGH、TAX_AUDIT、需预警、需立案、指标 7 条
shouldPassCompliantEnterprise 科技公司:零逾期零缺票、税负 0.05/均值 0.04、进项 0.70、A 级信用 总分 0、LOW、SELF_CHECK、不预警、不立案、含常规自查备注
shouldAssessMediumRiskEnterprise 制造公司:逾期 3、缺票 1、税负 0.03/0.04、进项 0.90、成立 2 年 总分 48(15+8+12+8+5)、MEDIUM、FIELD_VISIT、需预警、不立案
shouldInvestigateDueToAbnormalInvoice 服务公司:仅异常发票,其余正常 总分 20、LOW,但 auditLevel 为 TAX_AUDIT、不预警却需立案、含异常发票稽查备注
shouldWatchInputTaxRatio 供应链公司:逾期 1、税负 0.02/0.04、进项 0.96、成立 4 年 总分 32(5+12+15)、LOW、SELF_CHECK、不预警、不立案、含进项过高备注
shouldMonitorNewlyRegisteredEnterprise 新设电商:逾期 2、缺票 4、税负 0.02/0.05、进项 0.88、成立 0 年 总分 72(10+24+20+8+10)、HIGH、TAX_AUDIT、需预警、需立案、含重点监控期备注

USE-CASES.md 场景 8 收录的两组实测输出:

高风险企业(多维度全部命中):

风险分: 100 (HIGH) 稽查方式: TAX_AUDIT 立案调查: true
命中指标: 7 条(逾期申报、缺票、税负率显著偏低、进项异常、异常发票、新设企业……)

异常发票但总分不高(分数 20,LOW):

风险分: 20 (LOW) 稽查方式: TAX_AUDIT(异常发票一票路由,不受等级限制)
  • 六个维度每个是一个独立规则方法,单次分值、封顶值(25/24/20/15/10)与分级阈值全部外置 XML,税务口径调整改脚本即可热更新,适合审批严格、发布窗口少的税务系统。
  • 税负率比较采用交叉相乘写法(脚本中以实际税负率与「行业均值 × 0.5」直接比较大小),规避浮点除法的精度与除零风险,语义仍是「低于行业均值一半」。
  • Service 在 Java 侧把六维总分统一 clamp 到 0-100,并固定组装 7 条逐维得分指标,每家企业的评定依据天然可解释、可审计。
  • 异常发票「一票路由」在 auditLevel 与 needInvestigation 两个脚本方法中均前置判断,20 分 LOW 企业也直接稽查立案,不受分级结果限制。
  • 规则接口通过 @Param 按名绑定参数,代理实例一次创建即可在批量评估任务中复用,底层为 ASM 调用链,高并发批量打分无额外进程开销。
  • Java 8 即可运行、无重型规则平台依赖,可直接嵌入金税配套系统与信创 JDK 环境。