HL7协议对接
2026/5/16 13:14:18 网站建设 项目流程

背景

在对接医院中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数据类型

序号类型编码 类型说明
1ST 字符串
2TX 文本数据
3FT 格式化文本
4NM 数字
5SI 序列id
6SN 结构化数据
7ID HL7表的编码值
8IS 用户定义表的编码
9EI 实体标识符
10DT 日期
11TM 时间
12CE 编码要素
13CX 具有校验数位的扩展符合ID
14XCN 扩展符合ID号和ID名
15XAD 扩展地址
16XPN 扩展姓名
17XTN 扩展通讯号码

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

消息查看

业务中可以通过接口对接文档获取需要的字段,然后找到对应段中的域获取。

消息发送

需要专业的网站建设服务?

联系我们获取免费的网站建设咨询和方案报价,让我们帮助您实现业务目标

立即咨询