Pārlūkot izejas kodu

Merge branch 'dev' into local-slave

# Conflicts:
#	nb-im/src/main/java/com/nb/im/controller/ImRoomController.java
#	nb-im/src/main/java/com/nb/im/entity/ImRoomEntity.java
#	nb-im/src/main/java/com/nb/im/mapper/ImRoomMapper.java
#	nb-im/src/main/resources/mapper/im/ImRoomMapper.xml
#	nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/his/strategy/onlynew/DefaultHisNewStrategyHandler.java
18339543638 2 gadi atpakaļ
vecāks
revīzija
698d9fa166
39 mainītis faili ar 961 papildinājumiem un 133 dzēšanām
  1. 3 3
      nb-admin/src/main/resources/application-dev.yml
  2. 1 0
      nb-auth/src/main/java/com/nb/auth/sa/SaTokenConfig.java
  3. 1 1
      nb-common/ws-common/src/main/java/com/nb/common/websocket/DefaultWebSocketMsgHandler.java
  4. 5 0
      nb-common/ws-common/src/main/java/com/nb/common/websocket/WebSocketConstant.java
  5. 1 1
      nb-common/ws-common/src/main/java/com/nb/common/websocket/handler/Subscribe.java
  6. 46 0
      nb-core/src/main/java/com/nb/core/utils/CustomHorizontalCellStyleStrategy.java
  7. 32 0
      nb-core/src/main/java/com/nb/core/utils/ExcelUtil.java
  8. 114 0
      nb-core/src/main/java/com/nb/core/utils/StyleUtils.java
  9. 19 36
      nb-im/src/main/java/com/nb/im/controller/ImRoomController.java
  10. 2 3
      nb-im/src/main/java/com/nb/im/entity/ImRoomEntity.java
  11. 13 6
      nb-im/src/main/java/com/nb/im/mapper/ImRoomMapper.java
  12. 16 6
      nb-im/src/main/java/com/nb/im/room/ImRoomOperator.java
  13. 3 2
      nb-im/src/main/java/com/nb/im/room/ImRoomOperatorManager.java
  14. 45 0
      nb-im/src/main/java/com/nb/im/service/LocalImMsgService.java
  15. 42 0
      nb-im/src/main/java/com/nb/im/service/dto/ImRoomDoctorQuery.java
  16. 19 7
      nb-im/src/main/java/com/nb/im/utils/ImUtils.java
  17. 31 0
      nb-im/src/main/java/com/nb/im/ws/ImUnreadSubscribe.java
  18. 11 1
      nb-im/src/main/java/com/nb/im/ws/PubMsgRequestHandler.java
  19. 10 0
      nb-im/src/main/java/com/nb/im/ws/filter/MsgFormatFilter.java
  20. 55 26
      nb-im/src/main/resources/mapper/im/ImRoomMapper.xml
  21. 4 0
      nb-service-api/app-assistant-api/src/main/java/com/nb/app/assistant/api/bean/HandleBindResult.java
  22. 2 1
      nb-service-api/web-service-api/src/main/java/com/nb/web/api/enums/DeviceTypeEnum.java
  23. 2 1
      nb-service-api/web-service-api/src/main/java/com/nb/web/api/enums/HospitalLogEnum.java
  24. 5 1
      nb-service/app-assistant/src/main/java/com/nb/app/assistant/service/LocalAssistantUserBindService.java
  25. 95 0
      nb-service/iot-service/src/main/java/com/nb/aliyun/controller/IotController.java
  26. 30 0
      nb-service/iot-service/src/main/java/com/nb/aliyun/doc/IotDocConfig.java
  27. 2 2
      nb-service/web-service/src/main/java/com/nb/web/service/bus/controller/BusDeviceController.java
  28. 14 1
      nb-service/web-service/src/main/java/com/nb/web/service/bus/controller/BusEvaluationController.java
  29. 39 22
      nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/his/HisScriptSession.java
  30. 4 3
      nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/his/strategy/onlynew/DefaultHisNewStrategyHandler.java
  31. 2 2
      nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/his/strategy/part/DefaultHisPartStrategyHandler.java
  32. 7 4
      nb-service/web-service/src/main/java/com/nb/web/service/bus/listener/DeviceInfoListener.java
  33. 9 0
      nb-service/web-service/src/main/java/com/nb/web/service/bus/mapper/BusInfusionHistoryMapper.java
  34. 5 0
      nb-service/web-service/src/main/java/com/nb/web/service/bus/registry/constant/DeviceKeyConstant.java
  35. 62 3
      nb-service/web-service/src/main/java/com/nb/web/service/bus/service/LocalBusDeviceService.java
  36. 4 0
      nb-service/web-service/src/main/java/com/nb/web/service/bus/service/LocalBusInfusionHistoryService.java
  37. 26 1
      nb-service/web-service/src/main/java/com/nb/web/service/bus/service/dto/CombineEvalResult.java
  38. 99 0
      nb-service/web-service/src/main/java/com/nb/web/service/bus/service/dto/CombineEvalResultExcelDTO.java
  39. 81 0
      nb-service/web-service/src/main/resources/mapper/bus/BusInfusionHistoryMapper.xml

+ 3 - 3
nb-admin/src/main/resources/application-dev.yml

@@ -30,9 +30,9 @@ spring:
   datasource:
     type: com.alibaba.druid.pool.DruidDataSource
     driverClassName: com.mysql.cj.jdbc.Driver
-    url: jdbc:mysql://192.168.103.82:7001/nbnetpump?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&autoReconnect=true
+    url: jdbc:mysql://192.168.100.32:3306/nbnetpump?createDatabaseIfNotExist=true&useUnicode=true&characterEncoding=utf8&zeroDateTimeBehavior=convertToNull&useSSL=false&serverTimezone=GMT%2B8&autoReconnect=true
     username: root
-    password: Tuoren123.
+    password: 123456
     druid:
       # 初始连接数
       initialSize: 5
@@ -138,7 +138,7 @@ aliyun:
   # iotInstanceId:企业版实例请填写实例ID,公共实例请填空字符串""。
   iotInstanceId: "iot-060a0bgd"
   server-subscription:
-    enable: true  # 是否开启阿里云物联网服务端订阅
+    enable: false  # 是否开启阿里云物联网服务端订阅
   product:
     productKey: he1fy3RqSr0
 

+ 1 - 0
nb-auth/src/main/java/com/nb/auth/sa/SaTokenConfig.java

@@ -42,6 +42,7 @@ public class SaTokenConfig {
     private static final List<String> IGNORE_URL = new ArrayList<>();
 
     static {
+        IGNORE_URL.add("/iot/**");
         IGNORE_URL.add("/actuator/**");
         IGNORE_URL.add("/bus/version/page");
         IGNORE_URL.add("/druid/**");

+ 1 - 1
nb-common/ws-common/src/main/java/com/nb/common/websocket/DefaultWebSocketMsgHandler.java

@@ -107,7 +107,7 @@ public class DefaultWebSocketMsgHandler implements IWsMsgHandler {
                 }
             }
         }catch (Exception e){
-            log.warn("websocket 接收到异常请求,token:{},message:{},assistId:{}",channelContext.getToken(),message,JSONUtil.toJsonStr(channelContext.get(Constants.LOGIN_USER_KEY)));
+            log.warn("websocket 接收到异常请求,token:{},message:{},assistId:{},",channelContext.getToken(),message,JSONUtil.toJsonStr(channelContext.get(Constants.LOGIN_USER_KEY)),e);
             channelContext.setCloseCode(ChannelContext.CloseCode.HEARTBEAT_TIMEOUT);
             Tio.remove(channelContext,  "接受数据不符合业务规范");
         }

+ 5 - 0
nb-common/ws-common/src/main/java/com/nb/common/websocket/WebSocketConstant.java

@@ -30,6 +30,8 @@ public class WebSocketConstant {
 
     public static final String IM ="im";
 
+    public static final String IM_UNREAD ="imUnread";
+
     public static final String IM_ALL ="im-all";
     /**
      * 病人监控订阅
@@ -55,6 +57,9 @@ public class WebSocketConstant {
         return TopicWrapper.of(id+"-"+productName+"-"+param+"-"+tenantId,param);
     }
 
+    public static TopicWrapper getImUnreadTopic(String userId){
+        return getTopic(IM_UNREAD,null,userId,null);
+    }
 
     /**
      * 获取 设备状态变化主题

+ 1 - 1
nb-common/ws-common/src/main/java/com/nb/common/websocket/handler/Subscribe.java

@@ -1,6 +1,7 @@
 package com.nb.common.websocket.handler;
 
 import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
 import com.nb.auth.bean.LoginUser;
 import com.nb.common.websocket.*;
@@ -30,7 +31,6 @@ public abstract class Subscribe implements WsHandler {
 
     public TopicWrapper getTopic(String productName,String param,String tenantId){
         return  WebSocketConstant.getTopic(this.getId(),productName, param, tenantId);
-
     };
 
 

+ 46 - 0
nb-core/src/main/java/com/nb/core/utils/CustomHorizontalCellStyleStrategy.java

@@ -0,0 +1,46 @@
+package com.nb.core.utils;
+
+import com.alibaba.excel.metadata.data.WriteCellData;
+import com.alibaba.excel.write.handler.context.CellWriteHandlerContext;
+import com.alibaba.excel.write.metadata.style.WriteCellStyle;
+import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
+
+import javax.validation.constraints.NotNull;
+import java.lang.reflect.Field;
+
+/**
+ * @ClassName : CustomHorizontalCellStyleStrategy
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年06月17日
+ */
+
+public class CustomHorizontalCellStyleStrategy extends HorizontalCellStyleStrategy {
+    private final WriteCellStyle notNullCellStyle;
+    public CustomHorizontalCellStyleStrategy(WriteCellStyle headWriteCellStyle, WriteCellStyle contentWriteCellStyle) {
+        super(headWriteCellStyle,contentWriteCellStyle);
+        notNullCellStyle=StyleUtils.getNotNullHeadStyle();
+    }
+    @Override
+    public void afterCellDispose(CellWriteHandlerContext context) {
+        if (context.getHead() == null) {
+            return;
+        }
+        if (context.getHead()) {
+            customSetHeadCellStyle(context);
+        } else {
+            setContentCellStyle(context);
+        }
+    }
+
+    private void customSetHeadCellStyle(CellWriteHandlerContext context) {
+        Field field = context.getExcelContentProperty().getField();
+        NotNull notNull = field.getAnnotation(NotNull.class);
+        if(notNull!=null){
+            WriteCellData<?> cellData = context.getFirstCellData();
+            cellData.setWriteCellStyle(notNullCellStyle);
+        }else {
+            setHeadCellStyle(context);
+        }
+    }
+}

+ 32 - 0
nb-core/src/main/java/com/nb/core/utils/ExcelUtil.java

@@ -2,13 +2,24 @@ package com.nb.core.utils;
 
 import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.io.FileUtil;
+import cn.hutool.core.io.IoUtil;
 import cn.hutool.core.util.IdUtil;
 import cn.hutool.extra.spring.SpringUtil;
 import com.alibaba.excel.EasyExcel;
+import com.alibaba.excel.ExcelWriter;
+import com.alibaba.excel.write.builder.ExcelWriterBuilder;
+import com.alibaba.excel.write.metadata.WriteSheet;
+import com.alibaba.excel.write.style.HorizontalCellStyleStrategy;
+import com.alibaba.excel.write.style.column.SimpleColumnWidthStyleStrategy;
+import com.alibaba.excel.write.style.row.SimpleRowHeightStyleStrategy;
 import com.nb.core.AppProperties;
 import com.nb.core.enums.BizEnum;
 import lombok.extern.slf4j.Slf4j;
 
+import javax.servlet.ServletOutputStream;
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
+import java.util.Collection;
 import java.util.List;
 
 /**
@@ -18,6 +29,8 @@ import java.util.List;
  */
 @Slf4j
 public class ExcelUtil {
+    private static HorizontalCellStyleStrategy horizontalCellStyleStrategy =
+            new CustomHorizontalCellStyleStrategy(StyleUtils.getHeadStyle(), StyleUtils.getContentStyle());
 
     public static String export(String filename, Class clazz, List list) {
         return export(filename, clazz, list, filename);
@@ -33,4 +46,23 @@ public class ExcelUtil {
         return filepath;
     }
 
+    public static void exportResponse(HttpServletResponse response, String filename, Class clazz, List list) throws IOException {
+        ServletOutputStream outputStream = response.getOutputStream();
+        response.setHeader("content-type", "application/octet-stream;charset=UTF-8");
+        response.setContentType("application/octet-stream;charset=UTF-8");
+        ExcelWriter write = EasyExcel.write(outputStream, clazz).build();
+        createSheet(write,0,"全部数据",clazz,list);
+        IoUtil.flush(outputStream);
+        write.finish();
+        IoUtil.close(outputStream);
+    }
+
+    private static <T> void createSheet(ExcelWriter excelWriter, int sheetNo, String sheetName, Class<T> aClass, Collection<T> data){
+        WriteSheet writeSheet = EasyExcel.writerSheet(sheetNo, sheetName)
+                .registerWriteHandler(horizontalCellStyleStrategy)
+                .registerWriteHandler(new SimpleColumnWidthStyleStrategy(13))
+                .registerWriteHandler(new SimpleRowHeightStyleStrategy((short) 20, (short) 20))
+                .head(aClass).build();
+        excelWriter.write(data,writeSheet);
+    }
 }

+ 114 - 0
nb-core/src/main/java/com/nb/core/utils/StyleUtils.java

@@ -0,0 +1,114 @@
+package com.nb.core.utils;
+
+import com.alibaba.excel.write.metadata.style.WriteCellStyle;
+import com.alibaba.excel.write.metadata.style.WriteFont;
+import org.apache.poi.ss.usermodel.*;
+
+/**
+ * @ClassName : StyleUtils
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年05月19日
+ */
+
+public class StyleUtils {
+    public static WriteCellStyle getNotNullHeadStyle(){
+        WriteCellStyle headStyle = getHeadStyle();
+        WriteFont writeFont = headStyle.getWriteFont();
+        writeFont.setColor(IndexedColors.RED.getIndex());
+        return headStyle;
+    }
+
+    public static WriteCellStyle getHeadStyle(){
+        // 头的策略
+        WriteCellStyle headWriteCellStyle = new WriteCellStyle();
+        // 背景颜色
+        headWriteCellStyle.setFillForegroundColor(IndexedColors.GREY_25_PERCENT.getIndex());
+        headWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
+        // 字体
+        WriteFont headWriteFont = new WriteFont();
+        //设置字体名字
+        headWriteFont.setFontName("宋体");
+//        //斜体
+//        headWriteFont.setItalic(true);
+        //设置字体大小
+        headWriteFont.setFontHeightInPoints((short)9);
+        //字体加粗
+        headWriteFont.setBold(true);
+
+        //在样式用应用设置的字体
+        headWriteCellStyle.setWriteFont(headWriteFont); ;
+
+        // 样式
+        //设置底边框
+        headWriteCellStyle.setBorderBottom(BorderStyle.THIN);
+        //设置底边框颜色
+        headWriteCellStyle.setBottomBorderColor((short) 0);
+        //设置左边框
+        headWriteCellStyle.setBorderLeft(BorderStyle.THIN);
+        //设置左边框颜色
+        headWriteCellStyle.setLeftBorderColor((short) 0);
+        //设置右边框
+        headWriteCellStyle.setBorderRight(BorderStyle.THIN);
+        //设置右边框颜色
+        headWriteCellStyle.setRightBorderColor((short) 0);
+        //设置顶边框
+        headWriteCellStyle.setBorderTop(BorderStyle.THIN);
+        //设置顶边框颜色
+        headWriteCellStyle.setTopBorderColor((short) 0);
+
+
+        //设置自动换行
+        headWriteCellStyle.setWrapped(true);
+
+        //设置水平对齐的样式为居中对齐
+        headWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);
+        //设置垂直对齐的样式为居中对齐
+        headWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);
+
+
+        //设置文本收缩至合适
+        headWriteCellStyle.setShrinkToFit(true);
+
+        return headWriteCellStyle;
+    }
+
+    public static WriteCellStyle getContentStyle(){
+        // 内容的策略
+        WriteCellStyle contentWriteCellStyle = new WriteCellStyle();
+
+        // 背景绿色
+        // 这里需要指定 FillPatternType 为FillPatternType.SOLID_FOREGROUND 不然无法显示背景颜色.头默认了 FillPatternType所以可以不指定
+        contentWriteCellStyle.setFillForegroundColor(IndexedColors.WHITE.getIndex());
+        contentWriteCellStyle.setFillPatternType(FillPatternType.SOLID_FOREGROUND);
+
+        // 设置字体
+        contentWriteCellStyle.setWriteFont(getDefaultFont());;
+
+        //设置样式;
+        contentWriteCellStyle.setBorderBottom(BorderStyle.THIN);//设置底边框;
+        contentWriteCellStyle.setBottomBorderColor((short) 0);//设置底边框颜色;
+        contentWriteCellStyle.setBorderLeft(BorderStyle.THIN);  //设置左边框;
+        contentWriteCellStyle.setLeftBorderColor((short) 0);//设置左边框颜色;
+        contentWriteCellStyle.setBorderRight(BorderStyle.THIN);//设置右边框;
+        contentWriteCellStyle.setRightBorderColor((short) 0);//设置右边框颜色;
+        contentWriteCellStyle.setBorderTop(BorderStyle.THIN);//设置顶边框;
+        contentWriteCellStyle.setTopBorderColor((short) 0); ///设置顶边框颜色;
+
+        contentWriteCellStyle.setHorizontalAlignment(HorizontalAlignment.CENTER);// 水平居中
+        contentWriteCellStyle.setVerticalAlignment(VerticalAlignment.CENTER);// 垂直居中
+        contentWriteCellStyle.setWrapped(true); //设置自动换行;
+
+        // contentWriteCellStyle.setShrinkToFit(true);//设置文本收缩至合适
+
+        return contentWriteCellStyle;
+    }
+
+    private static WriteFont getDefaultFont(){
+        // 设置字体
+        WriteFont contentWriteFont = new WriteFont();
+        contentWriteFont.setFontHeightInPoints((short) 9);//设置字体大小
+        contentWriteFont.setFontName("宋体"); //设置字体名字
+        return contentWriteFont;
+    }
+}

+ 19 - 36
nb-im/src/main/java/com/nb/im/controller/ImRoomController.java

@@ -8,6 +8,7 @@ import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.baomidou.mybatisplus.core.mapper.Mapper;
 import java.util.*;
+import java.util.concurrent.TimeUnit;
 import java.util.concurrent.atomic.AtomicLong;
 
 import com.nb.auth.utils.SecurityUtil;
@@ -23,13 +24,10 @@ import com.nb.im.entity.ImRoomEntity;
 import com.nb.im.room.ImRoomOperator;
 import com.nb.im.room.ImRoomOperatorManager;
 import com.nb.im.service.LocalImMsgService;
-import com.nb.im.service.dto.ImRoomDTO;
-import com.nb.im.service.dto.ImRoomDetailDTO;
+import com.nb.im.service.dto.*;
 import com.nb.im.enums.ImStatusEnum;
 import com.nb.im.enums.SponsorEnum;
 import com.nb.im.service.LocalImRoomService;
-import com.nb.im.service.dto.ImRoomQuery;
-import com.nb.im.service.dto.ImRoomResult;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
@@ -60,25 +58,24 @@ public class ImRoomController implements BaseSaveController<ImRoomEntity,String>
     }
 
 
-    @PostMapping("/doctor/no_page/{doctorId}")
+    @PostMapping("/doctor/no_page")
     @ApiOperation(value = "医生聊天室全部列表查询(最近一个月)",notes = "当一个病人存在多个聊天室时,进查询最近的一次,走非条件查询,条件查询请于安卓端自行实现")
-    public R<List<ImRoomResult>> recentList(@PathVariable("doctorId") String doctorId){
+    public R<List<ImRoomResult>> recentList(@RequestBody@Validated ImRoomDoctorQuery query){
         //仅查询最近一个月信息
-        List<ImRoomResult> result= imRoomService.getBaseMapper().queryDoctorRecentList(doctorId);
+        List<ImRoomResult> result= imRoomService.getBaseMapper().queryDoctorRecentList(query);
         return R.success(result);
     }
 
-
-    @ApiOperation(value = "医生聊天室置顶")
-    @PostMapping("/doctor/setTop/{roomId}")
-    public R<Boolean> setTop(@PathVariable("roomId") String roomId){
+    @PostMapping("/invert/top/{roomId}")
+    @ApiOperation(value = "反转医生聊天室置顶状态")
+    public R<Boolean> invertTop(@PathVariable("roomId") String roomId){
+        //仅查询最近一个月信息
         ImRoomEntity room = imRoomService.getById(roomId);
-        if(ObjectUtil.isNull(room)){
-            return R.success(true);
-        }else {
-            room.setDoctorTop(true);
+        if (ObjectUtil.isNotNull(room)) {
+            room.setDoctorTop(!Boolean.TRUE.equals(room.getDoctorTop()));
             return R.success(imRoomService.updateById(room));
         }
+        return R.success(true);
     }
 
     @ApiOperation(value = "查询并自动创建与病人看护人的聊天室",notes = "若存在,则返回聊天室信息,若不存在,则返回空")
@@ -94,6 +91,11 @@ public class ImRoomController implements BaseSaveController<ImRoomEntity,String>
                 .eq(ImRoomEntity::getDoctorId, source.getDoctorId())
                 .eq(ImRoomEntity::getPatientId, source.getPatientId())
                 .in(ImRoomEntity::getStatus,ImStatusEnum.WAITING,ImStatusEnum.SUCCESS)
+                .nested(r->
+                    r .gt(ImRoomEntity::getCreateTime,System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30))
+                            .or()
+                            .gt(ImRoomEntity::getSuccessTime,System.currentTimeMillis() - TimeUnit.DAYS.toMillis(30))
+                )
                 .orderByDesc(ImRoomEntity::getCreateTime)
                 .last("limit 1"));
         if(chatRoom==null&&source.isAutoCreate()){
@@ -122,7 +124,7 @@ public class ImRoomController implements BaseSaveController<ImRoomEntity,String>
         result.parallelStream().forEach(room->
                 room.setUnreadCount( roomOperatorManager.getRoomOperator(room)
                         .unReadCount(userId)
-        ));
+                ));
         return R.success(result);
     }
 
@@ -175,26 +177,7 @@ public class ImRoomController implements BaseSaveController<ImRoomEntity,String>
     @PostMapping("/read/unread")
     @ApiOperation("获取未读消息总数量")
     public R<Long> unReadCount(@RequestBody UnreadVo vo){
-        if (StrUtil.isEmpty(vo.getAssistId()) && StrUtil.isEmpty(vo.getDoctorId())) {
-            throw new CustomException("看护人id和医生id不能同时为空");
-        }
-        String userId=StrUtil.isEmpty(vo.getAssistId())?vo.getDoctorId():vo.getAssistId();
-        AtomicLong result=new AtomicLong(0);
-        List<ImRoomEntity> rooms = imRoomService.list(new QueryWrapper<ImRoomEntity>()
-                .lambda()
-                .select(ImRoomEntity::getId)
-                .eq(StrUtil.isNotEmpty(vo.getAssistId()), ImRoomEntity::getAssistId, vo.getAssistId())
-                .eq(StrUtil.isNotEmpty(vo.getDoctorId()), ImRoomEntity::getDoctorId, vo.getDoctorId())
-                .eq(ImRoomEntity::getStatus,ImStatusEnum.SUCCESS)
-        );
-        if(CollectionUtil.isNotEmpty(rooms)){
-            rooms.stream()
-                    .map(ImRoomEntity::getId).map(roomOperatorManager::getRoomOperator)
-                    .forEach(operator->{
-                        result.addAndGet(operator.unReadCount(userId));
-                    });
-        }
-        return R.success(result.get());
+        return R.success(msgService.unReadCount(vo));
     }
 
     @Override

+ 2 - 3
nb-im/src/main/java/com/nb/im/entity/ImRoomEntity.java

@@ -100,9 +100,8 @@ public class ImRoomEntity extends GenericEntity<String> {
     @ApiModelProperty("监控结束时间")
     private Date monitorFinishedTime;
 
-    @ApiModelProperty("医生的聊天室是否置顶")
+    @ApiModelProperty("医生置顶状态")
     private Boolean doctorTop;
-
     /**
      * 描述: 由看护人发起的咨询或一件疼痛呼叫
      * @author lifang
@@ -122,9 +121,9 @@ public class ImRoomEntity extends GenericEntity<String> {
         result.setDoctorId(source.getReceiverId());
         result.setSponsorType(SponsorEnum.assist);
         result.setTotalCount(1);
-        result.setDoctorTop(false);
         //等待医生确认
         result.setStatus(ImStatusEnum.WAITING);
+        result.setDoctorTop(false);
         //默认院内管理
         Map<String, Object> properties = source.getProperties();
         ClinicManageEnum manageType=ClinicManageEnum.hospital;

+ 13 - 6
nb-im/src/main/java/com/nb/im/mapper/ImRoomMapper.java

@@ -4,6 +4,7 @@ import com.baomidou.mybatisplus.core.mapper.BaseMapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.nb.im.entity.ImRoomEntity;
+import com.nb.im.service.dto.ImRoomDoctorQuery;
 import com.nb.im.service.dto.ImRoomQuery;
 import com.nb.im.service.dto.ImRoomResult;
 import org.apache.ibatis.annotations.Mapper;
@@ -28,12 +29,18 @@ public interface ImRoomMapper extends BaseMapper<ImRoomEntity> {
      */
     List<ImRoomResult> queryPageNoneMsgBlurry(@Param("query") ImRoomQuery query);
 
-    Long maxSort(@Param("roomId") String roomId);
-
     /**
-     * 查询医生的最近聊天室列表
-     * @param doctorId
-     * @return
+     * 描述: 分页查询(查询条件包含信息内容)
+     * @author lifang
+     * @date 2022/8/16 23:21
+     * @param page
+     * @param query
+     * @return IPage<ImRoomResult>
      */
-    List<ImRoomResult> queryDoctorRecentList(String doctorId);
+    IPage<ImRoomResult> queryPageMsgBlurry(Page<ImRoomResult> page, @Param("query") ImRoomQuery query);
+
+    Long maxSort(@Param("roomId") String roomId);
+
+    List<ImRoomResult> queryDoctorRecentList(@Param("query") ImRoomDoctorQuery query);
+
 }

+ 16 - 6
nb-im/src/main/java/com/nb/im/room/ImRoomOperator.java

@@ -47,13 +47,15 @@ public class ImRoomOperator {
         this.doctorId=doctorId;
         this.supplierMaxSort=supplierMaxSort;
 
+        msgKeyFilter.expire(Duration.of(40,ChronoUnit.DAYS));
         Arrays.asList(assitId,doctorId)
-                .forEach(userId-> unReadMap.computeIfAbsent(userId,k->redissonClient.getSet("im:unread:"+id+":"+userId)));
+                .forEach(this::getSet);
     }
 
     private synchronized void initMaxSort(){
         if(this.atomicLong==null){
             this.atomicLong = redissonClient.getAtomicLong("im:incr." + id);
+            atomicLong.expire(Duration.of(40,ChronoUnit.DAYS));
             atomicLong.getAsync()
                     .thenAcceptAsync(count->{
                         if(count==null){
@@ -72,7 +74,7 @@ public class ImRoomOperator {
      * @return void
      */
     public void addUnreadMsg(String userId, List<String> key){
-        unReadMap.computeIfAbsent(userId,k->redissonClient.getSet("im:unread:"+id+":"+userId)).addAll(key);
+        getSet(userId).addAll(key);
     }
 
 
@@ -84,7 +86,7 @@ public class ImRoomOperator {
      * @return void
      */
     public void readUnreadMsg(String userId, List<String> key){
-        unReadMap.computeIfAbsent(userId,k->redissonClient.getSet("im:unread:"+id+":"+userId)).removeAll(key);
+        getSet(userId).removeAll(key);
     }
 
     /**
@@ -95,7 +97,7 @@ public class ImRoomOperator {
      * @return void
      */
     public void readUnreadMsgAll(String userId){
-        unReadMap.computeIfAbsent(userId,k->redissonClient.getSet("im:unread:"+id+":"+userId)).delete();
+        getSet(userId).delete();
     }
     /**
      * 描述: 获取用户未读消息数量
@@ -105,7 +107,7 @@ public class ImRoomOperator {
      * @return long
      */
     public Integer unReadCount(String userId){
-        return  unReadMap.computeIfAbsent(userId,k->redissonClient.getSet("im:unread:"+id+":"+userId))
+        return  getSet(userId)
                 .size();
     }
 
@@ -118,7 +120,7 @@ public class ImRoomOperator {
      * @return Set<String>
      */
     public Set<String> allUnreadMsgKey(String userId){
-        return  unReadMap.computeIfAbsent(userId,k->redissonClient.getSet("im:unread:"+id+":"+userId)).readAll();
+        return  getSet(userId).readAll();
     }
 
 
@@ -173,4 +175,12 @@ public class ImRoomOperator {
     public void setDoctorOnline(boolean online){
         this.doctorOnline=online;
     }
+
+    private RSet<String> getSet(String userId){
+       return unReadMap.computeIfAbsent(userId, k -> {
+           RSet<String> result = redissonClient.getSet("im:unread:" + id + ":" + userId);
+           result.expire(Duration.of(40,ChronoUnit.DAYS));
+           return result;
+       });
+    }
 }

+ 3 - 2
nb-im/src/main/java/com/nb/im/room/ImRoomOperatorManager.java

@@ -2,6 +2,7 @@ package com.nb.im.room;
 
 import cn.hutool.core.date.DateUnit;
 import cn.hutool.core.date.DateUtil;
+import cn.hutool.core.util.ObjectUtil;
 import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.nb.im.entity.ImRoomEntity;
 import com.nb.im.entity.ImRoomUserEntity;
@@ -82,8 +83,8 @@ public class ImRoomOperatorManager {
                     return  new ImRoomOperator(roomId,
                             redissonClient,
                             _roomId->roomService.getMasSort(roomId),
-                            room.getAssistId(),
-                            room.getDoctorId()
+                            ObjectUtil.isNull(room)?null:room.getAssistId(),
+                            ObjectUtil.isNull(room)?null:room.getDoctorId()
 
                     );
                 }

+ 45 - 0
nb-im/src/main/java/com/nb/im/service/LocalImMsgService.java

@@ -1,20 +1,33 @@
 package com.nb.im.service;
 
+import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.spring.SpringUtil;
+import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
 import com.nb.app.msg.enums.MsgEnum;
 import com.nb.auth.enums.StpTypeEnum;
 import com.nb.common.crud.BaseService;
+import com.nb.core.exception.CustomException;
+import com.nb.im.controller.vo.UnreadVo;
 import com.nb.im.entity.ImMsgEntity;
+import com.nb.im.entity.ImRoomEntity;
 import com.nb.im.enums.ImMsgType;
+import com.nb.im.enums.ImStatusEnum;
 import com.nb.im.enums.SponsorEnum;
 import com.nb.im.event.ImMsgEvent;
 import com.nb.im.mapper.ImRoomMsgMapper;
+import com.nb.im.room.ImRoomOperatorManager;
 import com.nb.im.utils.ImUtils;
 import com.nb.im.ws.PubMsgInfo;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 
+import java.util.Date;
+import java.util.List;
+import java.util.concurrent.TimeUnit;
+import java.util.concurrent.atomic.AtomicLong;
+
 /**
  * @author lifang
  * @version 1.0.0
@@ -25,6 +38,13 @@ import org.springframework.stereotype.Service;
 @Service
 public class LocalImMsgService extends BaseService<ImRoomMsgMapper, ImMsgEntity,String> {
 
+    @Autowired
+    @Lazy
+    private LocalImRoomService imRoomService;
+
+    @Autowired
+    @Lazy
+    private ImRoomOperatorManager roomOperatorManager;
 
     @Override
     public void validateBeforeSave(ImMsgEntity entity) {
@@ -46,4 +66,29 @@ public class LocalImMsgService extends BaseService<ImRoomMsgMapper, ImMsgEntity,
     public void validateBeforeDelete(String id) {
 
     }
+
+    public Long unReadCount(UnreadVo vo){
+        if (StrUtil.isEmpty(vo.getAssistId()) && StrUtil.isEmpty(vo.getDoctorId())) {
+            throw new CustomException("看护人id和医生id不能同时为空");
+        }
+
+        String userId=StrUtil.isEmpty(vo.getAssistId())?vo.getDoctorId():vo.getAssistId();
+        AtomicLong result=new AtomicLong(0);
+        List<ImRoomEntity> rooms = imRoomService.list(new QueryWrapper<ImRoomEntity>()
+                .lambda()
+                .select(ImRoomEntity::getId)
+                .eq(StrUtil.isNotEmpty(vo.getAssistId()), ImRoomEntity::getAssistId, vo.getAssistId())
+                .eq(StrUtil.isNotEmpty(vo.getDoctorId()), ImRoomEntity::getDoctorId, vo.getDoctorId())
+                .eq(ImRoomEntity::getStatus, ImStatusEnum.SUCCESS)
+                .ge(ImRoomEntity::getSuccessTime,new Date(System.currentTimeMillis()- TimeUnit.DAYS.toMillis(30)))
+        );
+        if(CollectionUtil.isNotEmpty(rooms)){
+            rooms.stream()
+                    .map(ImRoomEntity::getId).map(roomOperatorManager::getRoomOperator)
+                    .forEach(operator->{
+                        result.addAndGet(operator.unReadCount(userId));
+                    });
+        }
+        return result.get();
+    }
 }

+ 42 - 0
nb-im/src/main/java/com/nb/im/service/dto/ImRoomDoctorQuery.java

@@ -0,0 +1,42 @@
+package com.nb.im.service.dto;
+
+import cn.hutool.core.date.DateUtil;
+import com.fasterxml.jackson.annotation.JsonIgnoreProperties;
+import io.swagger.annotations.ApiModel;
+import io.swagger.annotations.ApiModelProperty;
+import lombok.Data;
+
+import javax.validation.constraints.NotNull;
+import java.util.Date;
+import java.util.List;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName ImRoomQuery.java
+ * @Description TODO
+ * @createTime 2022年08月16日 22:46:00
+ */
+@Data
+@ApiModel("聊天室查询参数")
+public class ImRoomDoctorQuery {
+    @ApiModelProperty(value = "医生id",required = true)
+    @NotNull(message = "医生id不能为空")
+    private String doctorId;
+
+    @ApiModelProperty(value = "病人id")
+    private String patientId;
+
+    @ApiModelProperty(value = "管理位置",allowableValues = "0(院内管理) 1(居家管理)")
+    private Integer manageType;
+
+    @ApiModelProperty("病人信息(名称或住院号)")
+    private String patientInfo;
+
+//    @ApiModelProperty("聊天内容")
+//    private String record;
+
+    @ApiModelProperty(value = "成功建立连接时间,默认查询30天以内的聊天室列表",hidden = true)
+    @JsonIgnoreProperties
+    private Date successTime= DateUtil.offsetDay(new Date(),-30);
+}

+ 19 - 7
nb-im/src/main/java/com/nb/im/utils/ImUtils.java

@@ -2,6 +2,7 @@ package com.nb.im.utils;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.nb.common.websocket.PubResponse;
@@ -9,6 +10,7 @@ import com.nb.common.websocket.TopicMessage;
 import com.nb.common.websocket.WebSocketConstant;
 import com.nb.common.websocket.handler.TopicWrapper;
 import com.nb.common.websocket.msg.MessageResponse;
+import com.nb.im.controller.vo.UnreadVo;
 import com.nb.im.entity.ImMsgEntity;
 import com.nb.im.entity.ImRoomEntity;
 import com.nb.im.entity.ImRoomUserEntity;
@@ -26,6 +28,7 @@ import org.redisson.api.RedissonClient;
 import org.springframework.stereotype.Component;
 import org.tio.core.ChannelContext;
 import org.tio.core.Tio;
+import org.tio.core.utils.TioUtils;
 import org.tio.websocket.common.WsPacket;
 import org.tio.websocket.common.WsResponse;
 
@@ -112,9 +115,9 @@ public class ImUtils {
                 .publishAsync( TopicMessage.of(roomMsg,param,pubMsgInfo.getKey()))
                 .whenComplete((r,e)->{
                     if(e==null){
-                        log.info("发送聊天室消息{}成功",JSONUtil.toJsonStr(roomMsg));
+                        log.info("topic:{},发送聊天室消息{}成功",topic,JSONUtil.toJsonStr(roomMsg));
                     }else{
-                        log.error("发送聊天室消息{}失败,",JSONUtil.toJsonStr(roomMsg),e);
+                        log.error("topic:{},发送聊天室消息{}失败,",topic,JSONUtil.toJsonStr(roomMsg),e);
                     }
                     if(channelContext!=null){
                         Tio.send(channelContext, WsResponse.fromText(JSONUtil.toJsonStr(
@@ -127,14 +130,23 @@ public class ImUtils {
                     }
                 });
 
-        if(!pubMsgInfo.isTmp()){
-            String senderId = pubMsgInfo.getSenderId();
-            roomOperator.addUnreadMsg(senderId.equals(roomOperator.getAssitId())?roomOperator.getDoctorId():roomOperator.getAssitId(),Arrays.asList(pubMsgInfo.getKey()));
-            //消息发送给个人
-            String receiveId=senderId.equals(roomOperator.getAssitId())?roomOperator.getDoctorId():roomOperator.getAssitId();
+        String senderId = pubMsgInfo.getSenderId();
+        roomOperator.addUnreadMsg(senderId.equals(roomOperator.getAssitId())?roomOperator.getDoctorId():roomOperator.getAssitId(),Arrays.asList(pubMsgInfo.getKey()));
+        //消息发送给个人
+        String receiveId=senderId.equals(roomOperator.getAssitId())?roomOperator.getDoctorId():roomOperator.getAssitId();
 
+        if(!pubMsgInfo.isTmp()){
             pubTopicMap.computeIfAbsent( WebSocketConstant.getAllImByUserId(receiveId).getTopic(), k->redissonClient.getTopic(k))
                     .publishAsync( TopicMessage.of(roomMsg,param,pubMsgInfo.getKey()));
         }
+
+        UnreadVo unreadVo = new UnreadVo();
+        if(StrUtil.equals(pubMsgInfo.getSenderId(),roomOperator.getAssitId())){
+            unreadVo.setDoctorId(receiveId);
+        }else {
+            unreadVo.setAssistId(receiveId);
+        }
+        Long unReadCount = imRoomMsgService.unReadCount(unreadVo);
+        redissonClient.getTopic(WebSocketConstant.getImUnreadTopic(receiveId).getTopic()).publishAsync(TopicMessage.of(unReadCount,receiveId,pubMsgInfo.getKey()));
     }
 }

+ 31 - 0
nb-im/src/main/java/com/nb/im/ws/ImUnreadSubscribe.java

@@ -0,0 +1,31 @@
+package com.nb.im.ws;
+
+import com.nb.common.websocket.WebSocketConstant;
+import com.nb.common.websocket.handler.Subscribe;
+import org.springframework.stereotype.Component;
+import org.tio.core.ChannelContext;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName ImSubscribe.java
+ * @Description 即时通信未读消息主题
+ * @createTime 2022年08月16日 14:01:00
+ */
+@Component
+public class ImUnreadSubscribe extends Subscribe {
+    @Override
+    public String getId() {
+        return WebSocketConstant.IM_UNREAD;
+    }
+
+    @Override
+    public void close(ChannelContext channelContext) {
+
+    }
+
+    @Override
+    public boolean needParam() {
+        return true;
+    }
+}

+ 11 - 1
nb-im/src/main/java/com/nb/im/ws/PubMsgRequestHandler.java

@@ -2,6 +2,7 @@ package com.nb.im.ws;
 
 import cn.hutool.core.bean.BeanUtil;
 import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.fasterxml.jackson.core.JsonProcessingException;
@@ -66,7 +67,16 @@ public class PubMsgRequestHandler implements IMsgRequestHandler {
                     .allMatch(filter -> filter.doFilter(channelContext, message));
             if(result){
                 try {
-                    PubMsgInfo pubMsgInfo = objectMapper.readerFor(PubMsgInfo.class).readValue(String.valueOf(message.getPayload()));
+                    PubMsgInfo pubMsgInfo=null;
+                    String key=null;
+                    if (message.getPayload() instanceof JSONObject) {
+                        JSONObject obj= (JSONObject) message.getPayload();
+                        key=obj.getStr("key");
+                    }else {
+                        key=JSONUtil.parseObj(message.getPayload()).getStr("key");
+                    }
+                    pubMsgInfo= objectMapper.readerFor(PubMsgInfo.class).readValue(String.valueOf(message.getPayload()));
+                    pubMsgInfo.setKey(key);
                     pubMsgInfo.setOperationType(MsgEnum.NORMAL);
                     imUtils.send(param,topicWrapper,pubMsgInfo,channelContext,false);
                 }catch (Exception e){

+ 10 - 0
nb-im/src/main/java/com/nb/im/ws/filter/MsgFormatFilter.java

@@ -1,6 +1,8 @@
 package com.nb.im.ws.filter;
 
 import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.json.JSON;
+import cn.hutool.json.JSONObject;
 import cn.hutool.json.JSONUtil;
 import com.fasterxml.jackson.core.JsonProcessingException;
 import com.fasterxml.jackson.databind.ObjectMapper;
@@ -46,7 +48,15 @@ public class MsgFormatFilter implements PubMsgFilter {
             }
             PubMsgInfo pubMsgInfo =null;
             try {
+                String key=null;
+                if (source.getPayload() instanceof JSONObject) {
+                    JSONObject obj= (JSONObject) source.getPayload();
+                    key=obj.getStr("key");
+                }else {
+                    key=JSONUtil.parseObj(source.getPayload()).getStr("key");
+                }
                 pubMsgInfo = objectMapper.readerFor(PubMsgInfo.class).readValue(String.valueOf(source.getPayload()));
+                pubMsgInfo.setKey(key);
                 pubMsgInfo.validate();
             }catch (Exception e){
 //                Tio.send(channelContext,WsResponse.fromText(JSONUtil.toJsonStr(PubResponse.fail(pubMsgInfo!=null?pubMsgInfo.getKey():"",e.getMessage())),WsPacket.CHARSET_NAME));

+ 55 - 26
nb-im/src/main/resources/mapper/im/ImRoomMapper.xml

@@ -88,10 +88,19 @@
             ) as ru
             on ru.im_room_id=r.id
         </if>
-
+        <where>
+            <if test="query.successTime != null">
+                and (r.success_time &gt; #{query.successTime}
+                or r.create_time &gt; #{query.successTime})
+            </if>
+        </where>
         order by last_msg_time desc
     </select>
 
+    <select id="queryPageMsgBlurry" resultMap="imRoomResult">
+        --         todo
+    </select>
+
 
     <select id="maxSort" resultType="java.lang.Long">
         SELECT
@@ -106,32 +115,52 @@
 
     <select id="queryDoctorRecentList" resultMap="imRoomResult">
         select
-        r.id as id,
-        r.patient_code as patient_code,
-        r.patient_age as patient_age,
-        r.patient_name_ps as patient_name_ps,
-        r.patient_name as patient_name,
-        r.patient_sex as patient_sex,
-        r.manage_type as manage_type,
-        r.`status` as status,
-        r.last_msg_time as last_msg_time,
-        r.sponsor_type as sponsor_type,
-        r.total_count as total_count,
-        r.create_time as create_time,
-        r.update_time as update_time,
-        r.doctor_avatar as doctor_avatar,
-        r.assist_avatar as assist_avatar,
-        r.assist_id as assist_id,
-        r.patient_id as patient_id,
-        rm.payload as payload,
-        rm.msg_type as msg_type,
-        rm.operation_type as operation_type
+            r.id as id,
+            r.patient_code as patient_code,
+            r.patient_age as patient_age,
+            r.patient_name_ps as patient_name_ps,
+            r.patient_name as patient_name,
+            r.patient_sex as patient_sex,
+            r.manage_type as manage_type,
+            r.`status` as status,
+            r.last_msg_time as last_msg_time,
+            r.sponsor_type as sponsor_type,
+            r.total_count as total_count,
+            r.create_time as create_time,
+            r.update_time as update_time,
+            r.doctor_avatar as doctor_avatar,
+            r.assist_avatar as assist_avatar,
+            r.assist_id as assist_id,
+            r.patient_id as patient_id,
+            rm.payload as payload,
+            rm.msg_type as msg_type,
+            rm.operation_type as operation_type
         from
-            im_room as r
-        join (select MAX(id) as id,doctor_id,patient_id,assist_id from im_room group by doctor_id,patient_id,assist_id) as ir2 on r.id=ir2.id
-        left join (select * from im_msg) as rm
-        on rm.id=r.last_msg_id
-        where r.doctor_id=#{doctorId}
+        (select * from im_room
+        <where>
+            <if test="query.doctorId != null">
+                and  doctor_id=#{query.doctorId}
+            </if>
+            <if test="query.patientInfo != null">
+                and (patient_code like concat('%',#{query.patientInfo},'%') or patient_name like concat('%',#{query.patientInfo},'%') )
+            </if>
+            <if test="query.manageType != null">
+                and manage_type=#{query.manageType}
+            </if>
+            <if test="query.patientId != null">
+                and patient_id=#{query.patientId}
+            </if>
+        </where>
+        ) as r
+                join (select MAX(id) as id,doctor_id,patient_id,assist_id from im_room group by doctor_id,patient_id,assist_id) as ir2 on r.id=ir2.id
+                left join (select * from im_msg) as rm
+                          on rm.id=r.last_msg_id
+        <where>
+            <if test="query.successTime != null">
+                and (r.success_time &gt; #{query.successTime}
+                or r.create_time &gt; #{query.successTime})
+            </if>
+        </where>
         order by doctor_top desc , case when (status =1) then  0 else 1  end desc ,create_time desc
     </select>
 </mapper>

+ 4 - 0
nb-service-api/app-assistant-api/src/main/java/com/nb/app/assistant/api/bean/HandleBindResult.java

@@ -1,5 +1,6 @@
 package com.nb.app.assistant.api.bean;
 
+import com.nb.web.api.enums.ClinicManageEnum;
 import io.swagger.annotations.ApiModel;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
@@ -32,6 +33,9 @@ public class HandleBindResult {
     @NotNull(message = "是否同意申请结果不能为空")
     private Boolean agree;
 
+    @ApiModelProperty(value = "看护位置",required = true,example = "0(院内管理)1(居家管理)")
+    private ClinicManageEnum manage;
+
     @ApiModelProperty("拒绝申请结果")
     private String refuseReason;
 

+ 2 - 1
nb-service-api/web-service-api/src/main/java/com/nb/web/api/enums/DeviceTypeEnum.java

@@ -20,7 +20,8 @@ public enum DeviceTypeEnum implements IEnum<Integer> {
     continuous(1,"持续型"),
     intelligent(2,"智能型"),
     pulse(3,"脉冲型"),
-    other(4,"其他");
+    other(4,"其他"),
+    chemotherapy(5,"化疗泵"),;
 
     @Getter
     @ApiModelProperty("设备编码")

+ 2 - 1
nb-service-api/web-service-api/src/main/java/com/nb/web/api/enums/HospitalLogEnum.java

@@ -22,7 +22,8 @@ public enum HospitalLogEnum  implements IEnum<Integer> {
     ALI_STATUS(2,"阿里云-设备上下线"),
     ALI_LIFECYCLE(3,"阿里云-设备生命周期"),
     ALI_DATA_UPLOAD(4,"阿里云-设备数据上传"),
-    ALI_DEL(5,"阿里云-设备删除"),;
+    ALI_DEL(5,"阿里云-设备删除"),
+    IOT_INTERFACE(6,"IOT-接口传输");
 
     private Integer value;
     @Getter

+ 5 - 1
nb-service/app-assistant/src/main/java/com/nb/app/assistant/service/LocalAssistantUserBindService.java

@@ -55,6 +55,7 @@ import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Component;
 import org.springframework.transaction.annotation.Transactional;
 
+import javax.validation.constraints.NotNull;
 import java.util.Date;
 import java.util.List;
 import java.util.Optional;
@@ -340,7 +341,10 @@ public class LocalAssistantUserBindService extends BaseService<AssistantUserBind
                     DelayMessageProperties.of(TimeUnit.HOURS, 24));
             delayMessageManager.add(delayMessage);
         }
-
+         String clinicId = patientDetail.getClinic().getId();
+        if(ObjectUtil.isNotNull(source.getManage())){
+            patientClient.setManageType(clinicId,source.getManage());
+        }
         //查询是否存在默认看护
         return result;
     }

+ 95 - 0
nb-service/iot-service/src/main/java/com/nb/aliyun/controller/IotController.java

@@ -0,0 +1,95 @@
+package com.nb.aliyun.controller;
+
+
+import cn.dev33.satoken.annotation.SaIgnore;
+import cn.hutool.core.util.ObjectUtil;
+import cn.hutool.core.util.StrUtil;
+import cn.hutool.json.JSONUtil;
+import com.baomidou.mybatisplus.core.toolkit.IdWorker;
+import com.nb.core.exception.CustomException;
+import com.nb.core.result.R;
+import com.nb.core.utils.ExceptionUtil;
+import com.nb.web.api.entity.BusDeviceEntity;
+import com.nb.web.api.entity.BusHospitalLogEntity;
+import com.nb.web.api.entity.common.BusDeviceRunningEntity;
+import com.nb.web.api.enums.DeviceStatusEnum2;
+import com.nb.web.api.enums.HospitalLogEnum;
+import com.nb.web.api.feign.IDeviceClient;
+import com.nb.web.api.feign.IHospitalLogClient;
+import com.nb.web.api.feign.IIotMsgHandler;
+import io.netty.channel.DefaultEventLoopGroup;
+import io.swagger.annotations.Api;
+import lombok.AllArgsConstructor;
+import org.springframework.validation.annotation.Validated;
+import org.springframework.web.bind.annotation.PostMapping;
+import org.springframework.web.bind.annotation.RequestBody;
+import org.springframework.web.bind.annotation.RequestMapping;
+import org.springframework.web.bind.annotation.RestController;
+
+import java.util.concurrent.ExecutorService;
+
+/**
+ * @ClassName : IotController
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年10月17日
+ */
+@RestController
+@RequestMapping("/iot")
+@Api(description="iot数据处理")
+@AllArgsConstructor
+public class IotController {
+
+    private final IIotMsgHandler iotMsgHandler;
+    private final IHospitalLogClient hospitalLogService;
+    private final IDeviceClient deviceService;
+    private final ExecutorService executorService=new DefaultEventLoopGroup();
+    /**
+     * 分配菜单
+     */
+    @PostMapping("/data/save")
+    @SaIgnore
+    public R assignMenu(@Validated @RequestBody BusDeviceRunningEntity source) {
+        if(StrUtil.isBlank(source.getDeviceId())){
+            throw new CustomException("[deviceId]设备id不能为空");
+        }
+        if(StrUtil.isBlank(source.getClassification())){
+            throw new CustomException("[classification]分类标识号不能为空");
+        }
+        if(ObjectUtil.isNull(source.getDataNumber())){
+            throw new CustomException("[dataNumber]数据编号不能为空");
+        }
+        if(ObjectUtil.isNull(source.getType())){
+            throw new CustomException("[type]设备类型不能为空");
+        }
+        if(ObjectUtil.isNull(source.getRunState())){
+            throw new CustomException("[runState]设备运行状态不能为空");
+        }
+        executorService.execute(()->{
+            BusDeviceEntity device = new BusDeviceEntity();
+            device.setStatus(DeviceStatusEnum2.ONLINE);
+            device.setDeviceId(source.getDeviceId());
+            device.setStatus(DeviceStatusEnum2.ONLINE);
+            device.setEnable(true);
+            deviceService.saveDevice(device);
+            BusHospitalLogEntity hospitalLog = new BusHospitalLogEntity();
+            hospitalLog.setIdentityCode(source.getDeviceId());
+            hospitalLog.setInput(JSONUtil.toJsonStr(source));
+            hospitalLog.setType(HospitalLogEnum.IOT_INTERFACE);
+            hospitalLog.setTenantId(device.getTenantId());
+            long startTime = System.currentTimeMillis();
+            try {
+                iotMsgHandler.sync(source,source.getDeviceId());
+                hospitalLog.setSuccess(true);
+            }catch (Exception e) {
+                hospitalLog.setSuccess(false);
+                hospitalLog.setMessage(ExceptionUtil.getExceptionMsg(e));
+            }
+            long entTime = System.currentTimeMillis();
+            hospitalLog.setUseTime(entTime - startTime);
+            hospitalLog.setId(IdWorker.getIdStr());
+            hospitalLogService.save(hospitalLog);
+        });
+        return R.success();
+    }
+}

+ 30 - 0
nb-service/iot-service/src/main/java/com/nb/aliyun/doc/IotDocConfig.java

@@ -0,0 +1,30 @@
+package com.nb.aliyun.doc;
+
+import org.springframework.context.annotation.Bean;
+import org.springframework.stereotype.Component;
+import springfox.documentation.builders.PathSelectors;
+import springfox.documentation.builders.RequestHandlerSelectors;
+import springfox.documentation.spi.DocumentationType;
+import springfox.documentation.spring.web.plugins.Docket;
+
+/**
+ * @author lifang
+ * @version 1.0.0
+ * @ClassName SwaggerDocConfig.java
+ * @Description
+ * @createTime 2022年08月01日 22:01:00
+ */
+@Component
+public class IotDocConfig {
+
+    @Bean
+    public Docket iotDoc(){
+        return new Docket(DocumentationType.SWAGGER_2)
+                .select()
+                .apis(RequestHandlerSelectors.basePackage("com.nb.aliyun.controller"))
+                .paths(PathSelectors.any())
+                .build()
+                .groupName("Iot数据传输");
+    }
+
+}

+ 2 - 2
nb-service/web-service/src/main/java/com/nb/web/service/bus/controller/BusDeviceController.java

@@ -46,11 +46,11 @@ public class BusDeviceController implements
     @PostMapping("/shift/bind")
     @SaCheckPermission("device:info:shift")
     @ApiOperation(value = "设备换绑",notes = "权限【device:info:shift】")
-    public R shift(@RequestBody DeviceBindVo vo){
+    public R shift(@RequestBody DeviceBindVo vo, @RequestAttribute("tenantId")@ApiParam(hidden = true) String tenantId){
         if(StrUtil.isEmpty(vo.getBindTenantId())|| CollectionUtil.isEmpty(vo.getDeviceIds())){
             throw new CustomException("选择的医院、设备不能为空");
         }
-        deviceService.shift(vo.getDeviceIds(),vo.getBindTenantId());
+        deviceService.shift(vo.getDeviceIds(),vo.getBindTenantId(),tenantId);
         return R.success(DeviceAlarmEnum.values());
     }
 

+ 14 - 1
nb-service/web-service/src/main/java/com/nb/web/service/bus/controller/BusEvaluationController.java

@@ -1,9 +1,11 @@
 package com.nb.web.service.bus.controller;
 
 import cn.dev33.satoken.annotation.SaCheckPermission;
+import cn.hutool.core.bean.BeanUtil;
 import com.baomidou.mybatisplus.core.mapper.Mapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.nb.core.exception.CustomException;
+import com.nb.core.utils.ExcelUtil;
 import com.nb.web.api.entity.BusEvaluationEntity;
 import com.nb.web.service.bus.service.LocalBusEvaluationService;
 import com.nb.web.api.feign.query.EvalQuery;
@@ -12,8 +14,8 @@ import com.nb.common.crud.controller.BaseCrudController;
 import com.nb.core.result.R;
 import com.nb.web.service.bus.service.LocalBusInfusionHistoryService;
 import com.nb.web.service.bus.service.dto.CombineEvalResult;
+import com.nb.web.service.bus.service.dto.CombineEvalResultExcelDTO;
 import com.nb.web.service.bus.service.dto.CombineQuery;
-import com.nb.web.service.bus.service.dto.CombineResult;
 import io.swagger.annotations.Api;
 import io.swagger.annotations.ApiOperation;
 import lombok.AllArgsConstructor;
@@ -23,6 +25,8 @@ import org.springframework.web.bind.annotation.RequestBody;
 import org.springframework.web.bind.annotation.RequestMapping;
 import org.springframework.web.bind.annotation.RestController;
 
+import javax.servlet.http.HttpServletResponse;
+import java.io.IOException;
 import java.util.Arrays;
 import java.util.List;
 
@@ -67,6 +71,15 @@ public class BusEvaluationController extends BaseCrudController<BusEvaluationEnt
         return R.success(infusionHistoryService.pageStatsEval(query,query.getPage()));
     }
 
+    @PostMapping("/combine/export")
+    @SaCheckPermission({"*:eval:query"})
+    @ApiOperation(value = "导出(此查询中包括了所属的临床信息)",notes = "权限【*:eval:query】")
+    public void export(@RequestBody CombineQuery query, HttpServletResponse response) throws IOException {
+        List<CombineEvalResult> combineEvalResults = infusionHistoryService.listStatsEval(query);
+        List<CombineEvalResultExcelDTO> exportResult = BeanUtil.copyToList(combineEvalResults, CombineEvalResultExcelDTO.class);
+        ExcelUtil.exportResponse(response,"评价信息",CombineEvalResultExcelDTO.class,exportResult);
+    }
+
     /**
      * 权限控制前缀
      * @return

+ 39 - 22
nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/his/HisScriptSession.java

@@ -1,6 +1,7 @@
 package com.nb.web.service.bus.hospital.his;
 
 import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.date.DateUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.extra.spring.SpringUtil;
@@ -12,18 +13,16 @@ import com.baomidou.mybatisplus.core.toolkit.IdWorker;
 import com.google.common.cache.Cache;
 import com.google.common.cache.CacheBuilder;
 import com.nb.web.api.entity.BusClinicEntity;
+import com.nb.web.api.entity.BusInfusionHistoryEntity;
 import com.nb.web.service.bus.entity.*;
 import com.nb.web.service.bus.listener.event.bean.HisEvent;
 import com.nb.web.service.bus.hospital.script.DefaultParse;
-import com.nb.web.service.bus.service.LocalBusHospitalLogService;
-import com.nb.web.service.bus.service.LocalBusPatientService;
+import com.nb.web.service.bus.service.*;
 import com.nb.web.service.bus.service.constant.LocalBusConDoctorService;
 import com.nb.web.service.bus.service.constant.LocalBusConMixService;
 import com.nb.core.exception.ExecuteResult;
 import com.nb.web.service.bus.hospital.script.ScriptManager;
 import com.nb.web.service.bus.hospital.script.ScriptParse;
-import com.nb.web.service.bus.service.LocalBusClinicService;
-import com.nb.web.service.bus.service.LocalBusHospitalService;
 import com.nb.core.cache.ConfigStorage;
 import com.nb.core.cache.manager.ConfigStorageManager;
 import com.nb.core.Value;
@@ -32,6 +31,7 @@ import com.nb.core.exception.ScriptException;
 import com.nb.core.result.R;
 import com.nb.core.utils.ExceptionUtil;
 import com.nb.web.api.entity.BusHospitalLogEntity;
+import com.nb.web.service.bus.utils.WsPublishUtils;
 import lombok.Getter;
 import lombok.extern.slf4j.Slf4j;
 import org.springframework.util.Assert;
@@ -43,6 +43,7 @@ import org.tio.websocket.common.WsResponse;
 
 import java.util.*;
 import java.util.concurrent.*;
+import java.util.stream.Collectors;
 
 /**
  * @author lifang
@@ -64,6 +65,7 @@ public class HisScriptSession {
     private LocalBusHospitalLogService hospitalLogService;
     private LocalBusConMixService conMixService;
     private LocalBusConDoctorService conDoctorService;
+    private WsPublishUtils wsPublishUtils;
     //缓存发给医院的请求,对相同的请求进行批量处理
     private Cache<String, HisRequest> hisRequestMap = CacheBuilder.newBuilder()
             .expireAfterWrite(30, TimeUnit.SECONDS)
@@ -80,6 +82,7 @@ public class HisScriptSession {
         this.patientService=SpringUtil.getBean(LocalBusPatientService.class);
         this.conMixService=SpringUtil.getBean(LocalBusConMixService.class);
         this.conDoctorService=SpringUtil.getBean(LocalBusConDoctorService.class);
+        this.wsPublishUtils=SpringUtil.getBean(WsPublishUtils.class);
         init(configStorageManager,hospitalId);
     }
 
@@ -277,28 +280,42 @@ public class HisScriptSession {
             List<BusClinicEntity> publish=new ArrayList<>();
             //对数据去重, name+startTime进行唯一值判定
             Map<String, BusClinicEntity> distinct = new HashMap<>();
-            sources.forEach(source->{
-                String key=source.getSurgeryName()+source.getStartTime();
-                distinct.computeIfAbsent(key,k->{
+            for (BusClinicEntity source : sources) {
+                if(ObjectUtil.isNull(source.getStartTime())){
+                    continue;
+                }
+                String key=
+                        StrUtil.isNullOrUndefined(source.getSurgeryName())?"":source.getSurgeryName()+
+                        DateUtil.formatDateTime(source.getStartTime());
+                if(!distinct.containsKey(key)){
+                    distinct.put(key,source);
                     publish.add(source);
-                    return source;
-                });
-            });
-            publish.sort(Comparator.comparing(
-                    BusClinicEntity::getStartTime));
-            publish.forEach(source->{
-                source.setTenantId(hospitalId);
-                if(patient!=null){
-                    source.setPatientId(patient.getId());
-                    source.setMonitorType(true);
-                }else {
-                    source.setMonitorType(false);
                 }
-            });
+            }
+            publish=publish.stream().sorted(Comparator.comparing(
+                    BusClinicEntity::getStartTime))
+                    .peek(source->{
+                        source.setTenantId(hospitalId);
+                        if(patient!=null){
+                            source.setPatientId(patient.getId());
+                            source.setMonitorType(true);
+                        }else {
+                            source.setMonitorType(false);
+                        }
+                    })
+                    .collect(Collectors.toList());
 
             if(patient!=null){
-                //病号不存在,即认定为无泵输注信息
-                SpringUtil.publishEvent(new HisEvent(this,publish,hospitalId,patientCode));
+                if(CollectionUtil.isEmpty(publish)){
+                    BusClinicEntity first = CollectionUtil.getFirst(sources);
+                    publish.add(first);
+                    first.setId(patient.getClinicId());
+                    clinicService.updateById(first);
+                    wsPublishUtils.publishPatientMonitor(patient.getId(),patient.getTenantId());
+                }else {
+                    //病号不存在,即认定为无泵输注信息
+                    SpringUtil.publishEvent(new HisEvent(this,publish,hospitalId,patientCode));
+                }
             }
             return publish.get(publish.size()-1);
         }

+ 4 - 3
nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/his/strategy/onlynew/DefaultHisNewStrategyHandler.java

@@ -1,6 +1,7 @@
 package com.nb.web.service.bus.hospital.his.strategy.onlynew;
 
 import cn.hutool.core.collection.CollectionUtil;
+import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.json.JSONUtil;
 import com.nb.web.api.entity.BusClinicEntity;
 import com.nb.web.service.bus.service.LocalBusClinicService;
@@ -31,7 +32,7 @@ public class DefaultHisNewStrategyHandler implements HisNewStrategyHandler {
 
     @Override
     public String getDescription() {
-        return "拉取his只获取最新的一病人数据,对其进行处理";
+        return "拉取his只获取最新的一病人数据,对其进行处理";
     }
 
     @Override
@@ -40,7 +41,7 @@ public class DefaultHisNewStrategyHandler implements HisNewStrategyHandler {
         if(CollectionUtil.isEmpty(source)){
             return;
         }
-        log.info("拉取his只获取最新的一病人数据,对其进行处理,拉取手术信息{},数据库数据{}", JSONUtil.toJsonStr(source),JSONUtil.toJsonStr(target));
+        log.info("拉取his只获取最新的一病人数据,对其进行处理,拉取手术信息{},数据库数据{}", JSONUtil.toJsonStr(source),JSONUtil.toJsonStr(target));
         //是否插入新的数据
         boolean insert=true;
         //是否为设置为当前的临床数据
@@ -56,7 +57,7 @@ public class DefaultHisNewStrategyHandler implements HisNewStrategyHandler {
                 //临床数据未发生变化
                 clinicService.compareFromHis(clinic,newClinic);
                 insert=false;
-            }else if(clinic.getStartTime().after(newClinic.getStartTime())){
+            }else if(ObjectUtil.isNotNull(newClinic.getStartTime())||clinic.getStartTime().after(newClinic.getStartTime())){
                 isCurrent=true;
             }else {
                 //旧的数据丢掉

+ 2 - 2
nb-service/web-service/src/main/java/com/nb/web/service/bus/hospital/his/strategy/part/DefaultHisPartStrategyHandler.java

@@ -4,8 +4,8 @@ import cn.hutool.core.collection.CollUtil;
 import cn.hutool.json.JSONUtil;
 import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.nb.web.api.entity.BusClinicEntity;
+import com.nb.web.service.bus.hospital.his.strategy.HisStrategyHandler;
 import com.nb.web.service.bus.hospital.his.strategy.all.HisAllStrategyHandler;
-import com.nb.web.service.bus.hospital.his.strategy.onlynew.HisNewStrategyHandler;
 import com.nb.web.service.bus.service.LocalBusClinicService;
 import lombok.AllArgsConstructor;
 import lombok.extern.slf4j.Slf4j;
@@ -25,7 +25,7 @@ import java.util.List;
 @Component
 @Slf4j
 @AllArgsConstructor
-public class DefaultHisPartStrategyHandler implements HisNewStrategyHandler {
+public class DefaultHisPartStrategyHandler implements HisStrategyHandler {
     private final LocalBusClinicService clinicService;
     private final List<HisAllStrategyHandler> allStrategyHandlers;
     @Override

+ 7 - 4
nb-service/web-service/src/main/java/com/nb/web/service/bus/listener/DeviceInfoListener.java

@@ -1,5 +1,6 @@
 package com.nb.web.service.bus.listener;
 
+import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.comparator.CompareUtil;
 import cn.hutool.core.map.MapUtil;
 import cn.hutool.core.util.ObjectUtil;
@@ -18,6 +19,7 @@ import com.nb.web.api.enums.DeviceTypeEnum;
 import com.nb.web.api.enums.FlowStatusEnum;
 import com.nb.web.api.enums.PatientAlarmEnum;
 import com.nb.web.service.bus.hospital.HospitalManagerRegister;
+import com.nb.web.service.bus.registry.constant.DeviceKeyConstant;
 import com.nb.web.service.bus.registry.device.DeviceOperator;
 import com.nb.web.service.bus.registry.device.DeviceRegistry;
 import com.nb.web.service.bus.registry.patient.PatientOperator;
@@ -496,12 +498,10 @@ public class DeviceInfoListener implements IIotMsgHandler {
             device.setInfusionModifyId(modify.getId());
         }
         //判断临床是否已结束,若临床已结束,则采用当前输注作为主输注开启临床
+        BusPatientEntity patient = patientService.getById(device.getPatientId());
         if(clinicFinished){
             clinicService.resetClinic(device.getClinicId());
-            patientService.update(new UpdateWrapper<BusPatientEntity>()
-                    .lambda()
-                    .eq(BusPatientEntity::getId,device.getPatientId())
-                    .set(BusPatientEntity::getInfusionId,device.getInfusionId()));
+            patient.setInfusionId(device.getInfusionId());
             PatientOperator patientOperator = patientRegistry.getOperator(device.getTenantId(), device.getPatientCode());
             device.setMaster(true);
             device.setResetClinic(true);
@@ -509,7 +509,10 @@ public class DeviceInfoListener implements IIotMsgHandler {
                 patientOperator.setBindDeviceId(device.getDeviceId());
                 return null;
             });
+        } else if (StrUtil.isEmpty(patient.getInfusionId())) {
+            patient.setInfusionId(device.getInfusionId());
         }
+        patientService.updateById(patient);
     }
 
 

+ 9 - 0
nb-service/web-service/src/main/java/com/nb/web/service/bus/mapper/BusInfusionHistoryMapper.java

@@ -84,4 +84,13 @@ public interface BusInfusionHistoryMapper extends BaseMapper<BusInfusionHistoryE
      * @return List<CombineAlarmResult>
      */
     IPage<CombineEvalResult> pageStatsEval(@Param("query") CombineQuery query, Page<CombineResult> page);
+
+    /**
+     * 描述: 联合查询出输注评分信息-统计使用
+     * @author lifang
+     * @date 2022/6/7 10:03
+     * @param query
+     * @return List<CombineAlarmResult>
+     */
+    List<CombineEvalResult> listStatsEval(@Param("query") CombineQuery query);
 }

+ 5 - 0
nb-service/web-service/src/main/java/com/nb/web/service/bus/registry/constant/DeviceKeyConstant.java

@@ -38,4 +38,9 @@ public class DeviceKeyConstant {
     public static final String NON_INFUSION_TIME="non_infusion_time";
 
     public static final String IS_FLOW_LIMIT="is_flow_limit";
+
+    /**
+     * 当设备切换医院时,将设备的缓存输注标识更换
+     */
+    public static final String DEFAULT_CLASSIFY="-1";
 }

+ 62 - 3
nb-service/web-service/src/main/java/com/nb/web/service/bus/service/LocalBusDeviceService.java

@@ -1,6 +1,7 @@
 package com.nb.web.service.bus.service;
 
 import cn.hutool.core.collection.CollUtil;
+import cn.hutool.core.collection.CollectionUtil;
 import cn.hutool.core.util.ObjectUtil;
 import cn.hutool.core.util.StrUtil;
 import cn.hutool.json.JSONUtil;
@@ -12,25 +13,34 @@ import com.baomidou.mybatisplus.core.conditions.update.UpdateWrapper;
 import com.baomidou.mybatisplus.core.metadata.IPage;
 import com.baomidou.mybatisplus.extension.plugins.pagination.Page;
 import com.nb.aliyun.api.service.AliyunIotSdk;
+import com.nb.core.Value;
+import com.nb.web.api.entity.BusClinicEntity;
+import com.nb.web.api.entity.BusInfusionHistoryEntity;
 import com.nb.web.api.feign.IDeviceClient;
+import com.nb.web.api.feign.query.PatientMonitorQuery;
+import com.nb.web.api.feign.result.PatientMonitorResult;
 import com.nb.web.api.utils.Constants;
 import com.nb.web.api.bean.AliIotConfig;
+import com.nb.web.service.bus.entity.BusPatientEntity;
 import com.nb.web.service.bus.registry.constant.DeviceKeyConstant;
 import com.nb.web.service.bus.registry.device.DeviceRegistry;
 import com.nb.web.api.entity.BusDeviceEntity;
 import com.nb.web.service.bus.mapper.BusDeviceMapper;
 import com.nb.web.service.bus.registry.device.DeviceOperator;
+import com.nb.web.service.bus.registry.patient.PatientRegistry;
 import com.nb.web.service.bus.service.dto.DeviceQuery;
 import com.nb.web.service.bus.service.dto.DeviceResult;
 import com.nb.common.crud.BaseService;
 import com.nb.core.exception.CustomException;
 import com.nb.core.utils.ExceptionUtil;
 import lombok.extern.slf4j.Slf4j;
+import org.redisson.api.RMapCache;
 import org.springframework.beans.factory.annotation.Autowired;
 import org.springframework.context.annotation.Lazy;
 import org.springframework.stereotype.Service;
 import org.springframework.transaction.annotation.Transactional;
 
+import javax.security.auth.login.LoginContext;
 import java.util.*;
 import java.util.concurrent.CompletableFuture;
 import java.util.concurrent.atomic.AtomicReference;
@@ -51,6 +61,17 @@ public class LocalBusDeviceService extends BaseService<BusDeviceMapper, BusDevic
     @Lazy
     private DeviceRegistry deviceRegistry;
 
+    @Autowired
+    @Lazy
+    private LocalBusPatientService patientService;
+
+    @Autowired
+    @Lazy
+    private LocalBusInfusionHistoryService infusionHistoryService;
+
+    @Autowired
+    @Lazy
+    private LocalBusClinicService clinicService;
     @Autowired
     private AliyunIotSdk aliyunIotSdk;
 
@@ -367,19 +388,57 @@ public class LocalBusDeviceService extends BaseService<BusDeviceMapper, BusDevic
      * @return void
      */
     @Transactional(rollbackFor = Exception.class)
-    public void shift(List<String> deviceIds, String afterTenantId) {
+    public void shift(List<String> deviceIds, String afterTenantId,String originTenantId) {
         List<DeviceOperator> operators = deviceIds.stream()
                 .map(deviceRegistry::getOperator)
                 .collect(Collectors.toList());
-        if (CollUtil.isEmpty(operators)) {
+        if (CollUtil.isEmpty(operators)||StrUtil.equalsIgnoreCase(originTenantId,afterTenantId)) {
             return;
         }
         this.update(new UpdateWrapper<BusDeviceEntity>().lambda()
                 .in(BusDeviceEntity::getDeviceId,deviceIds)
                 .set(BusDeviceEntity::getTenantId,afterTenantId));
+        //对进行中的输注进行换绑,其他医院切换到主医院不进行输注换绑
         operators
                 .stream()
                 .filter(Objects::nonNull)
-                .forEach(deviceOperator -> deviceOperator.getCache().remove(DeviceKeyConstant.TENANT_ID));
+                .forEach(deviceOperator -> {
+                    deviceOperator.getCache().clear();
+                });
+
+        List<PatientMonitorResult> results = patientService.selectAll(new PatientMonitorQuery());
+        if(CollectionUtil.isEmpty(results)){
+            return;
+        }
+        Map<String, PatientMonitorResult> deviceIdMap = results
+                .stream()
+                .collect(Collectors.groupingBy(PatientMonitorResult::getDeviceId, Collectors.collectingAndThen(Collectors.toList(), CollectionUtil::getFirst)));
+        //输注信息需要变化的设备
+        Collection<String> changeDeviceIds = CollectionUtil.intersection(deviceIdMap.keySet(), deviceIds);
+        if(CollectionUtil.isEmpty(changeDeviceIds)){
+            return;
+        }
+
+        List<PatientMonitorResult> patientMonitorResults = results
+                .stream()
+                .filter(patientMonitorResult -> changeDeviceIds.contains(patientMonitorResult.getDeviceId()))
+                .collect(Collectors.toList());
+
+        //输注切换
+        infusionHistoryService.update(new UpdateWrapper<BusInfusionHistoryEntity>().lambda()
+                .in(BusInfusionHistoryEntity::getId,patientMonitorResults
+                        .stream()
+                        .map(PatientMonitorResult::getInfusionId)
+                        .collect(Collectors.toList()))
+                .set(BusInfusionHistoryEntity::getTenantId,afterTenantId));
+
+
+        //结束临床
+        clinicService.update(new UpdateWrapper<BusClinicEntity>().lambda()
+                .in(BusClinicEntity::getId,patientMonitorResults
+                        .stream()
+                        .map(PatientMonitorResult::getClinicId)
+                        .collect(Collectors.toList()))
+                .set(BusClinicEntity::getFinished,true));
     }
 }

+ 4 - 0
nb-service/web-service/src/main/java/com/nb/web/service/bus/service/LocalBusInfusionHistoryService.java

@@ -322,6 +322,10 @@ public class LocalBusInfusionHistoryService extends BaseService<BusInfusionHisto
         return this.baseMapper.queryStatsEval(query);
     }
 
+    public List<CombineEvalResult> listStatsEval(CombineQuery query) {
+        return this.baseMapper.listStatsEval(query);
+    }
+
     public IPage<CombineEvalResult> pageStatsEval(CombineQuery query, Page<CombineResult> page) {
         return this.baseMapper.pageStatsEval(query,page);
     }

+ 26 - 1
nb-service/web-service/src/main/java/com/nb/web/service/bus/service/dto/CombineEvalResult.java

@@ -1,11 +1,12 @@
 package com.nb.web.service.bus.service.dto;
 
 
+import com.alibaba.excel.annotation.ExcelProperty;
 import io.swagger.annotations.ApiModelProperty;
 import lombok.Data;
 
 import java.math.BigDecimal;
-import java.sql.Date;
+import java.util.Date;
 
 /**
  * @author lifang
@@ -18,79 +19,103 @@ import java.sql.Date;
 public class CombineEvalResult extends CombineResult {
 
     @ApiModelProperty("评价时间")
+    @ExcelProperty("评价时间")
     private Date evalTime;
 
     @ApiModelProperty(value = "评价id")
     private String evalId;
 
     @ApiModelProperty(value = "评价人")
+    @ExcelProperty("评价人")
     private String evaluator;
 
     @ApiModelProperty(value = "静息疼痛")
+    @ExcelProperty("静息疼痛")
     private Integer statics;
 
     @ApiModelProperty(value = "活动疼痛")
+    @ExcelProperty("活动疼痛")
     private Integer activity;
 
     @ApiModelProperty(value = "镇静评分")
+    @ExcelProperty("镇静评分")
     private Integer calm;
 
     @ApiModelProperty(value = "左上肢")
+    @ExcelProperty("左上肢")
     private Integer leftArm;
 
     @ApiModelProperty(value = "左下肢")
+    @ExcelProperty("左下肢")
     private Integer leftLeg;
 
     @ApiModelProperty(value = "右上肢")
+    @ExcelProperty("右上肢")
     private Integer rightArm;
 
     @ApiModelProperty(value = "右下肢")
+    @ExcelProperty("右下肢")
     private Integer rightLeg;
 
     @ApiModelProperty(value = "恶心呕吐")
+    @ExcelProperty("恶心呕吐")
     private Integer nauseaVomit;
 
     @ApiModelProperty(value = "瘙痒")
+    @ExcelProperty("瘙痒")
     private Integer itch;
 
     @ApiModelProperty(value = "眩晕")
+    @ExcelProperty("眩晕")
     private Integer vertigo;
 
     @ApiModelProperty(value = "咽喉疼痛")
+    @ExcelProperty("咽喉疼痛")
     private Integer soreThroat;
 
     @ApiModelProperty(value = "尿潴留")
+    @ExcelProperty("尿潴留")
     private Integer uroschesis;
 
     @ApiModelProperty(value = "呼吸抑制")
+    @ExcelProperty("呼吸抑制")
     private Integer breathDepression;
 
     @ApiModelProperty(value = "声音嘶哑")
+    @ExcelProperty("声音嘶哑")
     private Integer hoarseness;
 
     @ApiModelProperty(value = "认知障碍")
+    @ExcelProperty("认知障碍")
     private Integer cognitionObstacle;
 
     @ApiModelProperty(value = "满意度")
+    @ExcelProperty("满意度")
     private Integer satisfaction;
 
 
     @ApiModelProperty(value = "收缩压")
+    @ExcelProperty("收缩压")
     private BigDecimal shrinkPressure;
 
 
     @ApiModelProperty(value = "舒张压")
+    @ExcelProperty("舒张压")
     private BigDecimal diastensPressure;
 
     @ApiModelProperty(value = "心率")
+    @ExcelProperty("心率")
     private BigDecimal heartRate;
 
     @ApiModelProperty(value = "胎心")
+    @ExcelProperty("胎心")
     private BigDecimal fetalHeartRate;
 
     @ApiModelProperty(value = "呼吸频率")
+    @ExcelProperty("呼吸频率")
     private BigDecimal breathRate;
 
     @ApiModelProperty(value = "血氧饱和度")
+    @ExcelProperty("血氧饱和度")
     private BigDecimal bloodOxygenSaturation;
 }

+ 99 - 0
nb-service/web-service/src/main/java/com/nb/web/service/bus/service/dto/CombineEvalResultExcelDTO.java

@@ -0,0 +1,99 @@
+package com.nb.web.service.bus.service.dto;
+
+import com.alibaba.excel.annotation.ExcelProperty;
+import com.alibaba.excel.converters.date.DateDateConverter;
+import lombok.Data;
+import java.io.Serializable;
+import java.math.BigDecimal;
+import java.util.Date;
+
+/**
+ * @ClassName : CombineEvalResultExcelDTO
+ * @Description :
+ * @Author : LF
+ * @Date: 2023年08月18日
+ */
+@Data
+public class CombineEvalResultExcelDTO implements Serializable {
+    @ExcelProperty(value = "患者姓名",order = 1)
+    private String patientName;
+
+    @ExcelProperty(value = "住院号",order = 2)
+    private String patientCode;
+
+    @ExcelProperty(value = "手术名称",order = 3)
+    private String surgeryName;
+
+    @ExcelProperty(value = "评价时间",order = 4,converter = DateDateConverter.class)
+    private Date evalTime;
+
+    @ExcelProperty(value = "评价人",order = 5)
+    private String evaluator;
+
+    @ExcelProperty(value = "静息疼痛",order = 6)
+    private Integer statics;
+
+    @ExcelProperty(value = "活动疼痛",order = 7)
+    private Integer activity;
+
+    @ExcelProperty(value = "镇静评分",order = 8)
+    private Integer calm;
+
+    @ExcelProperty(value = "左上肢",order = 9)
+    private Integer leftArm;
+
+    @ExcelProperty(value = "左下肢",order = 10)
+    private Integer leftLeg;
+
+    @ExcelProperty(value = "右上肢",order = 11)
+    private Integer rightArm;
+
+    @ExcelProperty(value = "右下肢",order = 12)
+    private Integer rightLeg;
+
+    @ExcelProperty(value = "恶心呕吐",order = 13)
+    private Integer nauseaVomit;
+
+    @ExcelProperty(value = "瘙痒",order = 14)
+    private Integer itch;
+
+    @ExcelProperty(value = "眩晕",order = 15)
+    private Integer vertigo;
+
+    @ExcelProperty(value = "咽喉疼痛",order = 16)
+    private Integer soreThroat;
+
+    @ExcelProperty(value = "尿潴留",order = 17)
+    private Integer uroschesis;
+
+    @ExcelProperty(value = "呼吸抑制",order = 18)
+    private Integer breathDepression;
+
+    @ExcelProperty(value = "声音嘶哑",order = 19)
+    private Integer hoarseness;
+
+    @ExcelProperty(value = "认知障碍",order = 20)
+    private Integer cognitionObstacle;
+
+    @ExcelProperty(value = "满意度",order = 21)
+    private Integer satisfaction;
+
+    @ExcelProperty(value = "收缩压",order = 22)
+    private BigDecimal shrinkPressure;
+
+
+    @ExcelProperty(value = "舒张压",order = 23)
+    private BigDecimal diastensPressure;
+
+    @ExcelProperty(value = "心率",order = 24)
+    private BigDecimal heartRate;
+
+    @ExcelProperty(value = "胎心",order = 25)
+    private BigDecimal fetalHeartRate;
+
+    @ExcelProperty(value = "呼吸频率",order = 26)
+    private BigDecimal breathRate;
+
+    @ExcelProperty(value = "血氧饱和度",order = 27)
+    private BigDecimal bloodOxygenSaturation;
+}

+ 81 - 0
nb-service/web-service/src/main/resources/mapper/bus/BusInfusionHistoryMapper.xml

@@ -315,6 +315,22 @@
         i.id as infusion_id,
         i.last_upload_time as last_upload_time,
         i.start_time as infusion_start_time,
+        c.patient_code as patient_code,
+        c.ward as ward,
+        c.bed_no as bed_no,
+        c.surgery_name as surgery_name,
+        c.asa as asa,
+        c.patient_gender as patient_gender,
+        c.`patient_name` as patient_name,
+        c.`patient_age` as patient_age,
+        c.weight as weight,
+        c.`height` as height,
+        c.ana_doctor as ana_doctor,
+        c.ana_type as ana_type,
+        c.anal_type as anal_type,
+        c.surgery_doctor as surgery_doctor,
+        c.formula as formula,
+        c.entrust as entrust,
         e.evaluate_time as evaluate_time,
         e.statics as statics,
         e.activity as activity,
@@ -354,6 +370,7 @@
             ) as a
             on a.infusion_id = i.id
         </if>
+        order by e.evaluate_time desc
     </select>
 
 
@@ -421,6 +438,70 @@
         order by e.evaluate_time desc
     </select>
 
+    <select id="listStatsEval" resultMap="combineEvalResult">
+        select
+        i.id as infusion_id,
+        i.last_upload_time as last_upload_time,
+        i.start_time as infusion_start_time,
+        c.patient_code as patient_code,
+        c.ward as ward,
+        c.bed_no as bed_no,
+        c.surgery_name as surgery_name,
+        c.asa as asa,
+        c.patient_gender as patient_gender,
+        c.`patient_name` as patient_name,
+        c.`patient_age` as patient_age,
+        c.weight as weight,
+        c.`height` as height,
+        c.ana_doctor as ana_doctor,
+        c.ana_type as ana_type,
+        c.anal_type as anal_type,
+        c.surgery_doctor as surgery_doctor,
+        c.formula as formula,
+        c.entrust as entrust,
+        e.evaluate_time as evaluate_time,
+        e.statics as statics,
+        e.activity as activity,
+        e.calm as calm,
+        e.left_arm as left_arm,
+        e.left_leg as left_leg,
+        e.right_arm as right_arm,
+        e.right_leg as right_leg,
+        e.nausea_vomit as nausea_vomit,
+        e.itch as itch,
+        e.vertigo as vertigo,
+        e.sore_throat as sore_throat,
+        e.uroschesis as uroschesis,
+        e.breath_depression as breath_depression,
+        e.hoarseness as hoarseness,
+        e.cognition_obstacle as cognition_obstacle,
+        e.satisfaction as satisfaction,
+        e.shrink_pressure as shrink_pressure,
+        e.diastens_pressure as diastens_pressure,
+        e.heart_rate as heart_rate,
+        e.fetal_heart_rate as fetal_heart_rate,
+        e.breath_rate as breath_rate,
+        e.blood_oxygen_saturation as blood_oxygen_saturation,
+        e.id as eval_id,
+        e.evaluator as evaluator
+        from (
+        <include refid="commonInfusionQuery"/>
+        ) as i
+        JOIN (
+        <include refid="commonClinicQuery"/>
+        ) as c
+        on i.clinic_id=c.id
+        join (select * from bus_evaluation) as e
+        on e.infusion_id=i.id
+        <if test="query.warns != null or query.deviceAlarm != null">
+            JOIN (
+            <include refid="commonDeviceAlarmQuery"/>
+            ) as a
+            on a.infusion_id = i.id
+        </if>
+        order by e.evaluate_time desc
+    </select>
+
     <select id="queryStatsHistory" resultMap="queryStatsHistory">
         SELECT i.id AS infusion_id,
         c.id as clinic_id,