yxh
6 天以前 68ff33beff11310e830b0615b5a81f83fad0f683
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
package cn.lihu.jh.module.ecg.service.appointment;
 
import org.junit.Test;
 
import java.lang.reflect.Method;
import java.util.HashMap;
import java.util.Map;
 
import static org.junit.Assert.*;
 
/**
 * 锁定 S050401/S050501(检查预约)报文中检查类型的解析行为。
 *
 * <h3>背景(2026-10 排查结论)</h3>
 * 体检/门诊的 S050401 单若**先于** S0201ECG 到达,会走
 * {@code insertAppointmentFromCheckAppointment} 容错新增;
 * 而该路径原先**完全不解析检查类型**,导致 {@code appointment.book_check_type} 永远为空。
 * <p>实测:{@code id=213466 / apply_no=8826092420371106}(2026-10-08 07:15 容错新增),
 * 报文 {@code ZDY} 里明明有 {@code jcxmcode=201652, jcxmname=平板运动试验},
 * 应映射为本地 {@code book_check_type=400}(平板运动心电)。
 * 全库同类记录 279 条(2026-09-29 ~ 10-09)。
 *
 * <p>本次修复新增 {@code resolveBookCheckTypeFromZdy(ZDY, applyNo)},并接入两条路径:
 * <ol>
 *   <li>容错新增({@code insertAppointmentFromCheckAppointment});</li>
 *   <li>更新已有记录({@code handleCheckAppointmentUpdate})。</li>
 * </ol>
 * 两条路径均遵循**仅在为空时补**(不覆盖已有值)。
 */
public class HisCheckAppointmentCheckTypeTest {
 
    private Integer invokeResolve(Map<String, Object> zdy) throws Exception {
        AppointmentServiceImpl svc = new AppointmentServiceImpl();
        Method m = AppointmentServiceImpl.class.getDeclaredMethod(
                "resolveBookCheckTypeFromZdy", Map.class, String.class);
        m.setAccessible(true);
        return (Integer) m.invoke(svc, zdy, "TEST-APPLY-NO");
    }
 
    private Map<String, Object> zdy(String name, String code) {
        Map<String, Object> m = new HashMap<>();
        if (name != null) {
            m.put("jcxmname", name);
        }
        if (code != null) {
            m.put("jcxmcode", code);
        }
        return m;
    }
 
    /** 真实故障样本:平板运动试验 → 400(走名称映射) */
    @Test
    public void testRealCase_pingBanYunDong_shouldMapTo400() throws Exception {
        Integer v = invokeResolve(zdy("平板运动试验", "201652"));
        assertNotNull("真实故障单的检查类型必须能解析出来", v);
        assertEquals("平板运动试验 应映射为 400(平板运动心电)", Integer.valueOf(400), v);
        System.out.println("[真实样本] jcxmname=平板运动试验 jcxmcode=201652 -> " + v);
    }
 
    /** HIS 改名场景:仅给代码 201652 也要能映射(名称映射不到时按代码兜底) */
    @Test
    public void testNameUnmappable_shouldFallbackToCode() throws Exception {
        Integer v = invokeResolve(zdy("某个HIS新叫法未收录", "201652"));
        assertEquals("名称映射不到时应按代码兜底为 400", Integer.valueOf(400), v);
        System.out.println("[代码兜底] jcxmname=未收录 jcxmcode=201652 -> " + v);
    }
 
    /** 代码带 Y 前缀(S0201ECG 形态)也要兼容 */
    @Test
    public void testCodeWithYPrefix_shouldBeCompatible() throws Exception {
        assertEquals(Integer.valueOf(400), invokeResolve(zdy(null, "Y201652")));
        System.out.println("[前缀兼容] jcxmcode=Y201652 -> 400");
    }
 
    /** 多值:取第一个能映射成功的(实测 S0405 存在 jcxmcode=1080,3016,3014 形态) */
    @Test
    public void testMultiValue_shouldPickFirstMappable() throws Exception {
        // 第一个是无关代码,第二个才是心电项目
        Integer v = invokeResolve(zdy(null, "999999,201652"));
        assertEquals("多值应取第一个能映射的", Integer.valueOf(400), v);
        System.out.println("[多值] jcxmcode=999999,201652 -> " + v);
    }
 
    /** 名称多值(中文逗号)也要支持 */
    @Test
    public void testMultiValueName_shouldPickFirstMappable() throws Exception {
        Integer v = invokeResolve(zdy("胸部CT平扫,平板运动试验", null));
        assertEquals("名称多值应取第一个能映射的", Integer.valueOf(400), v);
        System.out.println("[多值名称] jcxmname=胸部CT平扫,平板运动试验 -> " + v);
    }
 
    /** 其它心电项目也应正确映射(防只看一个样本) */
    @Test
    public void testOtherCheckTypes_shouldMapCorrectly() throws Exception {
        assertEquals(Integer.valueOf(100), invokeResolve(zdy("常规心电图", "691133607")));
        assertEquals(Integer.valueOf(200), invokeResolve(zdy("24小时动态心电图", "201605")));
        assertEquals(Integer.valueOf(300), invokeResolve(zdy("动态血压", "200327")));
        assertEquals(Integer.valueOf(1100), invokeResolve(zdy("常规心电图-心电向量图", "559542128")));
        System.out.println("[多项目] 100/200/300/1100 均映射正确");
    }
 
    /** 无法映射时返回 null(调用方保持原值,不写坏数据)且不抛异常 */
    @Test
    public void testUnmappable_shouldReturnNullQuietly() throws Exception {
        assertNull("非心电项目应返回 null", invokeResolve(zdy("胸部CT平扫", "1080")));
        assertNull("空 ZDY 应返回 null", invokeResolve(new HashMap<>()));
        assertNull("null ZDY 应返回 null 且不抛异常", invokeResolve(null));
        System.out.println("[健壮性] 无法映射/空/null 均返回 null 且不抛异常");
    }
}