|
@@ -0,0 +1,336 @@
|
|
|
|
|
+package com.etotem.cfc.unit;
|
|
|
|
|
+
|
|
|
|
|
+import com.etotem.cfc.constant.AttributionLowSideRules;
|
|
|
|
|
+import com.etotem.cfc.service.attribution.IndicatorInterpretEngine;
|
|
|
|
|
+import com.etotem.cfc.service.attribution.model.IndicatorReading;
|
|
|
|
|
+import com.etotem.cfc.service.attribution.model.MemberAttribution;
|
|
|
|
|
+import com.etotem.cfc.service.attribution.model.MemberProfile;
|
|
|
|
|
+import org.junit.jupiter.api.DisplayName;
|
|
|
|
|
+import org.junit.jupiter.api.Test;
|
|
|
|
|
+
|
|
|
|
|
+import java.lang.reflect.Field;
|
|
|
|
|
+import java.lang.reflect.Method;
|
|
|
|
|
+import java.util.Collections;
|
|
|
|
|
+import java.util.List;
|
|
|
|
|
+
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertFalse;
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertNotNull;
|
|
|
|
|
+import static org.junit.jupiter.api.Assertions.assertTrue;
|
|
|
|
|
+
|
|
|
|
|
+/**
|
|
|
|
|
+ * 契约级单测:低侧判读规则入库(能力键 {@code guhe.lowSide.ruleLoad},单元 guhe.lowSide)。
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p>契约来源(2026-11-08《浠艾福平台程序修改计划(guhe_v2 兼容 · 知识库低侧口径 · 专家同步)》§三任务分解 T3,P0):
|
|
|
|
|
+ * <blockquote>
|
|
|
|
|
+ * T3 低侧判读规则入库(L4-1/L4-2):<b>条件致病菌偏低侧按四机制解释,全部标「关联非因果」</b>;
|
|
|
|
|
+ * <b>U 型双向口径</b>;<b>参考下限 0 的菌不标「偏低超标」</b>。
|
|
|
|
|
+ * <br>验收:任意「偏低」归因节点 {@code association_only==true};存量无「缺大肠杆菌→补菌」措辞;
|
|
|
|
|
+ * 低侧机制证据全部标 PUB/KB。
|
|
|
|
|
+ * </blockquote></p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p><b>契约语义</b>:本能力键不是「合规门禁事后回扫」(那是 T5 G-1 红线组),而是 <b>低侧判读规则
|
|
|
|
|
+ * 在 L4 规则层「入库」并为 L4 归因引擎所加载应用</b> —— 依据知识库新增子键
|
|
|
|
|
+ * {@code 条件致病菌低侧判读补充(人工)}(§2.2:判读铁律 U 型双向 / 参考下限 0 / 关联非因果 +
|
|
|
|
|
+ * 大肠杆菌偏低侧四机制),L4 引擎对偏低条件致病菌读数:
|
|
|
|
|
+ * <ul>
|
|
|
|
|
+ * <li>按<b>四机制</b>解释,全部标「关联非因果」(L4-1);</li>
|
|
|
|
|
+ * <li>套用 <b>U 型双向口径</b>:越低越好<b>不成立</b>(L4-2);</li>
|
|
|
|
|
+ * <li>参考下限 <b>0</b> 的菌<b>不标「偏低超标」</b>(L4-2,{@code ND} 只是低于检出丰度)。</li>
|
|
|
|
|
+ * </ul></p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p><b>实现现状(C 档探测,src/main 现状核实,2026-11-08)</b>:L4 引擎已存在且为纯 Java 确定性规则
|
|
|
|
|
+ * ({@code IndicatorInterpretEngine} 解读 + {@code AttributionComplianceGate} 门禁);已落地的是
|
|
|
|
|
+ * <b>T5 G-1 低侧红线组</b>({@code association_only} / {@code no_causal_bridge} /
|
|
|
|
|
+ * {@code no_prescription} 三规则码 + 措辞白名单,全部在 {@code AttributionLowSideRules})。
|
|
|
|
|
+ * <b>T3 低侧判读规则(L4-1/L4-2)未落地</b>:{@code AttributionLowSideRules} 无「四机制判读」/「U 型
|
|
|
|
|
+ * 双向口径」/「参考下限 0」三规则码、无「判读铁律」与「U 型口径」常量、无规则加载入口
|
|
|
|
|
+ * {@code lowSideInterpretRules()};{@code IndicatorInterpretEngine} 对偏低条件致病菌仅产通用
|
|
|
|
|
+ * 「先医后补」含义({@code buildMeaning} 身维兜底),未按四机制判读、未标「关联非因果」、未套
|
|
|
|
|
+ * U 型双向口径。故本测试以「反射结构 + 纯规则行为」双通道探测实现缺失:能编译、能运行、断言
|
|
|
|
|
+ * 失败(红),失败断言原文清晰可贴。</p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p><b>GREEN 升级指引</b>:实现者在 {@code AttributionLowSideRules} 增加
|
|
|
|
|
+ * {@code RULE_FOUR_MECHANISM="four_mechanism"} / {@code RULE_U_SHAPE="u_shape"} /
|
|
|
|
|
+ * {@code RULE_REF_LOWER_ZERO="ref_lower_zero"} 三规则码、{@code INTERPRET_LAW_LOW_SIDE}(含
|
|
|
|
|
+ * 「关联非因果」)与 {@code U_SHAPE_CALIBER_HINT}(含「越低越好不成立」与「参考下限」)常量、
|
|
|
|
|
+ * 规则加载入口 {@code lowSideInterpretRules()}(返回上述规则码),并在
|
|
|
|
|
+ * {@code IndicatorInterpretEngine} 的低侧(偏低条件致病菌)解读路径注入四机制 + 关联非因果 +
|
|
|
|
|
+ * U 型双向口径后,本测试自动转绿。</p>
|
|
|
|
|
+ *
|
|
|
|
|
+ * <p><b>权限流说明</b>:本能力为后端内部 L4 低侧判读规则,无用户权限维度,权限流不适用,以说明性用例标注。</p>
|
|
|
|
|
+ */
|
|
|
|
|
+class GuheLowSideRuleLoadContractTest {
|
|
|
|
|
+
|
|
|
|
|
+ /** T3(L4-1)期望的四机制判读规则码 */
|
|
|
|
|
+ private static final String RULE_FOUR_MECHANISM = "four_mechanism";
|
|
|
|
|
+
|
|
|
|
|
+ /** T3(L4-2)期望的 U 型双向口径规则码 */
|
|
|
|
|
+ private static final String RULE_U_SHAPE = "u_shape";
|
|
|
|
|
+
|
|
|
|
|
+ /** T3(L4-2)期望的参考下限 0 规则码 */
|
|
|
|
|
+ private static final String RULE_REF_LOWER_ZERO = "ref_lower_zero";
|
|
|
|
|
+
|
|
|
|
|
+ /** T3 期望的「判读铁律」常量名(含关联非因果) */
|
|
|
|
|
+ private static final String INTERPRET_LAW_FIELD = "INTERPRET_LAW_LOW_SIDE";
|
|
|
|
|
+
|
|
|
|
|
+ /** T3 期望的「U 型双向口径」常量名(含越低越好不成立 / 参考下限) */
|
|
|
|
|
+ private static final String U_SHAPE_HINT_FIELD = "U_SHAPE_CALIBER_HINT";
|
|
|
|
|
+
|
|
|
|
|
+ /** T3 期望的规则加载入口方法名(低侧判读规则入库后供 L4 引擎加载) */
|
|
|
|
|
+ private static final String RULE_LOAD_METHOD = "lowSideInterpretRules";
|
|
|
|
|
+
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+ // 正常流:规则层应暴露并入库低侧判读规则(T3,L4-1/L4-2)
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-1:AttributionLowSideRules 应暴露「四机制判读」规则码")
|
|
|
|
|
+ void lowSideRules_exposesFourMechanismRuleCode() {
|
|
|
|
|
+ Field f = declaredField(AttributionLowSideRules.class, "RULE_FOUR_MECHANISM");
|
|
|
|
|
+ assertNotNull(f,
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] AttributionLowSideRules 缺少 RULE_FOUR_MECHANISM 规则码:"
|
|
|
|
|
+ + "T3(L4-1)要求条件致病菌偏低侧按「四机制」解释(§2.2 大肠杆菌偏低侧四机制),"
|
|
|
|
|
+ + "当前只剩 T5 门禁规则,未落地低侧判读规则入库。");
|
|
|
|
|
+ try {
|
|
|
|
|
+ Object v = f.get(null);
|
|
|
|
|
+ assertNotNull(v, "[guhe.lowSide.ruleLoad] RULE_FOUR_MECHANISM 常量值不应为 null。");
|
|
|
|
|
+ assertTrue(RULE_FOUR_MECHANISM.equals(String.valueOf(v)),
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] RULE_FOUR_MECHANISM 应取值为 \"" + RULE_FOUR_MECHANISM
|
|
|
|
|
+ + "\",实际为 \"" + v + "\"。");
|
|
|
|
|
+ } catch (IllegalAccessException e) {
|
|
|
|
|
+ assertTrue(false, "[guhe.lowSide.ruleLoad] 读取 RULE_FOUR_MECHANISM 失败:" + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-2:AttributionLowSideRules 应暴露「U 型双向口径」规则码")
|
|
|
|
|
+ void lowSideRules_exposesUShapeRuleCode() {
|
|
|
|
|
+ Field f = declaredField(AttributionLowSideRules.class, "RULE_U_SHAPE");
|
|
|
|
|
+ assertNotNull(f,
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] AttributionLowSideRules 缺少 RULE_U_SHAPE 规则码:"
|
|
|
|
|
+ + "T3(L4-2)要求 U 型双向口径——条件致病菌「越低越好」不成立,"
|
|
|
|
|
+ + "当前仅剩 T5 门禁规则的 association_only,未落地 U 型判读规则。");
|
|
|
|
|
+ try {
|
|
|
|
|
+ Object v = f.get(null);
|
|
|
|
|
+ assertNotNull(v, "[guhe.lowSide.ruleLoad] RULE_U_SHAPE 常量值不应为 null。");
|
|
|
|
|
+ assertTrue(RULE_U_SHAPE.equals(String.valueOf(v)),
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] RULE_U_SHAPE 应取值为 \"" + RULE_U_SHAPE
|
|
|
|
|
+ + "\",实际为 \"" + v + "\"。");
|
|
|
|
|
+ } catch (IllegalAccessException e) {
|
|
|
|
|
+ assertTrue(false, "[guhe.lowSide.ruleLoad] 读取 RULE_U_SHAPE 失败:" + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-3:AttributionLowSideRules 应暴露「参考下限 0」规则码")
|
|
|
|
|
+ void lowSideRules_exposesRefLowerZeroRuleCode() {
|
|
|
|
|
+ Field f = declaredField(AttributionLowSideRules.class, "RULE_REF_LOWER_ZERO");
|
|
|
|
|
+ assertNotNull(f,
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] AttributionLowSideRules 缺少 RULE_REF_LOWER_ZERO 规则码:"
|
|
|
|
|
+ + "T3(L4-2)要求参考下限 0 的菌(ND 只是低于检出丰度)不标「偏低超标」,"
|
|
|
|
|
+ + "当前无此判读规则。");
|
|
|
|
|
+ try {
|
|
|
|
|
+ Object v = f.get(null);
|
|
|
|
|
+ assertNotNull(v, "[guhe.lowSide.ruleLoad] RULE_REF_LOWER_ZERO 常量值不应为 null。");
|
|
|
|
|
+ assertTrue(RULE_REF_LOWER_ZERO.equals(String.valueOf(v)),
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] RULE_REF_LOWER_ZERO 应取值为 \"" + RULE_REF_LOWER_ZERO
|
|
|
|
|
+ + "\",实际为 \"" + v + "\"。");
|
|
|
|
|
+ } catch (IllegalAccessException e) {
|
|
|
|
|
+ assertTrue(false, "[guhe.lowSide.ruleLoad] 读取 RULE_REF_LOWER_ZERO 失败:" + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-4:规则层应提供低侧判读规则加载入口 lowSideInterpretRules()")
|
|
|
|
|
+ void lowSideRules_ruleLoadEntry_registeredRuleCodes() {
|
|
|
|
|
+ Method m = declaredMethod(AttributionLowSideRules.class, RULE_LOAD_METHOD, 0);
|
|
|
|
|
+ assertNotNull(m,
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] AttributionLowSideRules 缺少规则加载入口 " + RULE_LOAD_METHOD
|
|
|
|
|
+ + "():T3「低侧判读规则入库」要求规则集可被 L4 引擎加载(四机制 / U 型 / 参考下限 0),"
|
|
|
|
|
+ + "当前仅 T5 门禁规则,无低侧判读规则加载入口。");
|
|
|
|
|
+ try {
|
|
|
|
|
+ Object rules = m.invoke(null);
|
|
|
|
|
+ assertTrue(rules instanceof Iterable,
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] " + RULE_LOAD_METHOD + "() 应返回规则码集合,实际 "
|
|
|
|
|
+ + (rules == null ? "null" : rules.getClass().getName()));
|
|
|
|
|
+ assertTrue(containsAny((Iterable<?>) rules, RULE_FOUR_MECHANISM),
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] 低侧判读规则集应含四机制规则码 " + RULE_FOUR_MECHANISM);
|
|
|
|
|
+ assertTrue(containsAny((Iterable<?>) rules, RULE_U_SHAPE),
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] 低侧判读规则集应含 U 型双向口径规则码 " + RULE_U_SHAPE);
|
|
|
|
|
+ assertTrue(containsAny((Iterable<?>) rules, RULE_REF_LOWER_ZERO),
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] 低侧判读规则集应含参考下限 0 规则码 " + RULE_REF_LOWER_ZERO);
|
|
|
|
|
+ } catch (Exception e) {
|
|
|
|
|
+ assertTrue(false, "[guhe.lowSide.ruleLoad] 调用 " + RULE_LOAD_METHOD + "() 失败:" + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-5:AttributionLowSideRules 应暴露「判读铁律」口径常量(含关联非因果)")
|
|
|
|
|
+ void lowSideRules_exposesInterpretLawCaliber() {
|
|
|
|
|
+ Field f = declaredField(AttributionLowSideRules.class, INTERPRET_LAW_FIELD);
|
|
|
|
|
+ assertNotNull(f,
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] AttributionLowSideRules 缺少 " + INTERPRET_LAW_FIELD
|
|
|
|
|
+ + " 判读铁律常量:T3(L4-1)要求低侧判读规则全部标「关联非因果」(§2.2 判读铁律),"
|
|
|
|
|
+ + "供 L4 引擎取用,而非仅门禁可回扫出违规。");
|
|
|
|
|
+ try {
|
|
|
|
|
+ Object v = f.get(null);
|
|
|
|
|
+ assertNotNull(v, "[guhe.lowSide.ruleLoad] " + INTERPRET_LAW_FIELD + " 常量值不应为 null。");
|
|
|
|
|
+ String text = String.valueOf(v);
|
|
|
|
|
+ assertTrue(text.contains("关联非因果"),
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] " + INTERPRET_LAW_FIELD
|
|
|
|
|
+ + " 应含「关联非因果」低侧判读铁律(L4-1 全部按关联非因果解释),实际为:「"
|
|
|
|
|
+ + text + "」。");
|
|
|
|
|
+ } catch (IllegalAccessException e) {
|
|
|
|
|
+ assertTrue(false, "[guhe.lowSide.ruleLoad] 读取 " + INTERPRET_LAW_FIELD + " 失败:" + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("正常流-6:AttributionLowSideRules 应暴露「U 型双向口径」常量(越低越好不成立 / 参考下限 0)")
|
|
|
|
|
+ void lowSideRules_exposesUShapeCaliberHint() {
|
|
|
|
|
+ Field f = declaredField(AttributionLowSideRules.class, U_SHAPE_HINT_FIELD);
|
|
|
|
|
+ assertNotNull(f,
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] AttributionLowSideRules 缺少 " + U_SHAPE_HINT_FIELD
|
|
|
|
|
+ + " 口径常量:T3(L4-2)要求 U 型双向口径——越低越好不成立、参考下限 0 的菌不标"
|
|
|
|
|
+ + "「偏低超标」(ND 只是低于检出丰度)。");
|
|
|
|
|
+ try {
|
|
|
|
|
+ Object v = f.get(null);
|
|
|
|
|
+ assertNotNull(v, "[guhe.lowSide.ruleLoad] " + U_SHAPE_HINT_FIELD + " 常量值不应为 null。");
|
|
|
|
|
+ String text = String.valueOf(v);
|
|
|
|
|
+ assertTrue(text.contains("越低越好不成立"),
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] " + U_SHAPE_HINT_FIELD
|
|
|
|
|
+ + " 应含「越低越好不成立」U 型口径(§2.3 专家口径①),实际为:「" + text + "」。");
|
|
|
|
|
+ assertTrue(text.contains("参考下限"),
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] " + U_SHAPE_HINT_FIELD
|
|
|
|
|
+ + " 应含「参考下限 0」口径(§2.3 专家口径②),实际为:「" + text + "」。");
|
|
|
|
|
+ } catch (IllegalAccessException e) {
|
|
|
|
|
+ assertTrue(false, "[guhe.lowSide.ruleLoad] 读取 " + U_SHAPE_HINT_FIELD + " 失败:" + e.getMessage());
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+ // 边界流:L4 引擎应加载并应用已入库的低侧判读规则(T3 L4-1/L4-2)
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("边界流-1:偏低大肠杆菌解读含义应携带「关联非因果」低侧判读口径")
|
|
|
|
|
+ void interpret_lowSideEColi_meaningCarriesAssociationNonCausal() {
|
|
|
|
|
+ IndicatorReading reading = lowSideEColi();
|
|
|
|
|
+ MemberProfile member = profile();
|
|
|
|
|
+ IndicatorInterpretEngine engine = new IndicatorInterpretEngine();
|
|
|
|
|
+
|
|
|
|
|
+ List<MemberAttribution> attrs = engine.interpret(Collections.singletonList(reading),
|
|
|
|
|
+ Collections.singletonList(member));
|
|
|
|
|
+ assertNotNull(attrs, "[guhe.lowSide.ruleLoad] interpret 不应返回 null。");
|
|
|
|
|
+ assertFalse(attrs.isEmpty(), "[guhe.lowSide.ruleLoad] interpret 应产出至少 1 人归因。");
|
|
|
|
|
+ String meaning = attrs.get(0).getReadings().get(0).getMeaning();
|
|
|
|
|
+ assertNotNull(meaning, "[guhe.lowSide.ruleLoad] 偏低大肠杆菌节点应产出干预含义。");
|
|
|
|
|
+ assertTrue(meaning.contains("关联非因果"),
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] 偏低大肠杆菌解读含义应按四机制判读并标「关联非因果」(L4-1),"
|
|
|
|
|
+ + "实际为:「" + meaning + "」。" + "当前引擎仅产通用「先医后补」兜底,未落地 T3 低侧判读规则。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("边界流-2:偏低大肠杆菌解读含义应携带「越低越好不成立」U 型双向口径")
|
|
|
|
|
+ void interpret_lowSideEColi_meaningCarriesUShapeCaliber() {
|
|
|
|
|
+ IndicatorReading reading = lowSideEColi();
|
|
|
|
|
+ MemberProfile member = profile();
|
|
|
|
|
+ IndicatorInterpretEngine engine = new IndicatorInterpretEngine();
|
|
|
|
|
+
|
|
|
|
|
+ List<MemberAttribution> attrs = engine.interpret(Collections.singletonList(reading),
|
|
|
|
|
+ Collections.singletonList(member));
|
|
|
|
|
+ String meaning = attrs.get(0).getReadings().get(0).getMeaning();
|
|
|
|
|
+ assertNotNull(meaning, "[guhe.lowSide.ruleLoad] 偏低大肠杆菌节点应产出干预含义。");
|
|
|
|
|
+ assertTrue(meaning.contains("越低越好不成立"),
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] 偏低大肠杆菌解读含义应套用 U 型双向口径「越低越好不成立」"
|
|
|
|
|
+ + "(L4-2,条件致病菌并非越少越好),实际为:「" + meaning + "」。"
|
|
|
|
|
+ + "当前引擎仅产通用「先医后补」兜底,未落地 T3 低侧判读规则。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("边界流-3:参考下限 0 的菌不应标「偏低超标」——解读应携带参考下限口径")
|
|
|
|
|
+ void interpret_lowSideEColi_refLowerZero_notFlaggedLowOverThreshold() {
|
|
|
|
|
+ IndicatorReading reading = lowSideEColi();
|
|
|
|
|
+ MemberProfile member = profile();
|
|
|
|
|
+ IndicatorInterpretEngine engine = new IndicatorInterpretEngine();
|
|
|
|
|
+
|
|
|
|
|
+ List<MemberAttribution> attrs = engine.interpret(Collections.singletonList(reading),
|
|
|
|
|
+ Collections.singletonList(member));
|
|
|
|
|
+ String meaning = attrs.get(0).getReadings().get(0).getMeaning();
|
|
|
|
|
+ assertNotNull(meaning, "[guhe.lowSide.ruleLoad] 偏低大肠杆菌节点应产出干预含义。");
|
|
|
|
|
+ assertTrue(meaning.contains("参考下限"),
|
|
|
|
|
+ "[guhe.lowSide.ruleLoad] 参考下限 0 的菌解读应携带「参考下限 0」口径(L4-2:ND 只是低于"
|
|
|
|
|
+ + "检出丰度,不标「偏低超标」、不强化越低越好),实际为:「" + meaning + "」。"
|
|
|
|
|
+ + "当前引擎仅产通用「先医后补」兜底,未落地 T3 低侧判读规则。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+ // 权限流(不适用):后端内部 L4 低侧判读规则,无用户权限维度
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ @Test
|
|
|
|
|
+ @DisplayName("权限流:本能力为后端内部 L4 低侧判读规则,无用户权限维度,权限流不适用")
|
|
|
|
|
+ void permissionFlow_notApplicable() {
|
|
|
|
|
+ // 说明性用例:契约 AC 为「L4 归因引擎 → 低侧判读规则入库/加载」内部规则,无权限校验维度。
|
|
|
|
|
+ assertTrue(true, "权限流不适用:guhe.lowSide.ruleLoad 为后端内部 L4 低侧判读规则,无用户权限维度。");
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+ // 反射与数据构造辅助
|
|
|
|
|
+ // ------------------------------------------------------------------
|
|
|
|
|
+
|
|
|
|
|
+ private static Field declaredField(Class<?> clazz, String name) {
|
|
|
|
|
+ for (Field f : clazz.getDeclaredFields()) {
|
|
|
|
|
+ if (f.getName().equals(name)) {
|
|
|
|
|
+ f.setAccessible(true);
|
|
|
|
|
+ return f;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static Method declaredMethod(Class<?> clazz, String name, int paramCount) {
|
|
|
|
|
+ for (Method m : clazz.getDeclaredMethods()) {
|
|
|
|
|
+ if (m.getName().equals(name) && m.getParameterCount() == paramCount) {
|
|
|
|
|
+ m.setAccessible(true);
|
|
|
|
|
+ return m;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static boolean containsAny(Iterable<?> iterable, String value) {
|
|
|
|
|
+ if (iterable == null) {
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+ for (Object o : iterable) {
|
|
|
|
|
+ if (o != null && value.equals(String.valueOf(o))) {
|
|
|
|
|
+ return true;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ return false;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /** 偏低条件致病菌读数(大肠杆菌,P3,参考下限 0:ND 只是低于检出丰度) */
|
|
|
|
|
+ private static IndicatorReading lowSideEColi() {
|
|
|
|
|
+ IndicatorReading r = new IndicatorReading();
|
|
|
|
|
+ r.setMemberId(1L);
|
|
|
|
|
+ r.setMemberName("妈妈");
|
|
|
|
|
+ r.setCode("Escherichia_coli");
|
|
|
|
|
+ r.setDisplayName("大肠杆菌");
|
|
|
|
|
+ r.setValue("0.5%");
|
|
|
|
|
+ r.setPercentile("P3");
|
|
|
|
|
+ r.setStatus(IndicatorReading.STATUS_LOW);
|
|
|
|
|
+ r.setBand(IndicatorReading.BAND_LOW);
|
|
|
|
|
+ return r;
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ private static MemberProfile profile() {
|
|
|
|
|
+ MemberProfile p = new MemberProfile();
|
|
|
|
|
+ p.setMemberId(1L);
|
|
|
|
|
+ p.setName("妈妈");
|
|
|
|
|
+ p.setRole("mom");
|
|
|
|
|
+ p.setRoleLabel("妈妈");
|
|
|
|
|
+ return p;
|
|
|
|
|
+ }
|
|
|
|
|
+}
|