Case 11: Government talent subsidy review
This case corresponds to Scenario 11 in the JQuick-Java repository’s USE-CASES.md, targeting online application review for talent settlement, startup, and rent subsidies in government enterprise services. For each subsidy application, the system first applies a one-vote veto for dishonesty (discredited person), criminal record, or duplicate claims, then scores five dimensions independently — education, age, local social security, talent recognition, and startup-driven employment — producing a conclusion of “approve / manual review / reject”. Only approved applications receive itemized amounts by education, talent category, and employment creation. All decision criteria are driven by JQuick-Java XML rule proxies.
Industry scenario
Section titled “Industry scenario”Talent subsidies are a high-frequency application item in local government services. Applicants submit settlement subsidy (SETTLEMENT), startup subsidy (STARTUP), or rent subsidy (RENT) applications online, filling in education (PHD / MASTER / BACHELOR / COLLEGE), age, consecutive months of local social security contributions, high-level talent recognition category (A / B / C / NONE), whether they have founded a company in the city, and how many jobs it created, while authorizing the reviewing department to verify discredited-person records, criminal records, prior claims of similar subsidies, and document completeness.
The review decision runs in two stages. The first stage is the eligibility gate: any hit among discredited-person status, criminal record, or prior claim of a similar subsidy triggers a one-vote veto and direct rejection; applications with incomplete documents are returned for correction and routed to manual review; applications with complete documents enter the five-dimension scoring with a total of 110 points — 75 or above passes (QUALIFIED), 50 to 74 goes to manual review (MANUAL_REVIEW), below 50 is rejected (REJECTED).
The second stage is itemized fixed amounts, effective only for QUALIFIED applications: the education settlement subsidy is fixed at 100,000 for PhD, 60,000 for Master’s, 30,000 for Bachelor’s, and 10,000 for College; the talent recognition reward is fixed at 200,000 for category A, 100,000 for B, and 50,000 for C; the startup employment reward is fixed at 100,000 for ≥10 employees, 50,000 for ≥5, and 20,000 for ≥1. The three items sum to the approved subsidy amount. Applications in review or rejected receive no amounts.
Beyond the conclusion and amounts, the decision result also contains item-by-item review rationales (reasons): the reason for a hit veto item, document correction hints, the eligibility score, and explanations for being in the review range or below standard, supporting counter notification, manual review, and post-hoc audit traceback.
Business pain points
Section titled “Business pain points”- Policy criteria are numerous and scattered in hard-coded logic: the five-dimension scoring brackets (e.g., social security at 36/24/12/6 months), the 75/50 grade lines, and the three subsidy amount tables are distributed across large
if-elseblocks, making the code hard to read and impossible for business staff to verify directly. - Frequent policy adjustments require full release cycles: talent policies often adjust scoring lines and reward amounts annually; each adjustment requires modifying Java code, recompiling, and repackaging the government system, with long release windows and heavy approval chains.
- Four kinds of logic — veto, correction, scoring, amount — are coupled: once the ordering of “veto first, incomplete documents corrected, score graded, amounts only when approved” is written incorrectly, principled errors such as “rejected but a subsidy amount was computed” can occur.
- The five scoring dimensions are hard to tune and audit independently: if education, age, social security, talent, and startup scores are mixed in one large method, you cannot explain to the applicant how many points each item earned, failing the traceability requirement of government review.
- IT-innovation and legacy environments cannot host heavy rule platforms: many government systems still run on JDK 8 or domestic JDK distributions, and standalone rule servers or database-backed rule platforms are hard to deploy.
- Veto and amount results must be explainable: rejections must name the specific veto item or correction reason hit, and approvals must give the itemized amount breakdown; black-box scoring cannot support administrative notification or administrative reconsideration.
Solution architecture
Section titled “Solution architecture”This case adopts the six-piece structure of “input model + decision result model + rule interface + XML rule script + Service orchestration + JUnit tests”. Scoring criteria, veto items, grading thresholds, and amount tables are all externalized in XML; the Service only aggregates and assembles:
| Layer | Responsibility | Files |
|---|---|---|
| Domain model | Subsidy application elements, review decision (score / conclusion / itemized amounts / explanations) | SubsidyApplication.java, SubsidyDecision.java |
| Rule set & XML | Five-dimension scoring + one-vote veto + graded conclusion + three itemized amounts | SubsidyRules.java, subsidy-eligibility.xml |
| Orchestration service | Five-dimension score aggregation, veto/correction precedence, amounts only when approved, explanation assembly | SubsidyService.java |
| Tests | 6 application profiles (approved, boundary, review, veto, correction, rejected) | SubsidyServiceTest.java |
Rule method breakdown
Section titled “Rule method breakdown”The rule interface has 10 methods in total: one criterion per scoring dimension, and the decision methods only handle mapping:
| Rule method | Criterion | Score / output |
|---|---|---|
scoreEducation |
PhD 30 / Master’s 22 / Bachelor’s 15 / College and others 8 | 0 ~ 30 |
scoreAge |
≤30 years 15 / ≤35 12 / ≤40 8 / ≤45 4 / otherwise 0 | 0 ~ 15 |
scoreSocialSecurity |
Local social security ≥36 months 20 / ≥24 15 / ≥12 10 / ≥6 5 / otherwise 0 | 0 ~ 20 |
scoreTalent |
Category A 25 / B 18 / C 10 / unrecognized 0 | 0 ~ 25 |
scoreStartup |
Company founded and ≥10 jobs 20 / ≥5 14 / ≥1 8; not registered 0 | 0 ~ 20 |
hardReject |
Returns true if any of discredited-person status, criminal record, or duplicate claim hits | boolean |
eligibilityStage |
Veto first REJECTED → incomplete documents MANUAL_REVIEW → ≥75 QUALIFIED / ≥50 MANUAL_REVIEW / otherwise REJECTED | Conclusion string |
educationAmount |
PhD 100000 / Master’s 60000 / Bachelor’s 30000 / College 10000 | Education subsidy amount |
talentAmount |
Category A 200000 / B 100000 / C 50000 / others 0 | Talent reward amount |
startupAmount |
Registered and ≥10 jobs 100000 / ≥5 50000 / ≥1 20000; not registered 0 | Startup reward amount |
Code implementation (complete & runnable)
Section titled “Code implementation (complete & runnable)”Place the following six files into the JQuick-Java test project using the original package names from the source: the two models go into com.github.paohaijiao.demo.model, the rule interface and test class go into com.github.paohaijiao.demo, the orchestration service goes into com.github.paohaijiao.demo.service, and the XML script goes into src/test/resources/demo/. Keeping directories and package names unchanged, run mvn test -Dtest=SubsidyServiceTest at the project root.
SubsidyApplication.java
Section titled “SubsidyApplication.java”package com.github.paohaijiao.demo.model;
import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;
/** * 人才补贴申请要素(资格审核所需的申请人画像) */@Data@NoArgsConstructor@AllArgsConstructorpublic class SubsidyApplication {
/** * 申请单号 */ private String applyNo;
/** * 申请人姓名 */ private String applicantName;
/** * 补贴类型:SETTLEMENT-安家补贴 / STARTUP-创业补贴 / RENT-租房补贴 */ private String subsidyType;
/** * 学历:PHD / MASTER / BACHELOR / COLLEGE */ private String education;
/** * 年龄 */ private int age;
/** * 本地社保连续缴纳月数 */ private int localSocialSecurityMonths;
/** * 高层次人才认定类别:A / B / C / NONE */ private String talentCategory;
/** * 是否在本市创办企业 */ private boolean startupRegistered;
/** * 创办企业带动就业人数 */ private int startupEmployees;
/** * 是否有失信被执行记录 */ private boolean creditViolation;
/** * 是否有刑事犯罪记录 */ private boolean criminalRecord;
/** * 是否已享受过同类补贴 */ private boolean receivedSimilarSubsidy;
/** * 申报材料是否齐全 */ private boolean documentsComplete;}SubsidyDecision.java
Section titled “SubsidyDecision.java”package com.github.paohaijiao.demo.model;
import lombok.Data;
import java.util.ArrayList;import java.util.List;
/** * 人才补贴审核决策结果 */@Datapublic class SubsidyDecision {
/** * 申请单号 */ private String applyNo;
/** * 资格评分(0-110) */ private int qualificationScore;
/** * 审核结论:QUALIFIED-通过 / MANUAL_REVIEW-人工复核 / REJECTED-驳回 */ private String stage;
/** * 核定补贴金额(仅审核通过时大于 0) */ private double subsidyAmount;
/** * 学历补贴 */ private double educationAmount;
/** * 人才认定奖励 */ private double talentAmount;
/** * 创业带动就业奖励 */ private double startupAmount;
/** * 审核依据与说明 */ private List<String> reasons = new ArrayList<String>();
public void addReason(String reason) { this.reasons.add(reason); }}SubsidyRules.java
Section titled “SubsidyRules.java”package com.github.paohaijiao.demo;
import com.github.paohaijiao.xml.param.Param;
/** * 场景 11:政务人才补贴资格审核规则集 * <p> * 规则按「硬性否决前置 → 五维资格评分 → 分级结论 → 分项定额」拆分: * <ul> * <li>评分类:学历、年龄、本地社保、人才认定、创业带动就业</li> * <li>决策类:一票否决、审核结论(通过/复核/驳回)、三项补贴金额</li> * </ul> * 规则脚本见 {@code src/test/resources/demo/subsidy-eligibility.xml}。 */public interface SubsidyRules {
int scoreEducation(@Param("education") String education);
int scoreAge(@Param("age") int age);
int scoreSocialSecurity(@Param("months") int months);
int scoreTalent(@Param("talentCategory") String talentCategory);
int scoreStartup(@Param("startupRegistered") boolean startupRegistered, @Param("startupEmployees") int startupEmployees);
boolean hardReject(@Param("creditViolation") boolean creditViolation, @Param("criminalRecord") boolean criminalRecord, @Param("receivedSimilarSubsidy") boolean receivedSimilarSubsidy);
String eligibilityStage(@Param("qualificationScore") int qualificationScore, @Param("hardReject") boolean hardReject, @Param("documentsComplete") boolean documentsComplete);
double educationAmount(@Param("education") String education);
double talentAmount(@Param("talentCategory") String talentCategory);
double startupAmount(@Param("startupRegistered") boolean startupRegistered, @Param("startupEmployees") int startupEmployees);}subsidy-eligibility.xml
Section titled “subsidy-eligibility.xml”<?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.SubsidyRules">
<!-- 学历评分 --> <java name="scoreEducation" returnClass="int"> <![CDATA[ int def scoreEducation(str:education) { if (education == "PHD") { return 30; } else if (education == "MASTER") { return 22; } else if (education == "BACHELOR") { return 15; } else { return 8; } } ]]> </java>
<!-- 年龄评分:青年人才优先 --> <java name="scoreAge" returnClass="int"> <![CDATA[ int def scoreAge(int:age) { if (age <= 30) { return 15; } else if (age <= 35) { return 12; } else if (age <= 40) { return 8; } else if (age <= 45) { return 4; } else { return 0; } } ]]> </java>
<!-- 本地社保连续性评分 --> <java name="scoreSocialSecurity" returnClass="int"> <![CDATA[ int def scoreSocialSecurity(int:months) { if (months >= 36) { return 20; } else if (months >= 24) { return 15; } else if (months >= 12) { return 10; } else if (months >= 6) { return 5; } else { return 0; } } ]]> </java>
<!-- 高层次人才认定评分 --> <java name="scoreTalent" returnClass="int"> <![CDATA[ int def scoreTalent(str:talentCategory) { if (talentCategory == "A") { return 25; } else if (talentCategory == "B") { return 18; } else if (talentCategory == "C") { return 10; } else { return 0; } } ]]> </java>
<!-- 创业带动就业评分 --> <java name="scoreStartup" returnClass="int"> <![CDATA[ int def scoreStartup(boolean:startupRegistered, int:startupEmployees) { if (startupRegistered) { if (startupEmployees >= 10) { return 20; } else if (startupEmployees >= 5) { return 14; } else if (startupEmployees >= 1) { return 8; } else { return 0; } } else { return 0; } } ]]> </java>
<!-- 硬性否决:失信、刑事犯罪、重复申领 --> <java name="hardReject" returnClass="boolean"> <![CDATA[ boolean def hardReject(boolean:creditViolation, boolean:criminalRecord, boolean:receivedSimilarSubsidy) { if (creditViolation || criminalRecord || receivedSimilarSubsidy) { return true; } else { return false; } } ]]> </java>
<!-- 审核结论:硬否决优先,其次材料补正,最后按分数分级 --> <java name="eligibilityStage" returnClass="java.lang.String"> <![CDATA[ str def eligibilityStage(int:qualificationScore, boolean:hardReject, boolean:documentsComplete) { if (hardReject) { return "REJECTED"; } else if (documentsComplete == false) { return "MANUAL_REVIEW"; } else if (qualificationScore >= 75) { return "QUALIFIED"; } else if (qualificationScore >= 50) { return "MANUAL_REVIEW"; } else { return "REJECTED"; } } ]]> </java>
<!-- 学历安家补贴定额 --> <java name="educationAmount" returnClass="double"> <![CDATA[ double def educationAmount(str:education) { if (education == "PHD") { return 100000.0; } else if (education == "MASTER") { return 60000.0; } else if (education == "BACHELOR") { return 30000.0; } else { return 10000.0; } } ]]> </java>
<!-- 人才认定奖励定额 --> <java name="talentAmount" returnClass="double"> <![CDATA[ double def talentAmount(str:talentCategory) { if (talentCategory == "A") { return 200000.0; } else if (talentCategory == "B") { return 100000.0; } else if (talentCategory == "C") { return 50000.0; } else { return 0.0; } } ]]> </java>
<!-- 创业带动就业奖励定额 --> <java name="startupAmount" returnClass="double"> <![CDATA[ double def startupAmount(boolean:startupRegistered, int:startupEmployees) { if (startupRegistered) { if (startupEmployees >= 10) { return 100000.0; } else if (startupEmployees >= 5) { return 50000.0; } else if (startupEmployees >= 1) { return 20000.0; } else { return 0.0; } } else { return 0.0; } } ]]> </java>
</javas>SubsidyService.java
Section titled “SubsidyService.java”package com.github.paohaijiao.demo.service;
import com.github.paohaijiao.demo.SubsidyRules;import com.github.paohaijiao.demo.model.SubsidyApplication;import com.github.paohaijiao.demo.model.SubsidyDecision;
/** * 场景 11:政务人才补贴资格审核服务 * <p> * 职责划分: * <ul> * <li><b>Java 侧</b>:五维评分汇总、结论装配、分项金额汇总、审核说明组装</li> * <li><b>规则侧</b>:评分口径、一票否决项、分级阈值、补贴定额,全部外置在 XML 中</li> * </ul> */public class SubsidyService {
private final SubsidyRules rules;
public SubsidyService(SubsidyRules rules) { this.rules = rules; }
/** * 审核人才补贴申请 * * @param application 申请要素 * @return 审核决策 */ public SubsidyDecision review(SubsidyApplication application) { SubsidyDecision decision = new SubsidyDecision(); decision.setApplyNo(application.getApplyNo());
int score = rules.scoreEducation(application.getEducation()) + rules.scoreAge(application.getAge()) + rules.scoreSocialSecurity(application.getLocalSocialSecurityMonths()) + rules.scoreTalent(application.getTalentCategory()) + rules.scoreStartup(application.isStartupRegistered(), application.getStartupEmployees());
boolean hardReject = rules.hardReject(application.isCreditViolation(), application.isCriminalRecord(), application.isReceivedSimilarSubsidy()); String stage = rules.eligibilityStage(score, hardReject, application.isDocumentsComplete());
decision.setQualificationScore(score); decision.setStage(stage);
double educationAmount = rules.educationAmount(application.getEducation()); double talentAmount = rules.talentAmount(application.getTalentCategory()); double startupAmount = rules.startupAmount(application.isStartupRegistered(), application.getStartupEmployees());
if ("QUALIFIED".equals(stage)) { decision.setEducationAmount(educationAmount); decision.setTalentAmount(talentAmount); decision.setStartupAmount(startupAmount); decision.setSubsidyAmount(educationAmount + talentAmount + startupAmount); }
buildReasons(application, decision, hardReject); return decision; }
private void buildReasons(SubsidyApplication application, SubsidyDecision decision, boolean hardReject) { if (application.isCreditViolation()) { decision.addReason("存在失信被执行记录,触发一票否决"); } if (application.isCriminalRecord()) { decision.addReason("存在刑事犯罪记录,触发一票否决"); } if (application.isReceivedSimilarSubsidy()) { decision.addReason("已享受同类补贴,不得重复申领"); } if (hardReject) { return; } if (!application.isDocumentsComplete()) { decision.addReason("申报材料不齐全,退回补正后转人工复核"); return; } decision.addReason("资格评分 " + decision.getQualificationScore() + " 分"); if ("QUALIFIED".equals(decision.getStage())) { decision.addReason("审核通过,核定补贴 " + decision.getSubsidyAmount() + " 元"); } else if ("MANUAL_REVIEW".equals(decision.getStage())) { decision.addReason("评分处于复核区间,转人工审核,暂不定额"); } else { decision.addReason("资格评分未达补贴标准,驳回申请"); } }}SubsidyServiceTest.java
Section titled “SubsidyServiceTest.java”package com.github.paohaijiao.demo;
import com.github.paohaijiao.demo.model.SubsidyApplication;import com.github.paohaijiao.demo.model.SubsidyDecision;import com.github.paohaijiao.demo.service.SubsidyService;import com.github.paohaijiao.xml.JQuickJava;import org.junit.Assert;import org.junit.Before;import org.junit.Test;
/** * 场景 11 生产级样板:政务人才补贴资格自动审核 * <p> * 覆盖 6 组申报形态:高层次人才创业、硕士就业达标线、本科临界复核、 * 失信一票否决、材料不齐补正、低学历创业不达标。 */public class SubsidyServiceTest {
private SubsidyService service;
@Before public void setUp() { SubsidyRules rules = JQuickJava.create() .createApi(SubsidyRules.class, "demo/subsidy-eligibility.xml"); this.service = new SubsidyService(rules); }
/** * 博士 A 类人才创业,带动就业 12 人 → 110 分,补贴 40 万 */ @Test public void shouldApproveTopTalentStartup() { SubsidyApplication app = baseApplication("SUB-1101"); app.setEducation("PHD"); app.setAge(28); app.setLocalSocialSecurityMonths(48); app.setTalentCategory("A"); app.setStartupRegistered(true); app.setStartupEmployees(12);
SubsidyDecision decision = service.review(app);
Assert.assertEquals(110, decision.getQualificationScore()); Assert.assertEquals("QUALIFIED", decision.getStage()); Assert.assertEquals(400000.0, decision.getSubsidyAmount(), 0.001); Assert.assertEquals(100000.0, decision.getEducationAmount(), 0.001); Assert.assertEquals(200000.0, decision.getTalentAmount(), 0.001); Assert.assertEquals(100000.0, decision.getStartupAmount(), 0.001); Assert.assertTrue(decision.getReasons().contains("审核通过,核定补贴 400000.0 元")); }
/** * 硕士 B 类人才稳定就业,评分恰为 75 达标线 → 通过,补贴 16 万 */ @Test public void shouldApproveAtQualificationLine() { SubsidyApplication app = baseApplication("SUB-1102"); app.setEducation("MASTER"); app.setAge(28); app.setLocalSocialSecurityMonths(36); app.setTalentCategory("B"); app.setStartupRegistered(false); app.setStartupEmployees(0);
SubsidyDecision decision = service.review(app);
Assert.assertEquals(75, decision.getQualificationScore()); Assert.assertEquals("QUALIFIED", decision.getStage()); Assert.assertEquals(160000.0, decision.getSubsidyAmount(), 0.001); Assert.assertEquals(60000.0, decision.getEducationAmount(), 0.001); Assert.assertEquals(100000.0, decision.getTalentAmount(), 0.001); Assert.assertEquals(0.0, decision.getStartupAmount(), 0.001); }
/** * 本科 C 类人才,评分 55 处于复核区间 → 人工复核,暂不定额 */ @Test public void shouldRouteBorderlineApplicationToManualReview() { SubsidyApplication app = baseApplication("SUB-1103"); app.setEducation("BACHELOR"); app.setAge(29); app.setLocalSocialSecurityMonths(24); app.setTalentCategory("C"); app.setStartupRegistered(false); app.setStartupEmployees(0);
SubsidyDecision decision = service.review(app);
Assert.assertEquals(55, decision.getQualificationScore()); Assert.assertEquals("MANUAL_REVIEW", decision.getStage()); Assert.assertEquals(0.0, decision.getSubsidyAmount(), 0.001); Assert.assertTrue(decision.getReasons().contains("评分处于复核区间,转人工审核,暂不定额")); }
/** * 硕士条件优秀但存在失信记录 → 一票否决直接驳回 */ @Test public void shouldRejectCreditViolationRegardlessOfScore() { SubsidyApplication app = baseApplication("SUB-1104"); app.setEducation("MASTER"); app.setAge(30); app.setLocalSocialSecurityMonths(40); app.setTalentCategory("B"); app.setStartupRegistered(false); app.setStartupEmployees(0); app.setCreditViolation(true);
SubsidyDecision decision = service.review(app);
Assert.assertTrue(decision.getQualificationScore() >= 70); Assert.assertEquals("REJECTED", decision.getStage()); Assert.assertEquals(0.0, decision.getSubsidyAmount(), 0.001); Assert.assertTrue(decision.getReasons().contains("存在失信被执行记录,触发一票否决")); }
/** * 博士评分 65 但材料不齐 → 退回补正并人工复核 */ @Test public void shouldHoldApplicationForMissingDocuments() { SubsidyApplication app = baseApplication("SUB-1105"); app.setEducation("PHD"); app.setAge(30); app.setLocalSocialSecurityMonths(36); app.setTalentCategory("NONE"); app.setStartupRegistered(false); app.setStartupEmployees(0); app.setDocumentsComplete(false);
SubsidyDecision decision = service.review(app);
Assert.assertEquals(65, decision.getQualificationScore()); Assert.assertEquals("MANUAL_REVIEW", decision.getStage()); Assert.assertEquals(0.0, decision.getSubsidyAmount(), 0.001); Assert.assertTrue(decision.getReasons().contains("申报材料不齐全,退回补正后转人工复核")); }
/** * 大专学历创业带动 6 人就业,评分 36 未达标 → 驳回 */ @Test public void shouldRejectBelowStandardApplication() { SubsidyApplication app = baseApplication("SUB-1106"); app.setEducation("COLLEGE"); app.setAge(42); app.setLocalSocialSecurityMonths(12); app.setTalentCategory("NONE"); app.setStartupRegistered(true); app.setStartupEmployees(6);
SubsidyDecision decision = service.review(app);
Assert.assertEquals(36, decision.getQualificationScore()); Assert.assertEquals("REJECTED", decision.getStage()); Assert.assertEquals(0.0, decision.getSubsidyAmount(), 0.001); Assert.assertTrue(decision.getReasons().contains("资格评分未达补贴标准,驳回申请")); }
private SubsidyApplication baseApplication(String applyNo) { SubsidyApplication app = new SubsidyApplication(); app.setApplyNo(applyNo); app.setApplicantName("申请人"); app.setSubsidyType("SETTLEMENT"); app.setCreditViolation(false); app.setCriminalRecord(false); app.setReceivedSimilarSubsidy(false); app.setDocumentsComplete(true); return app; }}Run & verified results
Section titled “Run & verified results”Run at the project root:
mvn test -Dtest=SubsidyServiceTestTest coverage
Section titled “Test coverage”| Test method | Constructed business profile | Key assertions / expected decision |
|---|---|---|
shouldApproveTopTalentStartup |
PhD category A, age 28, 48 months of social security, startup creating 12 jobs (SUB-1101) | Score 110, QUALIFIED, education 100k + talent 200k + startup 100k, approved 400000.0 |
shouldApproveAtQualificationLine |
Master’s category B, age 28, 36 months of social security, no startup (SUB-1102) | Score exactly 75, QUALIFIED, approved 160000.0 (60000+100000+0) |
shouldRouteBorderlineApplicationToManualReview |
Bachelor’s category C, age 29, 24 months of social security (SUB-1103) | Score 55, MANUAL_REVIEW, subsidy 0.0, explanation “routed to manual review, no amount for now” |
shouldRejectCreditViolationRegardlessOfScore |
Master’s category B with score ≥70 but creditViolation=true (SUB-1104) |
Still REJECTED regardless of score, subsidy 0.0, contains the credit-violation one-vote veto explanation |
shouldHoldApplicationForMissingDocuments |
PhD score 65 but documentsComplete=false (SUB-1105) |
MANUAL_REVIEW, subsidy 0.0, contains the “returned for correction and routed to manual review” explanation |
shouldRejectBelowStandardApplication |
College, age 42, 12 months of social security, startup creating 6 jobs (SUB-1106) | Score 36, REJECTED, subsidy 0.0, contains the below-standard rejection explanation |
Verified output
Section titled “Verified output”PhD category A talent startup (age 28, 48 months of social security, 12 jobs created):
资格评分: 110 审核结论: QUALIFIED学历补贴: 100000.0 人才奖励: 200000.0 创业奖励: 100000.0核定补贴: 400000.0Excellent Master’s profile but discredited (score ≥70, one-vote veto triggered):
审核结论: REJECTED 核定补贴: 0.0说明: 存在失信被执行记录,触发一票否决Technical highlights
Section titled “Technical highlights”- All scoring thresholds and amount tables externalized in XML, hot-updatable: the 75/50 grading lines, social-security month brackets, and three subsidy amounts all live in
subsidy-eligibility.xml; policy adjustments only require editing the XML and rebuilding the proxy — no recompilation or release. - One method per scoring dimension, independently tunable and auditable:
scoreEducation,scoreAge,scoreSocialSecurity,scoreTalent, andscoreStartupeach score independently and are summed linearly on the Service side; every point can be explained to the applicant. - One-vote veto and document correction precedence guaranteed by rule-script order:
eligibilityStagecheckshardRejectfirst, then document completeness, and only then grades by 75/50; veto priority is unaffected by the score. The script expresses negation with the positive comparisondocumentsComplete == false(the script does not use the!operator). - The Service gates amounts on the Java side: although the three amount methods are always invoked, results are only written into the decision when
"QUALIFIED".equals(stage), structurally preventing review/rejected applications from producing subsidy amounts. @Paramname-based binding + reusable proxy instances: interface parameters bind to the XML script’sstr:educationformal parameters by names such as@Param("education"), so refactoring does not depend on parameter order; the proxy is created once in@Beforeand reused across six test cases.- Java 8 with no heavy dependencies, ASM call chain embeddable in IT-innovation environments: the model uses only Lombok and JDK collections, and the rule proxy runs on an ASM-generated call chain with no standalone rule server, so it can be embedded directly into government systems still running on JDK 8 and domestic JDKs.