Skip to content

Case 33: Health screening risk flags

This case corresponds to scenario 33 of JQuick-Java USE-CASES (requirement 40: health-screening anomaly detection and risk flagging), in the medical public-health / health-management domain. The decision medically grades each of the eight physiological indicators in a health-screening report, then aggregates an overall risk level, anomaly counts, combined risk tags, retest advice, and the recommended first-visit department.

Health-screening centers and public-health agencies need to translate lab values into a unified medical risk language when issuing examination reports in bulk. The input model HealthScreeningReport carries the report ID, examinee name, gender (M/F), age, and eight indicators: systolic/diastolic blood pressure, fasting glucose, total cholesterol, body mass index (BMI), resting heart rate, alanine aminotransferase (ALT), and serum creatinine — plus two epidemiological factors: smoking history and family history of diabetes/hypertension.

The rule layer provides an independent grading method per indicator, uniformly returning NORMAL/ATTENTION/HIGH/CRITICAL (ALT returns a rank of 0/1/2, mapped by the Service). The two blood-pressure values are merged at the orchestration side by taking the higher grade as the single “blood pressure” item; serum creatinine uses gender-differentiated thresholds (attention lines at 97/115 for males, 70/80 for females); BMI covers both obesity and underweight below 18.5; heart rate covers both tachycardia and bradycardia below 50 beats/min. The orchestration layer walks the seven items (blood pressure merged) counting abnormalities, HIGH count, CRITICAL count, and taking the highest level, then applies risk tags by combined factors: blood pressure HIGH or above plus smoking history is tagged “high cardiovascular risk”; fasting glucose HIGH or above plus family history is tagged “high-risk diabetes population”.

The decision result is carried by HealthScreeningDecision: the overall highest level, three counts, risk tags riskTags, per-item advice prefixed with 【危急】/【异常】/【关注】 in advice, plus the recommended first-visit department and retest timeframe — critical values require a visit within 3 days, two HIGH items within two weeks, one HIGH item within one month, borderline items get lifestyle intervention with re-evaluation in 6 months, and all-normal results follow annual routine screening. Departments are routed by priority among cardiology, endocrinology, nephrology, gastroenterology, or general practice / health management.

  • Medical thresholds update with guidelines and populations: the cut-offs for blood pressure, glucose, and lipids differ across guideline versions and screening packages; hardcoding them in the HIS/screening system forces vendor-coordinated releases for every adjustment.
  • The eight indicators must be tunable independently: banding lines, gender differences (creatinine), and bidirectional abnormalities (BMI underweight/obesity, heart rate bradycardia/tachycardia) mixed into one big method are extremely error-prone to maintain.
  • Combined risk factors are hard to express: blood pressure or glucose alone does not trigger a high-risk conclusion; factors like smoking history and family history must be overlaid to form tags, and traditional hardcoded branches are numerous and scattered.
  • Abnormal results lack actionable referral guidance: red-flagged values without department and timeframe still require manual interpretation by the examinee, leaving screening institutions with heavy callback and health-intervention workloads.
  • Legacy medical systems and IT-innovation constraints: regional public-health platforms and hospital screening systems largely run on JDK 8 and domestic JDKs, unsuitable for heavyweight clinical rule platforms requiring separate deployment.

This case uses the standard JQuick-Java six-piece set: input model, decision result model, Rules interface, XML rule script, Service orchestration, and JUnit tests. All code is verified on Oracle JDK 1.8.

Layer Responsibility Files
Domain model Carries the eight screening indicators and the anomaly/risk-stratification decision HealthScreeningReport.java, HealthScreeningDecision.java
Rule layer Medical grading per indicator; thresholds and gender differences all externalized HealthScreeningRules.java, health-screening.xml
Orchestration layer Blood-pressure merge by higher grade, anomaly counting, combined risk tags, department and retest-timeframe routing HealthScreeningService.java
Tests 6 real-world screening profiles HealthScreeningServiceTest.java
Rule method Criteria Score/output
systolicLevel Systolic ≥ 180 CRITICAL; ≥ 140 HIGH; ≥ 130 ATTENTION; otherwise NORMAL NORMAL/ATTENTION/HIGH/CRITICAL
diastolicLevel Diastolic ≥ 110 CRITICAL; ≥ 90 HIGH; ≥ 85 ATTENTION; otherwise NORMAL NORMAL/ATTENTION/HIGH/CRITICAL
glucoseLevel Fasting glucose ≥ 7.0 CRITICAL; ≥ 6.1 HIGH; ≥ 5.6 ATTENTION; otherwise NORMAL NORMAL/ATTENTION/HIGH/CRITICAL
cholesterolLevel Total cholesterol ≥ 6.2 HIGH; ≥ 5.2 ATTENTION; otherwise NORMAL (no CRITICAL band) NORMAL/ATTENTION/HIGH
bmiLevel BMI ≥ 32 CRITICAL; ≥ 28 HIGH; ≥ 24 ATTENTION; below 18.5 also ATTENTION (underweight); otherwise NORMAL NORMAL/ATTENTION/HIGH/CRITICAL
heartRateLevel Resting heart rate ≥ 120 CRITICAL; ≥ 100 HIGH; below 50 HIGH; ≤ 55 ATTENTION; otherwise NORMAL NORMAL/ATTENTION/HIGH/CRITICAL
altLevelRank ALT above 120 returns 2; above 40 returns 1; otherwise 0 (Service maps to HIGH/ATTENTION/NORMAL) 0/1/2
creatinineLevel Creatinine above 115 HIGH, above 97 ATTENTION for males; above 80 HIGH, above 70 ATTENTION for females; otherwise NORMAL NORMAL/ATTENTION/HIGH

Place the two model classes under src/test/java/com/github/paohaijiao/demo/model/, the rules interface under demo/, the Service under demo/service/, the XML script under src/test/resources/demo/, and the test class under demo/, keeping the package names exactly as in the source. Run mvn test -Dtest=HealthScreeningServiceTest from the project root to reproduce all 6 cases.

package com.github.paohaijiao.demo.model;
import lombok.AllArgsConstructor;
import lombok.Data;
import lombok.NoArgsConstructor;
/**
* 体检指标要素
*/
@Data
@NoArgsConstructor
@AllArgsConstructor
public class HealthScreeningReport {
/**
* 体检报告号
*/
private String reportId;
/**
* 受检人
*/
private String personName;
/**
* 性别:M 男 / F 女
*/
private String gender;
/**
* 年龄
*/
private int age;
/**
* 收缩压(mmHg)
*/
private int systolicBp;
/**
* 舒张压(mmHg)
*/
private int diastolicBp;
/**
* 空腹血糖(mmol/L)
*/
private double fastingGlucose;
/**
* 总胆固醇(mmol/L)
*/
private double totalCholesterol;
/**
* 体质指数
*/
private double bmi;
/**
* 静息心率(次/分)
*/
private int heartRate;
/**
* 谷丙转氨酶(U/L)
*/
private int alt;
/**
* 血肌酐(μmol/L)
*/
private int creatinine;
/**
* 是否有吸烟史
*/
private boolean smokingHistory;
/**
* 是否有糖尿病/高血压家族史
*/
private boolean familyHistory;
}
package com.github.paohaijiao.demo.model;
import lombok.Data;
import java.util.ArrayList;
import java.util.List;
/**
* 体检异常筛查与风险分层决策
*/
@Data
public class HealthScreeningDecision {
/**
* 报告号
*/
private String reportId;
/**
* 整体风险级别:NORMAL / ATTENTION / HIGH / CRITICAL
*/
private String highestLevel;
/**
* 异常指标数(ATTENTION 及以上)
*/
private int abnormalCount;
/**
* CRITICAL 指标数
*/
private int criticalCount;
/**
* HIGH 指标数
*/
private int highCount;
/**
* 风险标签
*/
private List<String> riskTags = new ArrayList<String>();
/**
* 逐项异常说明与复查建议
*/
private List<String> advice = new ArrayList<String>();
/**
* 建议首诊科室
*/
private String followUpDepartment;
public void addRiskTag(String tag) {
this.riskTags.add(tag);
}
public void addAdvice(String line) {
this.advice.add(line);
}
}
package com.github.paohaijiao.demo;
import com.github.paohaijiao.xml.param.Param;
/**
* 场景 33:体检各指标医学分级规则(NORMAL/ATTENTION/HIGH/CRITICAL)
*/
public interface HealthScreeningRules {
/**
* 收缩压分级
*/
String systolicLevel(@Param("systolic") int systolic);
/**
* 舒张压分级
*/
String diastolicLevel(@Param("diastolic") int diastolic);
/**
* 空腹血糖分级
*/
String glucoseLevel(@Param("glucose") double glucose);
/**
* 总胆固醇分级
*/
String cholesterolLevel(@Param("cholesterol") double cholesterol);
/**
* BMI 分级(含消瘦提示)
*/
String bmiLevel(@Param("bmi") double bmi);
/**
* 静息心率分级
*/
String heartRateLevel(@Param("heartRate") int heartRate);
/**
* 谷丙转氨酶分级
*/
int altLevelRank(@Param("alt") int alt);
/**
* 血肌酐分级(男女阈值不同)
*/
String creatinineLevel(@Param("creatinine") int creatinine,
@Param("gender") String gender);
}
<?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.HealthScreeningRules">
<java name="systolicLevel" returnClass="java.lang.String">
<![CDATA[
str def systolicLevel(int:systolic) {
if (systolic >= 180) {
return "CRITICAL";
} else if (systolic >= 140) {
return "HIGH";
} else if (systolic >= 130) {
return "ATTENTION";
} else {
return "NORMAL";
}
}
]]>
</java>
<java name="diastolicLevel" returnClass="java.lang.String">
<![CDATA[
str def diastolicLevel(int:diastolic) {
if (diastolic >= 110) {
return "CRITICAL";
} else if (diastolic >= 90) {
return "HIGH";
} else if (diastolic >= 85) {
return "ATTENTION";
} else {
return "NORMAL";
}
}
]]>
</java>
<java name="glucoseLevel" returnClass="java.lang.String">
<![CDATA[
str def glucoseLevel(double:glucose) {
if (glucose >= 7.0) {
return "CRITICAL";
} else if (glucose >= 6.1) {
return "HIGH";
} else if (glucose >= 5.6) {
return "ATTENTION";
} else {
return "NORMAL";
}
}
]]>
</java>
<java name="cholesterolLevel" returnClass="java.lang.String">
<![CDATA[
str def cholesterolLevel(double:cholesterol) {
if (cholesterol >= 6.2) {
return "HIGH";
} else if (cholesterol >= 5.2) {
return "ATTENTION";
} else {
return "NORMAL";
}
}
]]>
</java>
<java name="bmiLevel" returnClass="java.lang.String">
<![CDATA[
str def bmiLevel(double:bmi) {
if (bmi >= 32) {
return "CRITICAL";
} else if (bmi >= 28) {
return "HIGH";
} else if (bmi >= 24) {
return "ATTENTION";
} else if (bmi < 18.5) {
return "ATTENTION";
} else {
return "NORMAL";
}
}
]]>
</java>
<java name="heartRateLevel" returnClass="java.lang.String">
<![CDATA[
str def heartRateLevel(int:heartRate) {
if (heartRate >= 120) {
return "CRITICAL";
} else if (heartRate >= 100) {
return "HIGH";
} else if (heartRate < 50) {
return "HIGH";
} else if (heartRate <= 55) {
return "ATTENTION";
} else {
return "NORMAL";
}
}
]]>
</java>
<java name="altLevelRank" returnClass="int">
<![CDATA[
int def altLevelRank(int:alt) {
if (alt > 120) {
return 2;
} else if (alt > 40) {
return 1;
} else {
return 0;
}
}
]]>
</java>
<java name="creatinineLevel" returnClass="java.lang.String">
<![CDATA[
str def creatinineLevel(int:creatinine, str:gender) {
if (gender == "M") {
if (creatinine > 115) {
return "HIGH";
} else if (creatinine > 97) {
return "ATTENTION";
} else {
return "NORMAL";
}
} else {
if (creatinine > 80) {
return "HIGH";
} else if (creatinine > 70) {
return "ATTENTION";
} else {
return "NORMAL";
}
}
}
]]>
</java>
</javas>
package com.github.paohaijiao.demo.service;
import com.github.paohaijiao.demo.HealthScreeningRules;
import com.github.paohaijiao.demo.model.HealthScreeningDecision;
import com.github.paohaijiao.demo.model.HealthScreeningReport;
/**
* 场景 33:体检多指标异常筛查、风险标注与就诊指引
*/
public class HealthScreeningService {
private final HealthScreeningRules rules;
public HealthScreeningService(HealthScreeningRules rules) {
this.rules = rules;
}
public HealthScreeningDecision screen(HealthScreeningReport r) {
HealthScreeningDecision d = new HealthScreeningDecision();
d.setReportId(r.getReportId());
String bp = higher(rules.systolicLevel(r.getSystolicBp()),
rules.diastolicLevel(r.getDiastolicBp()));
String glucose = rules.glucoseLevel(r.getFastingGlucose());
String cholesterol = rules.cholesterolLevel(r.getTotalCholesterol());
String bmi = rules.bmiLevel(r.getBmi());
String heartRate = rules.heartRateLevel(r.getHeartRate());
int altRank = rules.altLevelRank(r.getAlt());
String alt = rankToLevel(altRank);
String creatinine = rules.creatinineLevel(r.getCreatinine(), r.getGender());
addItem(d, "血压 " + r.getSystolicBp() + "/" + r.getDiastolicBp() + "mmHg", bp,
"家庭监测血压,低盐饮食并控制体重", "立即急诊排查高血压急症");
addItem(d, "空腹血糖 " + r.getFastingGlucose() + "mmol/L", glucose,
"控制精制糖摄入并增加有氧运动", "尽快到内分泌科明确糖尿病诊断");
addItem(d, "总胆固醇 " + r.getTotalCholesterol() + "mmol/L", cholesterol,
"减少饱和脂肪摄入,3 个月后复查血脂", "低脂饮食并到心内科评估调脂治疗");
addItem(d, "体质指数 " + r.getBmi(), bmi,
"通过膳食与运动把 BMI 控制在 18.5-24", "到营养/减重门诊干预肥胖");
addItem(d, "静息心率 " + r.getHeartRate() + " 次/分", heartRate,
"规律作息,避免浓茶咖啡后复查心电图", "尽快完善动态心电图检查");
addItem(d, "谷丙转氨酶 " + r.getAlt() + "U/L", alt,
"避免饮酒与熬夜,复查肝功能", "消化/肝病科排查肝炎、脂肪肝等病因");
addItem(d, "血肌酐 " + r.getCreatinine() + "μmol/L", creatinine,
"多饮水避免滥用止痛药,复查肾功能", "肾内科评估肾功能损伤");
if (rank(bp) >= 2 && r.isSmokingHistory()) {
d.addRiskTag("心血管高风险(血压异常合并吸烟史)");
}
if (rank(glucose) >= 2 && r.isFamilyHistory()) {
d.addRiskTag("糖尿病高危人群(空腹血糖异常合并家族史)");
}
String highest = "NORMAL";
for (String level : new String[]{bp, glucose, cholesterol, bmi, heartRate, alt, creatinine}) {
if (rank(level) > rank(highest)) {
highest = level;
}
if (!"NORMAL".equals(level)) {
d.setAbnormalCount(d.getAbnormalCount() + 1);
}
if ("CRITICAL".equals(level)) {
d.setCriticalCount(d.getCriticalCount() + 1);
}
if ("HIGH".equals(level)) {
d.setHighCount(d.getHighCount() + 1);
}
}
d.setHighestLevel(highest);
d.setFollowUpDepartment(resolveDepartment(bp, heartRate, glucose, creatinine, alt, highest));
appendFollowUpAdvice(d);
return d;
}
private void appendFollowUpAdvice(HealthScreeningDecision d) {
if (d.getCriticalCount() >= 1) {
d.addAdvice("存在危急值指标,请于 3 日内到 " + d.getFollowUpDepartment() + " 就诊");
} else if (d.getHighCount() >= 2) {
d.addAdvice("多项指标明显异常,请于 2 周内到 " + d.getFollowUpDepartment() + " 复查");
} else if (d.getHighCount() == 1) {
d.addAdvice("单项指标明显异常,请于 1 个月内到 " + d.getFollowUpDepartment() + " 复查");
} else if (d.getAbnormalCount() > 0) {
d.addAdvice("指标处于临界区间,以生活方式干预为主,6 个月后复评");
} else {
d.addAdvice("本次体检各项指标正常,请保持良好生活习惯,按年度常规体检即可");
}
}
private String resolveDepartment(String bp, String heartRate, String glucose,
String creatinine, String alt, String highest) {
if ("NORMAL".equals(highest)) {
return "全科/健康管理科";
}
if (rank(bp) >= 2) {
return "心血管内科";
}
if (rank(heartRate) >= 2) {
return "心血管内科";
}
if (rank(glucose) >= 2) {
return "内分泌科";
}
if ("HIGH".equals(creatinine)) {
return "肾内科";
}
if ("HIGH".equals(alt)) {
return "消化内科";
}
return "全科/健康管理科";
}
private void addItem(HealthScreeningDecision d, String label, String level,
String attentionAdvice, String highAdvice) {
if ("NORMAL".equals(level)) {
return;
}
String prefix;
if ("CRITICAL".equals(level)) {
prefix = "【危急】";
} else if ("HIGH".equals(level)) {
prefix = "【异常】";
} else {
prefix = "【关注】";
}
String body = "CRITICAL".equals(level) || "HIGH".equals(level) ? highAdvice : attentionAdvice;
d.addAdvice(prefix + label + ":" + level + "。" + body);
}
private String higher(String a, String b) {
return rank(a) >= rank(b) ? a : b;
}
private String rankToLevel(int rank) {
if (rank >= 2) {
return "HIGH";
}
if (rank == 1) {
return "ATTENTION";
}
return "NORMAL";
}
private int rank(String level) {
if ("CRITICAL".equals(level)) {
return 3;
}
if ("HIGH".equals(level)) {
return 2;
}
if ("ATTENTION".equals(level)) {
return 1;
}
return 0;
}
}
package com.github.paohaijiao.demo;
import com.github.paohaijiao.demo.model.HealthScreeningDecision;
import com.github.paohaijiao.demo.model.HealthScreeningReport;
import com.github.paohaijiao.demo.service.HealthScreeningService;
import com.github.paohaijiao.xml.JQuickJava;
import org.junit.Assert;
import org.junit.Before;
import org.junit.Test;
/**
* 场景 33:体检指标异常筛查与风险标注
* <p>
* 6 组形态:全指标正常、四项临界关注、高血压合并吸烟、糖尿病危急合并家族史、
* 多器官危急值、心动过缓与临界恢复分叉。
*/
public class HealthScreeningServiceTest {
private HealthScreeningService service;
@Before
public void setUp() {
HealthScreeningRules rules = JQuickJava.create()
.createApi(HealthScreeningRules.class, "demo/health-screening.xml");
this.service = new HealthScreeningService(rules);
}
/**
* 年轻男性全部指标正常 → 年度常规体检
*/
@Test
public void shouldReturnNormalForHealthyYoungMan() {
HealthScreeningReport r = base("RPT001", "M", 28);
r.setSystolicBp(115);
r.setDiastolicBp(75);
r.setFastingGlucose(5.0);
r.setTotalCholesterol(4.5);
r.setBmi(22);
r.setHeartRate(70);
r.setAlt(25);
r.setCreatinine(70);
HealthScreeningDecision d = service.screen(r);
Assert.assertEquals("NORMAL", d.getHighestLevel());
Assert.assertEquals(0, d.getAbnormalCount());
Assert.assertEquals("全科/健康管理科", d.getFollowUpDepartment());
Assert.assertTrue(d.getAdvice().get(0).contains("各项指标正常"));
}
/**
* 血压、血糖、胆固醇、BMI 四项临界 → ATTENTION,生活方式干预
*/
@Test
public void shouldMarkFourBorderlineMetrics() {
HealthScreeningReport r = base("RPT002", "M", 42);
r.setSystolicBp(132);
r.setDiastolicBp(86);
r.setFastingGlucose(5.8);
r.setTotalCholesterol(5.5);
r.setBmi(24.5);
r.setHeartRate(72);
r.setAlt(30);
r.setCreatinine(85);
HealthScreeningDecision d = service.screen(r);
Assert.assertEquals("ATTENTION", d.getHighestLevel());
Assert.assertEquals(4, d.getAbnormalCount());
Assert.assertEquals(0, d.getHighCount());
Assert.assertTrue(d.getAdvice().get(0).startsWith("【关注】血压"));
Assert.assertTrue(d.getAdvice().get(d.getAdvice().size() - 1).contains("生活方式干预"));
}
/**
* 血压 152/96、胆固醇 6.5、吸烟史 → 心血管高风险,2 周内心内科
*/
@Test
public void shouldTagCardioRiskForHypertensiveSmoker() {
HealthScreeningReport r = base("RPT003", "M", 51);
r.setSystolicBp(152);
r.setDiastolicBp(96);
r.setFastingGlucose(5.2);
r.setTotalCholesterol(6.5);
r.setBmi(27);
r.setHeartRate(82);
r.setAlt(35);
r.setCreatinine(90);
r.setSmokingHistory(true);
HealthScreeningDecision d = service.screen(r);
Assert.assertEquals("HIGH", d.getHighestLevel());
Assert.assertEquals(3, d.getAbnormalCount());
Assert.assertEquals(2, d.getHighCount());
Assert.assertTrue(d.getRiskTags().contains("心血管高风险(血压异常合并吸烟史)"));
Assert.assertEquals("心血管内科", d.getFollowUpDepartment());
Assert.assertTrue(d.getAdvice().get(d.getAdvice().size() - 1).contains("2 周内"));
}
/**
* 空腹血糖 7.2 危急 + 家族史 → 糖尿病高危,3 日内内分泌科
*/
@Test
public void shouldFlagCriticalGlucoseWithFamilyHistory() {
HealthScreeningReport r = base("RPT004", "F", 47);
r.setSystolicBp(125);
r.setDiastolicBp(80);
r.setFastingGlucose(7.2);
r.setTotalCholesterol(5.0);
r.setBmi(25.5);
r.setHeartRate(75);
r.setAlt(30);
r.setCreatinine(72);
r.setFamilyHistory(true);
HealthScreeningDecision d = service.screen(r);
Assert.assertEquals("CRITICAL", d.getHighestLevel());
Assert.assertEquals(1, d.getCriticalCount());
Assert.assertTrue(d.getRiskTags().contains("糖尿病高危人群(空腹血糖异常合并家族史)"));
Assert.assertEquals("内分泌科", d.getFollowUpDepartment());
Assert.assertTrue(d.getAdvice().get(d.getAdvice().size() - 1).contains("3 日内"));
}
/**
* 血压 182/112、血糖 8.1、ALT 130、肌酐 130、心率 105 → 多器官危急,首诊心内科
*/
@Test
public void shouldPrioritizeCardiologyForMultipleCriticalValues() {
HealthScreeningReport r = base("RPT005", "M", 58);
r.setSystolicBp(182);
r.setDiastolicBp(112);
r.setFastingGlucose(8.1);
r.setTotalCholesterol(4.8);
r.setBmi(26);
r.setHeartRate(105);
r.setAlt(130);
r.setCreatinine(130);
HealthScreeningDecision d = service.screen(r);
Assert.assertEquals("CRITICAL", d.getHighestLevel());
Assert.assertEquals(2, d.getCriticalCount());
Assert.assertEquals(3, d.getHighCount());
Assert.assertEquals("心血管内科", d.getFollowUpDepartment());
Assert.assertTrue(d.getAdvice().get(0).contains("立即急诊排查高血压急症"));
Assert.assertTrue(d.getAdvice().contains("【异常】谷丙转氨酶 130U/L:HIGH。消化/肝病科排查肝炎、脂肪肝等病因"));
}
/**
* 心率 46 为 HIGH 首诊心内科;恢复到 54 后整体仅 BMI 消瘦关注
*/
@Test
public void shouldHandleBradycardiaAndRecovery() {
HealthScreeningReport r = base("RPT006", "F", 34);
r.setSystolicBp(118);
r.setDiastolicBp(72);
r.setFastingGlucose(4.9);
r.setTotalCholesterol(4.6);
r.setBmi(17.8);
r.setHeartRate(46);
r.setAlt(22);
r.setCreatinine(65);
HealthScreeningDecision d1 = service.screen(r);
Assert.assertEquals("HIGH", d1.getHighestLevel());
Assert.assertEquals(1, d1.getHighCount());
Assert.assertEquals("心血管内科", d1.getFollowUpDepartment());
Assert.assertTrue(d1.getAdvice().get(1).contains("尽快完善动态心电图检查"));
r.setHeartRate(54);
HealthScreeningDecision d2 = service.screen(r);
Assert.assertEquals("ATTENTION", d2.getHighestLevel());
Assert.assertTrue(d2.getAdvice().get(d2.getAdvice().size() - 1).contains("生活方式干预"));
}
private HealthScreeningReport base(String id, String gender, int age) {
HealthScreeningReport r = new HealthScreeningReport();
r.setReportId(id);
r.setPersonName("测试受检人");
r.setGender(gender);
r.setAge(age);
r.setSystolicBp(120);
r.setDiastolicBp(80);
r.setFastingGlucose(5.0);
r.setTotalCholesterol(4.5);
r.setBmi(22);
r.setHeartRate(72);
r.setAlt(25);
r.setCreatinine(gender.equals("M") ? 80 : 65);
r.setSmokingHistory(false);
r.setFamilyHistory(false);
return r;
}
}

Run from the project root:

Terminal window
mvn test -Dtest=HealthScreeningServiceTest

Test coverage (6 @Test methods covering all-normal, borderline cluster, combined tags, critical values, multi-organ critical, and recovery branch):

Test method Business profile Key assertions / expected decision
shouldReturnNormalForHealthyYoungMan RPT001 male, 28: BP 115/75, glucose 5.0, cholesterol 4.5, BMI 22, heart rate 70, ALT 25, creatinine 70 Highest level NORMAL, abnormal count 0, department “全科/健康管理科”; first advice contains “各项指标正常”
shouldMarkFourBorderlineMetrics RPT002 male, 42: 132/86, glucose 5.8, cholesterol 5.5, BMI 24.5 (creatinine 85 still in the male NORMAL band) ATTENTION, abnormal count 4, HIGH count 0; first advice starts with “【关注】血压”, last contains “生活方式干预”
shouldTagCardioRiskForHypertensiveSmoker RPT003 male, 51: BP 152/96 HIGH, cholesterol 6.5 HIGH, BMI 27 attention, smoking history HIGH, abnormal count 3, HIGH count 2; contains tag “心血管高风险(血压异常合并吸烟史)”, department cardiology, last advice contains “2 周内”
shouldFlagCriticalGlucoseWithFamilyHistory RPT004 female, 47: fasting glucose 7.2 CRITICAL, BMI 25.5 attention, diabetes family history CRITICAL, critical count 1; contains tag “糖尿病高危人群(空腹血糖异常合并家族史)”, department endocrinology, last advice contains “3 日内”
shouldPrioritizeCardiologyForMultipleCriticalValues RPT005 male, 58: BP 182/112 and glucose 8.1 both CRITICAL; heart rate 105, ALT 130, creatinine 130 all HIGH; BMI 26 attention CRITICAL, critical count 2, HIGH count 3, department cardiology; first advice contains “立即急诊排查高血压急症”, and includes “【异常】谷丙转氨酶 130U/L:HIGH。消化/肝病科排查肝炎、脂肪肝等病因”
shouldHandleBradycardiaAndRecovery RPT006 female, 34: heart rate 46 (below 50) HIGH, BMI 17.8 underweight attention; same report with heart rate changed to 54 First pass HIGH, HIGH count 1, cardiology; second advice contains “尽快完善动态心电图检查”; after recovery highest level ATTENTION, last advice contains “生活方式干预”
  • Medical thresholds externalized in XML, hot-updated with guidelines: all banding lines for the eight indicators (including glucose 5.6/6.1/7.0, BMI 18.5/24/28/32, etc.) live in health-screening.xml; when a guideline revision or package change happens, edit the XML and rebuild the proxy — the screening system’s main program is untouched.
  • One method per indicator, bidirectional anomalies independently expressed: BMI covers both obesity and underweight, heart rate covers both tachycardia and bradycardia, each in its own method; ALT returns an integer rank 0/1/2 mapped by Java, so the script only owns objective banding and each item is independently tunable and auditable.
  • Combined risk factors tagged at the orchestration layer: the two cross rules — abnormal blood pressure plus smoking history, abnormal glucose plus family history — are held by the Service, decoupling risk tags from indicator grading; adding a new epidemiological factor touches no indicator script.
  • Dual blood-pressure values merged by higher grade, departments short-circuited by priority: systolic/diastolic are merged into one counted item via higher; department routing short-circuits in the order cardiology, endocrinology, nephrology, gastroenterology, keeping the first-visit department stable and predictable when critical values span multiple organs.
  • Integer-rank comparison and parameterized gender thresholds: the Service compares the four levels via a 0-3 rank; creatinine injects gender by name into the script through @Param("gender"), branching male/female thresholds within one method with no duplicate interface.
  • ASM proxy, Java 8, no heavyweight dependencies: the dynamic interface proxy is singleton-reusable with no external process or database, embeddable directly into regional public-health platforms and hospital screening systems running on JDK 8 and IT-innovation JDKs.