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.
Industry scenario
Section titled “Industry scenario”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.
Business pain points
Section titled “Business pain points”- 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.
Solution architecture
Section titled “Solution architecture”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 breakdown
Section titled “Rule method breakdown”| 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 |
Code implementation (complete & runnable)
Section titled “Code implementation (complete & runnable)”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.
HealthScreeningReport.java
Section titled “HealthScreeningReport.java”package com.github.paohaijiao.demo.model;
import lombok.AllArgsConstructor;import lombok.Data;import lombok.NoArgsConstructor;
/** * 体检指标要素 */@Data@NoArgsConstructor@AllArgsConstructorpublic 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;}HealthScreeningDecision.java
Section titled “HealthScreeningDecision.java”package com.github.paohaijiao.demo.model;
import lombok.Data;
import java.util.ArrayList;import java.util.List;
/** * 体检异常筛查与风险分层决策 */@Datapublic 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); }}HealthScreeningRules.java
Section titled “HealthScreeningRules.java”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);}health-screening.xml
Section titled “health-screening.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.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>HealthScreeningService.java
Section titled “HealthScreeningService.java”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; }}HealthScreeningServiceTest.java
Section titled “HealthScreeningServiceTest.java”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 & verified results
Section titled “Run & verified results”Run from the project root:
mvn test -Dtest=HealthScreeningServiceTestTest 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 “生活方式干预” |
Technical highlights
Section titled “Technical highlights”- 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.