Procházet zdrojové kódy

add 从his拉取病人信息
add 医院新增编码字段

A17404李放 před 3 roky
rodič
revize
733b22d3b1

+ 3 - 1
coffee-admin/src/test/java/com/coffee/admin/BusHospitalTest.java

@@ -5,6 +5,7 @@ import com.coffee.bus.bean.Script;
 import com.coffee.bus.controller.BusHospitalController;
 import com.coffee.bus.entity.BusHospitalEntity;
 import com.coffee.bus.service.LocalBusHospitalService;
+import com.coffee.bus.utils.CodeUtils;
 import com.coffee.common.result.R;
 import org.junit.Test;
 import org.junit.runner.RunWith;
@@ -49,7 +50,8 @@ public class BusHospitalTest {
     @Test
     public void query(){
         List<BusHospitalEntity> list = busHospitalService.list();
-        System.out.println(list);
+        list.forEach(busHospitalEntity -> busHospitalEntity.setCode(CodeUtils.genInviteCode(Long.valueOf(busHospitalEntity.getId()))));
+        busHospitalService.updateBatchById(list);
     }
 
     @Test

+ 1 - 1
coffee-common/src/main/java/com/coffee/common/config/CachingContentFilter.java

@@ -16,7 +16,7 @@ import java.io.IOException;
  * @createTime 2022年04月22日 14:59:00
  */
 @Slf4j
-@WebFilter(urlPatterns = "/*")
+@WebFilter(urlPatterns = "/*",asyncSupported = true)
 public class CachingContentFilter implements Filter {
 
     @Override

+ 2 - 1
coffee-common/src/main/java/com/coffee/common/config/websocket/DefaultMessageListener.java

@@ -14,6 +14,7 @@ import org.springframework.data.redis.connection.Message;
 import org.springframework.data.redis.connection.MessageListener;
 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.HashSet;
@@ -40,7 +41,7 @@ public class DefaultMessageListener implements MessageListener {
             Set<ChannelContext> closeChannel = new HashSet<>();
             channelContexts.parallelStream()
                     .forEach(channel -> {
-                        if (channel.isClosed||channel.isRemoved) {
+                        if (TioUtils.checkBeforeIO(channel)) {
                             closeChannel.add(channel);
                             return;
                         }

+ 15 - 7
coffee-system/src/main/java/com/coffee/bus/controller/BusPatientController.java

@@ -8,6 +8,7 @@ import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.mapper.Mapper;
+import com.coffee.bus.controller.vo.GetPatientInfoVo;
 import com.coffee.bus.controller.vo.MonitorFinishedVo;
 import com.coffee.bus.entity.*;
 import com.coffee.bus.enums.DeviceStatusEnum;
@@ -275,8 +276,8 @@ public class BusPatientController  implements BaseQueryController<BusPatientEnti
 
 
     //todo 使用
-    @PostMapping("/syn/{hospitalId}/{patientCode}")
-    @ApiOperation(value = "步更新患者信息,即等待更新完成后返回更新结果")
+    @PostMapping("/pull/async/{hospitalId}/{patientCode}")
+    @ApiOperation(value = "步更新患者信息,即等待更新完成后返回更新结果")
     public DeferredResult<R> syn(@PathVariable("hospitalId") String hospitalId, @PathVariable("patientCode")String patientCode){
         DeferredResult<R> result = new DeferredResult<>();
         result.onCompletion(()->{
@@ -288,11 +289,18 @@ public class BusPatientController  implements BaseQueryController<BusPatientEnti
         return result;
     }
 
-    //todo
-    @PostMapping("/async/{hospitalId}/{patientCode}")
-    @ApiOperation(value = "异步更新患者信息,即立刻返回更新结果")
-    public R async(@PathVariable("hospitalId") String hospitalId, @PathVariable("patientCode")String patientCode){
-        return R.success();
+    /**
+     * 描述:
+     * @author lifang
+     * @date 2022/5/15 21:56
+     * @param tenantId
+     * @param vo
+     * @return R
+     */
+    @PostMapping("/pull/sync")
+    @ApiOperation(value = "同步更新患者信息,超时时间默认为10s,即立刻返回更新结果")
+    public DeferredResult<R<BusClinicEntity>>  async(@RequestAttribute("tenantId") String tenantId, @RequestBody GetPatientInfoVo vo){
+        return patientService.syncGetPatientInfoFromHis(tenantId,vo.getPatientCode(),vo.getTimeout());
     }
 
 

+ 21 - 0
coffee-system/src/main/java/com/coffee/bus/controller/vo/GetPatientInfoVo.java

@@ -0,0 +1,21 @@
+package com.coffee.bus.controller.vo;
+
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName GetPatientInfoVo.java
+ * @Description TODO
+ * @createTime 2022年05月15日 21:52:00
+ */
+@Data
+@ApiModel("从his获取病人信息")
+public class GetPatientInfoVo {
+    @ApiModelProperty("病号")
+    private String patientCode;
+    @ApiModelProperty("超时时间,单位:秒;默认10s,当超时时间为-1时,若没有拉取到数据则立即返回")
+    private long timeout;
+}

+ 7 - 0
coffee-system/src/main/java/com/coffee/bus/entity/BusHospitalEntity.java

@@ -6,6 +6,9 @@ import com.coffee.bus.bean.Script;
 import com.coffee.bus.his.strategy.HisStrategyEnum;
 import com.coffee.common.entity.RecordCreationEntity;
 import com.coffee.common.entity.RecordModifierEntity;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import com.fasterxml.jackson.annotation.JsonSetter;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -73,6 +76,10 @@ public class BusHospitalEntity implements RecordModifierEntity, RecordCreationEn
     @TableField(typeHandler = FastjsonTypeHandler.class)
     private Script script;
 
+    @ApiModelProperty("医院唯一编码,自动生成")
+    @JsonIgnoreProperties(allowGetters = true)
+    private String code;
+
     @ApiModelProperty("医院脚本是否在线")
     private Boolean scriptOnline;
 

+ 81 - 0
coffee-system/src/main/java/com/coffee/bus/his/HisRequest.java

@@ -0,0 +1,81 @@
+package com.coffee.bus.his;
+
+import com.coffee.bus.entity.BusClinicEntity;
+import com.coffee.common.result.R;
+import com.fasterxml.jackson.annotation.JsonIgnore;
+import lombok.AllArgsConstructor;
+import lombok.Builder;
+import lombok.Data;
+import lombok.NoArgsConstructor;
+import org.springframework.web.context.request.async.DeferredResult;
+
+import javax.validation.constraints.NotNull;
+import java.io.Serializable;
+import java.util.Date;
+import java.util.concurrent.TimeUnit;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName HisRequest.java
+ * @Description 向his脚本发起的请求
+ * @createTime 2022年05月14日 16:38:00
+ */
+@Data
+@Builder
+@NoArgsConstructor
+@AllArgsConstructor
+public class HisRequest implements Serializable {
+    /**
+     *
+     * 消息id
+     *
+     */
+    private String messageId;
+
+    /**
+     *
+     * 病号
+     *
+     */
+    private String patientCode;
+
+    /**
+     *
+     * 超时时间
+     *
+     */
+    @JsonIgnore
+    private long timeout;
+
+    /**
+     *
+     * 超时时间单位
+     *
+     */
+    @JsonIgnore
+    private TimeUnit timeUnit;
+
+    /**
+     *
+     * 发送时间时间戳
+     *
+     */
+    private Date timestamp;
+
+    /**
+     *
+     * 请求是否为同步请求
+     *
+     */
+    private boolean sync;
+
+    /**
+     *
+     * 用户响应结果
+     *
+     */
+    @JsonIgnore
+    @NotNull
+    private DeferredResult<R<BusClinicEntity>> result;
+}

+ 73 - 0
coffee-system/src/main/java/com/coffee/bus/his/HisResponse.java

@@ -0,0 +1,73 @@
+package com.coffee.bus.his;
+
+import lombok.Data;
+
+import java.util.Date;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName HisResponse.java
+ * @Description his脚本返回的响应
+ * @createTime 2022年05月14日 16:38:00
+ */
+@Data
+public class HisResponse {
+    /**
+     *
+     * 消息id
+     *
+     */
+    private String messageId;
+
+    /**
+     *
+     * 病号
+     *
+     */
+    private String patientCode;
+
+    /**
+     *
+     * 响应类型
+     *
+     */
+    private String type;
+
+    /**
+     *
+     * 查询是否成功
+     *
+     */
+    private boolean success;
+
+    /**
+     *
+     * 请求是否为同步请求
+     *
+     */
+    private boolean sync;
+
+    /**
+     *
+     * 响应内容
+     *
+     */
+    private Object context;
+
+    /**
+     *
+     * 当查询失败时错误信息
+     *
+     */
+    private String errorMsg;
+
+    /**
+     *
+     * 响应时间戳
+     *
+     */
+    private Date timestamp;
+
+
+}

+ 235 - 16
coffee-system/src/main/java/com/coffee/bus/his/HisScriptSession.java

@@ -1,20 +1,40 @@
 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.json.JSON;
 import cn.hutool.json.JSONArray;
 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.BusHospitalEntity;
-import com.coffee.bus.his.strategy.HisStrategyManagerRegister;
 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.ScriptManager;
 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 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.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;
 
 /**
@@ -28,31 +48,142 @@ import java.util.concurrent.TimeUnit;
 public class HisScriptSession {
     private String hospitalId;
     private ChannelContext channelContext;
-    private BusHospitalEntity hospital;
     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
      * @date 2022/5/14 15:02
      * @param text
      * @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);
         if(script==null){
             log.warn("没有获取到医院{}的解析脚本信息,不处理该条数据",hospitalId);
+            script=new DefaultParse();
+        }
+        if(StrUtil.isBlank(text)){
+            return null;
         }
         ExecuteResult exec = script.exec(text);
         JSON result=null;
@@ -60,12 +191,18 @@ public class HisScriptSession {
             result = exec.getIfSuccess();
         }catch (Exception e){
             log.error("数据解析后转化为json失败,{},",text,e.getMessage());
-            return;
+            return null;
         }
         //数据解析完成后发布
         JSONArray jsonArray = JSONUtil.parseArray(result);
         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){
         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
      */
     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));
     }
 }

+ 48 - 15
coffee-system/src/main/java/com/coffee/bus/his/HisScriptSessionManager.java

@@ -1,7 +1,11 @@
 package com.coffee.bus.his;
 
+import com.coffee.bus.script.ScriptManager;
+import com.coffee.bus.service.LocalBusClinicService;
+import com.coffee.bus.service.LocalBusHospitalService;
+import org.springframework.beans.factory.annotation.Autowired;
+import org.springframework.data.redis.core.RedisTemplate;
 import org.springframework.stereotype.Component;
-import org.tio.core.ChannelContext;
 
 import java.util.Map;
 import java.util.concurrent.ConcurrentHashMap;
@@ -16,34 +20,63 @@ import java.util.concurrent.ConcurrentHashMap;
 @Component
 public class HisScriptSessionManager {
     private Map<String,HisScriptSession> sessionMap=new ConcurrentHashMap<>();
+    private LocalBusClinicService clinicService;
+    private ScriptManager scriptManager;
+    private RedisTemplate redisTemplate;
+    private LocalBusHospitalService hospitalService;
 
-    //获取会话
+    @Autowired
+    public HisScriptSessionManager(LocalBusClinicService clinicService, ScriptManager scriptManager, RedisTemplate redisTemplate, LocalBusHospitalService hospitalService) {
+        this.clinicService = clinicService;
+        this.scriptManager = scriptManager;
+        this.redisTemplate = redisTemplate;
+        this.hospitalService = hospitalService;
+    }
+
+    /**
+     * 描述: 根据医院id获取会话
+     * @author lifang
+     * @date 2022/5/15 1:06
+     * @param hospitalId
+     * @return HisScriptSession
+     */
     public HisScriptSession get(String hospitalId){
         return sessionMap.computeIfAbsent(hospitalId,k->register(hospitalId));
     };
 
-    //注册会话
+    /**
+     * 描述: 注册医院会话信息
+     * @author lifang
+     * @date 2022/5/15 1:07
+     * @param hospitalId
+     * @return HisScriptSession
+     */
     public HisScriptSession register(String hospitalId){
-        return null;
+        return new HisScriptSession(hospitalId, scriptManager,redisTemplate, clinicService,hospitalService);
     }
 
-    //注销会话
+    /**
+     * 描述: 注销医院会话
+     * @author lifang
+     * @date 2022/5/15 1:07
+     * @param hospitalId
+     * @return HisScriptSession
+     */
     public HisScriptSession unregister(String hospitalId){
-        return null;
+        return sessionMap.remove(hospitalId);
     }
 
 
-    //上线
-    public void online(String hospitalId, ChannelContext channelContext){
-
-    };
-
-    //下线
-    public void offline(String hospitalId){};
 
-    //是否在线
+    /**
+     * 描述: 判断医院脚本是否在线
+     * @author lifang
+     * @date 2022/5/15 1:08
+     * @param hospitalId
+     * @return boolean
+     */
     public boolean isOnline(String hospitalId){
-        return false;
+        return sessionMap.get(hospitalId).isOnline();
     };
 
 }

+ 2 - 1
coffee-system/src/main/java/com/coffee/bus/script/ScriptManager.java

@@ -42,7 +42,8 @@ public class ScriptManager implements CommandLineRunner {
     public ScriptParse getById(String id){
         ScriptParse scriptParse = scriptParseMap.get(id);
         if(scriptParse==null){
-            throw new RuntimeException("不支持解析[{"+id+"}]脚本");
+//            throw new RuntimeException("不支持解析[{"+id+"}]脚本");
+            return new DefaultParse();
         }
         return scriptParse;
     }

+ 13 - 8
coffee-system/src/main/java/com/coffee/bus/service/LocalBusHospitalService.java

@@ -4,9 +4,11 @@ import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
 import com.coffee.bus.entity.BusHospitalEntity;
 import com.coffee.bus.his.strategy.HisStrategyEnum;
 import com.coffee.bus.mapper.BusHospitalMapper;
+import com.coffee.bus.utils.CodeUtils;
 import com.coffee.common.config.mybatis.GetNameInterface;
 import com.coffee.common.crud.BaseService;
 import com.coffee.common.exception.CustomException;
@@ -32,13 +34,16 @@ public class LocalBusHospitalService extends BaseService<BusHospitalMapper, BusH
     private static Map<String,BusHospitalEntity> hospitalMap=new HashMap<>();
     @Override
     public void validateBeforeSave(BusHospitalEntity entity) {
-         String name = entity.getName();
-         if(validateName(name)){
-             throw new CustomException(String.format("医院名称[%s]不可重复",entity.getName()));
-         }
-         if(entity.getStrategy()==null){
-             entity.setStrategy(HisStrategyEnum.ALL);
-         }
+        long id = IdWorker.getId();
+        String name = entity.getName();
+        if(validateName(name)){
+            throw new CustomException(String.format("医院名称[%s]不可重复",entity.getName()));
+        }
+        if(entity.getStrategy()==null){
+            entity.setStrategy(HisStrategyEnum.ALL);
+        }
+        entity.setCode(CodeUtils.genInviteCode(id));
+        entity.setId(String.valueOf(id));
     }
 
     @Override
@@ -50,7 +55,7 @@ public class LocalBusHospitalService extends BaseService<BusHospitalMapper, BusH
         if(hospital==null){
             return;
         }
-        if (ObjectUtil.equal(entity.getName(), hospital.getName())) {
+        if (!ObjectUtil.equal(entity.getName(), hospital.getName())) {
             if(validateName(entity.getName())){
                 throw new CustomException(String.format("医院名称[%s]不可重复",entity.getName()));
             }

+ 11 - 7
coffee-system/src/main/java/com/coffee/bus/service/LocalBusPatientService.java

@@ -15,6 +15,8 @@ import com.coffee.bus.entity.PatientDeviceRepeatDomain;
 import com.coffee.bus.enums.DeviceAlarmEnum;
 import com.coffee.bus.enums.DeviceStatusEnum;
 import com.coffee.bus.enums.PatientAlarmEnum;
+import com.coffee.bus.his.HisScriptSessionManager;
+import com.coffee.bus.his.strategy.HisStrategyManager;
 import com.coffee.bus.service.dto.*;
 import com.coffee.bus.mapper.BusPatientMapper;
 import com.coffee.bus.registry.patient.PatientOperator;
@@ -24,12 +26,15 @@ import com.coffee.bus.utils.WsPublishUtils;
 import com.coffee.common.config.websocket.WebSocketConstant;
 import com.coffee.common.crud.BaseService;
 import com.coffee.common.enums.SexEnum;
+import com.coffee.common.result.R;
 import lombok.extern.slf4j.Slf4j;
+import net.bytebuddy.asm.Advice;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.scheduling.annotation.Async;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
+import org.springframework.web.context.request.async.DeferredResult;
 
 import java.time.LocalDateTime;
 import java.util.*;
@@ -57,14 +62,13 @@ public class LocalBusPatientService extends BaseService<BusPatientMapper, BusPat
 
     @Autowired
     @Lazy
-    private WsPublishUtils wsPublishUtils;
-
-    private ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
+    private HisScriptSessionManager scriptSessionManager;
 
     @Autowired
     @Lazy
-    private LocalBusPatientService patientService;
+    private WsPublishUtils wsPublishUtils;
 
+    private ScheduledExecutorService executorService = Executors.newSingleThreadScheduledExecutor();
 
     @Override
     public void validateBeforeSave(BusPatientEntity entity) {
@@ -102,9 +106,9 @@ public class LocalBusPatientService extends BaseService<BusPatientMapper, BusPat
      * @param hospitalId  医院id
      * @param patientCode 病号
      */
-    @Async
-    public void syncGetPatientInfoFromHis(String hospitalId,String patientCode){
-
+    public DeferredResult<R<BusClinicEntity>> syncGetPatientInfoFromHis(String hospitalId, String patientCode, long timeout){
+        return scriptSessionManager.get(hospitalId)
+                .syncGetPatientInfo(patientCode,timeout,TimeUnit.SECONDS);
     }
 
     /**

+ 72 - 0
coffee-system/src/main/java/com/coffee/bus/utils/CodeUtils.java

@@ -0,0 +1,72 @@
+package com.coffee.bus.utils;
+
+
+import java.util.*;
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName CodeUtils.java
+ * @Description TODO
+ * @createTime 2022年05月16日 09:15:00
+ */
+public class CodeUtils {
+    private static List<String> baseList;
+    private static final String INDEX = "4NQ5XMV7WRKC0ILEZO2J1PSDH8AU3FBTG6Y9";
+
+    static {
+        List<String> list = new ArrayList<>();
+        list.add("XLG27OR1QC34FK58JAMPEH9YWU0SZNIDVB6T");
+        list.add("W8DCRPJKYL0FE5QTIVZB6O3X1HUN92SGA47M");
+        list.add("07YHDTEK4NVBQRUZPSGI93C81O2AWJ6FXML5");
+        list.add("QWID3F97N1KY2SZXVUE4LORACB8TJ50G6HMP");
+        list.add("AYZQ8GVJR7PHS501B4CONL26X3WUK9TEFDIM");
+        list.add("SMCIQ4F3RWH56V8NA7TJOB1ZULD29KGY0PEX");
+        list.add("KI835MZWALV7DGP2JO041XHCQYEB9UF6TNRS");
+        list.add("4NQ5XMV7WRKC0ILEZO2J1PSDH8AU3FBTG6Y9");
+        list.add("3VPDTRGM0NUAEH7SW6945OIFC82BYZLXJK1Q");
+        list.add("DEH1MXNC6IULY3K9GPT4WQJVR08B5FSA27OZ");
+        list.add("PZ6WUQDT2MEIHA9Y8RKBL34XGFVSJN7OC150");
+        list.add("7KEDTSVQURXIBJ816Y29LOPCNF0G35WZMAH4");
+        list.add("JHOF7BVZ0ET1DWGX4Q3APCLRMI895K2UY6SN");
+        list.add("80UVJDY3IXA15TP69ZFEHC7QMLGBKSWN2RO4");
+        list.add("4653QA9NLIZCSJKTEPV2DU7BOGXF18MYH0RW");
+        list.add("SOGYRP3TWH8ZJBI6U4KN7X2DFA0QC5VEL1M9");
+        list.add("1S4YA6KNTZIXEC25P03JGOQUMBWRHL987DVF");
+        list.add("NJMXCUTGO7ZL016SWPR8YHEVABKFI423D5Q9");
+        list.add("CT02U35L1RH84FPKMGWJVYNBIZ7DE6SXAOQ9");
+        list.add("2U06HM7TWP1YIBX49SZFEAR8D5QGKO3JVNCL");
+        list.add("ABERN67VO40DFPJYS9H5KW21Z8M3CILGXUTQ");
+        list.add("QP43YJ2ZKA81F7TCSXH6V0BRGEMWLDNO9U5I");
+        list.add("0R7QKXBW3VLPHE48U1Z9DJYNAFTOCIMS56G2");
+        list.add("0C4XVG98NM71HBUDQYZLFRE65SIPKOT23WAJ");
+        list.add("T7YRN3LHWZJGBI4S2FM8UQP0DK69VXCO5EA1");
+        list.add("D2MR7059AGWTSJP8EKUHXLQ6BFZONIVC134Y");
+        list.add("MPB3SQOEU9XLV2R1Y6ZTHGKD8AF50JI4N7CW");
+        list.add("73X4LRF52JC0OETVHMKUNPDI1SQ8BZ6AWY9G");
+        list.add("GF9ASJP2V4HE5LOTY0R1I3C8MUNXW7BKDQ6Z");
+        list.add("J3BLZDY7VGNWSKX0CP4MU18FQ5EHROAI629T");
+        list.add("LTMR0UFP28BJ1OSWXV5G3AN4EKCQYH76IZD9");
+        list.add("MDL7E8TWYS2PI6A3ZXBVJH9OKGQ510RCU4NF");
+        list.add("L2K3WMCBQ6GUDFYZ810XSP4O7RIH59AVNTJE");
+        list.add("DLPBXJ0ROH8AFINQUSME1246VC3T9YZK5G7W");
+        list.add("7CHVSGR1ZEJDMXPF9OY0KT35I6NBLAQ4W28U");
+        list.add("X4V3G9SIET678NUK0FM1BZDYLR2OJC5QWAPH");
+        baseList = Collections.unmodifiableList(list);
+    }
+
+
+    public static String genInviteCode(long id) {
+        int key = (int) (id % 36);
+        char[] cs = new char[4];
+        cs[0] = INDEX.charAt(key);
+        String select = baseList.get(key);
+        id=id/36;
+        long code = 1_0000_0000 + id * 13;
+        for (int i = 1; i < cs.length; i++) {
+            cs[i] = select.charAt((int) (code % 36));
+            code = code / 36;
+        }
+        return new String(cs);
+    }
+
+}

+ 10 - 4
coffee-system/src/main/java/com/coffee/bus/websocket/HisConnectionHandler.java

@@ -1,5 +1,7 @@
 package com.coffee.bus.websocket;
 
+import com.coffee.bus.his.HisScriptSession;
+import com.coffee.bus.his.HisScriptSessionManager;
 import com.coffee.bus.service.LocalBusHospitalService;
 import com.coffee.common.config.websocket.MessagingRequest;
 import com.coffee.common.config.websocket.WebSocketConstant;
@@ -19,8 +21,7 @@ import org.tio.core.ChannelContext;
 @Component
 @AllArgsConstructor
 public class HisConnectionHandler extends Subscribe {
-    private final RedisTemplate redisTemplate;
-    private final LocalBusHospitalService hospitalService;
+    private final HisScriptSessionManager scriptSessionManager;
     @Override
     public String getId() {
         return WebSocketConstant.HIS_CONNECTION;
@@ -30,12 +31,17 @@ public class HisConnectionHandler extends Subscribe {
     public void onMessage(MessagingRequest message, ChannelContext channelContext) {
         //医院his脚本逻辑单独处理
         String tenantId = message.getTenantId();
-
+        channelContext.setAttribute(tenantId);
+        HisScriptSession hisScriptSession = scriptSessionManager.get(tenantId);
+        //绑定
+        hisScriptSession.bindChannel(channelContext);
     }
 
     @Override
     public void close(ChannelContext channelContext) {
-
+        String tenantId=String.valueOf(channelContext.getAttribute());
+        HisScriptSession hisScriptSession = scriptSessionManager.get(tenantId);
+        hisScriptSession.offline();
     }
 
     @Override