背景
在对接医院中PETCT设备的诊疗数据时用到。
HL7定义及重要性
HL7(Health Level Seven)是一种基于国际标准组织(ISO)的开放标准,用于在医疗信息系统之间进行数据交换。HL7协议在医疗行业中的重要性在于其提供了一种高效、准确和标准化的信息传输方式,这对于改善病人护理、简化医疗机构的运营和管理以及支持临床决策具有重大意义。
HL7 消息结构
HL7 标准包含256个事件、116个消息类型、139个段、55种数据类型、408个数据字典,涉及79种编码系统。 在 HL7 中,有四个最基本的术语概念:
触发事件(trigger evenDT):当现实世界中发生的事件产生了系统间数据流动的需求,则称其为触发事件。也可以理解为一个数据请求。
消息(message):它是系统间传输数据的最小单位,由一组有规定次序的段组成。每个消息都是用一个消息类型来表示其用途。
段(segment):它是数据字段的一个逻辑组合。每个段都用一个唯一的三字符代码所标志,这个代码称作段标志。
字段(field):它是一个字符串,是段的最小组成单位。
在 HL7 中,消息(Message)是数据在系统之间交换的基本单元,每条消息都有各自的消息类型,消息类型用于定义消息目的,包含了触发事件。一个消息由多个段(Segment)组成,每一个段都有相应的名称,用于界定其内容或者功能。 一个段又由多个字段(Field)组成。一个消息中的第一个段总是消息头段(Message head segment),它指明了发送和接收的程序名、消息类型、以及一个唯 一的消息ID号码等,接下去段的构成由消息的类型决定。 一个字段又有可能由多个组件(Component)组成。有些消息可进一步由事件码(event code)细分。
段(SegmenDT)
什么是段?
在HL7消息中,消息的每个部分都包含一类特定的信息,例如患者信息或患者就诊数据。这里提到的部分就是段,也就是每一行后都会有一个回车符 < CR >
消息中每个段的名称由该段的第一个域(fields)指定,该域始终为三个字符。HL7消息中可使用超过120个不同的HL7段。
比较常用的分别为
段名称: MSH (消息头) 段包含有关消息本身的信息。该信息包括消息的发送者和接收者、消息的类型以及发送的日期和时间。每个HL7消息都将MSH指定为其第一段。
段名称: PID (患者信息) 段包含有关患者的人口统计信息,例如姓名、患者ID和地址
段名称: NK1 (近亲信息) 细分包含患者近亲的联系信息
段名称: PV1 (患者就诊信息) 部分包含有关患者住院时的信息,例如分配的位置和推荐医生。
段名称: PV2(患者就诊附加信息)
段名称: ORC(医嘱命令所做的检查项目)
段名称: OBR 关于诊断以及观察的请求信息,用于记录医嘱信息
段名称: OBX(用于记录观察的结果)
段名称: QRD 查询定义段,用来定义查询的内容
查询时间、编码格式、优先等级、ID号、请求数据的最大值、请求方的信息、所要请求的内容、数据编码的部门信息
段名称: QRF 进一步定义查询内容
段名称: DSP 重复消息段 装载LIS返回的报告结果,需要用循环的方式把数据取出
备注
由于HL7消息用于将各种与医疗保健相关的信息传递到各种不同的系统,因此有时HL7消息需要包含自定义数据。为了适应这种情况,HL7标准使系统供应商可以创建带有自定义字段的Z段,以传输此数据。按照惯例,所有自定义段都以字母Z开头。例如,可以创建ZPD段以包含自定义的患者人口统计信息。Z段可以放置在HL7消息中的任何位置,但是通常位于消息中的最后一段。
域(fields)
分隔符 “|”
HL7消息的每个段都包含一个或多个域(也称为fields)。默认情况下,竖线(|)字符用于将一个域与另一个域分开。
域可以是原始数据类型(例如字符串或数字),也可以包含多个元素(Component)。如果某个域(fields)包含多个元素,则这些元素(Component)通常以^字符分隔。如果元素还包含子元素(Subcomponent),则这些子元素通常以&字符分隔,子元素(Subcomponent)必须 是原始数据类型(例如字符串或数字)。
"|"分隔符中可以包含其他的分隔符:
“^” 成分分隔符,表示该位置有多个属性,例如:|101761^熊婷| 该位置是患者信息,101761是患者编号 熊婷是患者名字
“~” 子成分分隔符,成分的下一分级
“&” 表示该位置是数组结构,类型相同可以循环 例如:|&张三|&李四
HL7数据类型
| 序号 | 类型编码 类型说明 |
|---|---|
| 1 | ST 字符串 |
| 2 | TX 文本数据 |
| 3 | FT 格式化文本 |
| 4 | NM 数字 |
| 5 | SI 序列id |
| 6 | SN 结构化数据 |
| 7 | ID HL7表的编码值 |
| 8 | IS 用户定义表的编码 |
| 9 | EI 实体标识符 |
| 10 | DT 日期 |
| 11 | TM 时间 |
| 12 | CE 编码要素 |
| 13 | CX 具有校验数位的扩展符合ID |
| 14 | XCN 扩展符合ID号和ID名 |
| 15 | XAD 扩展地址 |
| 16 | XPN 扩展姓名 |
| 17 | XTN 扩展通讯号码 |
java对接
引入依赖
<dependency> <groupId>ca.uhn.hapi</groupId> <artifactId>hapi-base</artifactId> <version>2.3</version> </dependency> <dependency> <groupId>ca.uhn.hapi</groupId> <artifactId>hapi-structures-v24</artifactId> <version>2.3</version> </dependency>@Slf4j @Component public class Hl7AppRunner implements ApplicationRunner { @Autowired SystemConfig systemConfig; /** * 设置核心线程数 */ private static final int CORE_POOL_SIZE = 50; /** * 设置最大线程数,只有在缓冲队列满了之后才会申请超过核心线程数的线程 */ private static final int MAX_POOL_SIZE = 100; /** * 设置缓冲队列大小 */ private static final int QUEUE_CAPACITY = 100; /** * 设置线程的最大空闲时间,超过了核心线程数之外的线程,在空闲时间到达之后会被销毁 */ private static final int KEEP_ALIVE_SECONDS = 60; private static HapiContext context = new DefaultHapiContext(); public void start() { try { ThreadPoolExecutor executor = new ThreadPoolExecutor( CORE_POOL_SIZE, MAX_POOL_SIZE, KEEP_ALIVE_SECONDS, TimeUnit.SECONDS, new ArrayBlockingQueue<Runnable>(QUEUE_CAPACITY)); executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy()); MinLowerLayerProtocol mllp = new MinLowerLayerProtocol(); mllp.setCharset("UTF-8"); context.setLowerLayerProtocol(mllp); context.setExecutorService(executor); //生产环境中可能出现段中域的类型不符合标准,例如病人死亡日期生产环境中出现0的情况导致数据无法接收,实际只能为null或标准的日期格式,而这些数据不影响业务,所以跳过校验。 context.setValidationContext(new NoValidation()); ServerConfiguration s = new ServerConfiguration(); Integer port = 9200; boolean useSecureConnection = false; // are you using TLS/SSL? HL7Service ourHl7Server = context.newServer(port, useSecureConnection); ourHl7Server.registerApplication(new MyReceivingApplication()); // ourHl7Server.setExceptionHandler(new MyHl7ExceptionHandler()); ourHl7Server.startAndWait(); log.info("socket 服务 启动成功 SUCCESS , port {}", port); } catch (Exception e) { e.printStackTrace(); } } @Override public void run(ApplicationArguments args) throws Exception { start(); } }@Slf4j @Component public class MyReceivingApplication implements ReceivingApplication { @Override public Message processMessage(Message message, Map map) throws ReceivingApplicationException, HL7Exception { Message ack = null; try { if (!message.isEmpty()) { log.info("ip,{},port,{}", map.get("SENDING_IP"), map.get("SENDING_PORT")); log.info("接收成功,{}", message); handleMessage(message); } } catch (Exception e) { log.error("处理HL7消息异常", e); } try { ack = message.generateACK(); log.info("返回处理结果ACK:{}", ack.toString()); } catch (Exception e) { log.error(e.getMessage()); } return ack; } @Override public boolean canProcess(Message message) { return true; } public String handleMessage(Message message) throws HL7Exception { String id = ""; if (message instanceof ORU_R01) { //检查报告类型的消息 ORU_R01 oruR01 = (ORU_R01) message; MSH msh = oruR01.getMSH(); id = msh.getMsh10_MessageControlID().getValue(); ORU_R01(oruR01); } else if (message instanceof SIU_S12) { SIU_S12 siuS12 = (SIU_S12) message; MSH msh = siuS12.getMSH(); id = msh.getMsh10_MessageControlID().getValue(); siuS12(siuS12); } else if (message instanceof ORM_O01) { ORM_O01 ormO01 = (ORM_O01) message; ORM_O01_ORDER order = ormO01.getORDER(); ORC orc = order.getORC(); String orderStatus = orc.getOrc5_OrderStatus().getValue(); messageDO.setType(orderStatus); if ("SC".equals(orderStatus)) { System.out.println("登记"); //处理登记类型的消息 ORM_O01_sc(ormO01); } else if ("CM".equalsIgnoreCase(orderStatus)) { //处理完成检查类型的消息 System.out.println("完成检查"); ORM_O01_cm(ormO01); } else if ("IP".equalsIgnoreCase(orderStatus)) { //处理检查收费类型的消息 System.out.println("检查收费"); ORM_O01_ip(ormO01); } } // 保存消息记录 saveMessage(messageDO) return id; } }可视化工具的使用
7Edit 2.x
消息查看
业务中可以通过接口对接文档获取需要的字段,然后找到对应段中的域获取。