Skip to content

Case 13: Medical insurance reimbursement

This case corresponds to Scenario 13 in the JQuick-Java repository’s USE-CASES.md, targeting instant medical insurance settlement in the healthcare domain. The system first determines the catalog scope of class A, class B, and class C drugs — items outside the catalog are fully self-paid. For in-catalog items, the deductible and reimbursement ratio are determined by insurance type (employee / resident) and hospital level (tier-3A / tier-2 / community); emergency visits halve the deductible and raise the ratio by 5 percentage points, and class B drugs take a 10% upfront self-pay first. The final pooled payment is truncated by the annual pooled-fund cap. Off-site treatment without filing is routed to manual review. All policy criteria are driven by JQuick-Java XML rule proxies.

Instant medical insurance settlement happens at the hospital cashier window: after the insured registers with a medical settlement slip (MedicalClaim), the system captures the insurance type (EMPLOYEE employee insurance / RESIDENT resident insurance), hospital level (THIRD_A tier-3A / SECOND_A tier-2 / COMMUNITY community), expense category (DRUG_A class A drug / DRUG_B class B drug / DRUG_C class C self-paid / TREATMENT treatment / EXAMINATION examination), the total expense of this visit, whether it is an emergency, whether it is off-site treatment and whether it has been filed, and the year-to-date accumulated pooled-fund payment.

The first gate of settlement is catalog scope determination: class C (DRUG_C) is outside the catalog, the conclusion is directly SELF_PAY, pooled payment is 0, all expenses are borne by the individual, and the downstream deductible and ratio chain is not executed. In-catalog items enter the full reimbursement chain: first deduct the class B drug’s 10% upfront self-pay, then deduct the deductible, multiply the remainder by the reimbursement ratio to get the theoretical pooled payment, and finally compare and truncate against the remaining allowance of the annual cap.

The deductible and ratio form a policy matrix of “insurance type × hospital level × emergency”: for regular visits, employee deductibles at tier-3A / tier-2 / community are 800/500/200 yuan with ratios 0.9/0.85/0.8; resident deductibles are 600/300/100 yuan with ratios 0.7/0.65/0.6. For emergencies, deductibles are halved (employee tier-3A 400, tier-2 250, community 100) and the ratio is raised by 0.05 on top of the matrix, capped at 1.0. The annual cap is 300,000 for employees and 200,000 for residents; when the payment exceeds the remaining annual allowance, it settles at the remaining allowance, and when the remaining allowance is ≤0 the payment is 0.

Compliance conclusions come in three tiers: out-of-catalog SELF_PAY; off-site treatment without filing MANUAL_REVIEW, with expenses held for manual review before settlement; otherwise APPROVE for instant settlement. In addition, needManualReview is set when a single pooled payment reaches 50,000 yuan or the conclusion itself is manual review. The decision result fully retains the deductible, upfront self-pay, ratio, pooled payment, personal self-pay, cap, whether the cap was hit, and item-by-item settlement notes.

  • The policy matrix has many dimensions and is hard-coded: the three dimensions of insurance type × hospital level × emergency stack into 12 deductible tiers and 6 ratio tiers, written in nested Java if-else blocks that are extremely hard to verify, and cashier staff cannot see the criteria directly.
  • Medical insurance policy adjustments require re-releasing hospital systems: deductibles, reimbursement ratios, caps, and the class B upfront self-pay ratio adjust annually with regional policies; HIS release windows are strict and regression costs are high.
  • The reimbursement chain order must not be wrong: out-of-catalog must return early; in-catalog must chain “upfront self-pay → deductible → ratio → cap truncation” in order — reversing the order or missing a deduction causes erroneous fund payments.
  • Deductible halving and ratio uplift need overflow protection: the emergency ratio adds +0.05 on top of the matrix; without the 1.0 cap, high tiers could produce reimbursement ratios above 100%.
  • The annual cap is a dynamic remaining allowance, not a fixed threshold: truncation is based on “annual cap - year-to-date accumulated payment”; when the remaining allowance is insufficient or even 0, deterministic behavior is required — a simple fixed-cap comparison is not enough.
  • Medical insurance audits require every settlement to be explainable: out-of-catalog self-pay, class B upfront self-pay, emergency preference, cap truncation, and off-site unfiled holds must all be traceable in settlement notes; moreover, many regional core systems still run on JDK 8 / IT-innovation environments where heavy rule platforms are hard to introduce.

This case adopts the six-piece structure of “input model + decision result model + rule interface + XML rule script + Service orchestration + JUnit tests”. The catalog, deductible/ratio matrix, upfront self-pay, cap, and filing criteria are all externalized in XML; the Service handles reimbursement chain sequencing, negative-value zeroing, and note assembly:

Layer Responsibility Files
Domain model Settlement elements, reimbursement decision (deductible / ratio / payment / conclusion / notes) MedicalClaim.java, ReimbursementDecision.java
Rule set & XML 8 rule methods for catalog / deductible / ratio / upfront self-pay / cap / conclusion MedicalReimbursementRules.java, medical-reimbursement.xml
Orchestration service Catalog → self-pay → deductible → ratio → cap chain sequencing, conclusion and note assembly MedicalReimbursementService.java
Tests 6 settlement profiles (class A, class B, class C, cap, emergency, off-site) MedicalReimbursementServiceTest.java

The rule interface has 8 methods in total, separating policy queries from conclusion determination:

Rule method Criterion Score / output
withinCatalog Returns false when the expense category is DRUG_C (class C); class A, class B, treatment, and examination are in the catalog boolean
deductible Employee 800/500/200, resident 600/300/100 (tier-3A/tier-2/community); halved in emergencies to 400/250/100 and 300/150/50 Deductible amount
reimburseRatio Employee 0.9/0.85/0.8, resident 0.7/0.65/0.6; emergency +0.05, truncated to 1.0 when exceeding Reimbursement ratio
prepaidSelfPay Class B drug DRUG_B upfront self-pay = total expense × 10%; 0 for other categories Upfront self-pay amount
annualCap Employee insurance 300000, resident insurance 200000 Annual cap
applyCap Returns 0 when remaining allowance ≤0; returns the remaining allowance when the theoretical payment exceeds it; otherwise pays as-is Truncated payment
complianceResult Out-of-catalog SELF_PAY; off-site and unfiled MANUAL_REVIEW; otherwise APPROVE Conclusion string
needManualReview Returns true when the conclusion is MANUAL_REVIEW, or APPROVE with a single pooled payment ≥50000 boolean

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=MedicalReimbursementServiceTest at the project root.

package com.github.paohaijiao.demo.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 医保结算要素(报销合规校验所需的就诊与费用快照)
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class MedicalClaim {
/**
* 结算单号
*/
private String claimNo;
/**
* 患者姓名
*/
private String patientName;
/**
* 参保类型:EMPLOYEE-职工医保 / RESIDENT-居民医保
*/
private String insuranceType;
/**
* 医院等级:THIRD_A-三甲 / SECOND_A-二级 / COMMUNITY-社区
*/
private String hospitalLevel;
/**
* 费用类别:DRUG_A-甲类药 / DRUG_B-乙类药 / DRUG_C-丙类自费 / TREATMENT-诊疗 / EXAMINATION-检查
*/
private String medicalCategory;
/**
* 本次费用总额
*/
private double totalExpense;
/**
* 是否急诊
*/
private boolean emergency;
/**
* 是否异地就医
*/
private boolean offSite;
/**
* 异地就医是否已备案
*/
private boolean filed;
/**
* 年度统筹基金已累计支付额
*/
private double annualAccumulated;
}
package com.github.paohaijiao.demo.model;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 医保报销决策结果
*/
@Data
public class ReimbursementDecision {
/**
* 结算单号
*/
private String claimNo;
/**
* 是否在医保目录内
*/
private boolean withinCatalog;
/**
* 起付线金额
*/
private double deductibleAmount;
/**
* 乙类先行自付金额
*/
private double prepaidSelfPay;
/**
* 报销比例
*/
private double reimburseRatio;
/**
* 统筹支付金额
*/
private double payableAmount;
/**
* 个人自付金额
*/
private double selfPayAmount;
/**
* 年度封顶线
*/
private double annualCap;
/**
* 是否触及封顶线
*/
private boolean capped;
/**
* 结算结论:APPROVE-即时结算 / MANUAL_REVIEW-人工审核 / SELF_PAY-全自费
*/
private String result;
/**
* 是否需要人工审核
*/
private boolean needManualReview;
/**
* 结算说明
*/
private List<String> notes = new ArrayList<String>();
public void addNote(String note) {
this.notes.add(note);
}
}
package com.github.paohaijiao.demo;
import com.github.paohaijiao.xml.param.Param;
/**
* 场景 13:医保报销项目与比例合规校验规则集
* <p>
* 规则按「目录范围 → 起付线/比例 → 先行自付 → 封顶线 → 结算结论」拆分:
* <ul>
* <li>政策类:目录判定、起付线、报销比例、乙类先行自付、年度封顶线</li>
* <li>决策类:结算结论、封顶截断、人工审核门槛、异地备案校验</li>
* </ul>
* 规则脚本见 {@code src/test/resources/demo/medical-reimbursement.xml}。
*/
public interface MedicalReimbursementRules {
boolean withinCatalog(@Param("medicalCategory") String medicalCategory);
double deductible(@Param("insuranceType") String insuranceType,
@Param("hospitalLevel") String hospitalLevel,
@Param("emergency") boolean emergency);
double reimburseRatio(@Param("insuranceType") String insuranceType,
@Param("hospitalLevel") String hospitalLevel,
@Param("emergency") boolean emergency);
double prepaidSelfPay(@Param("medicalCategory") String medicalCategory,
@Param("totalExpense") double totalExpense);
double annualCap(@Param("insuranceType") String insuranceType);
double applyCap(@Param("payableAmount") double payableAmount,
@Param("remainingCap") double remainingCap);
String complianceResult(@Param("withinCatalog") boolean withinCatalog,
@Param("offSite") boolean offSite,
@Param("filed") boolean filed);
boolean needManualReview(@Param("result") String result,
@Param("payableAmount") double payableAmount);
}
<?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.MedicalReimbursementRules">
<!-- 医保目录判定:丙类全部自费 -->
<java name="withinCatalog" returnClass="boolean">
<![CDATA[
boolean def withinCatalog(str:medicalCategory) {
if (medicalCategory == "DRUG_C") {
return false;
} else {
return true;
}
}
]]>
</java>
<!-- 起付线:参保类型 × 医院等级,急诊减半执行 -->
<java name="deductible" returnClass="double">
<![CDATA[
double def deductible(str:insuranceType, str:hospitalLevel, boolean:emergency) {
if (insuranceType == "EMPLOYEE") {
if (emergency) {
if (hospitalLevel == "THIRD_A") {
return 400.0;
} else if (hospitalLevel == "SECOND_A") {
return 250.0;
} else {
return 100.0;
}
} else {
if (hospitalLevel == "THIRD_A") {
return 800.0;
} else if (hospitalLevel == "SECOND_A") {
return 500.0;
} else {
return 200.0;
}
}
} else {
if (emergency) {
if (hospitalLevel == "THIRD_A") {
return 300.0;
} else if (hospitalLevel == "SECOND_A") {
return 150.0;
} else {
return 50.0;
}
} else {
if (hospitalLevel == "THIRD_A") {
return 600.0;
} else if (hospitalLevel == "SECOND_A") {
return 300.0;
} else {
return 100.0;
}
}
}
}
]]>
</java>
<!-- 报销比例:参保类型 × 医院等级,急诊上浮 5 个百分点,上限 100% -->
<java name="reimburseRatio" returnClass="double">
<![CDATA[
double def reimburseRatio(str:insuranceType, str:hospitalLevel, boolean:emergency) {
double ratio = 0.6;
if (insuranceType == "EMPLOYEE") {
if (hospitalLevel == "THIRD_A") {
ratio = 0.9;
} else if (hospitalLevel == "SECOND_A") {
ratio = 0.85;
} else {
ratio = 0.8;
}
} else {
if (hospitalLevel == "THIRD_A") {
ratio = 0.7;
} else if (hospitalLevel == "SECOND_A") {
ratio = 0.65;
} else {
ratio = 0.6;
}
}
if (emergency) {
ratio = ratio + 0.05;
}
if (ratio > 1.0) {
ratio = 1.0;
}
return ratio;
}
]]>
</java>
<!-- 乙类药品先行自付 10%,其余类别无先行自付 -->
<java name="prepaidSelfPay" returnClass="double">
<![CDATA[
double def prepaidSelfPay(str:medicalCategory, double:totalExpense) {
if (medicalCategory == "DRUG_B") {
return totalExpense * 0.1;
} else {
return 0.0;
}
}
]]>
</java>
<!-- 年度统筹基金封顶线 -->
<java name="annualCap" returnClass="double">
<![CDATA[
double def annualCap(str:insuranceType) {
if (insuranceType == "EMPLOYEE") {
return 300000.0;
} else {
return 200000.0;
}
}
]]>
</java>
<!-- 封顶截断:支付额不得超过年度剩余额度 -->
<java name="applyCap" returnClass="double">
<![CDATA[
double def applyCap(double:payableAmount, double:remainingCap) {
if (remainingCap <= 0.0) {
return 0.0;
} else if (payableAmount > remainingCap) {
return remainingCap;
} else {
return payableAmount;
}
}
]]>
</java>
<!-- 结算结论:目录外全自费,异地未备案转人工,其余即时结算 -->
<java name="complianceResult" returnClass="java.lang.String">
<![CDATA[
str def complianceResult(boolean:withinCatalog, boolean:offSite, boolean:filed) {
if (withinCatalog == false) {
return "SELF_PAY";
} else if (offSite && filed == false) {
return "MANUAL_REVIEW";
} else {
return "APPROVE";
}
}
]]>
</java>
<!-- 人工审核:异地未备案或单笔统筹支付达到 5 万 -->
<java name="needManualReview" returnClass="boolean">
<![CDATA[
boolean def needManualReview(str:result, double:payableAmount) {
if (result == "MANUAL_REVIEW") {
return true;
} else if (result == "APPROVE" && payableAmount >= 50000.0) {
return true;
} else {
return false;
}
}
]]>
</java>
</javas>
package com.github.paohaijiao.demo.service;
import com.github.paohaijiao.demo.MedicalReimbursementRules;
import com.github.paohaijiao.demo.model.MedicalClaim;
import com.github.paohaijiao.demo.model.ReimbursementDecision;
/**
* 场景 13:医保报销合规校验服务
* <p>
* 职责划分:
* <ul>
* <li><b>Java 侧</b>:报销链路串联(目录 → 先行自付 → 起付线 → 比例 → 封顶)、结算说明组装</li>
* <li><b>规则侧</b>:目录范围、起付线与比例矩阵、乙类先行自付、封顶线、备案口径,全部外置在 XML 中</li>
* </ul>
*/
public class MedicalReimbursementService {
private final MedicalReimbursementRules rules;
public MedicalReimbursementService(MedicalReimbursementRules rules) {
this.rules = rules;
}
/**
* 执行医保报销结算
*
* @param claim 就诊费用要素
* @return 报销决策
*/
public ReimbursementDecision settle(MedicalClaim claim) {
ReimbursementDecision decision = new ReimbursementDecision();
decision.setClaimNo(claim.getClaimNo());
boolean withinCatalog = rules.withinCatalog(claim.getMedicalCategory());
double annualCap = rules.annualCap(claim.getInsuranceType());
decision.setWithinCatalog(withinCatalog);
decision.setAnnualCap(annualCap);
if (!withinCatalog) {
decision.setResult("SELF_PAY");
decision.setPayableAmount(0.0);
decision.setSelfPayAmount(claim.getTotalExpense());
decision.addNote(claim.getMedicalCategory() + " 属目录外项目,全部自费");
return decision;
}
double deductible = rules.deductible(claim.getInsuranceType(),
claim.getHospitalLevel(), claim.isEmergency());
double ratio = rules.reimburseRatio(claim.getInsuranceType(),
claim.getHospitalLevel(), claim.isEmergency());
double prepaid = rules.prepaidSelfPay(claim.getMedicalCategory(), claim.getTotalExpense());
double baseAfterPrepaid = claim.getTotalExpense() - prepaid;
double baseAfterDeductible = baseAfterPrepaid - deductible;
if (baseAfterDeductible < 0.0) {
baseAfterDeductible = 0.0;
}
double rawPayable = baseAfterDeductible * ratio;
double remainingCap = annualCap - claim.getAnnualAccumulated();
double payable = rules.applyCap(rawPayable, remainingCap);
decision.setDeductibleAmount(deductible);
decision.setPrepaidSelfPay(prepaid);
decision.setReimburseRatio(ratio);
decision.setPayableAmount(payable);
decision.setSelfPayAmount(claim.getTotalExpense() - payable);
decision.setCapped(payable < rawPayable);
decision.setResult(rules.complianceResult(true, claim.isOffSite(), claim.isFiled()));
decision.setNeedManualReview(rules.needManualReview(decision.getResult(), payable));
buildNotes(claim, decision, rawPayable);
return decision;
}
private void buildNotes(MedicalClaim claim, ReimbursementDecision decision, double rawPayable) {
if (claim.isEmergency()) {
decision.addNote("急诊就诊,起付线减半、报销比例上浮 5 个百分点");
}
if (decision.getPrepaidSelfPay() > 0.0) {
decision.addNote("乙类药品先行自付 10%:" + decision.getPrepaidSelfPay() + " 元");
}
if (decision.isCapped()) {
decision.addNote("统筹支付触达年度封顶线,理论支付 " + rawPayable + " 元,按剩余额度 "
+ decision.getPayableAmount() + " 元结算");
}
if (claim.isOffSite() && !claim.isFiled()) {
decision.addNote("异地就医未备案,转人工审核后再行结算");
}
}
}
package com.github.paohaijiao.demo;
import com.github.paohaijiao.demo.model.MedicalClaim;
import com.github.paohaijiao.demo.model.ReimbursementDecision;
import com.github.paohaijiao.demo.service.MedicalReimbursementService;
import com.github.paohaijiao.xml.JQuickJava;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* 场景 13 生产级样板:医保报销项目与比例合规校验
* <p>
* 覆盖 6 组结算形态:职工三甲甲类、居民社区乙类、丙类全自费、
* 年度封顶截断、急诊政策倾斜、异地未备案人工审核。
*/
public class MedicalReimbursementServiceTest {
private MedicalReimbursementService service;
@Before
public void setUp() {
MedicalReimbursementRules rules = JQuickJava.create()
.createApi(MedicalReimbursementRules.class, "demo/medical-reimbursement.xml");
this.service = new MedicalReimbursementService(rules);
}
/**
* 职工三甲甲类药,费用 5000 元 → 起付线 800,比例 90%,支付 3780
*/
@Test
public void shouldReimburseEmployeeThirdGradeClassA() {
MedicalClaim claim = claim("MC-1301", "EMPLOYEE", "THIRD_A", "DRUG_A", 5000.0,
false, false, false, 0.0);
ReimbursementDecision decision = service.settle(claim);
Assert.assertTrue(decision.isWithinCatalog());
Assert.assertEquals("APPROVE", decision.getResult());
Assert.assertEquals(800.0, decision.getDeductibleAmount(), 0.001);
Assert.assertEquals(0.9, decision.getReimburseRatio(), 0.0001);
Assert.assertEquals(0.0, decision.getPrepaidSelfPay(), 0.001);
Assert.assertEquals(3780.0, decision.getPayableAmount(), 0.001);
Assert.assertEquals(1220.0, decision.getSelfPayAmount(), 0.001);
Assert.assertFalse(decision.isCapped());
Assert.assertFalse(decision.isNeedManualReview());
}
/**
* 居民社区乙类药,费用 1000 元 → 先行自付 100、起付线 100、比例 60%,支付 480
*/
@Test
public void shouldReimburseResidentCommunityClassB() {
MedicalClaim claim = claim("MC-1302", "RESIDENT", "COMMUNITY", "DRUG_B", 1000.0,
false, false, false, 0.0);
ReimbursementDecision decision = service.settle(claim);
Assert.assertEquals("APPROVE", decision.getResult());
Assert.assertEquals(100.0, decision.getDeductibleAmount(), 0.001);
Assert.assertEquals(100.0, decision.getPrepaidSelfPay(), 0.001);
Assert.assertEquals(0.6, decision.getReimburseRatio(), 0.0001);
Assert.assertEquals(480.0, decision.getPayableAmount(), 0.001);
Assert.assertEquals(520.0, decision.getSelfPayAmount(), 0.001);
Assert.assertTrue(decision.getNotes().contains("乙类药品先行自付 10%:100.0 元"));
}
/**
* 丙类药目录外 → 全部自费 800 元
*/
@Test
public void shouldDenyOutOfCatalogClassCDrug() {
MedicalClaim claim = claim("MC-1303", "EMPLOYEE", "THIRD_A", "DRUG_C", 800.0,
false, false, false, 0.0);
ReimbursementDecision decision = service.settle(claim);
Assert.assertFalse(decision.isWithinCatalog());
Assert.assertEquals("SELF_PAY", decision.getResult());
Assert.assertEquals(0.0, decision.getPayableAmount(), 0.001);
Assert.assertEquals(800.0, decision.getSelfPayAmount(), 0.001);
Assert.assertEquals(0.0, decision.getReimburseRatio(), 0.0001);
Assert.assertTrue(decision.getNotes().contains("DRUG_C 属目录外项目,全部自费"));
}
/**
* 大额费用触达年度封顶线:剩余额度仅 2 万 → 支付 20000
*/
@Test
public void shouldTruncateAtAnnualCap() {
MedicalClaim claim = claim("MC-1304", "EMPLOYEE", "THIRD_A", "DRUG_A", 600000.0,
false, false, false, 280000.0);
ReimbursementDecision decision = service.settle(claim);
Assert.assertEquals(300000.0, decision.getAnnualCap(), 0.001);
Assert.assertEquals(20000.0, decision.getPayableAmount(), 0.001);
Assert.assertEquals(580000.0, decision.getSelfPayAmount(), 0.001);
Assert.assertTrue(decision.isCapped());
Assert.assertTrue(decision.getNotes().contains(
"统筹支付触达年度封顶线,理论支付 539280.0 元,按剩余额度 20000.0 元结算"));
}
/**
* 职工二级医院急诊,费用 3000 元 → 起付线 250、比例 90%,支付 2475
*/
@Test
public void shouldApplyEmergencyPreferentialPolicy() {
MedicalClaim claim = claim("MC-1305", "EMPLOYEE", "SECOND_A", "DRUG_A", 3000.0,
true, false, false, 0.0);
ReimbursementDecision decision = service.settle(claim);
Assert.assertEquals(250.0, decision.getDeductibleAmount(), 0.001);
Assert.assertEquals(0.9, decision.getReimburseRatio(), 0.0001);
Assert.assertEquals(2475.0, decision.getPayableAmount(), 0.001);
Assert.assertTrue(decision.getNotes().contains("急诊就诊,起付线减半、报销比例上浮 5 个百分点"));
}
/**
* 异地就医未备案,费用 6000 元理论支付 4680 → 挂账转人工审核
*/
@Test
public void shouldHoldOffSiteClaimWithoutFiling() {
MedicalClaim claim = claim("MC-1306", "EMPLOYEE", "THIRD_A", "DRUG_A", 6000.0,
false, true, false, 0.0);
ReimbursementDecision decision = service.settle(claim);
Assert.assertEquals(4680.0, decision.getPayableAmount(), 0.001);
Assert.assertEquals("MANUAL_REVIEW", decision.getResult());
Assert.assertTrue(decision.isNeedManualReview());
Assert.assertTrue(decision.getNotes().contains("异地就医未备案,转人工审核后再行结算"));
}
private MedicalClaim claim(String claimNo, String insuranceType, String hospitalLevel,
String category, double totalExpense, boolean emergency,
boolean offSite, boolean filed, double annualAccumulated) {
MedicalClaim claim = new MedicalClaim();
claim.setClaimNo(claimNo);
claim.setPatientName("患者");
claim.setInsuranceType(insuranceType);
claim.setHospitalLevel(hospitalLevel);
claim.setMedicalCategory(category);
claim.setTotalExpense(totalExpense);
claim.setEmergency(emergency);
claim.setOffSite(offSite);
claim.setFiled(filed);
claim.setAnnualAccumulated(annualAccumulated);
return claim;
}
}

Run at the project root:

Terminal window
mvn test -Dtest=MedicalReimbursementServiceTest
Test method Constructed business profile Key assertions / expected decision
shouldReimburseEmployeeThirdGradeClassA Employee, tier-3A, class A drug, 5000 yuan, non-emergency (MC-1301) In catalog, APPROVE, deductible 800, ratio 0.9, payment 3780.0, self-pay 1220.0, not capped, no review
shouldReimburseResidentCommunityClassB Resident, community, class B drug, 1000 yuan (MC-1302) Upfront self-pay 100, deductible 100, ratio 0.6, payment 480.0, self-pay 520.0, contains class B self-pay note
shouldDenyOutOfCatalogClassCDrug Employee, tier-3A, class C drug, 800 yuan (MC-1303) Out-of-catalog SELF_PAY, payment 0.0, ratio 0.0, self-pay 800.0, contains full self-pay note
shouldTruncateAtAnnualCap Employee tier-3A class A, 600,000 yuan, 280,000 already paid this year (MC-1304) Cap 300000, theoretical 539280 truncated to 20000.0, self-pay 580000.0, capped=true
shouldApplyEmergencyPreferentialPolicy Employee, tier-2 hospital, emergency, class A, 3000 yuan (MC-1305) Emergency deductible 250 (500 halved), ratio 0.9 (0.85+0.05), payment 2475.0
shouldHoldOffSiteClaimWithoutFiling Employee tier-3A class A, 6000 yuan, off-site unfiled (MC-1306) Theoretical payment 4680.0 held, MANUAL_REVIEW, needManualReview=true

Employee tier-3A class A drug (expense 5000 yuan):

起付线: 800.0 报销比例: 0.9 统筹支付: 3780.0 个人自付: 1220.0
结论: APPROVE 人工审核: false

Large expense hitting the cap (expense 600,000, 280,000 already paid this year, remaining allowance 20,000):

理论支付: 539280.0 实际支付: 20000.0(按年度剩余额度截断)
个人自付: 580000.0 触达封顶线: true
  • The entire policy matrix is externalized in XML; hot updates do not touch the HIS main program: 12 deductible tiers, 6 ratio tiers, the class B 10% upfront self-pay, and the 300k/200k caps all live in medical-reimbursement.xml; regional policy adjustments take effect by editing the script and rebuilding the proxy.
  • The Service fixes the reimbursement chain order: the settle method strictly chains “catalog determination → upfront self-pay → deductible → ratio → cap truncation → conclusion”; out-of-catalog returns early on the Java side, structurally ensuring class C items never enter the reimbursement chain by mistake.
  • Ratio uplift capped at 1.0 inside the script: reimburseRatio adds 0.05 for emergencies and immediately truncates with ratio > 1.0, preventing any matrix tier from producing a reimbursement ratio above 100%.
  • Deductible subtraction zeroes negatives on the Java side; the cap truncates against the dynamic remaining allowance: if the base after upfront self-pay and deductible is negative, it is zeroed; applyCap uses “cap - year-to-date accumulated” as the remaining allowance — paying 0 when remaining ≤0, settling at the remainder when insufficient — and records the event via the capped flag.
  • Positive returns express negation, adapting to script syntax: complianceResult uses positive comparisons like withinCatalog == false and filed == false to express negative conditions, avoiding the script’s lack of ! negation; the branch semantics correspond one-to-one with the original policy text.
  • @Param name-based binding + Java 8 with no heavy dependencies: the 8 rule methods bind multi-tier inputs by parameter name, and the proxy instance created in @Before is reused across six test cases; the ASM call chain has no standalone service process, embeds into JDK 8 and IT-innovation medical-insurance core environments, and every settlement’s criteria and truncation process is auditable.