SpringBoot-springboot项目接收解析HL7 V2.3.1协议报文

整理不易,如果您觉得对您有所帮助 请点赞支持下~谢谢

背景

本协议基于 HL7 v2.3.1 来定义
接收消息类型为:ORU^R01(样本结果)

每收到一条样本结果,需要回应一条样本应答消息(ACK)

7Edit工具模拟HL7协议请求

工具安装包地址:
通过百度网盘分享的文件:7Edit
链接:https://pan.baidu.com/s/12P-8RdsMyYBAaR7R6r8jGg
提取码:sky1

File - new - 选择自己的协议类型(此处我用的是ORU^R01)和版本(此处我用的是 2.3.1)

image

报文例子:

MSH|^~\&|BC-5300|Mindray|||20081120171602||ORU^R01|1|Q|2.3.1||||||UNICODE
PID|1|7393670^^^^MR|患者ID唯一标识^刘佳|19950804000000|患者姓名|||F
PV1||I|LAB^LABORATORY^|||SUR^SURGEON^DR^^^^^|||||||
OBR|1|234567890|151515^LAB TEST PANEL^L|1|202301011100||||||||N|||F|||||202301011200
OBX|1|ST|151515^GLUCOSE^L|1|102|mg/dL|70_105|N|||F
OBX|2|NM|151516^CHOLESTEROL^L|1|185|mg/dL|<200|N|||F
OBX|3|ST|151517^HEMOGLOBIN^L|1|14.5|g/dL|12.0_16.0|N|||F
OBX|4|NM|151518^WHITE BLOOD CELL COUNT^L|1|7.2|K/uL|4.0_11.0|N|||F

代码

pom文件引入Hapi依赖

<!--        HL7协议-全自动血液细胞分析仪-->
		<dependency>
			<groupId>ca.uhn.hapi</groupId>
			<artifactId>hapi-base</artifactId>
			<version>2.3</version> <!-- 请根据实际情况选择最新版本 -->
		</dependency>
		<dependency>
			<groupId>ca.uhn.hapi</groupId>
			<artifactId>hapi-structures-v231</artifactId>
			<version>2.3</version> <!-- 请根据实际情况选择最新版本 -->
		</dependency>

创建Component组件 配置ourHl7Server服务信息

package io.hk.modules.ws.config;

import ca.uhn.hl7v2.DefaultHapiContext;
import ca.uhn.hl7v2.HapiContext;
import ca.uhn.hl7v2.app.HL7Service;
import ca.uhn.hl7v2.llp.MinLowerLayerProtocol;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;

import javax.annotation.PostConstruct;
import java.util.concurrent.ArrayBlockingQueue;
import java.util.concurrent.ThreadPoolExecutor;
import java.util.concurrent.TimeUnit;

/**
 * <p>@desc      OURHl7Server服务
 * 用于全自动血液细胞分析仪数据采集
 * 监听HL7协议发送请求
 * <p>@author
 * <p>@date
 **/
@Component
public class BasicListenerWithoutMessageHandling {

	@Autowired
	private MyReceivingApplication myReceivingApplication;

	private static int PORT_NUMBER = 1919;
	private static HapiContext context = new DefaultHapiContext();

	@PostConstruct
	public void newHL7Service() throws Exception {
		try {
			ThreadPoolExecutor executor = new ThreadPoolExecutor(
					10, 3100,
					30, TimeUnit.SECONDS,
					new ArrayBlockingQueue<Runnable>(100));
			executor.setRejectedExecutionHandler(new ThreadPoolExecutor.CallerRunsPolicy());
			MinLowerLayerProtocol mllp = new MinLowerLayerProtocol();
			mllp.setCharset("UTF-8");
			context.setLowerLayerProtocol(mllp);
			context.setExecutorService(executor);
			// 是否使用TLS/SSL
			boolean useSecureConnection = false;
			HL7Service ourHl7Server = context.newServer(PORT_NUMBER, useSecureConnection);
			ourHl7Server.registerApplication(myReceivingApplication);
			ourHl7Server.startAndWait();
		} catch (Exception e) {
			e.printStackTrace();
		}
	}
}

Hapi注册单元(获取报文和解析保存)

其中重写processMessage 方法用于HL协议报文解析

package io.hk.modules.ws.config;

import ca.uhn.hl7v2.HL7Exception;
import ca.uhn.hl7v2.model.*;
import ca.uhn.hl7v2.parser.PipeParser;
import ca.uhn.hl7v2.protocol.ReceivingApplication;
import ca.uhn.hl7v2.protocol.ReceivingApplicationException;
import ca.uhn.hl7v2.util.Terser;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import io.hk.common.DateUtil;
import io.hk.common.StringUtil;
import io.hk.common.utils.LogUtil;

import io.hk.common.utils.R;
import io.hk.common.utils.UuidUtil;
import io.hk.modules.ws.entity.IfInspResultDetailEntity;
import io.hk.modules.ws.entity.IfInspResultEntity;
import io.hk.modules.ws.entity.IfHealthInspDataEntity;
import io.hk.modules.ws.service.IfHealthInspDataService;
import io.hk.modules.ws.service.IfInspResultDetailService;
import io.hk.modules.ws.service.IfInspResultService;
import org.apache.shiro.mgt.SecurityManager;
import org.apache.shiro.util.ThreadContext;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.stereotype.Component;
import org.springframework.transaction.annotation.Transactional;

import java.io.IOException;
import java.util.*;

/**
 * <p>@desc      Hapi注册单元
 * <p>@author
 * <p>@date
 **/
@Component
public class MyReceivingApplication implements ReceivingApplication {

	@Autowired
	IfInspResultDetailService ifInspResultDetailService;
	@Autowired
	IfInspResultService ifInspResultService;
	@Autowired
	private SecurityManager securityManager;
	@Autowired
	private IfHealthInspDataService ifHealthInspDataService;

	/**
	 * <p>@desc      处理全自动血液细胞分析仪发来的报文数据
	 * <p>@author    gaoyt
	 * <p>@date      2024/11/22 15:27
	 *
	 * @param theMessage
	 * @param theMetadata
	 **/
	@Override
	@Transactional
	public Message processMessage(Message theMessage, Map theMetadata) throws ReceivingApplicationException, HL7Exception {
		String username = "processMessage";
		LogUtil.writelog("====================全自动血液细胞分析仪-获取HL7报文-开始=============================", username);
		// ACK应答
		Message response = null;
		ThreadContext.bind(securityManager);
		try {
			// 解析保存HL7报文数据
			R result = analysisModule(theMessage, theMetadata);
			if ("0".equals(result.get("code").toString())) {
				// AA  接收
				response = theMessage.generateACK();
			} else if ("101".equals(result.get("code").toString())) {
				// AE 丢失必填字段
				response = theMessage.generateACK("AE", new HL7Exception("101"));
			} else {
				// AE 错误
				response = theMessage.generateACK("AE", new HL7Exception("207"));
			}
		} catch (Exception e) {
			// AE 错误
			try {
				response = theMessage.generateACK("AE", new HL7Exception("207"));
			} catch (Exception ex) {
				LogUtil.writeExceptionLog(ex, username);
			}
			LogUtil.writeExceptionLog(e, username);
		} finally {
			ThreadContext.unbindSecurityManager();
			LogUtil.writelog("====================全自动血液细胞分析仪-获取HL7报文-结束=============================", username);
		}
		return response;
	}


	public R analysisModule(Message hapiMsg, Map hapiMetadata) {
		R result = R.ok();
		String username = "analysisModule";
		LogUtil.writelog("====================全自动血液细胞分析仪-解析HL7报文-开始=============================", username);
		try {
			// 获取体检结果
			R ifInspR = getifInspResultEntity(hapiMsg);
			if ("0".equals(ifInspR.get("code").toString())) {
				IfInspResultEntity ifInspResult = (IfInspResultEntity) ifInspR.get("ifInspResult");
				// 体检结果ID
				String ifInspResultId = UuidUtil.get32UUID();
				ifInspResult.setSysid(ifInspResultId);
				// 获取体检结果明细
				R ifInspResultDetailR = getifInspResultDetail(hapiMetadata, ifInspResult);
				List<IfInspResultDetailEntity> ifInspResultDetailList = (List<IfInspResultDetailEntity>) ifInspResultDetailR.get("ifInspResultDetailList");
				if ("0".equals(ifInspResultDetailR.get("code").toString())) {
					// 保存到数据库
					R saveResult = saveHapi(ifInspResult, ifInspResultDetailList);
					if (!"0".equals(saveResult.get("code").toString())) {
						result.put("code", 1);
						result.put("msg", "保存体检结果失败");
						return result;
					}
				} else if ("101".equals(ifInspResultDetailR.get("code").toString())) {
					result.put("code", 101);
					result.put("msg", "体检结果明细丢失必须的字段");
					return result;
				} else {
					result.put("code", 1);
					result.put("msg", "获取体检结果明细失败");
					return result;
				}
			} else if ("101".equals(ifInspR.get("code").toString())) {
				// 必填项校验不通过
				result.put("code", 101);
				result.put("msg", "体检结果丢失必须的字段");
				return result;
			} else {
				result.put("code", 1);
				result.put("msg", "获取体检结果失败");
				return result;
			}
		} catch (Exception e) {
			result.put("code", 1);
			result.put("msg", "异常:" + e.getMessage());
			LogUtil.writeExceptionLog(e, username);
		} finally {
			LogUtil.writelog("====================全自动血液细胞分析仪-解析HL7报文-结束=============================", username);
		}
		return result;
	}

	private R saveHapi(IfInspResultEntity ifInspResult, List<IfInspResultDetailEntity> ifInspResultDetailList) {
		R result = R.ok();
		String username = "saveHapi";
		LogUtil.writelog("====================全自动血液细胞分析仪-保存HL7报文-开始=============================", username);
		try {
			ifInspResultService.save(ifInspResult);
			ifInspResultDetailService.saveBatch(ifInspResultDetailList);
		} catch (Exception e) {
			result.put("code", 1);
			result.put("msg", "异常:" + e.getMessage());
			LogUtil.writeExceptionLog(e, username);
		} finally {
			LogUtil.writelog("====================全自动血液细胞分析仪-保存HL7报文-结束=============================", username);
		}
		return result;
	}

	/**
	 * <p>@desc      获取体检结果
	 * <p>@author    gaoyt
	 * <p>@date      2024/11/25 10:37
	 *
	 * @param hapiMsg
	 **/
	private R getifInspResultEntity(Message hapiMsg) {
		R result = R.ok();
		String username = "getifInspResult";
		LogUtil.writelog("====================全自动血液细胞分析仪-获取体检结果-开始=============================", username);
		IfInspResultEntity ifInspResult = new IfInspResultEntity();
		try {
			// 患者段信息
			Terser terser = new Terser(hapiMsg);
			// 患者姓名
			String patientName = terser.get("/.PID-5");
			// 患者性别 'M':表示男性(Male) 'F':表示女性(Female)
			String patientSex = terser.get("/.PID-8");
			// 存入输入库 0表示男性 1表示女性
			String sex = "M".equals(patientSex) ? "0" : "1";
			// 患者生日
			String patientBirth = terser.get("/.PID-7");
			// 根据出生日期获取到年龄
			int patientAge = DateUtil.getAgeByBirth(patientBirth);
			// 观察日期 (察请求的日期和时间)
			String patientCheckDate = terser.get("/.OBR-7");
			Date checkDate = DateUtil.getDateTime(patientCheckDate);
			// fileOrderNumber 预约编号
			String inspNo = terser.get("/.OBR-3");
			// 根据预约编号查询省份ID
			IfHealthInspDataEntity ifHealthInspData = ifHealthInspDataService.getBaseMapper().selectOne(new QueryWrapper<IfHealthInspDataEntity>()
					.eq("is_del", 0)
					.eq("is_active", 0)
					.eq("insp_no", inspNo));
			// 校验必填项
			boolean flag = StringUtil.validateStrings(inspNo, patientCheckDate);
			if (!flag) {
				// 存在必填项为空
				result.put("code", 101);
				result.put("msg", "丢失必须的字段");
				return result;
			}
			// 体检姓名
			ifInspResult.setInspName(patientName);
			// 性别(0-男,1-女)
			ifInspResult.setSex(sex);
			// 年龄
			ifInspResult.setAge(patientAge);
			// 血常规:BRT
			ifInspResult.setHisItemsGroupCode("BRT");
			// 是否存在图像文件,N-否、Y-是
			ifInspResult.setIsFile("N");
			// 检查日期
			ifInspResult.setCheckDate(checkDate);
			// 预约编号
			ifInspResult.setInspNo(inspNo);
			if (ifHealthInspData != null) {
				// 体检机构ID
				String corpId = ifHealthInspData.getCorpId();
				ifInspResult.setAppId(corpId);
			}
		} catch (Exception e) {
			result.put("code", 1);
			result.put("msg", "异常:" + e.getMessage());
			LogUtil.writeExceptionLog(e, username);
		} finally {
			LogUtil.writelog("====================全自动血液细胞分析仪-解析HL7患者标识段-结束=============================", username);
		}
		result.put("ifInspResult", ifInspResult);
		return result;
	}

	/**
	 * <p>@desc      获取体检结果明细
	 * <p>@author    gaoyt
	 * <p>@date      2024/11/25 10:35
	 *
	 * @param hapiMetadata
	 **/
	private R getifInspResultDetail(Map hapiMetadata, IfInspResultEntity ifInspResult) {
		R result = R.ok();
		String username = "getifInspResultDetail";
		LogUtil.writelog("====================全自动血液细胞分析仪-获取体检结果明细-开始=============================", username);
		List<IfInspResultDetailEntity> ifInspResultDetailList = new ArrayList<>();
		try {
			// 获取到报文字符串
			String[] message = hapiMetadata.get("raw-message").toString().split("\r");
			// 拼接报文中所有OB前的报文
			String messageBeforeObx = "";
			for (String s : message) {
				if (!s.startsWith("OBX")) {
					messageBeforeObx = messageBeforeObx + s + "\r";
				}
			}
			for (String s : message) {
				if (s.startsWith("OBX")) {
					PipeParser parser = new PipeParser();
					String messageTmp = messageBeforeObx + s;
					Message parse = parser.parse(messageTmp);
					Terser terserParse = new Terser(parse);
					//  检查项目 Observation Identifie
					String obxIdentifier = terserParse.get("/.OBX-3");
					// Observation Value:观察结果的具体数值或描述 反映了医疗观察的结果
					String obxValue = terserParse.get("/.OBX-5");
					// 观察结果值的单位 例如,血糖值可能以毫摩尔每升(mmol/L)为单位,这有助于接收方正确理解观察结果。
					String obxUnits = terserParse.get("/.OBX-6");
					// References Range 观察结果的参考范围
					String obxRange = terserParse.get("/.OBX-7");
					// Abnormal Flags: 用于标识观察结果是否异常。 常见的标识包括正常、高、低、异常等。
					// A:异常(Abnormal) N:正常(Normal) H:高于正常范围(Higher than normal) L:低于正常范围(Lower than normal) C:危急值(Critical)
					String obxFlag = terserParse.get("/.OBX-8");
					// 检查日期
					Date obxCheckDate = ifInspResult.getCheckDate();
					// 校验必填项
					boolean flag = StringUtil.validateStrings(obxIdentifier, obxValue, obxUnits, obxRange, obxFlag);
					if (!flag) {
						// 存在必填项为空
						result.put("code", 101);
						result.put("msg", "丢失必须的字段");
						return result;
					}
					IfInspResultDetailEntity ifInspResultDetail = new IfInspResultDetailEntity();
					// 检查项目
					ifInspResultDetail.setProjectCode(obxIdentifier);
					// 检测值
					ifInspResultDetail.setResultValue(obxValue);
					// 单位
					ifInspResultDetail.setMeasure(obxUnits);
					// 范围
					ifInspResultDetail.setRanges(obxRange);
					// 结果表示
					ifInspResultDetail.setResultFlag(obxFlag);
					// 检查日期
					ifInspResultDetail.setCheckDate(obxCheckDate);
					// 体检结果ID
					ifInspResultDetail.setHisInspResultId(ifInspResult.getSysid());

					ifInspResultDetailList.add(ifInspResultDetail);
				}
			}
		} catch (Exception e) {
			result.put("code", 1);
			result.put("msg", "异常:" + e.getMessage());
			LogUtil.writeExceptionLog(e, username);
		} finally {
			LogUtil.writelog("====================全自动血液细胞分析仪-解析HL7观察结果段-结束=============================", username);
		}
		result.put("ifInspResultDetailList", ifInspResultDetailList);
		return result;
	}

	@Override
	public boolean canProcess(Message message) {
		return true;
	}

}
posted @ 2024-11-29 20:29  skystrivegao  阅读(559)  评论(0)    收藏  举报