|
@@ -1,20 +1,40 @@
|
|
|
package com.coffee.bus.his;
|
|
package com.coffee.bus.his;
|
|
|
|
|
|
|
|
|
|
+import cn.hutool.core.collection.CollectionUtil;
|
|
|
|
|
+import cn.hutool.core.util.StrUtil;
|
|
|
import cn.hutool.extra.spring.SpringUtil;
|
|
import cn.hutool.extra.spring.SpringUtil;
|
|
|
import cn.hutool.json.JSON;
|
|
import cn.hutool.json.JSON;
|
|
|
import cn.hutool.json.JSONArray;
|
|
import cn.hutool.json.JSONArray;
|
|
|
import cn.hutool.json.JSONUtil;
|
|
import cn.hutool.json.JSONUtil;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
|
|
|
|
|
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
|
|
|
import com.coffee.bus.entity.BusClinicEntity;
|
|
import com.coffee.bus.entity.BusClinicEntity;
|
|
|
import com.coffee.bus.entity.BusHospitalEntity;
|
|
import com.coffee.bus.entity.BusHospitalEntity;
|
|
|
-import com.coffee.bus.his.strategy.HisStrategyManagerRegister;
|
|
|
|
|
import com.coffee.bus.listener.event.bean.HisEvent;
|
|
import com.coffee.bus.listener.event.bean.HisEvent;
|
|
|
|
|
+import com.coffee.bus.script.DefaultParse;
|
|
|
import com.coffee.bus.script.ExecuteResult;
|
|
import com.coffee.bus.script.ExecuteResult;
|
|
|
import com.coffee.bus.script.ScriptManager;
|
|
import com.coffee.bus.script.ScriptManager;
|
|
|
import com.coffee.bus.script.ScriptParse;
|
|
import com.coffee.bus.script.ScriptParse;
|
|
|
|
|
+import com.coffee.bus.service.LocalBusClinicService;
|
|
|
|
|
+import com.coffee.bus.service.LocalBusHospitalService;
|
|
|
|
|
+import com.coffee.common.cache.ClusterConfigStorage;
|
|
|
|
|
+import com.coffee.common.cache.value.Value;
|
|
|
|
|
+import com.coffee.common.exception.CustomException;
|
|
|
|
|
+import com.coffee.common.result.R;
|
|
|
import lombok.extern.slf4j.Slf4j;
|
|
import lombok.extern.slf4j.Slf4j;
|
|
|
|
|
+import org.springframework.data.redis.core.RedisTemplate;
|
|
|
|
|
+import org.springframework.util.Assert;
|
|
|
|
|
+import org.springframework.web.context.request.async.DeferredResult;
|
|
|
import org.tio.core.ChannelContext;
|
|
import org.tio.core.ChannelContext;
|
|
|
|
|
+import org.tio.core.Tio;
|
|
|
|
|
+import org.tio.core.utils.TioUtils;
|
|
|
|
|
+import org.tio.websocket.common.WsResponse;
|
|
|
|
|
|
|
|
-import java.util.List;
|
|
|
|
|
|
|
+import javax.validation.constraints.NotNull;
|
|
|
|
|
+import java.util.*;
|
|
|
|
|
+import java.util.concurrent.ConcurrentHashMap;
|
|
|
|
|
+import java.util.concurrent.Executors;
|
|
|
|
|
+import java.util.concurrent.ScheduledExecutorService;
|
|
|
import java.util.concurrent.TimeUnit;
|
|
import java.util.concurrent.TimeUnit;
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -28,31 +48,142 @@ import java.util.concurrent.TimeUnit;
|
|
|
public class HisScriptSession {
|
|
public class HisScriptSession {
|
|
|
private String hospitalId;
|
|
private String hospitalId;
|
|
|
private ChannelContext channelContext;
|
|
private ChannelContext channelContext;
|
|
|
- private BusHospitalEntity hospital;
|
|
|
|
|
private ScriptManager scriptManager;
|
|
private ScriptManager scriptManager;
|
|
|
- private HisStrategyManagerRegister strategyManagerRegister;
|
|
|
|
|
- //同步拉取病人信息
|
|
|
|
|
- public BusClinicEntity syncGetPatientInfo(String patientCode, long timeout, TimeUnit unit){
|
|
|
|
|
- return null;
|
|
|
|
|
- };
|
|
|
|
|
|
|
+ private LocalBusClinicService clinicService;
|
|
|
|
|
+ private ClusterConfigStorage clusterConfigStorage;
|
|
|
|
|
+ private LocalBusHospitalService hospitalService;
|
|
|
|
|
+ private Map<String,HisRequest> hisRequestMap=new ConcurrentHashMap<>();
|
|
|
|
|
+ public HisScriptSession(String hospitalId, ChannelContext channelContext, ScriptManager scriptManager,
|
|
|
|
|
+ RedisTemplate redisTemplate, LocalBusClinicService clinicService, LocalBusHospitalService hospitalService) {
|
|
|
|
|
+ this.hospitalId = hospitalId;
|
|
|
|
|
+ this.channelContext = channelContext;
|
|
|
|
|
+ this.scriptManager = scriptManager;
|
|
|
|
|
+ this.clinicService = clinicService;
|
|
|
|
|
+ this.hospitalService=hospitalService;
|
|
|
|
|
+ init(redisTemplate,hospitalId,hospitalService);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public HisScriptSession(String hospitalId, ScriptManager scriptManager, RedisTemplate redisTemplate, LocalBusClinicService clinicService,LocalBusHospitalService hospitalService) {
|
|
|
|
|
+ this.hospitalId = hospitalId;
|
|
|
|
|
+ this.scriptManager = scriptManager;
|
|
|
|
|
+ this.clinicService = clinicService;
|
|
|
|
|
+ this.hospitalService=hospitalService;
|
|
|
|
|
+ init(redisTemplate,hospitalId,hospitalService);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
- //异步拉取病人信息
|
|
|
|
|
- public void asyncGetPatientInfo(String patientCode){
|
|
|
|
|
|
|
+ private void init(RedisTemplate redisTemplate,String hospitalId,LocalBusHospitalService hospitalService){
|
|
|
|
|
+ BusHospitalEntity hospital = hospitalService.getById(hospitalId);
|
|
|
|
|
+ if(hospital==null){
|
|
|
|
|
+ throw new RuntimeException(String.format("医院{%s}不存在,获取医院脚本会话失败",hospitalId));
|
|
|
|
|
+ }
|
|
|
|
|
+ clusterConfigStorage=new ClusterConfigStorage(redisTemplate,hospitalId);
|
|
|
|
|
+ clusterConfigStorage.setConfig("info",hospital);
|
|
|
|
|
+ }
|
|
|
|
|
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 描述: 更新医院信息
|
|
|
|
|
+ * @author lifang
|
|
|
|
|
+ * @date 2022/5/15 1:03
|
|
|
|
|
+ * @param hospital
|
|
|
|
|
+ * @return void
|
|
|
|
|
+ */
|
|
|
|
|
+ public void refresh(BusHospitalEntity hospital){
|
|
|
|
|
+ clusterConfigStorage.setConfig("info",hospital);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 描述: 同步拉取病人信息,超时后该次请求不再处理
|
|
|
|
|
+ * @author lifang
|
|
|
|
|
+ * @date 2022/5/14 16:24
|
|
|
|
|
+ * @param patientCode
|
|
|
|
|
+ * @param timeout
|
|
|
|
|
+ * @param unit
|
|
|
|
|
+ * @return BusClinicEntity
|
|
|
|
|
+ */
|
|
|
|
|
+ public DeferredResult<R<BusClinicEntity>> syncGetPatientInfo(String patientCode, long timeout, TimeUnit unit){
|
|
|
|
|
+// if (!isOnline()) {
|
|
|
|
|
+// throw new CustomException("医院不在线,拉取信息失败");
|
|
|
|
|
+// }
|
|
|
|
|
+ String messageId = IdWorker.getIdStr();
|
|
|
|
|
+ DeferredResult<R<BusClinicEntity>> result = new DeferredResult<>(unit.toMillis(timeout));
|
|
|
|
|
+ HisRequest request = HisRequest.builder()
|
|
|
|
|
+ .messageId(messageId)
|
|
|
|
|
+ .patientCode(patientCode)
|
|
|
|
|
+ .sync(true)
|
|
|
|
|
+ .timeout(timeout)
|
|
|
|
|
+ .timeUnit(unit)
|
|
|
|
|
+ .result(result)
|
|
|
|
|
+ .timestamp(new Date())
|
|
|
|
|
+ .build();
|
|
|
|
|
+ sendRequest(channelContext,request);
|
|
|
|
|
+ result.onTimeout(()->{
|
|
|
|
|
+ BusClinicEntity clinic = clinicService.recentClinicByPatientCode(hospitalId, patientCode);
|
|
|
|
|
+ if(clinic==null){
|
|
|
|
|
+ result.setResult(R.fail("拉取信息超时,请稍后再试"));
|
|
|
|
|
+ }else {
|
|
|
|
|
+ result.setResult(R.success(clinic));
|
|
|
|
|
+ }
|
|
|
|
|
+ //超时后不在响应
|
|
|
|
|
+ hisRequestMap.remove(messageId);
|
|
|
|
|
+ });
|
|
|
|
|
+ return result;
|
|
|
|
|
+ };
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 描述: 异步拉取病人信息
|
|
|
|
|
+ * 当异步拉取超时时,若需要返回临床信息,则从数据库中拉取最新数据返回
|
|
|
|
|
+ * @author lifang
|
|
|
|
|
+ * @date 2022/5/14 16:26
|
|
|
|
|
+ * @param patientCode 病号
|
|
|
|
|
+ * @param timeout 超时时间
|
|
|
|
|
+ * @param unit 时间单位
|
|
|
|
|
+ * @param needResult 是否需要返回结果
|
|
|
|
|
+ * @return BusClinicEntity
|
|
|
|
|
+ */
|
|
|
|
|
+ public DeferredResult<R<BusClinicEntity>> asyncGetPatientInfo(String patientCode,long timeout, TimeUnit unit,boolean needResult){
|
|
|
|
|
+ String messageId = IdWorker.getIdStr();
|
|
|
|
|
+ DeferredResult<R<BusClinicEntity>> result = new DeferredResult<>(unit.toMillis(timeout));
|
|
|
|
|
+ HisRequest request = HisRequest.builder()
|
|
|
|
|
+ .messageId(messageId)
|
|
|
|
|
+ .patientCode(patientCode)
|
|
|
|
|
+ .sync(true)
|
|
|
|
|
+ .timeout(timeout)
|
|
|
|
|
+ .timeUnit(unit)
|
|
|
|
|
+ .result(result)
|
|
|
|
|
+ .timestamp(new Date())
|
|
|
|
|
+ .build();
|
|
|
|
|
+ result.onTimeout(()->{
|
|
|
|
|
+ if(needResult){
|
|
|
|
|
+ BusClinicEntity clinic = clinicService.recentClinicByPatientCode(hospitalId, patientCode);
|
|
|
|
|
+ if(clinic==null){
|
|
|
|
|
+ result.setResult(R.fail("拉取信息超时,请稍后再试"));
|
|
|
|
|
+ }else {
|
|
|
|
|
+ result.setResult(R.success(clinic));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ hisRequestMap.remove(messageId);
|
|
|
|
|
+ });
|
|
|
|
|
+ //将数据存储到数据库
|
|
|
|
|
+ sendRequest(channelContext,request);
|
|
|
|
|
+ return result;
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
|
- * 描述: 处理his返回数据
|
|
|
|
|
|
|
+ * 描述: 处理his返回数据并返回开始时间最大的手术信息
|
|
|
* @author lifang
|
|
* @author lifang
|
|
|
* @date 2022/5/14 15:02
|
|
* @date 2022/5/14 15:02
|
|
|
* @param text
|
|
* @param text
|
|
|
* @param patientCode
|
|
* @param patientCode
|
|
|
- * @return void
|
|
|
|
|
|
|
+ * @return BusClinicEntity 开始时间最大的手术信息
|
|
|
*/
|
|
*/
|
|
|
- public void handle(String text,String patientCode){
|
|
|
|
|
|
|
+ public BusClinicEntity handle(String text,String patientCode){
|
|
|
ScriptParse script = scriptManager.getById(hospitalId);
|
|
ScriptParse script = scriptManager.getById(hospitalId);
|
|
|
if(script==null){
|
|
if(script==null){
|
|
|
log.warn("没有获取到医院{}的解析脚本信息,不处理该条数据",hospitalId);
|
|
log.warn("没有获取到医院{}的解析脚本信息,不处理该条数据",hospitalId);
|
|
|
|
|
+ script=new DefaultParse();
|
|
|
|
|
+ }
|
|
|
|
|
+ if(StrUtil.isBlank(text)){
|
|
|
|
|
+ return null;
|
|
|
}
|
|
}
|
|
|
ExecuteResult exec = script.exec(text);
|
|
ExecuteResult exec = script.exec(text);
|
|
|
JSON result=null;
|
|
JSON result=null;
|
|
@@ -60,12 +191,18 @@ public class HisScriptSession {
|
|
|
result = exec.getIfSuccess();
|
|
result = exec.getIfSuccess();
|
|
|
}catch (Exception e){
|
|
}catch (Exception e){
|
|
|
log.error("数据解析后转化为json失败,{},",text,e.getMessage());
|
|
log.error("数据解析后转化为json失败,{},",text,e.getMessage());
|
|
|
- return;
|
|
|
|
|
|
|
+ return null;
|
|
|
}
|
|
}
|
|
|
//数据解析完成后发布
|
|
//数据解析完成后发布
|
|
|
JSONArray jsonArray = JSONUtil.parseArray(result);
|
|
JSONArray jsonArray = JSONUtil.parseArray(result);
|
|
|
List<BusClinicEntity> sources = JSONUtil.toList(jsonArray, BusClinicEntity.class);
|
|
List<BusClinicEntity> sources = JSONUtil.toList(jsonArray, BusClinicEntity.class);
|
|
|
- SpringUtil.publishEvent(new HisEvent(this,sources,hospitalId,patientCode));
|
|
|
|
|
|
|
+ if(CollectionUtil.isEmpty(sources)){
|
|
|
|
|
+ return null;
|
|
|
|
|
+ }else {
|
|
|
|
|
+ sources.sort(Comparator.comparing(BusClinicEntity::getStartTime));
|
|
|
|
|
+ SpringUtil.publishEvent(new HisEvent(this,sources,hospitalId,patientCode));
|
|
|
|
|
+ return sources.get(sources.size()-1);
|
|
|
|
|
+ }
|
|
|
}
|
|
}
|
|
|
|
|
|
|
|
/**
|
|
/**
|
|
@@ -77,6 +214,10 @@ public class HisScriptSession {
|
|
|
*/
|
|
*/
|
|
|
public void bindChannel(ChannelContext channelContext){
|
|
public void bindChannel(ChannelContext channelContext){
|
|
|
this.channelContext=channelContext;
|
|
this.channelContext=channelContext;
|
|
|
|
|
+ if(TioUtils.checkBeforeIO(channelContext)){
|
|
|
|
|
+ hospitalService.update(new UpdateWrapper<BusHospitalEntity>().lambda().eq(BusHospitalEntity::getId,hospitalId).set(BusHospitalEntity::getScriptOnline,true));
|
|
|
|
|
+ refresh(hospitalService.getById(hospitalId));
|
|
|
|
|
+ }
|
|
|
};
|
|
};
|
|
|
|
|
|
|
|
|
|
|
|
@@ -88,6 +229,84 @@ public class HisScriptSession {
|
|
|
* @return boolean
|
|
* @return boolean
|
|
|
*/
|
|
*/
|
|
|
public boolean isOnline(){
|
|
public boolean isOnline(){
|
|
|
- return channelContext!=null&&channelContext.isClosed;
|
|
|
|
|
|
|
+ return channelContext!=null&&TioUtils.checkBeforeIO(channelContext);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 描述:
|
|
|
|
|
+ * @author lifang
|
|
|
|
|
+ * @date 2022/5/14 16:37
|
|
|
|
|
+ * @param channelContext
|
|
|
|
|
+ * @param request
|
|
|
|
|
+ * @return void
|
|
|
|
|
+ */
|
|
|
|
|
+ private ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
|
|
|
|
|
+ private void sendRequest(ChannelContext channelContext,HisRequest request){
|
|
|
|
|
+ hisRequestMap.put(request.getMessageId(),request);
|
|
|
|
|
+ Tio.send(channelContext, WsResponse.fromText(JSONUtil.toJsonStr(request),"utf-8"));
|
|
|
|
|
+ executorService.schedule(()->{
|
|
|
|
|
+ BusClinicEntity clinicEntity = clinicService.recentClinicByPatientCode("1", request.getPatientCode());
|
|
|
|
|
+ clinicEntity.setId(null);
|
|
|
|
|
+ clinicEntity.setMonitorType(true);
|
|
|
|
|
+ clinicEntity.setName("最新的临床数据3");
|
|
|
|
|
+ HisResponse hisResponse = new HisResponse();
|
|
|
|
|
+ hisResponse.setMessageId(request.getMessageId());
|
|
|
|
|
+ hisResponse.setSuccess(true);
|
|
|
|
|
+ hisResponse.setTimestamp(new Date());
|
|
|
|
|
+ hisResponse.setPatientCode(request.getPatientCode());
|
|
|
|
|
+ hisResponse.setContext(JSONUtil.toJsonStr(Arrays.asList(clinicEntity)));
|
|
|
|
|
+ response(hisResponse);
|
|
|
|
|
+ },1,TimeUnit.SECONDS);
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ public void response(HisResponse hisResponse){
|
|
|
|
|
+ //判断是否成功,是否超时
|
|
|
|
|
+ String messageId = hisResponse.getMessageId();
|
|
|
|
|
+ Assert.hasText(messageId,"his脚本响应数据无messageId");
|
|
|
|
|
+ HisRequest hisRequest = hisRequestMap.remove(messageId);
|
|
|
|
|
+ if(hisResponse.isSuccess()){
|
|
|
|
|
+ //同步请求当超时之后,不再处理数据
|
|
|
|
|
+ boolean sync = hisResponse.isSync();
|
|
|
|
|
+ if (hisRequest == null) {
|
|
|
|
|
+ log.warn("响应[{}]请求不存在,或已超时",messageId);
|
|
|
|
|
+ if(sync){
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ //正常响应
|
|
|
|
|
+ handle(Value.simple(hisResponse.getContext()).asString(), hisResponse.getPatientCode());
|
|
|
|
|
+ }else {
|
|
|
|
|
+ Date responseTimestamp = hisResponse.getTimestamp();
|
|
|
|
|
+ Date requestTimestamp = hisRequest.getTimestamp();
|
|
|
|
|
+ long timeout = hisRequest.getTimeout();
|
|
|
|
|
+ TimeUnit timeUnit = hisRequest.getTimeUnit();
|
|
|
|
|
+ DeferredResult<R<BusClinicEntity>> result = hisRequest.getResult();
|
|
|
|
|
+ if (requestTimestamp.getTime()+timeUnit.toMillis(timeout)>responseTimestamp.getTime()) {
|
|
|
|
|
+ log.warn("请求[{}]已超时,请求时间[{}],响应时间[{}],是否为同步请求[{}]",messageId,requestTimestamp,responseTimestamp,sync);
|
|
|
|
|
+ if(sync){
|
|
|
|
|
+ result.setErrorResult("响应超时");
|
|
|
|
|
+ return;
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ //正常响应
|
|
|
|
|
+ BusClinicEntity clinic = handle(Value.simple(hisResponse.getContext()).asString(), hisResponse.getPatientCode());
|
|
|
|
|
+ result.setResult(R.success(clinic));
|
|
|
|
|
+ }
|
|
|
|
|
+ }else {
|
|
|
|
|
+ if (hisRequest != null) {
|
|
|
|
|
+ log.warn("医院[{}]拉取信息失败,失败原因[{}]",hospitalId,hisResponse.getErrorMsg());
|
|
|
|
|
+ hisRequest.getResult().setResult(R.fail("更新失败,失败原因["+ hisResponse.getErrorMsg()+"]"));
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+ }
|
|
|
|
|
+
|
|
|
|
|
+ /**
|
|
|
|
|
+ * 描述: 会话下线
|
|
|
|
|
+ * @author lifang
|
|
|
|
|
+ * @date 2022/5/15 21:45
|
|
|
|
|
+ * @param
|
|
|
|
|
+ * @return void
|
|
|
|
|
+ */
|
|
|
|
|
+ public void offline() {
|
|
|
|
|
+ hospitalService.update(new UpdateWrapper<BusHospitalEntity>().lambda().eq(BusHospitalEntity::getId,hospitalId).set(BusHospitalEntity::getScriptOnline,false));
|
|
|
}
|
|
}
|
|
|
}
|
|
}
|